/* ────────────────────────────────────────────────
   POLYMESH · base.css — tokens, reset, primitives
   ──────────────────────────────────────────────── */

/* ═══════════ THEME TOKENS ═══════════
   DARK IS THE PRIMARY DESIGN and lives in :root, so it applies to every page
   (landing, auth, studio) with no opt-in class and no media query. The canvas is a
   cool near-black rather than pure black — #000 reads harsher and flattens the
   surface hierarchy. Light is an opt-in alternative the user picks in their profile:
   it only engages when `data-theme="light"` is explicitly set on <html>. There is
   deliberately no prefers-color-scheme rule anywhere — the app must never flip itself
   based on the OS.

   The visual system is restraint-first: structure comes from spacing, then tonal
   surface shifts, then typography, and only then borders. Accent is scarce by design —
   if everything is indigo, nothing reads as important.

   Anything that needs to differ between themes must go through a token here.
   Hardcoding a literal in a component rule is what broke this the first time round;
   if you need a new one, add a token rather than a one-off value. */
:root {
  /* surfaces — bg0 is the page canvas, the rest are progressively raised.
     The steps are deliberately tiny: the hierarchy should be felt before it's noticed. */
  --bg0: #08090a;
  --bg1: #0f1011;
  --bg2: #141516;
  --bg3: #1c1c1f;
  --glass: rgba(255, 255, 255, 0.025);
  --glass-2: rgba(255, 255, 255, 0.05);

  /* strokes — hairlines, never outlines */
  --line: rgba(255, 255, 255, 0.08);
  --line-2: #34343a;

  /* ink — four levels, no boxes needed to separate them */
  --ink: #f7f8f8;          /* headlines, primary UI */
  --ink-2: #d0d4da;        /* supporting copy */
  --ink-dim: #8a8f98;      /* metadata, labels, body */
  --ink-faint: #62666d;    /* figure labels, timestamps, inactive */

  /* brand — muted indigo, used for state and focus, not decoration */
  --brand: #5e6ad2;
  --brand-2: #7b86e8;
  /* Gradients are deliberately near-flat: the spec's dominant failure mode is a
     violet→cyan sweep on every surface. Anything still painting with --grad gets a
     restrained indigo instead of a rainbow. */
  --grad: linear-gradient(100deg, #5e6ad2 0%, #6b74d8 100%);
  --grad-soft: linear-gradient(100deg, rgba(94, 106, 210, 0.14), rgba(94, 106, 210, 0.07));
  --accent-ink: #8b95e8;
  /* selected/active pills (rail nav, segmented controls, sub-tabs) */
  --brand-soft: rgba(94, 106, 210, 0.16);
  --on-brand: #c4caf3;
  --brand-edge: rgba(94, 106, 210, 0.42);

  /* floating chrome — menus, toasts, sticky nav, viewport HUDs */
  --overlay: rgba(15, 16, 17, 0.92);
  --overlay-line: rgba(255, 255, 255, 0.1);
  --overlay-ink: #f7f8f8;
  --scrim: rgba(4, 5, 6, 0.72);            /* modal backdrop */

  /* elevation — the system is low-shadow. Depth comes from tonal separation and
     hairlines; only menus and modals earn a real shadow, and then to signal
     elevation rather than to decorate. */
  --shadow-sm: none;
  --shadow-md: none;
  --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.4);
  --shadow-xl: 0 20px 60px rgba(0, 0, 0, 0.35);

  /* the recessed stage a 3D canvas or generated image sits in */
  --viewport: #0b0c0d;
  --viewport-2: #0f1011;

  /* highlight sweep used by .busy shimmers — must contrast with the surface beneath */
  --sheen: rgba(255, 255, 255, 0.06);

  /* status — these belong to product UI, not to the marketing canvas */
  --ok: #27a644;
  --warn: #f2a93b;
  --err: #ff5c5c;
  --err-ink: #ff8f8f;
  --err-wash: rgba(255, 92, 92, 0.09);

  /* type — one family carries the whole system. Hierarchy comes from size, weight
     and tracking, not from mixing display faces. */
  --font-display: Inter, 'Inter Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-body: Inter, 'Inter Variable', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', 'SFMono-Regular', Consolas, monospace;

  /* shape — tight. Large radii read as consumer software; this should read as a tool. */
  --r-sm: 6px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-xl: 16px;

  /* motion */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.22, 1, 0.36, 1);
}

/* Light — opt-in only. Set on <html> (not <body>) so the pre-paint script in each
   page's <head> can flip it before <body> exists, avoiding a flash of the wrong theme. */
html[data-theme="light"] {
  --bg0: #ffffff;
  --bg1: #fbfbfc;
  --bg2: #f4f5f6;
  --bg3: #eceef0;
  --glass: rgba(8, 9, 10, 0.025);
  --glass-2: rgba(8, 9, 10, 0.05);
  --line: rgba(8, 9, 10, 0.1);
  --line-2: #d7d9dd;
  --ink: #08090a;
  --ink-2: #3c4149;
  --ink-dim: #6b7079;
  --ink-faint: #9199a1;
  --accent-ink: #4c58c0;
  --brand-soft: rgba(94, 106, 210, 0.1);
  --on-brand: #3b46ad;
  --overlay: rgba(255, 255, 255, 0.94);
  --overlay-line: rgba(8, 9, 10, 0.12);
  --overlay-ink: #08090a;
  --scrim: rgba(8, 9, 10, 0.4);
  --shadow-lg: 0 16px 48px rgba(8, 9, 10, 0.12);
  --shadow-xl: 0 20px 60px rgba(8, 9, 10, 0.1);
  --viewport: #f4f5f6;
  --viewport-2: #eceef0;
  --sheen: rgba(8, 9, 10, 0.05);
  --err-ink: #c22a2a;
}

*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}

html { scroll-behavior: smooth; font-family: var(--font-body); }

body {
  font-family: var(--font-body);
  background: var(--bg0);
  color: var(--ink);
  font-size: 15px;
  line-height: 1.6;
  letter-spacing: -0.01em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* Headings carry the hierarchy through size and tracking rather than weight — 510 is
   the whole trick, and it is why 700/800 headlines look wrong in this system. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 510;
  letter-spacing: -0.03em;
  line-height: 1.1;
  color: var(--ink);
}

/* The UA's `[hidden] { display: none }` loses to any author rule that sets `display`
   (e.g. `.modal-back { display: flex }`), so hiding via the attribute silently fails.
   This makes the attribute authoritative everywhere. */
[hidden] { display: none !important; }

img, svg, canvas { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }
b, strong { font-weight: 500; color: var(--ink); }

::selection { background: rgba(94, 106, 210, 0.35); color: var(--ink); }

/* Visible but understated — the accent earns its keep on focus rings. */
:focus-visible {
  outline: 2px solid rgba(94, 106, 210, 0.55);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #23252a; border-radius: 999px; border: 3px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--line-2); background-clip: content-box; }
* { scrollbar-color: #23252a transparent; scrollbar-width: thin; }

/* The grain overlay is retired: the product UI supplies all the texture this design
   needs, and a visible noise layer fights the near-black canvas. Kept as an inert
   no-op so the existing .grain elements in the markup cost nothing. */
.grain { display: none; }

/* ── buttons ──
   Compact and confident. Oversized marketing buttons are an anti-pattern here: the
   primary action is an inverse chip, not a banner. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 36px;
  padding: 0 14px;
  border-radius: var(--r-md);
  font-weight: 500; font-size: 14px; line-height: 20px; letter-spacing: -0.01em;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease, opacity 150ms ease;
  white-space: nowrap;
  position: relative;
}
.btn:active { transform: translateY(1px); }

/* Primary is inverse — light chip on the dark canvas. It reads as the single most
   important control on the page precisely because nothing else is bright. */
.btn-primary {
  background: var(--ink);
  color: var(--bg0);
}
.btn-primary:hover { background: #ffffff; }
html[data-theme="light"] .btn-primary:hover { background: #23252a; }

.btn-ghost {
  background: var(--bg1);
  border: 1px solid var(--line);
  color: var(--ink);
}
.btn-ghost:hover { background: var(--bg2); border-color: var(--line-2); }

/* text-only tertiary action */
.btn-text {
  background: transparent;
  color: var(--ink-dim);
  padding: 0 6px;
}
.btn-text:hover { color: var(--ink); }

.btn-sm { height: 30px; padding: 0 11px; font-size: 13px; }
.btn-lg { height: 40px; padding: 0 18px; font-size: 14px; }

/* ── chips / badges ── */
.chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  background: var(--bg1);
  border: 1px solid var(--line);
  font-size: 12px; font-weight: 500; letter-spacing: -0.01em;
  color: var(--ink-dim);
}
.chip .dot {
  width: 5px; height: 5px; border-radius: 50%;
  background: var(--brand);
  animation: pulse-dot 2.2s ease infinite;
}
@keyframes pulse-dot { 0%,100% { opacity: 0.45; } 50% { opacity: 1; } }

/* ── editorial metadata ──
   Small uppercase figure labels ("FIG 0.2", "1.0 INTAKE") are what make the page read
   as a technical document rather than a marketing brochure. Keep them quiet. */
.eyebrow {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ── logo wordmark ── */
.logo-ai { font-style: normal; color: var(--ink-dim); }

/* ── text helpers ── */
/* Gradient headline text is an explicit anti-pattern in this system — the class stays
   so existing markup keeps rendering, but it now resolves to plain primary ink. */
.grad-text {
  background: none;
  -webkit-background-clip: initial;
  background-clip: initial;
  color: var(--ink);
}
.mono { font-family: var(--font-mono); }

/* ── skeleton loaders ──
   Add .skeleton to any box whose content is still loading; remove it when the real content
   lands. The element must already have its final size (aspect-ratio / fixed dims) so the
   swap causes no layout shift — the skeleton is a paint effect, never a sizing mechanism.
   Low-contrast by design: loading should feel integrated, never like a blocking event. */
.skeleton { position: relative; overflow: hidden; background: var(--glass-2); }
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 32%, var(--sheen) 50%, transparent 68%);
  transform: translateX(-100%);
  animation: skel-sweep 1.5s ease-in-out infinite;
}
@keyframes skel-sweep { to { transform: translateX(100%); } }
/* text-shaped placeholder bar (profile numbers, labels awaiting data) */
.skel-line { display: inline-block; width: 3.5em; height: 1em; border-radius: var(--r-xs, 4px); vertical-align: middle; }
/* content fades in where it replaces a skeleton, so the swap reads as a soft transition */
.skel-fade { animation: skel-fade-in 0.4s ease; }
@keyframes skel-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ── reveal on scroll ──
   One restrained entrance: opacity plus a short lift and a hair of scale. Stacking
   blur/rotation/bounce on top is what makes a page feel slow even when it is fast. */
.rv {
  opacity: 0;
  transform: translateY(24px) scale(0.99);
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out);
  transition-delay: var(--rv-delay, 0s);
}
.rv.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
  html { scroll-behavior: auto; }
}
