/*
 * design.css — FROZEN platform design system. The agent cannot write this file;
 * frozenSync re-copies it into every app on every build, so a fix here reaches
 * the whole v2 fleet on each app's next rebuild.
 *
 * THREE GENRES, ONE SYSTEM
 * The token layer below has three treatments of the same structure:
 *   (default)             app / dashboard — warm paper, cobalt accent
 *   <body data-genre="landing">  marketing page — same palette, editorial scale
 *   <body data-genre="game">     arcade — near-black, violet/mint/amber HUD
 * A genre is chosen by setting data-genre on <body> in views/layout.ts. Every
 * component below reads tokens, so switching genre restyles the whole app
 * without touching a single component rule.
 *
 * THE TOKEN CONTRACT
 * Every brand-able value is consumed as var(--token) and every token's DEFAULT
 * is defined here. public/theme.css is loaded AFTER this file and is agent-
 * writable, so an app brands itself by re-declaring only the tokens it needs.
 * Defaults live here, not there, on purpose: theme.css is writable, and a
 * broken or emptied theme must degrade to this look rather than leave the
 * system with undefined values and an unstyled app.
 *
 * CSP: generated apps run under script-src 'self' and default-src 'self'.
 * No @import, no external url(), no CDN, no data: URI fonts (data: is allowed
 * in img-src only). A webfont must be a self-hosted file under public/ that is
 * ALSO registered in the frozen src/lib/assets.ts allowlist, or it 404s. The
 * font stacks below name Public Sans and IBM Plex Mono first and fall back to
 * system faces, so they upgrade automatically once those files are hosted.
 */

/* ================================================================== *
 * FONTS — self-hosted, latin subset. Both are SIL OFL 1.1 (see
 * public/fonts/LICENSE.txt). They must be served from our own origin:
 * default-src 'self' blocks every font CDN, and a data: URI would be blocked
 * too (data: is allowed in img-src only). Each file also needs an entry in
 * the frozen src/lib/assets.ts allowlist or it 404s and the stack silently
 * falls back to system faces.
 * ================================================================== */

@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/public-sans-400.woff2") format("woff2");
}

@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/public-sans-500.woff2") format("woff2");
}

@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/static/fonts/public-sans-600.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-400.woff2") format("woff2");
}

@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/static/fonts/ibm-plex-mono-500.woff2") format("woff2");
}

/* ================================================================== *
 * TOKENS
 * ================================================================== */

:root {
  /* surfaces */
  --canvas: #f7f6f3;
  --surface: #fbfaf8;
  --card: #ffffff;
  --border: #e7e3db;
  --border-strong: #d8d3c8;

  /* ink */
  --ink: #16151a;
  --muted: #6f6b62;
  --icon: #8a8579;

  /* accent + status */
  --accent: #2b44e0;
  --accent-soft: #eef0fe;
  --accent-ink: #ffffff;
  --success: #17845a;
  --success-soft: #e7f4ee;
  --warning: #b45309;
  --warning-soft: #fdf0e3;
  --danger: #b42318;
  --danger-soft: #fdeceb;

  /* type */
  --font-sans: "Public Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* shape — 6 small controls, 8 inputs/nav, 12 cards */
  --radius-sm: 6px;
  --radius: 8px;
  --radius-lg: 12px;

  /* Elevation stays HAIRLINE by rule — the flat card shadow is the spec'd one
     and must never get heavier. Depth comes from a second, wider and softer
     ambient layer at very low alpha, not from a darker drop: that is the
     difference between "raised" and "cheap". --shadow-raised is for a surface
     that is deliberately lifted (hover, popover), never the resting card. */
  --shadow: 0 1px 2px rgba(22, 21, 26, 0.04);
  --shadow-raised:
    0 1px 2px rgba(22, 21, 26, 0.05),
    0 8px 24px -8px rgba(22, 21, 26, 0.08);

  /* Interaction. A focus ring that is the accent at low alpha reads as part of
     the system rather than an OS artefact; the tint is the hover surface, so a
     hover never has to invent a colour. */
  --ring: 0 0 0 3px var(--accent-soft);
  --tint: color-mix(in srgb, var(--ink) 3%, transparent);

  /* Motion. One duration and one easing for the whole system — inconsistent
     timing is the most common reason an otherwise clean UI feels cheap. The
     curve settles rather than bounces. */
  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  --dur: 150ms;

  /* Spacing rhythm — a 4px scale, so padding across components is related
     rather than hand-picked per rule. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  /* Optical tracking. Type tightens as it grows: at display sizes default
     spacing reads loose and amateur, at small sizes it needs to stay open. */
  --tracking-display: -0.038em;
  --tracking-head: -0.03em;
  --tracking-tight: -0.02em;
  --tracking-caps: 0.06em;

  /* layout */
  --sidebar-w: 244px;
  --row-h: 46px;
  --content-max: 1180px;
}

/* Landing: same palette, editorial scale, alternating section bands. */
body[data-genre="landing"] {
  --canvas: #f7f6f3;
  --surface: #fbfaf8;
  --content-max: 1120px;
}

/* Game: inverted shell, mono HUD. */
body[data-genre="game"] {
  --canvas: #0d0c11;
  --surface: #131118;
  --card: #1b1922;
  --border: #2f2c39;
  --border-strong: #3b3747;
  --ink: #f4f2f7;
  --muted: #7b7688;
  --icon: #7b7688;
  --accent: #7c5cff;
  --accent-soft: #1f1b33;
  --accent-ink: #ffffff;
  --success: #7cf7c1;
  --success-soft: #14261f;
  --warning: #ffd166;
  --warning-soft: #2a2318;
  --shadow: none;
}

/* ================================================================== *
 * BASE
 * ================================================================== */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  background: var(--canvas);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}

/* A bare <a> is usually STRUCTURAL — nav, a card that links, a brand mark —
   and those must not read as body-copy links. Left at the browser default they
   came out underlined and, once tinted, painted every nav item accent-blue,
   which breaks the one-accent-per-page rule (the accent belongs to the primary
   CTA alone). So the base link inherits its surroundings and only reveals
   itself on hover; a link inside PROSE opts back into looking like a link.
   Anything carrying .btn keeps its own colour — a class beats an element. */
a {
  color: inherit;
  text-decoration: none;
}

a:hover {
  color: var(--accent);
}

p a,
.empty a,
.share-sub a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}

/* Every number, ID, badge and metric is mono — this is what stops a data app
   from reading like a document. */
.num,
td.num,
th.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* ================================================================== *
 * APP SHELL — sidebar + topbar + content
 * ================================================================== */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) 1fr;
  min-height: 100vh;
}

.sidebar {
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.sidebar .brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 600;
  padding: 4px 8px 0;
}

.sidebar .brand .mark {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
  font-size: 13px;
}

.navgroup {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.navgroup > .label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--icon);
  padding: 6px 8px;
}

.navgroup a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  border-radius: var(--radius);
  color: var(--ink);
  text-decoration: none;
  font-weight: 500;
  box-shadow: inset 0 0 0 1px transparent;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease);
}

.navgroup a:hover {
  background: var(--tint);
}

.navgroup a[aria-current="page"] {
  background: var(--accent-soft);
  color: var(--accent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 18%, transparent);
}

.navgroup a .count {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
}

.sidebar .foot {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 22px;
  background: var(--card);
  border-bottom: 1px solid var(--border);
}

.topbar .spacer {
  flex: 1;
}

.crumbs {
  color: var(--muted);
}

.crumbs b {
  color: var(--ink);
  font-weight: 500;
}

.content {
  padding: 22px;
  max-width: var(--content-max);
}

.page-head {
  margin-bottom: 18px;
}

/* ================================================================== *
 * TYPE
 * ================================================================== */

h1 {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  margin: 0 0 4px;
}

h2 {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 4px;
}

.muted {
  color: var(--muted);
}

.small {
  font-size: 12px;
}

/* ================================================================== *
 * CARD
 * ================================================================== */

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--border);
}

.card-head h2 {
  margin: 0;
}

.card-head .spacer {
  flex: 1;
}

.card-body {
  padding: 16px;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

/* Stat tile — label, mono figure, delta chip. */
.stat {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 14px 16px;
}

.stat .label {
  color: var(--muted);
  font-size: 12px;
  margin-bottom: 6px;
}

.stat .figure {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  display: flex;
  align-items: baseline;
  gap: 8px;
}

/* ================================================================== *
 * BUTTONS — primary is solid ink, never a coloured gradient
 * ================================================================== */

button,
.btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 13px;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: var(--ink);
  color: var(--card);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  line-height: 1.3;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}

button,
.btn {
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
    box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}

/* Fading a button to 90% opacity fades it into the PAGE — the label goes grey
   with it. Mixing the surface toward ink keeps the text at full strength. */
button:hover,
.btn:hover {
  background: color-mix(in srgb, var(--ink) 88%, #fff);
}

button:active,
.btn:active {
  transform: translateY(0.5px);
}

button:focus-visible,
.btn:focus-visible,
a:focus-visible {
  outline: none;
  box-shadow: var(--ring);
}

button.ghost,
.btn.ghost {
  background: var(--card);
  color: var(--ink);
  border-color: var(--border);
}

button.ghost:hover,
.btn.ghost:hover {
  background: var(--tint);
  border-color: var(--border-strong);
}

button.accent,
.btn.accent {
  background: var(--accent);
  color: var(--accent-ink);
}

button.accent:hover,
.btn.accent:hover {
  background: color-mix(in srgb, var(--accent) 88%, #000);
}

button.danger,
.btn.danger {
  background: var(--card);
  color: var(--danger);
  border-color: var(--border);
}

button.sm,
.btn.sm {
  padding: 4px 9px;
  font-size: 12px;
}

button:disabled,
.btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.actions {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Segmented control (30 days / Quarter / Year). */
.segmented {
  display: inline-flex;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 2px;
  gap: 2px;
}

.segmented a,
.segmented button {
  background: transparent;
  color: var(--muted);
  border: 0;
  padding: 5px 11px;
  border-radius: var(--radius-sm);
  font-size: 12px;
  font-weight: 500;
  text-decoration: none;
}

.segmented [aria-current="true"] {
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--shadow);
}

/* ================================================================== *
 * FORMS
 * ================================================================== */

/* Works both ways round: <label class="field"><span>Name</span><input></label>
   (no id/for needed) and <div class="field"><label>Name</label><input></div>. */
.field {
  display: block;
  margin-bottom: 14px;
}

.field > span,
.field > label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  margin-bottom: 5px;
}

input,
textarea,
select {
  width: 100%;
  padding: 8px 11px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--card);
  color: var(--ink);
  font-family: inherit;
  font-size: 13px;
}

input::placeholder,
textarea::placeholder {
  color: var(--icon);
}

input,
textarea,
select {
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}

input:hover,
textarea:hover,
select:hover {
  border-color: var(--border-strong);
}

input:focus,
textarea:focus,
select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--ring);
}

.search {
  position: relative;
  min-width: 260px;
}

.search input {
  border-radius: var(--radius);
  background: var(--surface);
}

/* ================================================================== *
 * TABLE — 46px rows, mono IDs and amounts
 * ================================================================== */

.tablewrap {
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--muted);
  padding: 9px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
  white-space: nowrap;
}

td {
  height: var(--row-h);
  padding: 0 16px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

tbody tr:last-child td {
  border-bottom: 0;
}

tbody tr {
  transition: background var(--dur) var(--ease);
}

tbody tr:hover td {
  background: var(--tint);
}

th.right,
td.right {
  text-align: right;
}

/* Avatar chip used in identity cells. */
.avatar {
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-family: var(--font-mono);
  font-size: 10px;
  font-weight: 600;
}

.cell {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* Plain list — the non-tabular row stack (run logs, simple item lists). */
.list {
  list-style: none;
  padding: 0;
  margin: 0;
}

/* The inset lives on the ROW, not on the list box, for two reasons. A list is
   usually dropped straight into a .card, and `.list { padding: 0 }` (which kills
   the browser's 40px ul indent) is declared after .card-body — equal specificity,
   later wins — so `class="list card-body"` silently lost ALL its padding and the
   rows ran to the card edges while the header stayed inset (2026-08-08). Putting
   it here makes both `list` and `list card-body` render identically, with no
   doubled padding. It also lets the divider span the full card width while the
   content stays aligned, which is what the reference tables do. */
.list > li {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: var(--row-h);
  padding: 10px 16px;
  border-top: 1px solid var(--border);
}

.list > li:first-child {
  border-top: 0;
}

/* ================================================================== *
 * BADGE — mono, dot + label
 * ================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--surface);
  border: 1px solid var(--border);
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
}

.badge::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
}

.badge.plain::before {
  display: none;
}

.badge.success {
  background: var(--success-soft);
  border-color: transparent;
  color: var(--success);
}

.badge.warning {
  background: var(--warning-soft);
  border-color: transparent;
  color: var(--warning);
}

.badge.danger {
  background: var(--danger-soft);
  border-color: transparent;
  color: var(--danger);
}

.badge.accent {
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent);
}

/* ================================================================== *
 * EMPTY STATE — graphic + one-line reason + primary action.
 * Never a bare zero and never a blank table.
 * ================================================================== */

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 10px;
  padding: 46px 24px;
}

.empty .graphic {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-lg);
  background: var(--accent-soft);
  color: var(--accent);
}

.empty h2 {
  margin: 0;
}

.empty p {
  margin: 0;
  color: var(--muted);
  max-width: 42ch;
}

/* ================================================================== *
 * LANDING
 * ================================================================== */

body[data-genre="landing"] .band {
  background: var(--canvas);
  padding: 72px 22px;
}

body[data-genre="landing"] .band:nth-child(even) {
  background: var(--surface);
}

body[data-genre="landing"] .band > .inner {
  max-width: var(--content-max);
  margin: 0 auto;
}

.hero {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 54px;
  align-items: center;
}

.hero h1 {
  font-size: clamp(40px, 5vw, 64px);
  font-weight: 600;
  line-height: 1.04;
  letter-spacing: var(--tracking-display);
  margin: 0 0 18px;
}

/* The one coloured phrase in a headline. */
.hero h1 em,
.display em {
  font-style: normal;
  color: var(--accent);
}

.hero p {
  font-size: 16px;
  color: var(--muted);
  max-width: 46ch;
  margin: 0 0 24px;
}

.display {
  font-size: clamp(26px, 3vw, 34px);
  font-weight: 600;
  letter-spacing: var(--tracking-head);
  line-height: 1.15;
  margin: 0 0 10px;
}

.eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent);
  margin-bottom: 10px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--card);
  border: 1px solid var(--border);
  font-size: 12px;
  margin-bottom: 22px;
}

/* Exactly one dark card per landing page, for contrast. */
.card.invert {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--card);
}

.card.invert .muted,
.card.invert p {
  color: color-mix(in srgb, var(--card) 68%, transparent);
}

.steps {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

.steps .card {
  padding: 18px;
}

.steps .n {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 10px;
}

/* ================================================================== *
 * GAME
 * ================================================================== */

body[data-genre="game"] .hud {
  display: flex;
  align-items: stretch;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  font-family: var(--font-mono);
}

body[data-genre="game"] .hud .slot {
  padding: 9px 18px;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 3px;
}

body[data-genre="game"] .hud .slot .label {
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

body[data-genre="game"] .hud .slot .value {
  font-size: 19px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

body[data-genre="game"] .hud .spacer {
  flex: 1;
  border-right: 0;
}

/* Radial glow + masked grid, never flat black. */
.playfield {
  position: relative;
  background:
    radial-gradient(ellipse at 50% 40%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 62%),
    linear-gradient(var(--border) 1px, transparent 1px) 0 0 / 40px 40px,
    linear-gradient(90deg, var(--border) 1px, transparent 1px) 0 0 / 40px 40px,
    var(--canvas);
  -webkit-mask-image: radial-gradient(ellipse at 50% 45%, #000 55%, transparent 100%);
  mask-image: radial-gradient(ellipse at 50% 45%, #000 55%, transparent 100%);
  min-height: 420px;
}

/* Entities glow in their own colour. */
.glow {
  filter: drop-shadow(0 0 6px currentColor);
}

.keycap {
  display: inline-grid;
  place-items: center;
  min-width: 22px;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  background: var(--card);
  border: 1px solid var(--border);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--muted);
}

/* Pause / game-over overlay. */
.overlay {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(9, 8, 12, 0.72);
}

.overlay .panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 26px;
  text-align: center;
  min-width: 300px;
}

/* ================================================================== *
 * UTILITIES
 * ================================================================== */

.row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.grow {
  flex: 1;
}

.right {
  text-align: right;
}

/* ================================================================== *
 * RESPONSIVE — the sidebar folds above the content on narrow screens.
 * ================================================================== */

@media (max-width: 860px) {
  .shell {
    grid-template-columns: 1fr;
  }

  .sidebar {
    border-right: 0;
    border-bottom: 1px solid var(--border);
  }

  .sidebar .foot {
    margin-top: 0;
  }

  .hero {
    grid-template-columns: 1fr;
    gap: 30px;
  }
}
