/* ============================================================================
 * theme-tokens.css - FAUCETRIX design system tokens (plan SEC01, Slab 1)
 * ----------------------------------------------------------------------------
 * First-party, no deps. Drives dark + light via the existing
 * <html data-theme="dark|light"> resolver (theme.js). Custom --ct-primary /
 * --ct-accent stay inline from PHP Config, so admin branding wins.
 * Presentation-layer only.
 * ========================================================================== */

/* ---------------------------------------------------------------------------
 * THEME HANDOFF — read this before adding a colour or a transition.
 *
 * The old approach transitioned `background`/`color`/`border-color` on a list
 * of elements (html, body, .card, .panel, button, input, select, textarea) at
 * --fade. That cannot look synchronised: the components in home-fx.css declare
 * their own 0.15s / 0.18s / 0.2s / 0.25s / 0.35s colour transitions, so on a
 * theme switch every element ran its own clock and the page visibly arrived in
 * stages. ~20 competing durations for one colour change.
 *
 * Instead the palette itself is animated. Registering each colour token with
 * @property makes it interpolable, so ONE transition on <html> animates the
 * inherited custom property and every element that reads var(--bg) / var(--lime)
 * / ... interpolates the same value in the same frame. There is a single clock,
 * so the page cannot desynchronise, and elements that had no transition at all
 * (sidebar, topbar, tables, badges) now animate with everything else instead of
 * snapping.
 *
 * The .theme-switching lock below suppresses the components' own hover
 * transitions for exactly the length of the switch. Without it a button whose
 * background comes from an animating token would smooth that token a second
 * time on its own 0.2s clock and visibly trail the rest of the page.
 *
 * Consequence to respect: do NOT add a colour transition to a component and
 * expect it to cover a theme switch. It only needs to cover hover/press.
 * ------------------------------------------------------------------------- */
@property --bg { syntax: '<color>'; inherits: true; initial-value: #070a0b; }
@property --surface { syntax: '<color>'; inherits: true; initial-value: #0d1512; }
@property --surface-2 { syntax: '<color>'; inherits: true; initial-value: #121c18; }
@property --surface-border { syntax: '<color>'; inherits: true; initial-value: rgba(184, 255, 56, 0.16); }
@property --lime { syntax: '<color>'; inherits: true; initial-value: #b8ff38; }
@property --mint { syntax: '<color>'; inherits: true; initial-value: #7fffd5; }
@property --gold { syntax: '<color>'; inherits: true; initial-value: #ddbb63; }
@property --magenta { syntax: '<color>'; inherits: true; initial-value: #ff4fa3; }
@property --text-1 { syntax: '<color>'; inherits: true; initial-value: #e9fbef; }
@property --text-2 { syntax: '<color>'; inherits: true; initial-value: #8ca79b; }
@property --text-3 { syntax: '<color>'; inherits: true; initial-value: #5b726b; }

html {
  --font-display: 'Space Grotesk', 'Inter', system-ui, sans-serif;
  --font-body: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* theme-agnostic base */
  --ct-primary: #00ff66;
  --ct-accent: #0d1117;
  --radius: 0.625rem;
  --radius-sm: 0.375rem;
  --edge: 1px solid var(--surface-border);
  --shadow-card: 0 4px 20px -6px rgba(0, 0, 0, 0.45);
}

/* ---------------------------------------------------------------------------
 * MOTION TOKENS
 * Single source of truth for easing and duration. Before this layer the
 * stylesheets carried ~21 hand-typed timings and two near-identical
 * cubic-beziers (cubic-bezier(0.22,1,0.36,1) appeared 5x and is only a hair
 * off the strong ease-out below), which is how two adjacent controls end up
 * finishing at different moments. Reference values: .agents/skills/
 * improve-animations/AUDIT.md sections 2 and 7.
 *
 * Easing decision order (do not guess):
 *   entering / exiting  -> --ease-out
 *   moving on screen    -> --ease-in-out
 *   hover / colour      -> --ease-ui
 *   constant motion     -> linear (marquee, progress)
 *   `ease-in` is never correct on UI: it starts slow, delaying the exact
 *   moment the user is watching.
 * ------------------------------------------------------------------------- */
html {
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);      /* strong ease-out, UI default */
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);  /* on-screen movement + morphs */
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);   /* iOS-like drawer curve */
  --ease-ui: ease;                                 /* hover / colour change only */

  /* Duration budget. UI motion stays under 300ms; only marketing and
     first-time/celebration moments are allowed past it. */
  --dur-press: 150ms;   /* button press feedback */
  --dur-pop: 180ms;     /* tooltips, small popovers */
  --dur-ui: 200ms;      /* generic control state change */
  --dur-menu: 250ms;    /* dropdowns, selects */
  --dur-modal: 300ms;   /* modals, drawers */
  --dur-panel: 350ms;   /* large panels, cards lifting */
  --dur-reveal: 520ms;  /* pack flip, reveal sequences (marketing/rare) */

  /* Kept for compatibility with existing call sites. --fade covers colour
     changes, which the audit says should use --ease-ui, so the easing is
     expressed explicitly rather than silently strengthened. */
  --fade: var(--dur-modal) var(--ease-ui);
  --speed-reveal: 450ms var(--ease-out);
}

/* ---------------- DARK (native default) ---------------- */
html,
html[data-theme='dark'] {
  --bg: #070a0b;
  --surface: #0d1512;
  --surface-2: #121c18;
  /* fx tokens follow the DB theme color — --ct-primary / --ct-accent are set
     inline on <html> from Config by the guest, user and admin headers, so the
     accent color chosen in admin Settings drives the whole app (Slab 3 rule). */
  --surface-border: color-mix(in srgb, var(--ct-primary, #b8ff38) 16%, transparent);
  --lime: var(--ct-primary, #b8ff38);
  --mint: color-mix(in srgb, var(--ct-primary, #4df5c5) 72%, #ffffff);
  --gold: #ddbb63;
  --magenta: #ff4fa3;
  --text-1: #e9fbef;
  --text-2: #8ca79b;
  --text-3: #5b726b;
  color-scheme: dark;
  background: var(--bg);
  color: var(--text-1);
}

/* ---------------- LIGHT ---------------- */
html[data-theme='light'] {
  --bg: #f4fff5;
  --surface: #effbf1;
  --surface-2: #e3f5e8;
  --surface-border: color-mix(in srgb, var(--ct-primary, #00ff66) 20%, transparent);
  --lime: var(--ct-primary, #00ff66);
  --mint: color-mix(in srgb, var(--ct-primary, #12b892) 68%, #0a2a17);
  --gold: #a9822f;
  --magenta: #e11d8f;
  --text-1: #06220b;
  --text-2: #4e6254;
  --text-3: #78907f;
  color-scheme: light;
  background: var(--bg);
  color: var(--text-1);
}

/* materialize tokens onto the body so Tailwind/utilities can consume them */
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text-1);
  line-height: 1.5;
  word-break: break-word;
}

h1, h2, h3, h4, h5, .font-display, .logo, .site-brand {
  font-family: var(--font-display);
  letter-spacing: -0.01em;
}

code, pre, .font-mono, .tabular, [data-fx-balance], td.num, .timer,
.stat-num, .progress-label {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

a { color: var(--ct-primary); text-decoration: none; }
@media (hover: hover) {
a:hover { color: color-mix(in srgb, var(--ct-primary) 82%, #fff); text-decoration: none; }
}
a:focus-visible { outline: 2px solid var(--ct-primary); outline-offset: 2px; }
::selection { background: var(--ct-primary); color: var(--bg); }

/* shared surface primitive used app-wide. Cards/panels default to FLAT:
   no surface fill, no border, so composition layers (Tailwind utility boxes,
   admin stat tiles, modal panels) own their own surface. Surfaces that need
   the token fill still set it themselves (e.g. .fx-card, .fx-menu,
   .fx-modal-panel). */
.card, .panel, [class*='card'], [class*='panel'] {
  background: none;
  border: none;
  border-radius: var(--radius);
}
.hairline { border-color: var(--surface-border); }
.muted { color: var(--text-2); }
.text-faint { color: var(--text-3); }
.accent-lime { color: var(--lime); }
.accent-success { color: var(--mint); }
.accent-gold { color: var(--gold); }
.accent-danger { color: var(--magenta); }

/* No element-level colour transition is declared here on purpose. The old
   `html, body, .card, .panel, button, input, select, textarea { transition:
   background/color/border-color }` list could not look synchronised — it ran one
   clock while the components ran ~20 others. Theme handoff is now driven from
   <html> alone, at the bottom of this file. Hover/press transitions stay on the
   individual components where they already were. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001s !important; }
}

/* scrollbars follow tokens (WebKit) */
* { scrollbar-width: thin; scrollbar-color: var(--surface-border) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: var(--surface-border); border-radius: 999px; }
*::-webkit-scrollbar-track { background: transparent; }

@media (max-width: 640px) {
  html { font-size: 15px; }
}

/* ============ THEME HANDOFF (dark <-> light) ============ */
/* One transition, declared on <html>, over the registered colour tokens above.
   Because the tokens are inherited, that single clock drives every element that
   reads them — the sidebar, topbar, cards, tables, badges and borders all
   interpolate the same value in the same frame, with nothing left to snap.
   Duration comes from --fx-theme-fade, which the matrix_effect_intensity tier
   publishes on <html data-fx-fx>, so one admin setting governs both the ambient
   effects and how heavy the theme animation is allowed to be. Lives here rather
   than home-fx.css because this is the one stylesheet the guest, signed-in and
   admin shells all load. */
:root {
  --fx-theme-fade: 240ms;
  --fx-theme-ease: cubic-bezier(0.4, 0, 0.2, 1);
}
html[data-fx-fx="high"] { --fx-theme-fade: 320ms; }
html[data-fx-fx="medium"] { --fx-theme-fade: 240ms; }
html[data-fx-fx="low"] { --fx-theme-fade: 1ms; }
html[data-fx-fx="none"] { --fx-theme-fade: 1ms; }

html {
  transition:
    --bg var(--fx-theme-fade) var(--fx-theme-ease),
    --surface var(--fx-theme-fade) var(--fx-theme-ease),
    --surface-2 var(--fx-theme-fade) var(--fx-theme-ease),
    --surface-border var(--fx-theme-fade) var(--fx-theme-ease),
    --lime var(--fx-theme-fade) var(--fx-theme-ease),
    --mint var(--fx-theme-fade) var(--fx-theme-ease),
    --gold var(--fx-theme-fade) var(--fx-theme-ease),
    --magenta var(--fx-theme-fade) var(--fx-theme-ease),
    --text-1 var(--fx-theme-fade) var(--fx-theme-ease),
    --text-2 var(--fx-theme-fade) var(--fx-theme-ease),
    --text-3 var(--fx-theme-fade) var(--fx-theme-ease);
}

/* While a switch is in flight, every element's own colour transitions are
   suppressed - including <html>'s token transition. That last part is the whole
   point: under View Transitions the incoming frame has to be captured in its
   FINAL colours. If the root tokens were still interpolating when the snapshot
   was taken, the browser would capture a half-faded page and then cross-fade it,
   so the palette would move twice and the switch would stutter. The lock has to
   reach the root as well as every descendant, so it is listed explicitly. */
/* ---- Document-level motion + text wrapping --------------------------
   Anchor jumps were instant, which reads as a jolt on the long guest pages
   (and on the admin tables, where a hash link scrolls past a lot of rows).
   Gated on no-preference so this can never add motion to a visitor who asked
   for none - the same rule the theme switch and view transitions already use. */
/* ---- Skip to content -------------------------------------------------
   Every shell already renders a real <main>, so this can target it. Visually
   hidden until focused, then pinned top-left in the shell accent. Without it a
   keyboard user has to Tab through the whole top bar on every page. */
.skip-link {
  position: fixed;
  left: 0.5rem;
  top: 0.5rem;
  z-index: 100;
  transform: translateY(-200%);
  transition: transform var(--dur-ui) var(--ease-ui);
  padding: 0.5rem 0.9rem;
  border-radius: var(--radius-sm);
  border: 1px solid color-mix(in srgb, var(--ct-primary, #00ff66) 45%, transparent);
  background: var(--surface, #0d1512);
  color: var(--ct-primary, #00ff66);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}

.skip-link:focus-visible,
.skip-link:focus {
  transform: translateY(0);
  outline: 2px solid var(--ct-primary, #00ff66);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .skip-link { transition: none; }
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

/* Headings and standalone paragraphs get balanced wrapping. Without this a
   narrow column leaves single words stranded on the last line ("Total
   opens" -> "opens" on its own row), which is what makes a heading look
   accidental rather than typeset. balance is the right mode for the short
   heading-length strings this project uses; pretty is reserved for body copy
   so it does not fight the fixed-height stat tiles. */
h1, h2, h3, h4,
.fx-display,
.fx-stat-value,
.adm-stat > p {
  text-wrap: balance;
}

p, li, dd {
  text-wrap: pretty;
}

html.theme-switching,
html.theme-switching *,
html.theme-switching *::before,
html.theme-switching *::after {
  transition: none !important;
}

/* Reduced motion: silence the token transition as well. `*` cannot reach the
   root element, so it is reset explicitly. */
@media (prefers-reduced-motion: reduce) {
  html { transition: none !important; }
}

/* ---- View Transitions: the switch animation itself --------------------
   startViewTransition() has the browser snapshot the outgoing and incoming
   frames as bitmaps and cross-fade them on the compositor. Nothing restyles per
   frame, so the whole page changes colour for about one frame of main-thread
   work. Duration and easing are read from the same vars the matrix_effect_intensity
   tier publishes, so one admin setting still governs how heavy this may be. */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--fx-theme-fade);
  animation-timing-function: var(--fx-theme-ease);
  /* The UA default is plus-lighter, which washes the midpoint of a dark-to-light
     cross-fade out to grey. normal is the correct blend for a theme change. */
  mix-blend-mode: normal;
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation-duration: 0.001s !important; }
}

/* Fallback: a browser without View Transitions (older Safari/Firefox), plus every
   path the tier or prefers-reduced-motion rules already reduce to an instant
   switch. theme.js holds .theme-switching for one frame around the write, so the
   token transition above is suppressed and those browsers change theme in a single
   repaint - deliberately instant rather than a laggy tween on the slowest devices.
   No layout or paint regression, so no feature gate is needed beyond the support
   check in theme.js. */