/* ============================================================================
   SayPays Web OS — Design Tokens
   web/css/tokens.css
   ----------------------------------------------------------------------------
   PHASE 1 — "Foundation". This file declares the design-token NAMESPACE only.
   It MUST cause ZERO visual change: it defines CSS custom properties plus
   accessibility/responsive guards that emit ONLY custom-property overrides
   (the prefers-reduced-motion guard is the single exception that touches real
   properties, and it is inert unless the user requests reduced motion). It
   does NOT restyle any element in the default state (no body{}, no *{} colour
   rules, no component selectors). Tailwind Play CDN utility classes still
   drive all layout/colour for now.

   THEME-ABLE ARCHITECTURE (three layers, cascade-driven):
     LAYER 1 — PRIMITIVES  --sp-*  : raw palette swatches (named by VALUE).
                                       This is the ONLY layer a named theme
                                       normally overrides.
     LAYER 2 — SEMANTIC    --color-* / --surface-* / --text-* / scales
                                       (named by ROLE). Components consume THIS
                                       layer, never --sp-* directly. Each tone
                                       ships its own fill + on-fill + surface +
                                       on-surface so WCAG AA pairing is
                                       guaranteed by the token, not the caller.
     LAYER 3 — COMPONENT   --btn-* / --pill-* …  : added LATER (Phase 2) as
                                       leaf components migrate off raw Tailwind
                                       utilities. Empty in Phase 1 (no dead
                                       indirection).

   PRECEDENCE (low -> high), each scope overrides only what it names:
     :root (light = default)  <  html[data-theme="x"] (named: primitives)
                              <  html style="" inline (runtime "ตามใจ" custom)

   FIRST PAINT: :root carries the full light theme unconditionally, so the UI
   renders correctly before any JS — no theme FOUC. A tiny inline bootstrap in
   <head> (NOT this file) replays localStorage theme/custom vars pre-paint.

   NOTE on Tailwind Play CDN: the CDN generates utilities (e.g. bg-indigo-600)
   as literal hex and does NOT read these vars. Primitive hex below mirrors
   Tailwind v3 so var-backed and utility-backed elements match during the
   mixed-migration period. To consume a token from markup, use an
   arbitrary-value utility: bg-[var(--color-primary)].

   THEME-PICKER REACH (important expectation, ties to C3#2): a named/custom
   theme that overrides --sp-* (or --color-*) repaints ONLY elements that
   consume var(--color-*) via arbitrary-value utilities or component CSS. Raw
   Tailwind utility colours still in the live app (bg-indigo-600, text-slate-800
   …) are literal hex and are UNAFFECTED until those elements migrate off raw
   utilities (Phase 2+) or a Tailwind build step maps utilities to vars. Until
   then the picker can only repaint already-migrated surfaces — a full theme is
   a Phase 2+ deliverable, not something this file alone can guarantee.

   CONTRAST: all AA-load-bearing semantic tokens use contrast-verified shades.
   Independent recomputation (sRGB relative luminance, WCAG 2.x) of every
   pairing in this file; ratios are stated against the surface noted.
   Corrections applied at the SEMANTIC layer vs the raw brief palette (raw
   primitives remain available for decorative use):
     • in-service FILL carrying white text -> sky-700 (sky-600 + white =
       4.10:1 FAILS AA 4.5; sky-700 + white = 5.93:1 PASS). sky-600 demoted to
       a decorative dot token, mirroring the emerald handling below.
     • locked/disabled TEXT on slate-100 -> slate-600 (slate-400 = 2.34:1
       FAILS even the 3:1 UI bar; slate-600 = 6.92:1 PASS). Pure inert disabled
       controls are WCAG-exempt, but locked fields still SHOW data + a lock
       icon, so the legible shade is the default.
     • muted META text -> slate-500 is AA on white (4.76) and slate-50 (4.55)
       but only 4.34:1 on slate-100 (FAILS). On slate-100 surfaces
       (locked/expanded rows) use --text-muted-on-subtle (slate-600, 6.92:1).
   SUCCESS NOTE: --color-success is emerald-600 to match blueprint A2's stated
   fill (emerald-600 fill / emerald-700 on emerald-50). A2/A4 only ever pair
   emerald with DARK text (pills/status text-on-surface), never white-on-fill,
   so the base fill is NOT darkened. A separate --color-success-strong
   (emerald-700, white = 5.48:1 PASS) is provided ONLY for the rare future case
   of a solid emerald control carrying white text; the base success tone stays
   on-brand with A2.
   ============================================================================ */

:root {

  /* ==========================================================================
     LAYER 1 — PRIMITIVES  (raw palette, named by value, theme-overridable)
     --------------------------------------------------------------------------
     These carry NO meaning. They are the literal swatches a named theme
     redeclares. Hex mirrors Tailwind v3 exactly. Do NOT consume --sp-* in
     components — always go through the semantic layer below.
     ========================================================================== */

  /* Brand — indigo (BRAND/primary hue; distinct from in-progress per C2#5) */
  --sp-indigo-50:  #eef2ff;
  --sp-indigo-500: #6366f1;
  --sp-indigo-600: #4f46e5;
  --sp-indigo-700: #4338ca;

  /* In-progress / in-service — sky (deliberately a SEPARATE hue from brand) */
  --sp-sky-50:  #f0f9ff;
  --sp-sky-600: #0284c7;   /* decorative dots/icons w/ dark text only */
  --sp-sky-700: #0369a1;   /* AA-safe fill for white label text (5.93:1) */

  /* Success — emerald */
  --sp-emerald-50:  #ecfdf5;
  --sp-emerald-600: #059669;  /* A2 success fill; on-text must be DARK */
  --sp-emerald-700: #047857;  /* on-emerald-50 text + AA fill for white text */

  /* Warning — amber */
  --sp-amber-50:  #fffbeb;
  --sp-amber-500: #f59e0b;
  --sp-amber-800: #92400e;    /* on-surface text (amber-700 fails AA on -50) */

  /* Danger — rose */
  --sp-rose-50:  #fff1f2;
  --sp-rose-400: #fb7185;     /* form-field error border (decorative; pair w/ text) */
  --sp-rose-600: #e11d48;
  --sp-rose-700: #be123c;

  /* Neutral — slate */
  --sp-slate-50:  #f8fafc;
  --sp-slate-100: #f1f5f9;
  --sp-slate-200: #e2e8f0;
  --sp-slate-300: #cbd5e1;
  --sp-slate-400: #94a3b8;    /* true WCAG-exempt inert disabled only */
  --sp-slate-500: #64748b;
  --sp-slate-600: #475569;    /* AA-safe locked/disabled TEXT on slate-100 */
  --sp-slate-700: #334155;
  --sp-slate-800: #1e293b;    /* body text on app bg (matches live text-slate-800) */
  --sp-slate-900: #0f172a;
  /* slate-900 as space-separated RGB channels so rgb()/alpha composites
     (scrim, shadows) reference the PRIMITIVE and re-tint when a theme overrides
     the slate ramp. Keep numerically in sync with --sp-slate-900 (#0f172a). */
  --sp-slate-900-rgb: 15 23 42;

  /* Base — split by INTENT so a dark theme can invert surfaces and on-fill
     text independently (both happen to be #fff in light). NEVER point a surface
     at --sp-on-fill or vice-versa. */
  --sp-surface-base: #ffffff;   /* cards/overlays/popovers background */
  --sp-on-fill:      #ffffff;   /* text/icon sitting ON a solid colour fill */
  /* Back-compat alias; prefer the two intent swatches above. */
  --sp-white: #ffffff;


  /* ==========================================================================
     LAYER 2 — SEMANTIC  (meaning; references primitives via var())
     --------------------------------------------------------------------------
     Named by ROLE, never by value. THIS is the contract components consume.
     on-* tokens always travel with their background so AA can't be broken by
     a caller.
     ========================================================================== */

  /* ---- Typography ------------------------------------------------------- */
  /* Font family — Thai-safe (th/en today, vi planned). DIVERGES from blueprint
     A1 'system-ui/Inter' by adding 'Noto Sans Thai' (mandatory for Thai).
     Ordering rationale: the live page sets NO font-family today, so it renders
     in the Tailwind/CDN default sans (system-ui first). Inter is NOT loaded yet,
     so leading with Inter is inert; system-ui is placed BEFORE 'Noto Sans Thai'
     so that whenever an element first adopts var(--font-sans), LATIN text still
     resolves to the SAME system family it shows today (no Latin reflow), while
     THAI glyphs fall through to Noto. This is the TARGET token; adopting it is a
     deliberate, near-zero change deferred to a later phase — NOT a pixel-for-
     pixel guarantee. Nothing consumes --font-sans in Phase 1, so no change yet.
     TODO: self-host/link Inter; until then Inter is silently skipped. */
  --font-sans: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Noto Sans Thai', sans-serif;
  --font-mono: ui-monospace, 'SFMono-Regular', Menlo, Consolas, 'Liberation Mono', monospace;

  /* Type scale */
  --text-xs:   0.75rem;   /* 12px — column header, meta, badge/pill, label */
  --text-sm:   0.875rem;  /* 14px — body/cell/input/H2; FLOOR for inputs (iOS no-zoom) */
  --text-base: 1rem;      /* 16px — base anchor only; A1 assigns no content role to 16px */
  --text-xl:   1.25rem;   /* 20px — page title (H1) */
  --text-2xl:  1.5rem;    /* 24px — metric value */

  /* Weight — EXACTLY two (A1). Do not introduce a 500/700 token.
     Badge/Button 'medium' maps to --font-weight-semibold in the component phase. */
  --font-weight-normal:   400;  /* data/body/cells */
  --font-weight-semibold: 600;  /* labels/titles/emphasis */

  /* Line height */
  --leading-none:   1;
  --leading-tight:  1.25;
  --leading-normal: 1.5;

  /* Tracking */
  --tracking-tight: -0.0125em;  /* H1 */
  --tracking-wide:  0.025em;    /* uppercase group/swimlane/metric labels */

  /* Numeric — apply via font-variant-numeric on money/qty/metric cells (A1) */
  --font-numeric: tabular-nums;

  /* ---- Brand / Primary (indigo) ----------------------------------------- */
  --color-primary:            var(--sp-indigo-600);
  --color-primary-hover:      var(--sp-indigo-700);
  --color-primary-ring:       var(--sp-indigo-500);
  /* Ready-to-use 40% focus ring (A2 'ring 500/40'). color-mix has broad but
     not universal support — falls back gracefully to the solid ring color.
     Opacity sourced from --ring-alpha so 'ตามใจ' can tune ring strength. */
  --color-primary-ring-alpha: color-mix(in srgb, var(--sp-indigo-500) var(--ring-alpha), transparent);
  --color-on-primary:         var(--sp-on-fill);      /* white/indigo-600 = 6.29:1 */
  --color-primary-surface:    var(--sp-indigo-50);    /* row-hover/selected tint */

  /* ---- Success (emerald) ------------------------------------------------ */
  /* Base fill = emerald-600 to MATCH A2 ('emerald-600 fill'); A2/A4 only pair
     emerald with DARK text, so white-on-fill is NOT a base contract here. */
  --color-success:            var(--sp-emerald-600);
  --color-on-success:         var(--sp-slate-900);    /* DARK on emerald-600 (white = 3.77 FAIL) */
  --color-success-strong:     var(--sp-emerald-700);  /* opt-in solid for WHITE text (5.48:1) */
  --color-on-success-strong:  var(--sp-on-fill);
  --color-success-surface:    var(--sp-emerald-50);
  --color-on-success-surface: var(--sp-emerald-700);  /* 5.21:1 on -50 */
  --color-success-dot:        var(--sp-emerald-600);  /* decorative dot w/ dark text */

  /* ---- Warning (amber) -------------------------------------------------- */
  /* C2#6: edit-COMMIT is destructive → use Danger, not Warning. Warning stays
     for benign states (pending pay, commission >50%, margin 25-40%). */
  --color-warning:            var(--sp-amber-500);
  /* amber-500 is light → on-fill text must be dark (white = 2.15 FAIL AA). */
  --color-on-warning:         var(--sp-slate-900);    /* 8.31:1 */
  --color-warning-surface:    var(--sp-amber-50);
  --color-on-warning-surface: var(--sp-amber-800);    /* 6.84:1 on -50 */

  /* ---- Danger (rose) ---------------------------------------------------- */
  --color-danger:             var(--sp-rose-600);
  --color-danger-hover:       var(--sp-rose-700);
  --color-on-danger:          var(--sp-on-fill);       /* 4.70:1 — just over 4.5 floor */
  --color-danger-surface:     var(--sp-rose-50);
  --color-on-danger-surface:  var(--sp-rose-700);      /* 5.72:1 on -50 */
  /* Error border is decorative (rose-400/white = 2.69:1 < 3:1). NEVER let it be
     the SOLE error indicator — pair with error text/icon (WCAG 1.4.11). */
  --color-danger-border:      var(--sp-rose-400);

  /* ---- Info / neutral (slate) ------------------------------------------- */
  --color-info:               var(--sp-slate-600);
  --color-info-surface:       var(--sp-slate-100);
  --color-on-info-surface:    var(--sp-slate-700);     /* 9.45:1 — most headroom */

  /* ---- In-progress / in-service (sky) — DISTINCT from brand (C2#5) ------- */
  /* Never alias to --color-primary; keeps selected/focused/in-service from
     collapsing to one hue in the queue. FILL bound to sky-700 (NOT -600) so
     white label text passes AA (sky-600 + white = 4.10 FAIL; -700 = 5.93 PASS),
     mirroring the emerald-700 white-text correction. sky-600 stays decorative. */
  --color-inprogress:            var(--sp-sky-700);
  --color-on-inprogress:         var(--sp-on-fill);     /* 5.93:1 */
  --color-inprogress-surface:    var(--sp-sky-50);
  --color-on-inprogress-surface: var(--sp-sky-700);     /* 5.57:1 on -50 */
  --color-inprogress-dot:        var(--sp-sky-600);     /* decorative dot w/ dark text */

  /* ---- Surfaces / structure --------------------------------------------- */
  --surface-app:         var(--sp-slate-50);   /* A2 target app bg (slate-50).
                                                  Current live body is slate-100;
                                                  reconcile in a later phase —
                                                  do NOT restyle body here. */
  --surface-card:        var(--sp-surface-base);
  --surface-card-border: var(--sp-slate-200);
  /* USE ON WHITE CARDS, not directly on --surface-app: slate-50 zebra/sticky
     header reads against a white card. When app bg becomes slate-50 (later
     phase) this collapses against it — use --table-zebra (alpha) for rows that
     must survive that reconciliation. */
  --surface-subtle:      var(--sp-slate-50);   /* column-header / sticky on a card */
  --surface-overlay:     var(--sp-surface-base); /* drawer/modal/popover bg */
  --surface-locked:      var(--sp-slate-100);  /* locked/disabled bg (NOT opacity) */
  --surface-expanded:    var(--sp-slate-100);  /* expanded detail row */
  /* Backdrop scrim = slate-900 @ --scrim-alpha, sourced from the RGB-channel
     primitive so a theme override of slate-900 re-tints the scrim for free. */
  --surface-scrim:       rgb(var(--sp-slate-900-rgb) / var(--scrim-alpha));

  /* ---- Borders ---------------------------------------------------------- */
  --border-default: var(--sp-slate-200);   /* card/panel/table (decorative <3:1) */
  --border-strong:  var(--sp-slate-300);   /* input / secondary-button */
  --border-accent:  var(--color-primary);  /* 4px left accent on expanded row */

  /* ---- Text roles ------------------------------------------------------- */
  --text-strong:   var(--sp-slate-900);   /* page title + primary body/cell */
  --text-default:  var(--sp-slate-700);   /* H2 / strong secondary */
  --text-on-app:   var(--sp-slate-800);   /* body text on app bg (themeable primitive) */
  --text-muted:    var(--sp-slate-500);   /* meta/help on WHITE or slate-50 only */
  /* Muted text on slate-100 surfaces (locked/expanded rows): slate-500 drops to
     4.34:1 on slate-100 (FAIL). Use this darker token there (6.92:1 PASS). */
  --text-muted-on-subtle: var(--sp-slate-600);
  /* Locked/disabled TEXT bound to slate-600 (NOT slate-400) for AA on slate-100
     (slate-400 = 2.34:1 fails). slate-400 primitive remains for inert,
     WCAG-exempt controls only. */
  --text-locked:   var(--sp-slate-600);
  --text-on-fill:  var(--sp-on-fill);     /* default text/icon on a solid fill */
  /* Column headers — A1 upgrades from slate-400 to slate-600 on slate-50. */
  --text-column-header:    var(--sp-slate-600);
  --surface-column-header: var(--sp-slate-50);
  /* Required-field asterisk (FormField '* แดงถ้า required'). */
  --color-required: var(--sp-rose-600);

  /* ---- Table tints (A3 alpha tints — survive on white cards) ------------ */
  /* Blueprint names these as alphas: zebra slate-50/60, row-hover indigo-50/50,
     expanded slate-100/50. Modelled via color-mix so the underlying primitive
     stays themeable; solid --surface-expanded / --color-primary-surface remain
     for callers that want the opaque variant. */
  --table-zebra:      color-mix(in srgb, var(--sp-slate-50) 60%, transparent);
  --table-row-hover:  color-mix(in srgb, var(--sp-indigo-50) 50%, transparent);
  --row-expanded-bg:  color-mix(in srgb, var(--sp-slate-100) 50%, transparent);

  /* ---- Radius ----------------------------------------------------------- */
  --radius-sm:   0.375rem;  /* 6px — tooltip, inline chip (matches current tip) */
  --radius-lg:   0.5rem;    /* 8px — default control/card/drawer (rounded-lg) */
  --radius-full: 9999px;    /* pills/badges/avatars */

  /* ---- Spacing — 4px base grid (A3) ------------------------------------- */
  --space-0: 0;
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-3: 0.75rem;  /* 12px */
  --space-4: 1rem;     /* 16px */
  --space-5: 1.25rem;  /* 20px */
  --space-6: 1.5rem;   /* 24px */
  --space-8: 2rem;     /* 32px */

  /* ---- Layout ----------------------------------------------------------- */
  --form-max-w:   40rem;  /* single/2-col form width + default right-drawer width */
  --modal-max-w:  28rem;  /* A4 centered confirm dialog (max-w-md) */

  /* ---- Density — ONE role vocabulary, toggled by [data-density] scope ---- */
  /* Comfortable = default (forms/cards/dashboard). Components read these role
     tokens ONLY; the compact mode below redefines the SAME names under a single
     scope (mirrors the [data-theme] pattern), so one attribute flips density —
     no parallel vocabulary at the call site (A3 'one utility class'). */
  --density-row-py: var(--space-3);  /* 12px — py-3 */
  --density-card-p: var(--space-4);  /* 16px — p-4 */
  --density-gap:    var(--space-4);  /* 16px */
  --density-cell-p: var(--space-4);  /* 16px */

  /* ---- Control heights / hit targets (A3/A4/A8) ------------------------- */
  /* --control-h RESOLVES to touch height under coarse pointer / <1024 (see the
     media block below — custom-property override only). Read this ONE token;
     the responsive split is handled by the system, not the call site. */
  --control-h:       2.5rem;   /* 40px desktop button/input/select (resolves to 44px on touch) */
  --control-h-touch: 2.75rem;  /* 44px touch / coarse-pointer / <1024 */
  /* C2#1: icon/action hit-areas stay 44px REGARDLESS of density (compact rows
     must not shrink the touch target below this). */
  --hit-target-min:  2.75rem;  /* 44px min interactive area for icon buttons */

  /* ---- Shadow / elevation (slate-tinted via themeable channel primitive) -- */
  --shadow-sm:     0 1px 2px 0 rgb(var(--sp-slate-900-rgb) / 0.06), 0 1px 1px 0 rgb(var(--sp-slate-900-rgb) / 0.04);
  --shadow-lg:     0 10px 24px -4px rgb(var(--sp-slate-900-rgb) / 0.18), 0 4px 8px -4px rgb(var(--sp-slate-900-rgb) / 0.10);
  --shadow-sticky: 0 4px 6px -4px rgb(var(--sp-slate-900-rgb) / 0.12);

  /* ---- Opacity scalars (tunable by 'ตามใจ' without editing composites) --- */
  --scrim-alpha: 0.40;  /* backdrop darkness */
  --ring-alpha:  40%;   /* focus-ring strength (consumed by color-mix above) */

  /* ---- Focus ring geometry (A4 'focus-visible:ring-2') ------------------ */
  --focus-ring-width:  2px;
  --focus-ring-offset: 2px;

  /* ---- z-index ladder (A4 nesting: palette > drawer > modal > toast; C3#5) */
  --z-base:    0;
  --z-sticky:  10;   /* sticky table header / toolbar */
  --z-drawer:  40;   /* slide-over right drawer */
  --z-modal:   50;   /* centered confirm dialog */
  --z-palette: 55;   /* command palette (above drawer/modal) */
  --z-toast:   60;   /* toasts above everything actionable */
  --z-tooltip: 70;   /* transient hover tip (topmost) */

  /* ---- Motion (durations + easing) -------------------------------------- */
  --motion-fast:    120ms;  /* hover, focus ring, copy-tick, toggle */
  --motion-base:    200ms;  /* drawer/modal/toast enter-exit, expand row */
  --motion-slow:    320ms;  /* larger surface transitions */
  --motion-pulse:   2s;     /* Critical-row pulse loop (Part B) */
  --motion-shimmer: 1.5s;   /* skeleton shimmer loop (A7) */
  --ease-standard:  cubic-bezier(0.2, 0, 0, 1);   /* enters/moves (decelerate) */
  --ease-exit:      cubic-bezier(0.4, 0, 1, 1);   /* exits/dismiss (accelerate) */

  /* ---- Toast timing (A4/A7 design constants) ---------------------------- */
  --toast-duration:      4000ms;  /* default auto-dismiss */
  --toast-undo-duration: 6000ms;  /* longer for Undo affordance (5-10s band) */
}


/* ============================================================================
   LAYER 1 LIGHT ANCHOR (no-op, for symmetry with named themes)
   :root above IS the light theme — this empty block is just a documented
   anchor so tooling/authors see where "light" lives. Do NOT make this the
   source of truth (that would defeat the unconditional first paint).
   ============================================================================ */
html[data-theme="light"] { /* light = :root defaults; intentionally empty */ }


/* ============================================================================
   THEME SEAM — DARK (and future named themes)
   ----------------------------------------------------------------------------
   PHASE 1: SEAM ONLY. Do NOT add real dark values yet (per brief). The hook is
   reserved and visible but inert.

   A named theme overrides ONLY Layer 1 primitives (--sp-*); the semantic layer
   inherits and recomputes for free — zero component edits. After the leak fixes
   in this file (channel-decomposed slate-900, real slate-800 primitive, split
   surface-base vs on-fill), a dark theme that overrides only the --sp-* ramp
   re-tints scrim/shadows/body-text/cards AND keeps on-fill text light, with no
   component churn. Semantic remapping (overriding --color-* directly) is the
   rare escape hatch, allowed but discouraged. Set with:
       document.documentElement.setAttribute('data-theme', 'dark');

   When filling dark later, override (at minimum):
     --sp-surface-base (dark) while LEAVING --sp-on-fill light (inversion-safe),
     the slate ramp + --sp-slate-900-rgb (re-tints scrim/shadows), and the
     brand/tone ramps. Consider raising --scrim-alpha / shadow opacity for dark.

   html[data-theme="dark"] {
     --sp-surface-base: #0b1220;  --sp-on-fill: #ffffff;
     --sp-slate-900-rgb: 0 0 0;   --sp-slate-50: #...;  ...
   }

   RUNTIME CUSTOM ("ตามใจ"): the picker writes per-token overrides as INLINE
   style on <html> (inline beats :root and [data-theme] by specificity):
       const el = document.documentElement;
       el.setAttribute('data-theme', 'custom');               // optional base
       el.style.setProperty('--sp-indigo-600', userHex);      // raw ramp tune
       el.style.setProperty('--color-primary', userHex);      // or target role
   One setProperty on a primitive cascades to every semantic token (and every
   MIGRATED component) that references it. CAVEAT: raw Tailwind utility colours
   (bg-indigo-600 …) are literal hex and are NOT repainted until migrated —
   see the THEME-PICKER REACH note in the header. Persist diffs to localStorage
   and replay in the <head> bootstrap BEFORE first paint. Persistence belongs
   to the app/picker, NOT this file. For a scoped preview swatch, apply inline
   vars to a CONTAINER element, not <html>.

   Never use !important here — it would block the inline-custom override path.
   ============================================================================ */
html[data-theme="dark"] {
  /* DARK SEAM — do not fill real values yet (brief).
     Override --sp-* ramp here later; the semantic layer recomputes
     automatically with zero component churn. */
}


/* ============================================================================
   DENSITY SEAM — COMPACT (custom-property override only; no element restyle)
   ----------------------------------------------------------------------------
   A3 'two density modes toggled by a single utility class'. Flip with:
       document.documentElement.setAttribute('data-density', 'compact');
   (or scope to a subtree via the attribute on a container). This redefines the
   SAME --density-* role tokens components already read — one switch, no second
   vocabulary. --hit-target-min is intentionally NOT shrunk here, so action
   touch targets stay 44px in compact rows (C2#1).
   Compact = sales_history / queue / audit / stock / reports.
   ============================================================================ */
html[data-density="compact"] {
  --density-row-py: var(--space-2);  /* 8px  — py-2 */
  --density-cell-p: var(--space-3);  /* 12px — p-3 */
  --density-card-p: var(--space-3);  /* 12px */
  --density-gap:    var(--space-3);  /* 12px */
}


/* ============================================================================
   CONTROL-HEIGHT RESOLUTION (custom-property override only; within Phase 1)
   ----------------------------------------------------------------------------
   A3/A6: 44px touch / 40px desktop, switched at the 1024 breakpoint or under a
   coarse pointer. Consumers read ONE token (--control-h); this block resolves
   it to the touch height so the responsive decision is NOT pushed to every call
   site. It emits a custom-property override only (no element selector, no
   restyle), so it stays within the 'custom-properties + reduced-motion only'
   Phase-1 constraint and causes zero visual change until a control reads it.
   ============================================================================ */
@media (pointer: coarse), (max-width: 1023px) {
  :root { --control-h: var(--control-h-touch); }  /* 44px */
}


/* ============================================================================
   LAYER 3 — COMPONENT TOKENS (intentionally empty in Phase 1)
   ----------------------------------------------------------------------------
   Add per-component aliases lazily as leaf components migrate off raw Tailwind
   utilities (Phase 2+), so there is no dead indirection. Each points at a
   semantic token, e.g.:
       --btn-primary-bg:       var(--color-primary);
       --btn-primary-bg-hover: var(--color-primary-hover);
       --pill-success-bg:      var(--color-success-surface);
       --table-row-hover-bg:   var(--table-row-hover);
   COMPOSITE pills (e.g. A4 'In service · Pending pay' = inprogress base +
   warning secondary segment) COMPOSE existing tone tokens — no new primitive is
   owed; each segment uses its tone's surface/on-surface pair.
   ============================================================================ */


/* ============================================================================
   ACCESSIBILITY GUARD — prefers-reduced-motion
   ----------------------------------------------------------------------------
   The ONLY rule this file emits that touches real (non-custom) properties
   (Part D, Phase 1). It neutralizes motion for users who request it; it sets NO
   colours and NO layout, so it causes no visual restyle for everyone else. The
   0.01ms duration still fires transitionend, so Alpine x-transition enter/leave
   that key off the event still complete.
   ============================================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}