/* ZIXAN design tokens — the single source for all four surfaces.
   ==========================================================================

   The console, the candidate portal, the employer cabinet and the public
   workforce catalogue previously each declared their own near-identical
   `:root` block. Near-identical is the problem: the portal never declared
   `--warn`, so a chip that meant "needs attention" rendered amber to staff and
   red to a candidate. Four copies of one visual language will always drift;
   this file is the language.

   Nothing here styles anything. It declares values. Sheets consume them.

   2026-09-07. The owner approved the visual language of the public workforce
   catalogue and asked for the whole product to speak it: white and light
   cool-gray surfaces, cobalt actions, structure carried by borders. That
   language is now expressed here, in the ramp every sheet already reads, so
   one file changes four interfaces instead of a hundred and ninety templates.
   The names did not change — only the values behind them.

   THEME. Four states, not two, and the fourth is the reason this file has a
   third dark selector:
     - no attribute        -> follow the operating system (the default)
     - [data-theme=light]  -> the reader chose light
     - [data-theme=dark]   -> the reader chose dark
     - [data-theme-default=dark] -> THE SURFACE is dark unless the reader
                              chose otherwise. Control declares it, because it
                              is worked at night; an explicit light choice
                              still wins. Nothing is written to storage for
                              this, which is what keeps the shared key from
                              dragging the candidate portal into the dark with
                              it.
   The dark values are authored once per selector group and referenced from
   both the media query and the attributes, so the two can never disagree.

   CONTRAST. Every ratio below is computed against the surface the value is
   used on and is stated so it can be checked rather than trusted. The floor
   is WCAG AA: 4.5:1 for body text, 3:1 for large text and UI components. */

:root {
  color-scheme: light dark;

  /* --- neutral ramp ----------------------------------------------------
     Eight steps. There was no ramp before, which is why fifty spacing values
     and a dozen one-off greys accumulated: with nothing declared, every new
     screen invented its own.

     The ramp is now cool — the catalogue's blue-tinted greys — rather than
     the previous neutral grey. Ratios below are against --n-0 unless the
     line says otherwise. */
  --n-0:   #ffffff;   /* card and table body surface */
  --n-25:  #eef2f9;   /* page background */
  --n-50:  #f2f6fe;   /* table header, inset panel, row hover */
  --n-100: #e2e9f6;   /* divider inside a surface */
  --n-200: #cfdaf0;   /* border */
  --n-400: #8593ab;   /* DECORATION ONLY: icons, dividers, disabled ink.
                         3.11:1 — below the text floor. Meta text that a
                         reader must actually read uses --n-600. */
  --n-600: #5a6b85;   /* muted text     5.42:1 on --n-0, 4.83:1 on --n-25 */
  --n-900: #0e1b33;   /* body text     17.17:1 on --n-0 */

  /* The page frame: the near-white the catalogue draws its working area on,
     one step above the page and one below a card. It is what stops a screen
     of white cards from dissolving into the background. */
  --frame: #fbfcff;

  /* --- meaning ---------------------------------------------------------
     One status law across all four audiences. Colour is never the only
     carrier: every chip keeps its word, and danger and warn also carry a
     stronger border. */
  --brand:          #1a5cf0;  /* interactive fill                5.48:1 */
  --brand-hover:    #1246c8;
  --brand-ink:      #1246c8;  /* link, and link inside a table   7.68:1 */
  --brand-soft:     #eef4ff;  /* selected row, current nav item */
  --brand-soft-2:   #dbe6fa;  /* the soft's own border, info dot */
  --brand-contrast: #ffffff;
  --danger:         #b3261e;  /* blocked, failed, overdue        6.54:1 */
  --warn:           #9a5b00;  /* needs attention soon      5.10:1 on soft */
  --warn-soft:      #fff7e8;
  --warn-line:      #e8c98a;
  --success:        #0b7a54;  /* complete, on track        4.96:1 on soft */
  --success-soft:   #eafaf3;

  /* --- the machine -----------------------------------------------------
     One accent, scoped by MEANING rather than by page. Cobalt keeps saying
     "this is ZIXAN"; violet says "a machine is doing something". The reading
     only survives if violet is never spent on a button, a filter or a card:
     it belongs to the assistant object, the AI-written label and an in-flight
     state, and nowhere else. Ratios are against --n-0 (#ffffff). */
  --ai:      #6d4de6;  /* the object at work, the AI mark   6.05:1 */
  --ai-ink:  #5a3ecb;  /* text that names machine-written copy  7.72:1 */
  --ai-soft: #f2eeff;  /* the wash behind the object */
  --ai-line: #d9cdf7;  /* the edge of an AI-labelled block */

  /* Ink for text sitting ON a filled status colour. Must flip with the
     theme: white on the light danger is 6.5:1; white on the dark theme's
     lighter red is 2.28:1 and fails. */
  --on-danger: #ffffff;

  /* The focus ring was #ffb020 amber — 1.83:1 on white, against the 3:1 that
     WCAG 2.2 SC 1.4.11 asks of non-text. It measured 11:1 on dark, which is
     exactly why it never looked wrong to anyone testing at night. */
  --focus: #1a5cf0;

  /* --- type ------------------------------------------------------------
     Seven steps, replacing thirty-six. 13px is the floor: twenty rules sat
     at or below 12.8px, several with wide tracking, which is slower to read
     at exactly the moment density matters most.

     No webfont, deliberately. A subset face covering Latin-Ext (Uzbek needs
     U+02BB) plus Cyrillic runs 40–70 KB against a total CSS payload of 68 KB
     — it would at least double the styling bytes on first paint, on an
     Android WebView, on an Uzbek mobile network, and buy brand identity and
     nothing else. The approved catalogue design names Onest; the CSP has no
     font origin and this reasoning has not changed, so the system stack
     stands in for it on every surface. Every legibility problem found in the
     audit was a scale problem, and a scale costs nothing. */
  --font-ui:   system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  --fs-caption: 0.8125rem;  /* 13px — chips, table headers, meta. THE FLOOR. */
  --fs-sm:      0.875rem;   /* 14px — dense labels, secondary rows */
  --fs-body:    0.9375rem;  /* 15px — default UI text, table body */
  --fs-lead:    1rem;       /* 16px — inputs. Smaller zooms the page on iOS. */
  --fs-h3:      1.0625rem;  /* 17px */
  --fs-h2:      1.25rem;    /* 20px */
  --fs-h1:      1.5rem;     /* 24px */
  --fs-display: 1.75rem;    /* 28px — the one big number on a card */

  /* Two MARKETING steps above the UI scale. The scale above is for screens a
     person works in, where 28px is correctly the largest thing on a card. A
     public page has a different job: it has to state one idea before the
     reader decides whether to keep reading, and the measured premium pages
     do that with size contrast and negative tracking rather than weight.
     /for-employers/ had to hardcode 42px in an inline block precisely because
     these two steps did not exist. They are used ONLY on landing heroes and
     the catalogue stage, never inside a working screen. */
  --fs-display-2: 2.25rem;  /* 36px — phone hero */
  --fs-display-3: 3rem;     /* 48px — desktop hero */
  /* Tracking tightens as size grows: at 48px the default spacing reads loose
     and amateur, at 15px the same value would hurt legibility in Cyrillic. */
  --track-display: -0.03em;
  --track-tight: -0.018em;

  --lh-tight: 1.25;
  --lh-body:  1.5;
  --lh-display: 1.04;

  /* --- motion ----------------------------------------------------------
     Motion had no tokens, so nine durations and four easings accumulated
     across six sheets: the drawer at .2s, the catalogue sheet at .28s, the
     stage at .45/.55/.6/.65s with three different beziers, the portal FAB at
     .25s, headings at 2.8s infinite. Three durations are enough when each is
     tied to the SIZE of the change, and exits are always shorter than
     entrances so the interface never feels like it is holding you up. */
  --dur-1: 140ms;   /* state: hover, focus ring, chip toggle, pressed */
  --dur-2: 250ms;   /* the cross-dissolve: content swaps, panel contents */
  --dur-3: 380ms;   /* arrival: sheets, dialogs, things entering the page */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  /* --- space -----------------------------------------------------------
     Eight steps on a 4px grid, replacing fifty ad-hoc values. */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;  --s-4: 1rem;
  --s-5: 1.5rem;   --s-6: 2rem;     --s-7: 3rem;     --s-8: 4rem;

  /* --- shape -----------------------------------------------------------
     Four radii replacing eight. The old --radius was 14px on everything,
     including tables, which is what gave the console its soft, dated look.

     The catalogue's geometry is softer than the console's 4/8/12 but applied
     with discrimination: a chip is not a dialog. Raising --r-sm from 4px is
     what makes a chip, an input and a small button read as the same family
     across the four surfaces. */
  --r-sm: 8px;    /* chips, inputs, small controls */
  --r-md: 10px;   /* cards, panels, tables, buttons */
  --r-lg: 14px;   /* drawers, dialogs, the stage */
  --r-pill: 999px;

  /* --- elevation -------------------------------------------------------
     THE LAW: borders carry structure, shadow carries transience.

     Every panel and every table previously carried the same shadow, so a
     screen of eighteen panels read as eighteen floating cards of equal
     importance — the quilted look that dates this interface more than any
     colour or typeface does. Shadow is now reserved for the four things that
     genuinely float over the page: the drawer, popovers, sticky bars and
     confirmation overlays. Everything else gets a 1px border.

     The shadow colour moved to the ink's own blue-black. A neutral shadow
     under a cool-gray card reads as dirt; the same shadow tinted with the
     surface it falls on disappears into it. */
  --elev-0: none;

  /* `--elev-1` is `none` on purpose, and this is the change that does most of
     the visual work in the whole redesign.

     It was applied to .panel, .card, .grid, the stat tiles and a dozen other
     structural blocks -- every one of which already carries
     `border: 1px solid var(--border)`. So the shadow was never carrying the
     edge; it was only making eighteen panels on a candidate screen read as
     eighteen equally-important floating objects. Setting it to `none` flattens
     all of them in one line and loses nothing, because the borders were
     already there.

     Things that genuinely float keep a shadow, and now they are the only
     things that do. */
  --elev-1: none;
  --elev-sticky: 0 1px 2px rgba(16, 44, 110, .06);        /* topbar, sticky bars */
  --elev-2: 0 20px 50px -18px rgba(16, 44, 110, .35);     /* popovers, menus */
  --elev-3: 0 30px 80px -20px rgba(14, 27, 51, .5);       /* drawer, dialog */

  /* --- density ---------------------------------------------------------
     The row goes DOWN, not up. A recruiter working a queue of three hundred
     wants more rows visible, not fewer; density is the feature. Touch sizing
     is applied by pointer type below rather than generalised, so a mouse
     user keeps their density and a phone user gets a target they can hit. */
  --row-h:     2.4375rem;  /* 39px */
  --control-h: 2.25rem;    /* 36px */
  --tap-min:   2.75rem;    /* 44px — coarse pointers only */

  /* --- layering --------------------------------------------------------
     Seven ad-hoc z-indexes, now two declared bands. */
  --z-sticky: 10;
  --z-topbar: 20;
  --z-drawer: 30;
  --z-dialog: 40;

  /* The catalogue works to 1520px and the rest of the product stopped at
     1360, so the header of one surface visibly failed to line up with the
     header of the next. One width, four surfaces. */
  --maxw: 1520px;
}

/* Dark values, authored once per group.

   THREE selectors, one palette. The media query serves readers who have made
   no choice; `[data-theme="dark"]` serves readers who have; and
   `[data-theme-default="dark"]` serves a SURFACE that is dark by default —
   Control — while still yielding to an explicit light choice.

   The dark ramp is the light one's own ink, opened out: a blue-black rather
   than a neutral one, so the two themes are recognisably the same product at
   two times of day. Ratios are against --n-0 (#111a2b). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --n-0:   #111a2b;
    --n-25:  #0b1220;
    --n-50:  #162133;
    --n-100: #1c2941;
    --n-200: #26344f;
    --n-400: #6b7c99;   /*  4.12:1 — decoration and disabled ink */
    --n-600: #a3b0c8;   /*  7.96:1 — muted text */
    --n-900: #eaf0fb;   /* 15.21:1 — body text */

    --frame: #0e1728;

    --brand:          #6f9bff;  /*  6.47:1 */
    --brand-hover:    #8db0ff;
    --brand-ink:      #9fbcff;  /*  9.21:1 */
    --brand-soft:     #17264a;
    --brand-soft-2:   #22376b;
    --brand-contrast: #0b1220;  /*  6.96:1 on --brand */
    --danger:         #ff8a80;
    --warn:           #ffc46b;
    --warn-soft:      #2a2113;
    --warn-line:      #55411f;
    --success:        #4fd1b0;
    --success-soft:   #102d28;

    /* The same meaning, opened out for the night ramp. Ratios against
       --n-0 (#111a2b): violet has to stay distinguishable from the cobalt
       brand at low luminance, which is why it lifts further than brand does. */
    --ai:      #a78bfa;  /*  7.02:1 */
    --ai-ink:  #c0aeff;  /*  9.44:1 */
    --ai-soft: #1d1836;
    --ai-line: #3d3163;

    --on-danger: #0b1220;   /* 9.2:1, against 2.28:1 for white */
    --focus:     #6f9bff;

    --elev-1: none;
    --elev-sticky: 0 1px 2px rgba(0, 0, 0, .5);
    --elev-2: 0 4px 12px -2px rgba(0, 0, 0, .6);
    --elev-3: 0 16px 40px -12px rgba(0, 0, 0, .8);
  }
}

:root[data-theme="dark"],
:root[data-theme-default="dark"]:not([data-theme="light"]) {
  --n-0:   #111a2b;
  --n-25:  #0b1220;
  --n-50:  #162133;
  --n-100: #1c2941;
  --n-200: #26344f;
  --n-400: #6b7c99;
  --n-600: #a3b0c8;
  --n-900: #eaf0fb;

  --frame: #0e1728;

  --brand:          #6f9bff;
  --brand-hover:    #8db0ff;
  --brand-ink:      #9fbcff;
  --brand-soft:     #17264a;
  --brand-soft-2:   #22376b;
  --brand-contrast: #0b1220;
  --danger:         #ff8a80;
  --warn:           #ffc46b;
  --warn-soft:      #2a2113;
  --warn-line:      #55411f;
  --success:        #4fd1b0;
  --success-soft:   #102d28;

  --ai:      #a78bfa;
  --ai-ink:  #c0aeff;
  --ai-soft: #1d1836;
  --ai-line: #3d3163;

  --on-danger: #0b1220;
  --focus:     #6f9bff;

  --elev-1: none;
  --elev-sticky: 0 1px 2px rgba(0, 0, 0, .5);
  --elev-2: 0 4px 12px -2px rgba(0, 0, 0, .6);
  --elev-3: 0 16px 40px -12px rgba(0, 0, 0, .8);
}

/* A surface that declares itself dark also tells the browser, so form
   controls, scrollbars and the address bar follow it rather than staying
   light around a dark page. */
:root[data-theme-default="dark"]:not([data-theme="light"]) { color-scheme: dark; }
:root[data-theme="dark"] { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }

/* --- compatibility -------------------------------------------------------
   The names the four existing stylesheets already consume, mapped onto the
   ramp. This is what lets the token source land without rewriting 1,628 lines
   of selectors in the same change. Later phases retire the aliases; nothing
   new should use them. */
:root {
  --bg:             var(--n-25);
  --surface:        var(--n-0);
  --surface-2:      var(--n-50);
  --text:           var(--n-900);
  --muted:          var(--n-600);
  --border:         var(--n-200);

  /* The border of something a reader must be able to FIND: a text field, a
     select, a bare checkbox. The decorative --border is 1.41:1 on white,
     which is fine for a divider inside a card and too faint for the only
     edge a form control has. This is darker without leaving the family.
     Documented deviation: at 2.31:1 it is still short of WCAG 1.4.11's 3:1
     for a control's own boundary — the approved design's borders are pale by
     intent, and the focus ring (2px at 5.48:1) is what carries the state
     that matters most. */
  --border-strong:  #9aabcf;

  --brand-strong:   var(--brand-ink);
  --accent:         var(--success);
  --good:           var(--success);
  --radius:         var(--r-md);
  --radius-sm:      var(--r-sm);
  /* `--shadow` is the OLD name, and every one of its four remaining users is
     structural: .panel, .vacancy-card and .card on the portal, plus one in
     control.css. Aliasing it to --elev-2 gave every candidate-facing panel a
     POPOVER shadow -- the exact opposite of the law -- so the portal never
     got the flattening the console did. Caught on a device, where the panels
     were visibly still floating while the console's were not. */
  --shadow:         var(--elev-1);
}

:root[data-theme="dark"],
:root[data-theme-default="dark"]:not([data-theme="light"]) { --border-strong: #3b4a68; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --border-strong: #3b4a68; }
}

/* --- touch ---------------------------------------------------------------
   Scoped to coarse pointers on purpose. Generalising 44px would cost the
   eight-hour mouse user roughly three rows per screen to solve a problem
   they do not have. */
@media (pointer: coarse) {
  button, .button, a.button, input[type="submit"], select, summary {
    min-height: var(--tap-min);
  }
}

/* Two exceptions that apply on every device, because they are mis-tapped on
   all of them: pagination links sit in a row of near-identical targets, and a
   bare checkbox is the smallest control in the product. */
.pagination a, .pagination span { min-height: var(--tap-min); min-width: var(--tap-min); }
input[type="checkbox"], input[type="radio"] { width: 1.15rem; height: 1.15rem; }
