/* ──────────────────────────────────────────────────────────────────────────
   tokens.css — the single, theme-agnostic foundation layer
   ----------------------------------------------------------------------------
   WHY THIS FILE EXISTS
   The app grew with NINE separate `:root` blocks (style.css, dashboard.css,
   field.css, …) and ~3,800 hardcoded `px` literals. There was no spacing scale
   and no type scale, so every panel re-invented its own rhythm and long strings
   / unusual viewports caused layout shift and clipping.

   This file is loaded *first* on every page, BEFORE the page stylesheet and any
   theme (classic / ui-next / theme-calm). It only DEFINES NEW token names
   (--sp-*, --text-*, --leading-*, --ease-*, --dur-*, --z-*) — it never collides
   with the existing colour/radius/shadow tokens, so it is purely additive and
   cannot regress the current look. Themes keep overriding colours/radii on top.

   The values are chosen to map 1:1 onto the px literals already in the codebase
   (4 / 6 / 8 / 10 / 12 / 14 / 16 / 20 / 24 …) so migration is a safe,
   like-for-like substitution rather than a redesign.
   ────────────────────────────────────────────────────────────────────────── */

:root {
  /* ── Spacing scale (4px base grid) ───────────────────────────────────────
     Use for padding / margin / gap. Picked to match the literals already in
     use so swaps are visually identical. Prefer these over raw px. */
  --sp-0:    0;
  --sp-px:   1px;
  --sp-0_5:  2px;
  --sp-1:    4px;
  --sp-1_5:  6px;
  --sp-2:    8px;
  --sp-2_5:  10px;
  --sp-3:    12px;
  --sp-3_5:  14px;   /* very common gap/padding in the current UI */
  --sp-4:    16px;
  --sp-5:    20px;
  --sp-6:    24px;
  --sp-7:    28px;
  --sp-8:    32px;
  --sp-10:   40px;
  --sp-12:   48px;
  --sp-16:   64px;

  /* ── Type scale ──────────────────────────────────────────────────────────
     Small UI text stays FIXED on purpose — dense data tables should not reflow
     fluidly with the viewport (that hurts legibility and alignment). Only the
     display/heading sizes scale fluidly via clamp() so big headings stay
     proportional on phones and ultrawide monitors without media queries. */
  --text-2xs:  10px;
  --text-xs:   11px;
  --text-sm:   12px;
  --text-base: 13px;   /* app body default */
  --text-md:   14px;
  --text-lg:   15px;
  --text-xl:   16px;
  --text-2xl:  18px;
  /* Fluid display sizes: clamp(min, preferred-vw, max). */
  --text-h3:   clamp(18px, 1.2vw + 14px, 22px);
  --text-h2:   clamp(20px, 1.8vw + 14px, 28px);
  --text-h1:   clamp(24px, 3vw + 16px, 40px);
  --text-display: clamp(32px, 5vw + 16px, 64px);

  --leading-tight:  1.2;
  --leading-snug:   1.35;
  --leading-normal: 1.55;
  --leading-relaxed: 1.7;

  --weight-regular:  400;
  --weight-medium:   500;
  --weight-semibold: 600;
  --weight-bold:     700;

  /* ── Motion ──────────────────────────────────────────────────────────────
     Names the existing --spring/--transition tokens did not provide. The
     primitives (states.js, disclosure.js, stagger.js) read these so motion is
     consistent and tunable from one place. */
  --dur-1: 80ms;    /* taps / instant state flips */
  --dur-2: 150ms;   /* default — matches the legacy --transition */
  --dur-3: 240ms;   /* panels, popovers, list entry */
  --dur-4: 360ms;   /* large / modal-scale movement */
  --ease-standard: cubic-bezier(.2, .8, .2, 1);   /* matches legacy --spring */
  --ease-out:      cubic-bezier(0, 0, .2, 1);
  --ease-in-out:   cubic-bezier(.4, 0, .2, 1);
  --ease-emphasis: cubic-bezier(.2, .9, .1, 1);

  /* ── Z-index ladder ──────────────────────────────────────────────────────
     A named ladder prevents the magic-number z-index wars (the app already has
     z:30 topbars, ad-hoc modal overlays, toasts on body, …). */
  --z-base:     0;
  --z-sticky:   20;
  --z-topbar:   30;
  --z-dropdown: 100;
  --z-overlay:  900;
  --z-modal:    1000;
  --z-toast:    1100;

  /* Standard focus-ring offset, so every focusable shares one rhythm. */
  --focus-ring: 2px solid var(--accent, #3b82f6);
  --focus-offset: 2px;
}

/* ── Global defensive defaults ───────────────────────────────────────────────
   Cheap, safe rules that stop the most common "extreme content" breakages
   (long unbroken strings, oversized media, runaway localized labels) WITHOUT
   touching component layout. We intentionally do NOT set a blanket
   `* { min-width: 0 }` (that would silently change flex behaviour app-wide);
   instead we provide the opt-in `.u-min0` utility below for flex children that
   need to be allowed to shrink. */
img, svg, video, canvas { max-width: 100%; }
/* Headings/paragraphs should wrap mid-word rather than overflow their box. */
h1, h2, h3, h4, h5, h6, p, li, dd, dt, figcaption { overflow-wrap: break-word; }

/* ── Accessible-motion guard ─────────────────────────────────────────────────
   Honour the OS "reduce motion" setting globally. Animations collapse to a
   single near-instant frame instead of being removed (so state still changes
   visibly), and smooth-scroll is disabled. Individual primitives ALSO check
   this in JS, but the CSS guard covers any animated element we miss. */
@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;
  }
  /* Functional loading spinners are exempt: a frozen single frame reads as
     "stuck/broken", whereas a steady rotation (no flashing) conveys ongoing
     work and is the accepted reduced-motion exception. Covers .spin/.ti-spin,
     the login .spinner, Tabler ti-loader icons, and inline animation:spin. */
  .spin, .ti-spin, .spinner,
  [class*="ti-loader"], [style*="spin"] {
    animation-duration: 0.8s !important;
    animation-iteration-count: infinite !important;
  }
}

/* ── Defensive layout utilities (opt-in) ─────────────────────────────────────
   Reach for these instead of re-writing the same truncation/wrap rules inline.
   They are the antidote to text clipping under long strings / localization. */
.u-min0      { min-width: 0; min-height: 0; }        /* let a flex/grid child shrink */
.u-truncate  { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.u-clamp-1,
.u-clamp-2,
.u-clamp-3   { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.u-clamp-1   { -webkit-line-clamp: 1; }
.u-clamp-2   { -webkit-line-clamp: 2; }
.u-clamp-3   { -webkit-line-clamp: 3; }
.u-break     { overflow-wrap: anywhere; word-break: break-word; }
.u-nowrap    { white-space: nowrap; }

/* A visually-hidden helper for screen-reader-only labels (used by the new
   a11y primitives when a control has only an icon). */
.sr-only {
  position: absolute !important; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
