/* ============================================================
   CLEO design tokens — THE single source of truth
   ============================================================
   Loaded by BOTH entry points, before every other authored sheet:
     - templates/layouts/base.html            (staff app)
     - apps/portal/templates/portal/base.html (client portal)

   The staff app and the client portal are two separate shells that
   happen to share a brand. Before this file existed each one carried
   its own copy of the navy/amber values and invented its own radii and
   font sizes, which is how portal.css drifted 16 lines behind its twin
   and how the two shells reached 15 radius values and 50 font sizes
   between them.

   Custom properties only. Nothing in here paints anything on its own,
   so loading it can change nothing until a sheet consumes a token.
   The one exception is the reduced-motion block at the bottom, which
   is inert unless the visitor has asked for reduced motion.

   RULE: a colour, radius, font size or control height that more than
   one sheet needs is declared HERE and nowhere else.
   apps/portal/tests_design_tokens.py fails the build if a second sheet
   redeclares one.
   ============================================================ */

:root {

  /* ---- Brand ------------------------------------------------
     Amber is the EMPHASIS colour and the only thing allowed to shout -
     one call to action per page. Navy is structure. Blue is links, and
     links are not emphasis: they are the same weight as body text with a
     different hue, which is why a page can carry many of them without
     competing with its one amber button.
     A fourth hue, #185FA5, was a genuinely stray second link blue and is
     gone. Do not add a fifth. */
  --navy:            #1f3357;
  --navy-700:        #18283f;
  --navy-deep:       #1a2f50;
  --amber:           #f5a623;
  --amber-hover:     #d98a0c;

  /* The link blue. NAMED --blue and valued #2f4b7c to match the marketing
     site's own token exactly (cleo-site/src/app/globals.css), because two
     codebases for one brand should at least agree on the vocabulary.
     It was hardcoded 25 times across 17 templates and had no token: brand.css
     declared --brand-blue once and nothing ever used it via var(), so it
     survived every colour sweep this session and made "CLEO has one accent"
     an overstatement.
     Unlike --navy and --amber this one IS safe to flip for dark, because all
     25 uses are `color:` and none is a fill. A guard enforces that. */
  --blue:            #2f4b7c;   /* 8.68:1 on white */
  --amber-soft:      #fff3df;
  --amber-ink:       #97590a;   /* 5.60:1 on white, 5.10:1 on --amber-soft */

  /* ---- Ink and surfaces --------------------------------------
     These values are the MARKETING SITE's, adopted 2026-08-20 on the owner's
     call (site -> portal). cleo-site/src/app/globals.css is the brand source;
     brand.css has always said this file "carries the website look", and the
     two had drifted into six near-misses - three of them sharing a token NAME
     with a different value, which is the kind anyone copying between the two
     codebases would get wrong.

     It was not only cosmetic. --amber-ink was #9a6a04, whose own comment
     claimed 4.6:1 "on white" - true, but it is paired with --amber-soft, where
     it measured 4.31:1 and FAILED AA. The site's #97590a is 5.10:1 there.

     Re-check:  python Desktop/Cleo/_staging/brand_match.py  (expect 9/9) */
  --ink:             #1d2733;
  --ink-muted:       #6b7280;
  --ink-quiet:       #5b6573;
  --surface:         #ffffff;
  --surface-sunk:    #f5f6f8;
  --line:            #e6e9ef;
  --line-strong:     #d8dce3;

  /* ---- Status ------------------------------------------------
     Used to mark the MINORITY that needs attention. A passing or
     normal state is neutral grey, never green — colour on every row
     is the same as colour on no row. */
  --ok-bg:           #eaf3de;  --ok-ink:   #27500a;  --ok-key:  #3b6d11;
  --warn-bg:         #faeeda;  --warn-ink: #633806;  --warn-key:#854f0b;
  --bad-bg:          #fcebeb;  --bad-ink:  #791f1f;  --bad-key: #a32d2d;
  --neutral-bg:      #f2f4f7;  --neutral-ink: var(--ink-quiet);

  /* ---- Solid amber CTA ---------------------------------------
     THE ONE PAIR THAT DELIBERATELY DOES NOT FLIP, and it is defined
     here rather than in .dark for exactly that reason. Do not add a
     dark override: that override IS the bug this replaces.

     `.rn-btn` was `background:#f59e0b; color:var(--warn-ink)` in 15
     templates. The background is a literal, so it cannot move between
     themes, while --warn-ink does: #633806 in light (4.66:1, fine) and
     #fde68a in dark (1.72:1, all but invisible). A themed foreground on
     an unthemeable ground can only be right in one theme.

     #451a03 on #f59e0b is 6.97:1 in BOTH. The value is not invented -
     templates/pages/toolbox/_listchrome.html had already pinned that
     exact literal, so this promotes the fix somebody had already found
     to a token the whole app can use.

     Named `cta` because band A's family regex is ^--([a-z0-9]+)-(...)$
     and will not match a hyphenated family: --amber-solid-ink would
     never be scored by anything. As `--cta-ink` / `--cta-bg` the pair
     is picked up automatically and checked in both themes forever. */
  --cta-bg:          #f59e0b;  --cta-ink:  #451a03;

  /* ---- Radius ------------------------------------------------
     Four steps, each a clear jump from the last. A fifth value is a
     second family pretending to be a variant. */
  --r-inline:        4px;    /* things inside other things: marks, tags, page scans */
  --r-control:       8px;    /* things you click or type into */
  --r-card:          12px;   /* containers */
  --r-pill:          999px;  /* pills and chips */
  --r-round:         50%;    /* avatars only */

  /* ---- Typeface ----------------------------------------------
     Inter for text, Oswald for headings -- the pairing the CLEO website
     already uses. Confirmed intentional by the owner 2026-08-19, so the
     design hook's "overused font" warning on Inter is waived, not a defect.

     Before this, one product rendered in three faces: the client portal in
     Arial, the staff sign-in in Tailwind's ui-sans-serif default (that shell
     never loaded brand.css), and the staff app itself in Inter/Oswald. A
     client saw Arial, a staff member saw system-ui at sign-in and Inter one
     click later.

     The VALUE lives here; each shell applies it in its own sheet. That keeps
     this file declaration-only -- it must stay unable to paint anything, so
     that loading it can never change a page on its own -- and it avoids
     pulling brand.css's Tailwind !important overrides into the portal, which
     would restyle the portal's buttons as a side effect.

     Every shell that uses these MUST also carry the Google Fonts <link>, or
     the face silently falls back to the stack below and nothing reports it. */
  --font-body:       'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Arial, sans-serif;
  --font-display:    'Oswald', ui-sans-serif, system-ui, 'Segoe UI', Arial, sans-serif;
  --display-tracking: .015em;

  /* ---- Type scale --------------------------------------------
     Eight steps. Half-pixel sizes (9.5px, 12.5px, 13.5px) are not a
     scale, they are the absence of one. */
  --fs-2xs:          0.6875rem; /* 11px - pill text, fine print */
  --fs-xs:           0.75rem;   /* 12px - labels, meta */
  --fs-sm:           0.8125rem; /* 13px - table body, secondary */
  --fs-md:           0.875rem;  /* 14px - body */
  --fs-lg:           1rem;      /* 16px - lead, section head */
  --fs-xl:           1.25rem;   /* 20px - page title */
  --fs-2xl:          1.5rem;    /* 24px - KPI figure (2:1 against its --fs-xs label) */
  --fs-3xl:          2rem;      /* 32px - display */

  /* ---- Control height ----------------------------------------
     Two heights, and the second one is a size decision rather than an
     accident. Inputs at 34, form fields at 36 and buttons at 38 was three
     heights for three things sitting in the same row; the sign-in form was
     a 40px input above a 42px button, a 2px difference nobody chose.
     --h-control is the default. --h-control-lg is for a surface whose whole
     job is one action -- sign in, reset a password -- where the field and
     its button are BOTH larger, and still match each other. */
  --h-control:       36px;
  --h-control-lg:    44px;

  /* ---- Elevation ---------------------------------------------
     Four steps, layered, low-opacity, and tinted with the navy. The sheet
     previously carried ten distinct shadows, two of them a single hard
     rgba(0,0,0,.15) slab, and black shadows read as grey dirt over a navy
     and amber palette rather than as depth. */
  --shadow-sm:       0 1px 3px rgba(31,51,87,.12);
  --shadow-lift:     0 1px 2px rgba(31,51,87,.08), 0 4px 12px rgba(31,51,87,.10);
  --shadow-page:     0 1px 3px rgba(31,51,87,.12), 0 6px 18px rgba(31,51,87,.18);
  --shadow-modal:    0 4px 12px rgba(31,51,87,.10), 0 12px 36px rgba(31,51,87,.16);
  /* The focus ring is amber on purpose: it is the one place the accent is
     allowed to appear without being an emphasis decision. */
  --shadow-focus:    0 0 0 3px rgba(245,166,35,.18);

  /* ---- Motion ------------------------------------------------
     One named feel, so a transition added in a template matches the
     one added six months ago in another. */
  --ease:            cubic-bezier(.2, 0, .2, 1);
  --dur-fast:        120ms;
  --dur:             200ms;
}

/* ============================================================
   Dark theme
   ============================================================
   Class-based, matching how the app actually switches: layouts/base.html adds
   `.dark` to <html> from localStorage. NOT prefers-color-scheme - CLEO's dark
   mode is an explicit choice the user makes with the header toggle, and the
   shipped Tailwind build is class-based too.

   TWO TOKENS ARE DELIBERATELY NOT REDEFINED HERE: --navy and --amber.
   They are SURFACE colours as often as text ones (.portal-bar, .chip.on and
   the primary button all fill with them), so flipping them for dark would turn
   every navy bar into a pale blue one. Text that needs to change gets
   --navy-text / --amber-text instead, which is why those exist as separate
   tokens rather than as "the navy, but lighter".

   SCOPE, honestly stated: the client portal has no dark mode at all (no
   toggle, no .dark on its shell), and the semantic surface/ink tokens below
   are consumed only by portal.css today. So these values change nothing that
   renders right now. They are here so the layer can express both themes -
   36 staff templates currently hand-write 278 `.dark` rules between them,
   which is the same duplicated state this file exists to end, and those can
   now migrate onto tokens one template at a time instead of in one risky
   sweep. Dark mode is NOT broken today; it is hand-maintained.

   Every value below was measured against dark:bg-gray-800 (#1f2937), the
   surface these templates actually use: ink 13.3:1, ink-muted 5.8:1,
   navy-text 10.3:1, amber-text 10.2:1. */
:root {
  /* Light values for the two text tokens; the dark block overrides them. */
  --navy-text:  var(--navy);
  --amber-text: var(--amber-ink);
}

.dark {
  --ink:            #f3f4f6;   /* gray-100  13.34:1 on gray-800 */
  --ink-muted:      #9ca3af;   /* gray-400   5.78:1 */
  --ink-quiet:      #9ca3af;
  --surface:        #1f2937;   /* gray-800, the card surface these pages use */
  --surface-sunk:   #111827;   /* gray-900 */
  --line:           #374151;   /* gray-700 */
  --line-strong:    #4b5563;   /* gray-600 */

  --navy-text:      #bfdbfe;   /* blue-200  10.33:1 - navy is 1.17:1 here */
  --blue:           #93c5fd;   /* blue-300   8.14:1 - #2f4b7c is 1.69:1 here */
  --amber-text:     #fcd34d;   /* amber-300 10.18:1 - amber-ink is 3.10:1 */

  /* Status tints have to invert: a pale wash under dark text becomes a deep
     wash under light text, or the row glows. */
  --ok-bg:      #14320a;  --ok-ink:   #bbf7d0;  --ok-key:  #86efac;
  --warn-bg:    #422006;  --warn-ink: #fde68a;  --warn-key:#fcd34d;
  --bad-bg:     #450a0a;  --bad-ink:  #fecaca;  --bad-key: #fca5a5;
  --neutral-bg: #374151;  --neutral-ink: #d1d5db;

  /* Shadows do almost nothing on a dark surface; depth comes from the border.
     Keeping the navy tint but deepening the alpha stops them turning to mud. */
  --shadow-sm:    0 1px 3px rgba(0,0,0,.40);
  --shadow-lift:  0 1px 2px rgba(0,0,0,.30), 0 4px 12px rgba(0,0,0,.36);
  --shadow-page:  0 1px 3px rgba(0,0,0,.40), 0 6px 18px rgba(0,0,0,.48);
  --shadow-modal: 0 4px 12px rgba(0,0,0,.40), 0 12px 36px rgba(0,0,0,.52);
}

/* ---- Reduced motion ------------------------------------------
   16 templates declare a transition or an animation; exactly one of
   them guarded it. Guarding them one at a time is how the seventeenth
   gets missed, so this is global and applies to both shells.
   Inert unless the visitor has actually asked for reduced motion. */
@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;
  }
}
