/* ============================================================================
   Life Command Center — Design Tokens
   ----------------------------------------------------------------------------
   The single source of truth for colour, type, space, motion, and elevation.
   No raw hex, px font-size, or magic number appears anywhere else in the CSS.

   THEMING
   Colours are declared once using light-dark(), which resolves against the
   element's `color-scheme`. One token definition, no duplicated dark block to
   drift out of sync.

   LIGHT VS DARK IS NOT A SETTING. `color-scheme: light dark` hands the
   decision to the operating system, permanently. There was a Light/System/
   Dark control here and removing it was the right call: nobody wants to set
   their colour scheme twice, and a manual override is how you end up with a
   blinding white screen at 1am because you picked "light" in a cafe six
   months ago. It also means zero pre-paint scripting and zero flash.

   What IS switchable is the SKIN — `data-skin` on the root element — which
   changes the whole personality while still resolving light and dark from the
   system. `calm` is these tokens; `arcade` overrides them in arcade.css.

   Setting `color-scheme` also makes the browser theme native form controls,
   scrollbars, and the text caret correctly — which is exactly the kind of
   detail that separates a web page from an app.

   The dark palette is hand-tuned, NOT an inversion of the light one. Surfaces
   get LIGHTER as they rise (matching how real elevation reads on an OLED
   screen), saturation drops so colour doesn't glare in a dark room, and
   shadows are replaced by hairline borders.
   ========================================================================== */

@layer tokens {

  :root {
    color-scheme: light dark;

    /* ---------------------------------------------------------- surfaces --
       0 = page, 1 = card, 2 = inset/input, 3 = raised/hover                */
    --surface-0: light-dark(oklch(98.4% 0.003 250), oklch(15.5% 0.014 265));
    --surface-1: light-dark(oklch(100%  0     0  ), oklch(19.5% 0.015 265));
    --surface-2: light-dark(oklch(96.6% 0.004 250), oklch(23.5% 0.016 265));
    --surface-3: light-dark(oklch(93.4% 0.006 252), oklch(28%   0.017 265));

    /* Translucent chrome for the app bar and tab bar. */
    --surface-chrome: light-dark(oklch(100% 0 0 / 0.82), oklch(17% 0.014 265 / 0.86));
    --scrim:          light-dark(oklch(20% 0.02 265 / 0.36), oklch(0% 0 0 / 0.58));

    /* -------------------------------------------------------------- text -- */
    /* Lightness values here are solved, not eyeballed: every text token
       clears WCAG AA (4.5:1) against the WORSE of surface-0 and surface-1 in
       its own theme. Verified by bin/check-contrast.php — run it after any
       palette change. */
    --text-primary:   light-dark(oklch(21%   0.017 265), oklch(96.5% 0.004 260));
    --text-secondary: light-dark(oklch(45.5% 0.014 265), oklch(74%   0.010 262));
    --text-tertiary:  light-dark(oklch(53.8% 0.012 265), oklch(62.4% 0.013 264));
    --text-on-accent: light-dark(oklch(100%  0     0  ), oklch(14%   0.015 265));

    /* ------------------------------------------------------------ border -- */
    --border-subtle: light-dark(oklch(92.5% 0.005 260), oklch(26% 0.015 265));
    --border-strong: light-dark(oklch(86%   0.008 260), oklch(34% 0.016 265));

    /* ------------------------------------------------------------ accent --
       Dark mode lifts lightness and drops chroma; the light-mode accent at
       full saturation vibrates uncomfortably against a dark surface.        */
    --accent:        light-dark(oklch(55%   0.185 274), oklch(72%   0.142 276));
    --accent-hover:  light-dark(oklch(49%   0.190 274), oklch(77%   0.135 276));
    --accent-subtle: light-dark(oklch(95.5% 0.032 274), oklch(30%   0.055 274));
    --accent-ring:   light-dark(oklch(55% 0.185 274 / 0.28), oklch(72% 0.142 276 / 0.34));

    /* ---------------------------------------------------------- semantic -- */
    /* These are used as BANNER TEXT on their own -subtle background, not just
       as graphical fills. That pairing — tinted foreground on tinted
       background of the same hue — is the tightest contrast constraint in the
       system, so the light-mode subtles sit close to white (97.2%) and each
       foreground is solved against it. A brighter amber/green for charts
       belongs in a separate --*-vivid token rather than compromising these. */
    --success:        light-dark(oklch(52.6% 0.145 152), oklch(74%   0.130 155));
    --success-subtle: light-dark(oklch(97.2% 0.030 152), oklch(28%   0.052 155));
    --warn:           light-dark(oklch(54.2% 0.150  75), oklch(80%   0.135  80));
    --warn-subtle:    light-dark(oklch(97.2% 0.036  75), oklch(30%   0.055  75));
    --danger:         light-dark(oklch(55.8% 0.195  25), oklch(71%   0.155  25));
    --danger-subtle:  light-dark(oklch(97.2% 0.030  25), oklch(29%   0.062  25));
    --info:           light-dark(oklch(53.2% 0.140 240), oklch(74%   0.120 240));
    --info-subtle:    light-dark(oklch(97.2% 0.028 240), oklch(28%   0.050 240));

    /* -------------------------------------------- event category colours --
       A CATEGORICAL palette: colour carries identity, so any two badges may
       sit side by side in the timeline and must be tellable apart — including
       under colour-vision deficiency.

       These hues and lightnesses are SEARCH OUTPUT, not taste. The first
       attempt (green/blue/purple/orange) failed hard: purple↔blue measured
       ΔE 1.4 under protanopia and 14.5 even with normal vision, and
       orange↔green measured ΔE 4.6 — the classic red-green collision that
       makes those two impossible to keep together in one categorical set.

       Verified all-pairs in BOTH modes against the dataviz validator:
         light  worst CVD ΔE 8.9  ·  worst normal ΔE 18.3
         dark   worst CVD ΔE 9.6  ·  worst normal ΔE 18.3

       The LIGHTNESS is load-bearing, not decorative — dark mode's band is
       only L 0.48–0.67, so hue alone cannot carry separation there. Do not
       "harmonise" these to equal lightness; re-run the validator after any
       change. Badges also pair the colour with a text label, so identity is
       never colour-alone. */
    --cat-sport:    light-dark(oklch(66% 0.145 160), oklch(62% 0.130 160));
    --cat-work:     light-dark(oklch(74% 0.130 260), oklch(66% 0.180 260));
    --cat-personal: light-dark(oklch(50% 0.200   0), oklch(52% 0.200   0));
    --cat-family:   light-dark(oklch(58% 0.130  60), oklch(57% 0.130  60));

    /* -------------------------------------------------- progress rings --- */
    --ring-track: light-dark(oklch(91% 0.008 260), oklch(27% 0.016 265));

    /* ------------------------------------------------------------ typography
       System stack: zero bytes, zero layout shift, and instantly native on
       each OS. Fluid sizing between a 360px and 1280px viewport.            */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, "Noto Sans", sans-serif,
                 "Apple Color Emoji", "Segoe UI Emoji";
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    --text-2xs: 0.6875rem;   /* 11px — badges, meta */
    --text-xs:  0.75rem;     /* 12px */
    --text-sm:  0.8125rem;   /* 13px */
    --text-base: 0.9375rem;  /* 15px — body */
    --text-md:  1rem;        /* 16px — inputs; below this iOS zooms on focus */
    --text-lg:  1.125rem;
    --text-xl:  1.3125rem;
    --text-2xl: clamp(1.5rem,  1.2rem + 1.4vw, 1.875rem);
    --text-3xl: clamp(1.875rem, 1.4rem + 2.2vw, 2.5rem);
    --text-metric: clamp(2.5rem, 1.9rem + 3vw, 3.5rem);   /* hero numbers */

    --leading-tight: 1.18;
    --leading-snug:  1.35;
    --leading-normal: 1.55;

    --weight-normal: 400;
    --weight-medium: 500;
    --weight-semi:   600;
    --weight-bold:   700;

    --tracking-tight: -0.018em;
    --tracking-normal: 0;
    --tracking-wide:  0.03em;

    /* --------------------------------------------------------------- space --
       4px base scale.                                                       */
    --space-0:  0;
    --space-1:  0.25rem;
    --space-2:  0.5rem;
    --space-3:  0.75rem;
    --space-4:  1rem;
    --space-5:  1.25rem;
    --space-6:  1.5rem;
    --space-8:  2rem;
    --space-10: 2.5rem;
    --space-12: 3rem;
    --space-16: 4rem;

    /* -------------------------------------------------------------- radius -- */
    --radius-sm:  0.5rem;
    --radius-md:  0.75rem;
    --radius-lg:  1rem;
    --radius-xl:  1.25rem;
    --radius-2xl: 1.75rem;
    --radius-sheet: 1.5rem;
    --radius-full: 999px;

    /* ----------------------------------------------------------- elevation --
       Light mode uses layered shadows. Dark mode uses almost none — shadows
       are invisible on dark surfaces, so depth comes from the lighter surface
       colour plus a hairline ring instead.

       light-dark() WRAPS EACH COLOUR, not the shadow list. It takes exactly
       two <color> arguments, so `light-dark(0 1px 2px red, 0 0 0 1px blue)`
       is invalid — and an invalid var() substitution makes the whole
       declaration compute to `box-shadow: none`. Written that way originally,
       which meant NO card, sheet or button in the app had a shadow in either
       mode; the design read as flat and nobody could see why.

       The two looks therefore share one STRUCTURE — blur layers plus a ring —
       and each mode zeroes out the half it does not want by setting that
       colour transparent. Same result, valid CSS.                           */
    --elev-1:
      0 1px 2px light-dark(oklch(20% 0.02 265 / 0.05), transparent),
      0 1px 3px light-dark(oklch(20% 0.02 265 / 0.06), transparent),
      0 0 0 1px light-dark(transparent, oklch(30% 0.015 265 / 0.55));
    --elev-2:
      0 2px 4px  light-dark(oklch(20% 0.02 265 / 0.05), transparent),
      0 4px 12px light-dark(oklch(20% 0.02 265 / 0.08), transparent),
      0 0 0 1px  light-dark(transparent, oklch(32% 0.015 265 / 0.6));
    --elev-3:
      0 4px 8px   light-dark(oklch(20% 0.02 265 / 0.06), transparent),
      0 12px 28px light-dark(oklch(20% 0.02 265 / 0.12), transparent),
      0 0 0 1px   light-dark(transparent, oklch(35% 0.016 265 / 0.65));
    --elev-sheet:
      0 -4px 12px  light-dark(oklch(20% 0.02 265 / 0.06), transparent),
      0 -16px 48px light-dark(oklch(20% 0.02 265 / 0.16), transparent),
      0 -1px 0 0   light-dark(transparent, oklch(36% 0.016 265 / 0.7));

    /* -------------------------------------------------------------- motion --
       One physical personality across the whole app. --ease-spring is a
       sampled spring curve; it overshoots slightly, which is what makes a
       sheet feel like an object rather than a fading rectangle.             */
    --dur-instant: 90ms;
    --dur-fast:    150ms;
    --dur-base:    220ms;
    --dur-slow:    320ms;
    --dur-slower:  480ms;

    --ease-out:   cubic-bezier(0.2, 0, 0, 1);
    --ease-in:    cubic-bezier(0.4, 0, 1, 1);
    --ease-inout: cubic-bezier(0.4, 0, 0.2, 1);
    --ease-spring: linear(
      0, 0.0088, 0.0344, 0.0754, 0.1305, 0.1984, 0.2777, 0.3667, 0.4638,
      0.5673, 0.6754, 0.7863, 0.898, 1.0086, 1.0637, 1.1063, 1.1355, 1.1509,
      1.153, 1.1428, 1.1218, 1.0921, 1.0561, 1.0273, 1.0083, 0.9981, 0.9946,
      0.9957, 0.999, 1.0013, 1.0018, 1.0011, 1.0002, 1
    );

    /* ------------------------------------------------------------- layout -- */
    --appbar-height: 3.25rem;   /* 52px */
    --tabbar-height: 3.5rem;    /* 56px */
    --tap-min: 2.75rem;         /* 44px — minimum touch target */
    --content-max: 34rem;       /* single-column reading width on mobile */
    --shell-max: 90rem;

    /* Safe areas, with a 0px fallback for browsers without notches. */
    --safe-top:    env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left:   env(safe-area-inset-left, 0px);
    --safe-right:  env(safe-area-inset-right, 0px);

    /* ------------------------------------------------------------ z-index -- */
    --z-base: 0;
    --z-sticky: 10;
    --z-appbar: 20;
    --z-tabbar: 30;
    --z-fab: 35;
    --z-scrim: 40;
    --z-sheet: 50;
    --z-toast: 60;
  }

  /* No manual light/dark override exists, by design — see the file header. */

  /* Honour the OS accessibility setting. Motion is removed, not merely
     shortened, but transitions are kept at ~0 duration so JS transitionend
     handlers still fire and nothing gets stuck mid-animation. */
  @media (prefers-reduced-motion: reduce) {
    :root {
      --dur-instant: 0.01ms;
      --dur-fast:    0.01ms;
      --dur-base:    0.01ms;
      --dur-slow:    0.01ms;
      --dur-slower:  0.01ms;
      --ease-spring: linear;
    }
  }
}
