@charset "utf-8";
/*
 * ============================================================================
 * STRATAMIZE — UNIFIED BRAND TOKENS  ·  foundations / single source of truth
 * ============================================================================
 * ONE token set. TWO intentional, deliberately-authored themes:
 *   [data-theme="dark"]  → Operator cockpit  (near-black field, mint live-cursor)
 *   [data-theme="light"] → Customer surfaces (warm paper, ink, mint accent)
 *
 * Every surface — CRM (Javalin+JTE), POS (Javalin+FreeMarker),
 * Invoicing (Spark+FreeMarker), marketing + customer invoice/checkout/receipt
 * (static HTML) — links THIS file, then css/elements.css, and consumes ONLY
 * the semantic tokens in section B. Components never reach past the semantic
 * layer: no hex literals outside the anchor/theme blocks, no per-route <style>,
 * no new --vars. The templating engine is irrelevant — the classes are locked.
 *
 * Authored in OKLCH; locked brand hex preserved in comments. The mint #3BF5B0
 * equity is preserved verbatim and rationed as a live accent / light source in
 * BOTH themes — never decoration. On light paper, mint TEXT drops to --mint-700
 * (oklch(50% .15 165), ~5:1 on paper — the load-bearing AA fix) while fills,
 * underlines and edges keep true brand mint #3BF5B0.
 *
 * File map:
 *   §0  Font faces (the three brand voices)
 *   §A  Raw brand anchors + theme-invariant scales (type, space, radius, motion,
 *       z-index, breakpoints) — the only literals in the system
 *   §B  Semantic layer — DARK defaults (components reference ONLY these)
 *   §C  Semantic layer — LIGHT override
 *   §D  Status wash/edge derivations
 *   §R  Role / scope layer — the [data-role] third axis (no new accent/hex)
 *   §E  Reduced-motion + forced-colors
 *   §F  Tiny base reset + body wiring + the live-caret keyframe
 * ============================================================================
 */

/* ── §0 · FONT FACES — three voices carried verbatim from marketing equity ── */
@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap");

/* ════════════════════════════════════════════════════════════════════════
   §A · RAW BRAND ANCHORS + THEME-INVARIANT SCALES — the only literals
   ════════════════════════════════════════════════════════════════════════ */
:root {
  /* ---- Mint equity + theme anchors (the "physics", theme-invariant) ---- */
  --mint-500:  oklch(86.4% 0.174 163);   /* #3BF5B0  THE equity color */
  --mint-700:  oklch(50%   0.150 165);   /* AA mint TEXT on paper — ~5:1 on paper, ~4.8:1 over accent-wash (was 64%/#12A877 = 2.9:1, failed AA) */
  --mint-300:  oklch(92%   0.110 165);   /* soft mint wash anchor */
  --ink-black: oklch(6.8%  0.012 250);   /* #04060A  cockpit floor */
  --paper:     oklch(98.4% 0.006 95);    /* #FBFAF6  warm editorial paper */

  /* Semantic hues — shared across themes; lightness re-tuned per theme */
  --hue-info:   209;   /* cyan   — info only, never competes with mint */
  --hue-warn:   91;    /* amber  */
  --hue-danger: 13;    /* red    */
  --hue-rare:   343;   /* magenta — <=1 per view */

  /* ---- TYPE — three voices, strictly zoned ---- */
  --font-display: "Space Grotesk", "Inter", system-ui, sans-serif;                        /* mastheads / headlines / wordmark / big money */
  --font-text:    "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;              /* prose / UI / labels / nav / buttons */
  --font-mono:    "JetBrains Mono", "SFMono-Regular", ui-monospace, "Cascadia Code", monospace; /* ALL money + IDs + data */

  /* ---- FLUID TYPE SCALE — magazine display ramp + dedicated numeral ramp ---- */
  --fs-kicker:  clamp(0.6875rem, 0.66rem + 0.10vw, 0.75rem);   /* tracked overline */
  --fs-meta:    clamp(0.75rem,   0.72rem + 0.12vw, 0.8125rem); /* captions / meta */
  --fs-sm:      clamp(0.8125rem, 0.78rem + 0.16vw, 0.875rem);  /* dense cells */
  --fs-base:    clamp(0.9375rem, 0.90rem + 0.22vw, 1.0625rem); /* body — bigger than SaaS default */
  --fs-lead:    clamp(1.125rem,  1.03rem + 0.45vw, 1.375rem);  /* standfirst / lead */
  --fs-h4:      clamp(1.125rem,  1.02rem + 0.50vw, 1.375rem);
  --fs-h3:      clamp(1.375rem,  1.18rem + 0.90vw, 1.875rem);
  --fs-h2:      clamp(1.875rem,  1.50rem + 1.80vw, 3rem);
  --fs-h1:      clamp(2.75rem,   1.90rem + 4.20vw, 5.5rem);    /* magazine headline */
  --fs-display: clamp(3.5rem,    2.00rem + 7.00vw, 8.5rem);    /* cover / hero masthead */
  --fs-num-s:   clamp(1.125rem,  1.02rem + 0.55vw, 1.375rem);  /* inline figure */
  --fs-num-m:   clamp(1.625rem,  1.35rem + 1.30vw, 2.25rem);   /* KPI tile count */
  --fs-num-l:   clamp(2.5rem,    1.90rem + 3.00vw, 4rem);      /* hero money figure */

  --tracking-kicker: 0.22em;    /* editorial overline */
  --tracking-caps:   0.10em;    /* nav / labels / chips */
  --tracking-tight:  -0.02em;   /* large display headlines */
  --tracking-wordmark: -0.03em; /* the STRATAMIZE lockup */
  --leading-display: 0.96;
  --leading-tight:   1.08;
  --leading-snug:    1.30;
  --leading-body:    1.62;      /* generous reading measure */
  --measure:         68ch;      /* optimal editorial line length */

  /* ---- SPACE — 4px base, applied as editorial rhythm (not uniform) ---- */
  --s-px:1px; --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:20px;
  --s-6:24px; --s-8:32px; --s-10:40px; --s-12:48px; --s-16:64px; --s-20:80px; --s-24:96px;
  --space-section: clamp(4rem, 3rem + 6vw, 10rem);

  /* ---- LAYOUT ---- */
  --shell-h:     56px;
  --content-max: 1320px;
  --measure-max: 760px;         /* customer reading column cap */
  --gutter:      clamp(20px, 1.2vw + 14px, 48px);
  --grid-gap:    clamp(16px, 1vw + 10px, 28px);
  --rail-w:      2px;           /* signal-rail thickness — marks a LIVE surface */

  /* ---- RADIUS — editorial is mostly flush; soft only on customer fills ---- */
  --r-0:0; --r-1:2px; --r-2:4px; --r-3:6px; --r-md:10px; --r-lg:16px; --r-xl:24px; --r-full:999px;

  /* ---- MOTION — compositor-friendly only (transform / opacity / clip-path) ---- */
  --dur-1:90ms;  /* state flips */
  --dur-2:160ms; /* controls */
  --dur-3:280ms; /* panels / overlays */
  --dur-4:520ms; /* slow editorial reveal */
  --ease-out:  cubic-bezier(0.16, 1, 0.3, 1);   /* entrances */
  --ease-snap: cubic-bezier(0.2,  0, 0, 1);      /* decisive state changes */
  --ease-edit: cubic-bezier(0.22, 1, 0.36, 1);   /* magazine reveal */
  --blink: 1.05s;                                 /* live-cursor blink period */

  /* ---- Z-INDEX — single ordered stack for the whole platform ---- */
  --z-base:      0;
  --z-raised:    1;
  --z-sticky:    100;   /* sticky table heads, page-head */
  --z-nav:       200;   /* top-nav / sidebar chrome */
  --z-dropdown:  300;   /* menus, selects, popovers */
  --z-drawer:    400;   /* slide-in drawers */
  --z-overlay:   500;   /* modal scrim */
  --z-modal:     510;   /* modal dialog */
  --z-toast:     600;   /* toasts / live alerts */
  --z-tooltip:   700;   /* tooltips ride above everything */
  --z-max:       9999;  /* skip-link, dev overlays */

  /* ---- BREAKPOINTS — reference values (media queries can't read vars) ---- */
  --bp-xs:  480px;
  --bp-sm:  640px;
  --bp-md:  768px;
  --bp-lg:  1024px;
  --bp-xl:  1280px;
  --bp-2xl: 1440px;
}

/* ════════════════════════════════════════════════════════════════════════
   §B · SEMANTIC LAYER — DARK DEFAULTS (operator cockpit; CRM + POS chrome).
        Components reference ONLY these. Ratios vs --surface-page #04060A.
   ════════════════════════════════════════════════════════════════════════ */
:root,
[data-theme="dark"] {
  color-scheme: dark;

  /* Surfaces — near-black field + lifted wells (no card-soup) */
  --surface-page:    oklch(6.8%  0.012 250);   /* #04060A floor */
  --surface-raised:  oklch(10.6% 0.013 250);   /* panels / cards */
  --surface-sunken:  oklch(5.0%  0.010 250);   /* inputs / wells */
  --surface-header:  oklch(8.6%  0.012 250);   /* shell / table head */
  --surface-inverse: oklch(98.4% 0.006 95);    /* paper chip on dark */
  --row-hover: color-mix(in oklab, var(--accent) 8%, transparent);

  /* Ink tiers (ratios vs --surface-page) */
  --ink:        oklch(96% 0.015 234);  /* #EAF7FF  ~18.6:1 AAA */
  --ink-2:      oklch(84% 0.014 232);  /* body     ~13.0:1 AAA */
  --ink-3:      oklch(66% 0.034 233);  /* labels   ~6.4:1  AA  */
  --ink-4:      oklch(60% 0.022 235);  /* meta     ~4.8:1  AA  (fixed floor) */
  --on-accent:  oklch(26% 0.07  163);  /* ink on solid mint */
  --on-inverse: oklch(18% 0.01  250);  /* ink on paper chip */

  /* Hairlines — slate-alpha structure, not shadow */
  --hairline:        color-mix(in oklab, oklch(62% 0.040 233) 22%, transparent);
  --hairline-strong: color-mix(in oklab, oklch(62% 0.040 233) 38%, transparent);
  --hairline-faint:  color-mix(in oklab, oklch(62% 0.040 233) 12%, transparent);
  --rule-ink: var(--ink);     /* editorial masthead rule */

  /* Accent — mint as the live cursor / light source */
  --accent:       var(--mint-500);
  --accent-text:  var(--mint-500);   /* AAA on dark — links/active read mint */
  --accent-wash:  color-mix(in oklab, var(--mint-500) 13%, transparent);
  --accent-edge:  color-mix(in oklab, var(--mint-500) 42%, transparent);
  --accent-glow:  color-mix(in oklab, var(--mint-500) 30%, transparent);
  --on-accent-solid: var(--on-accent);

  /* Semantic status — bright on near-black */
  --info:   oklch(84.8% 0.142 var(--hue-info));   /* #22E6FF */
  --warn:   oklch(87.9% 0.163 var(--hue-warn));   /* #FFD23C */
  --danger: oklch(69.7% 0.198 var(--hue-danger)); /* #FF5C7A */
  --rare:   oklch(69.3% 0.258 var(--hue-rare));   /* #FF3DC4 */
  --success: var(--mint-500);

  /* Depth — hairlines + one inset lip; one reserved bloom */
  --shadow-1: inset 0 1px 0 0 color-mix(in oklab, var(--ink) 5%, transparent);
  --shadow-2: var(--shadow-1), 0 12px 30px -18px oklch(0% 0 0 / 0.85);
  --shadow-3: var(--shadow-1), 0 28px 60px -28px oklch(0% 0 0 / 0.9);
  --glow-accent: 0 0 0 1px var(--accent-edge), 0 0 28px -6px var(--accent-glow);
  --ring: 0 0 0 2px var(--surface-page), 0 0 0 4px var(--accent);
}

/* ════════════════════════════════════════════════════════════════════════
   §C · LIGHT THEME — warm paper for customer surfaces (invoice/checkout/POS).
        An intentional derivation, NOT an inversion. Ratios vs paper #FBFAF6.
   ════════════════════════════════════════════════════════════════════════ */
[data-theme="light"] {
  color-scheme: light;

  --surface-page:    oklch(98.4% 0.006 95);   /* #FBFAF6 warm paper */
  --surface-raised:  oklch(100%  0     0);     /* pure white card lifts off paper */
  --surface-sunken:  oklch(96.4% 0.008 95);   /* input wells / zebra */
  --surface-header:  oklch(97.2% 0.006 95);
  --surface-inverse: oklch(14%   0.012 250);  /* near-black chip on paper */
  --row-hover: color-mix(in oklab, var(--accent) 10%, transparent);

  /* Ink tiers (ratios vs paper) */
  --ink:        oklch(20% 0.018 250);  /* near-black  ~14:1 AAA */
  --ink-2:      oklch(34% 0.016 250);  /* body        ~9:1  AAA */
  --ink-3:      oklch(46% 0.018 250);  /* labels      ~5.6:1 AA */
  --ink-4:      oklch(52% 0.016 250);  /* meta        ~4.6:1 AA */
  --on-accent:  oklch(26% 0.07  163);  /* ink on solid mint fill (mint stays light) */
  --on-inverse: oklch(96% 0.01  95);   /* paper ink on near-black chip */

  --hairline:        color-mix(in oklab, var(--ink) 14%, transparent);
  --hairline-strong: color-mix(in oklab, var(--ink) 26%, transparent);
  --hairline-faint:  color-mix(in oklab, var(--ink) 8%,  transparent);
  --rule-ink: var(--ink);

  /* Mint: fills/underlines stay #3BF5B0; TEXT drops to --mint-700 for AA */
  --accent:       var(--mint-500);
  --accent-text:  var(--mint-700);   /* oklch(50% .15 165) — links/active clear AA on paper (~5:1) */
  --accent-wash:  color-mix(in oklab, var(--mint-500) 20%, var(--surface-raised));
  --accent-edge:  color-mix(in oklab, var(--mint-700) 50%, transparent);
  --accent-glow:  color-mix(in oklab, var(--mint-500) 40%, transparent);
  --on-accent-solid: var(--on-accent);

  /* Status re-tuned darker so they pass AA as TEXT on paper AND over their own
     tinted washes (pills / status cells), where same-hue washes cut ~0.5 off
     contrast. Verified ≥4.5:1 on wash: info 4.68, warn 4.56, danger 4.73, rare 4.75
     (all ≥5.0:1 on bare paper). */
  --info:   oklch(46% 0.16 var(--hue-info));
  --warn:   oklch(50% 0.16 var(--hue-warn));
  --danger: oklch(50% 0.21 var(--hue-danger));
  --rare:   oklch(50% 0.24 var(--hue-rare));
  --success: var(--mint-700);

  /* Depth — soft layered paper shadows, no outer glow */
  --shadow-1: inset 0 1px 0 0 color-mix(in oklab, var(--ink) 4%, transparent);
  --shadow-2: 0 1px 2px -1px oklch(20% 0.02 250 / 0.12), 0 12px 28px -16px oklch(20% 0.02 250 / 0.16);
  --shadow-3: 0 2px 4px -2px oklch(20% 0.02 250 / 0.14), 0 30px 60px -28px oklch(20% 0.02 250 / 0.22);
  --glow-accent: 0 0 0 1px var(--accent-edge), 0 0 24px -8px var(--accent-glow);
  --ring: 0 0 0 2px var(--surface-page), 0 0 0 4px var(--accent-text);
}

/* ── §D · Status wash/edge derivations (theme-agnostic; built from semantic) ── */
:root {
  --info-wash:   color-mix(in oklab, var(--info)   14%, transparent);
  --info-edge:   color-mix(in oklab, var(--info)   42%, transparent);
  --warn-wash:   color-mix(in oklab, var(--warn)   16%, transparent);
  --warn-edge:   color-mix(in oklab, var(--warn)   44%, transparent);
  --danger-wash: color-mix(in oklab, var(--danger) 15%, transparent);
  --danger-edge: color-mix(in oklab, var(--danger) 44%, transparent);
  --danger-glow: color-mix(in oklab, var(--danger) 26%, transparent);
  --rare-wash:   color-mix(in oklab, var(--rare)   14%, transparent);
  --rare-edge:   color-mix(in oklab, var(--rare)   44%, transparent);
  --success-wash: var(--accent-wash);
  --success-edge: var(--accent-edge);
}

/* ════════════════════════════════════════════════════════════════════════
   §R · ROLE / SCOPE LAYER — the third orthogonal axis: [data-role]
   ────────────────────────────────────────────────────────────────────────
   Surface is governed by TWO independent attributes on <html>:
       <html data-theme="dark|light" data-role="operator|agent|merchant">
   data-theme decides the FIELD (cockpit vs paper); data-role decides the
   SCOPE — "whose data am I seeing" — the visual encoding of tenant_closure.

   Role introduces NO new accent and NO new hex. Mint stays the single brand
   signal. Role is expressed STRUCTURALLY through exactly three mapping vars,
   each DERIVED from the existing semantic layer (§B/§C) so they re-resolve
   per theme automatically and inherit both themes' AA/AAA guarantees:

     --scope-rail       2px scope rail that marks WHOSE surface this is
     --scope-chip-tint  background tint of the mandatory scope chip
     --scope-chip-ink   label ink of the scope chip

   Hue logic (od-color-expert): MINT = "you are home / full own context"
   (operator + merchant); INFO-CYAN = "you are in a scoped subtree" — the
   single non-mint tell, reserved for agent so the P8 IDOR/scoping lesson is
   visible. operator vs merchant differ by chip-tint density (merchant is the
   restrained, white-label-capable expression). The rail references
   --accent-text (NOT --accent) so it stays >=5:1 on light paper too.

   Contrast (computed OKLCH->sRGB; see DESIGN.md §9):
     rail vs surface-page  operator/merchant 14.78 dark · 5.02 light
                           agent             13.72 dark · 5.74 light   (>=3:1 UI)
     scope-chip ink on tint  >=14.5:1 every role x theme               (AAA)
   ──────────────────────────────────────────────────────────────────────── */
:root {
  /* Role-neutral defaults — no rail / neutral chip until a role is set. */
  --scope-rail:      transparent;
  --scope-chip-tint: var(--surface-raised);
  --scope-chip-ink:  var(--ink-2);
}

[data-role="operator"] {            /* full control plane — mint, full strength */
  --scope-rail:      var(--accent-text);
  --scope-chip-tint: var(--accent-wash);
  --scope-chip-ink:  var(--ink);
}

[data-role="agent"] {               /* tenant_closure subtree — the only non-mint tell */
  --scope-rail:      var(--info);
  --scope-chip-tint: var(--info-wash);
  --scope-chip-ink:  var(--ink);
}

[data-role="merchant"] {            /* own data only — mint, restrained / white-label-capable */
  --scope-rail:      var(--accent-text);
  --scope-chip-tint: color-mix(in oklab, var(--accent-wash) 65%, var(--surface-raised));
  --scope-chip-ink:  var(--ink);
}

/* ════════════════════════════════════════════════════════════════════════
   §E · MOTION / ACCESSIBILITY GUARDS
   ════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-1:1ms; --dur-2:1ms; --dur-3:1ms; --dur-4:1ms; --blink:0s; }
}

/* ════════════════════════════════════════════════════════════════════════
   §F · TINY BASE RESET + BODY WIRING + LIVE-CARET KEYFRAME
        Foundation only — no component classes (those live in elements.css).
   ════════════════════════════════════════════════════════════════════════ */
*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -moz-tab-size: 4;
  tab-size: 4;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  min-height: 100svh;
  background-color: var(--surface-page);
  color: var(--ink);
  font-family: var(--font-text);
  font-size: var(--fs-base);
  line-height: var(--leading-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-feature-settings: "cv11", "ss01";
  /* theme transition is a deliberate, single non-layout property change */
  transition: background-color var(--dur-3) var(--ease-out),
              color var(--dur-3) var(--ease-out);
}

/* Headlines speak in the display voice; balance short lines */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: var(--leading-tight);
  text-wrap: balance;
}

/* The money/data voice — every figure is mono + tabular */
code, kbd, samp, pre, .mono, [data-mono] {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
}

/* Media defaults */
img, picture, svg, video, canvas { display: block; max-width: 100%; }
svg { fill: currentColor; }

/* Forms inherit type instead of UA defaults */
button, input, select, textarea { font: inherit; color: inherit; }

/* Links read in the live accent; AA in both themes via --accent-text */
a { color: var(--accent-text); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 0.18em; }

/* The single global focus contract — mint halo on every interactive element */
:focus-visible {
  outline: none;
  box-shadow: var(--ring);
  border-radius: var(--r-2);
}
:focus:not(:focus-visible) { outline: none; }

/* Selection carries the live signal, quietly */
::selection {
  background: var(--accent-wash);
  color: var(--ink);
}

/* Custom scrollbars stay in-system (WebKit + Firefox) */
* { scrollbar-width: thin; scrollbar-color: var(--hairline-strong) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb {
  background: var(--hairline-strong);
  border: 2px solid var(--surface-page);
  border-radius: var(--r-full);
}
*::-webkit-scrollbar-thumb:hover { background: var(--ink-4); }

/*
 * THE BRAND'S LIVING SIGNATURE — the signal-rail caret.
 * Defined here so the logomark is CSS, not a raster asset. step-end snaps
 * (terminal cursor), not fades. Reduced-motion holds it steady (see §E:
 * --blink:0s) AND we belt-and-braces with the media query below.
 */
@keyframes caret-blink {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.15; }
}
@media (prefers-reduced-motion: reduce) {
  @keyframes caret-blink { 0%, 100% { opacity: 1; } }
}

/* Honor OS high-contrast: keep focus + accent legible */
@media (forced-colors: active) {
  :focus-visible { outline: 2px solid CanvasText; box-shadow: none; }
}
