/* =========================================================================
   Navis design tokens. The single source of truth.

   Colour is authored in oklch, not hex. That is not a style preference: oklch
   is perceptually uniform, so lightness is the same perceived step everywhere
   on the wheel. Deriving a ramp, checking a contrast ratio and building a
   dark theme all become arithmetic instead of eyeballing. The values below
   are the proposed palette converted exactly, so nothing renders differently.

   Both the app shell and the marketing pages import this file. Before it
   existed, --accent and --display were declared twice by hand with no link
   between the copies, and index.html carried a third palette of sRGB
   approximations for the phone mock. Every change had to be made twice, and
   the copies drifted.

   Colour is authored in oklch and keeps the names it already had. The audit
   found 44 semantic tokens with a complete parallel dark theme, which is a
   working system; renaming it across 1,620 CSS lines and 7,100 JS lines
   would buy nothing and risk everything.

   What this file adds is the four families that genuinely did not exist:
   spacing, type scale, weights, and a warning colour. Those were 33, 31, 16
   and 5 values respectively, all authored inline at the call site.
   ========================================================================= */

:root{
    /* ---- material ----
       Two themes from one token set. Light is the default because the product
       is meant to feel like a calm object in daylight rather than a console.
       Nothing below is a colour picked to look nice: each one is a role, so
       swapping the theme swaps meaning consistently rather than by hand. */

    /* light, the default */
    --ground:      oklch(0.972 0.004 255);
    --ground-deep: oklch(0.962 0.007 250);
    --surface:     oklch(1 0 0);      /* glass, not a flat panel */
    --surface-2:   oklch(0.950 0.005 255);
    --surface-solid: oklch(1 0 0);
    --line:        oklch(0.62 0.03 255 / 0.16);
    --line-soft:   oklch(0.62 0.03 255 / 0.09);
    --ink:         oklch(0.24 0.035 262);
    --ink-soft:    oklch(0.44 0.03 258);
    /* Metadata, timestamps, secondary lines. Was 0.60, which measured 3.9:1
     against a white card: under the 4.5 floor and exactly the "light grey for
     elegance" failure that makes an interface tiring to read. Darkened until
     it measured, not until it looked about right. */
  --ink-mute:    oklch(0.52 0.03 258);

    /* primary is the blue that carries every action */
    --accent:      oklch(0.55 0.20 262);
    --accent-deep: oklch(0.47 0.19 262);
    --accent-ink:  oklch(0.99 0.005 255);
    --accent-wash: oklch(0.55 0.20 262 / 0.09);
    --accent-line: oklch(0.55 0.20 262 / 0.22);

    /* gold is reserved for what you have earned, never for navigation */
    --gold:        oklch(0.78 0.14 78);
    --gold-wash:   oklch(0.78 0.14 78 / 0.14);

    --tide:        oklch(0.55 0.12 200);
    --tide-deep:   oklch(0.46 0.11 205);
    --good:        oklch(0.62 0.14 155);
    --warn:        oklch(0.72 0.14 70);
    --bad:         oklch(0.60 0.17 22);
    --bad-wash:    oklch(0.60 0.17 22 / 0.10);

    /* light comes from above and slightly behind, so the top edge catches it */
    --edge-hi:  inset 0 1px 0 oklch(1 0 0 / 0.75);
    --edge-lo:  inset 0 -1px 0 oklch(0.62 0.03 255 / 0.06);
    /* shadows are blue and layered, never black and never single */
    --lift-1: 0 1px 2px oklch(0.45 0.06 258 / 0.05), 0 3px 10px oklch(0.45 0.06 258 / 0.05);
    --lift-2: 0 2px 6px oklch(0.45 0.06 258 / 0.06), 0 12px 30px oklch(0.45 0.06 258 / 0.08);
    --lift-3: 0 4px 12px oklch(0.45 0.06 258 / 0.07), 0 26px 60px oklch(0.45 0.06 258 / 0.12);
    --glass-blur: saturate(1.7) blur(22px);

    /* The material's rest state. Script moves these as the surface moves; the
       values here are what it looks like standing still, so a page that never
       scrolls still gets a lit rim rather than a flat one. */
    --lens-angle: 176deg;
    --spec-x: 30%;
    --spec-y: -20%;
    --spec: 1;
    --glass-thick: 0;

    --radius: 22px;      /* cards, sheets, anything holding content */
    --radius-s: 14px;    /* inputs, buttons, rows */
    --radius-xs: 10px;   /* chips, tags, icon wells */
    --ease: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-soft: cubic-bezier(0.22, 1, 0.36, 1);
    --z-nav: 40; --z-overlay: 60; --z-reward: 80;
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);

  /* ---------------------------------------------------------------------
     SPACE.  Seven steps. 33 values across 550 uses collapse to these.
     Every integer from 2 to 22 was in use, which is not a scale, it is a
     series of nudges fixing problems that belonged elsewhere.
     --------------------------------------------------------------------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;

  /* ---------------------------------------------------------------------
     TYPE SCALE.  Six steps in rem, replacing 31 px values across 236 uses.
     Six of the old values did 64% of the work inside a 5px band and were
     not on a scale.
     --------------------------------------------------------------------- */
  --fs-1: 0.75rem;      /* 12px  metadata, timestamps, tags */
  --fs-2: 0.8125rem;    /* 13px  secondary text, field notes */
  --fs-3: 0.9375rem;    /* 15px  body, the default */
  --fs-4: 1.0625rem;    /* 17px  emphasised body, list titles */
  --fs-5: 1.375rem;     /* 22px  section headings */
  --fs-6: 2rem;         /* 32px  screen title */

  /* One step above the scale, for numerics read at a distance rather than
     text read at reading distance. The session countdown is glanceable from
     across a desk; at 32px it stops being. Two elements use it. */
  --fs-numeric: 3.5rem;

  /* ---------------------------------------------------------------------
     WEIGHT.  Three, product-wide, no exceptions.

     The stack resolves variable weights on macOS and iOS. On Windows and
     most Android builds Segoe UI and Roboto snap to the nearest static cut,
     so 540, 560 and 580 all render as 500 or 600. Eleven of the old sixteen
     weights were invisible to most users, which means the hierarchy that
     read correctly on the phone this was designed on was flat everywhere
     else.
     --------------------------------------------------------------------- */
  --fw-body: 400;
  --fw-med:  500;
  --fw-hi:   600;

  /* ---------------------------------------------------------------------
     WARNING.  Five untokenised reds existed in hue 25 to 28, all meaning
     the same thing. Two now, and they are different states: --warn is
     recoverable, --danger is not.
     --------------------------------------------------------------------- */
  --warn:   oklch(0.72 0.17 55);
  --danger: oklch(0.58 0.20 27);

  /* ---------------------------------------------------------------------
     RADIUS.  Controls, cards, pills. Nothing else.
     --------------------------------------------------------------------- */
  /* ---------------------------------------------------------------------
     TYPE FACES.  The display serif was declared three times by hand, once
     per marketing page, with no link between the copies. It appears in
     exactly two places in the product: the wordmark and the screen title.
     --------------------------------------------------------------------- */
  --font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", system-ui, sans-serif;
  --display: "Instrument Serif", Georgia, "Times New Roman", serif;

  --r-control: 10px;
  --r-card:    16px;
  --r-pill:    999px;

  /* ---------------------------------------------------------------------
     MOTION and LAYER. Durations were authored inline as .12s, .3s, .45s and
     a dozen others; three names cover every use, and reduced motion can
     then be honoured by changing three values rather than hunting rules.
     --------------------------------------------------------------------- */
  --dur-fast: 140ms;
  --dur-base: 240ms;
  --dur-slow: 420ms;

  --z-nav:   50;
  --z-scrim: 90;
  --z-sheet: 100;

  /* The informational tint. --tide is machine state, this is its wash. */
  --tide-wash: oklch(0.72 0.09 210 / 0.14);
}

@media (prefers-reduced-motion: reduce){
  :root{ --dur-fast:0ms; --dur-base:0ms; --dur-slow:0ms; }

  /* Behind a sheet or a dialog. Translucent, not opaque: an overlay that
     replaces the screen loses the person's place in the app. Paired with a
     blur so what is behind reads as context rather than content. */
  --scrim:       oklch(0.24 0.02 258 / 0.40);

  /* Motion.
     Exponential ease-out, no bounce and no elastic. Things arrive and settle;
     they do not overshoot and correct, which reads as a UI showing off rather
     than a UI responding. Durations are short enough to feel like cause and
     effect: past about 300ms a transition stops being feedback and becomes a
     wait. */
  /* One focus ring, applied everywhere. A keyboard user currently gets no
     visible focus anywhere except the nav rail, across 205 pressable
     elements. Three pixels of accent at low alpha reads clearly on both
     themes without becoming a second border. */
  --focus-ring: 0 0 0 3px oklch(0.55 0.20 262 / 0.28);
  /* Same shape in the error hue, so a bad field is obvious without needing
     colour alone to carry it: the aria-invalid border changes too. */
  --focus-ring-bad: 0 0 0 3px oklch(0.58 0.20 27 / 0.24);

  /* Elevation, four steps.
     There was one, so a card and a modal floated identically and depth said
     nothing about what kind of thing you were looking at. Each step is a
     answer to "how far is this from the page": a card sits on it, a sheet
     sits above it, a dialog sits above everything.

     Tinted toward the ink hue rather than pure black, because a neutral-black
     shadow over a cool-grey surface reads as dirt. */
  --shadow-1: 0 1px 2px oklch(0.24 0.03 258 / 0.06),
              0 1px 3px oklch(0.24 0.03 258 / 0.10);
  --shadow-2: 0 2px 4px oklch(0.24 0.03 258 / 0.06),
              0 4px 8px oklch(0.24 0.03 258 / 0.10);
  --shadow-3: 0 4px 8px oklch(0.24 0.03 258 / 0.06),
              0 12px 20px oklch(0.24 0.03 258 / 0.12);
  --shadow-4: 0 12px 24px oklch(0.24 0.03 258 / 0.10),
              0 28px 56px oklch(0.24 0.03 258 / 0.22);

  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
}

/* Dark is a token swap, not a second stylesheet. Every component is written
   once and both themes resolve it. */
html[data-theme="dark"]{
  --scrim:       oklch(0.08 0.015 258 / 0.62);
  --focus-ring:  0 0 0 3px oklch(0.70 0.15 262 / 0.40);
  --focus-ring-bad: 0 0 0 3px oklch(0.70 0.18 27 / 0.38);

  /* Dark needs its own. A soft shadow is invisible against near-black, so
     these are deeper and carry a hairline of light at the top edge, which is
     what actually separates a raised surface on a dark ground. */
  --shadow-1: 0 1px 2px oklch(0 0 0 / 0.40),
              inset 0 1px 0 oklch(1 0 0 / 0.04);
  --shadow-2: 0 4px 10px oklch(0 0 0 / 0.46),
              inset 0 1px 0 oklch(1 0 0 / 0.05);
  --shadow-3: 0 10px 24px oklch(0 0 0 / 0.52),
              inset 0 1px 0 oklch(1 0 0 / 0.06);
  --shadow-4: 0 20px 52px oklch(0 0 0 / 0.62),
              inset 0 1px 0 oklch(1 0 0 / 0.07);
    --ground:      oklch(0.155 0.02 258);
    --ground-deep: oklch(0.13 0.02 258);
    --surface:     oklch(0.235 0.022 258);
    --surface-2:   oklch(0.295 0.022 258);
    --surface-solid: oklch(0.205 0.02 258);
    /* 0.14 alpha was invisible: every card edge dissolved into the ground and
       the whole screen read as one sheet. A hairline on dark has to be light
       enough to see, because there is no shadow doing the work down here. */
    --line:        oklch(0.80 0.03 258 / 0.22);
    --line-soft:   oklch(0.80 0.03 258 / 0.13);
    --ink:         oklch(0.965 0.006 258);
    --ink-soft:    oklch(0.80 0.014 258);
    --ink-mute:    oklch(0.68 0.02 258);

    --accent:      oklch(0.70 0.15 262);
    --accent-deep: oklch(0.60 0.16 262);
    --accent-ink:  oklch(0.16 0.03 258);
    --accent-wash: oklch(0.70 0.15 262 / 0.16);
    --accent-line: oklch(0.70 0.15 262 / 0.34);

    --gold:        oklch(0.82 0.13 76);
    --gold-wash:   oklch(0.82 0.13 76 / 0.16);

    --tide:        oklch(0.74 0.09 200);
    --tide-deep:   oklch(0.55 0.08 203);
    --good:        oklch(0.74 0.13 158);
    --warn:        oklch(0.80 0.13 72);
    --bad:         oklch(0.70 0.16 25);
    --bad-wash:    oklch(0.70 0.16 25 / 0.14);

    --edge-hi: inset 0 1px 0 oklch(1 0 0 / 0.07);
    --edge-lo: inset 0 -1px 0 oklch(0 0 0 / 0.14);
    --lift-1: 0 1px 2px oklch(0.05 0.02 258 / 0.30), 0 3px 10px oklch(0.05 0.02 258 / 0.24);
    --lift-2: 0 2px 6px oklch(0.05 0.02 258 / 0.34), 0 12px 30px oklch(0.05 0.02 258 / 0.34);
    --lift-3: 0 4px 12px oklch(0.05 0.02 258 / 0.38), 0 26px 60px oklch(0.05 0.02 258 / 0.46);
  
  --warn:   oklch(0.78 0.15 55);
  --danger: oklch(0.68 0.18 27);
  --tide-wash: oklch(0.74 0.10 210 / 0.18);
}


/* =========================================================================
   Numerics. Every time, count and currency. Applied here rather than per
   component so a new counter cannot forget it and jitter as it counts.
   ========================================================================= */

.num, time, [data-num]{
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* =========================================================================
   Spacing utilities.

   108 inline style attributes existed, 42 of them margin-top, which is what
   happens when no rule owns the space between siblings. These let a template
   ask for a step on the scale without reaching for a literal.
   ========================================================================= */

[data-u~="mt-1"]{ margin-top:var(--space-1); }
[data-u~="mt-2"]{ margin-top:var(--space-2); }
[data-u~="mt-3"]{ margin-top:var(--space-3); }
[data-u~="mt-4"]{ margin-top:var(--space-4); }
[data-u~="mt-5"]{ margin-top:var(--space-5); }
[data-u~="mt-6"]{ margin-top:var(--space-6); }
[data-u~="mt-7"]{ margin-top:var(--space-7); }
[data-u~="mb-1"]{ margin-bottom:var(--space-1); }
[data-u~="mb-2"]{ margin-bottom:var(--space-2); }
[data-u~="mb-3"]{ margin-bottom:var(--space-3); }
[data-u~="mb-4"]{ margin-bottom:var(--space-4); }
[data-u~="mb-5"]{ margin-bottom:var(--space-5); }

[data-u~="flex-1"]{ flex:1; }
[data-u~="hidden"]{ display:none; }
[data-u~="center"]{ text-align:center; }
[data-u~="pad-3"]{ height:auto; padding:var(--space-3) var(--space-4); }
[data-u~="bg-page"]{ background:var(--ground); }
[data-u~="fg-system"]{ color:var(--tide); }
[data-u~="fade"]{ transition:opacity var(--dur-slow) var(--ease); }


/* The Story theme is gone.

   It existed to make one tab change register: near-black ground, cyan signal,
   mono numerals, angular cuts. In isolation it worked. In the product it read
   as a monitoring tool bolted onto a warm, calm app, and the seam was the
   most noticeable thing on the screen.

   One visual language, and the scene carries the atmosphere instead. Nothing
   here needed a second palette to be worth looking at.
   ========================================================================= */

/* =========================================================================
   Numerics. Every time, count and currency. Applied here rather than per
   component so a new counter cannot forget it and jitter as it counts.
   ========================================================================= */

.num, time, [data-num]{
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* =========================================================================
   Spacing utilities.

   108 inline style attributes existed, 42 of them margin-top, which is what
   happens when no rule owns the space between siblings. These let a template
   ask for a step on the scale without reaching for a literal.
   ========================================================================= */

[data-u~="mt-1"]{ margin-top:var(--space-1); }
[data-u~="mt-2"]{ margin-top:var(--space-2); }
[data-u~="mt-3"]{ margin-top:var(--space-3); }
[data-u~="mt-4"]{ margin-top:var(--space-4); }
[data-u~="mt-5"]{ margin-top:var(--space-5); }
[data-u~="mt-6"]{ margin-top:var(--space-6); }
[data-u~="mt-7"]{ margin-top:var(--space-7); }
[data-u~="mb-1"]{ margin-bottom:var(--space-1); }
[data-u~="mb-2"]{ margin-bottom:var(--space-2); }
[data-u~="mb-3"]{ margin-bottom:var(--space-3); }
[data-u~="mb-4"]{ margin-bottom:var(--space-4); }
[data-u~="mb-5"]{ margin-bottom:var(--space-5); }

[data-u~="flex-1"]{ flex:1; }
[data-u~="hidden"]{ display:none; }
[data-u~="center"]{ text-align:center; }
[data-u~="pad-3"]{ height:auto; padding:var(--space-3) var(--space-4); }
[data-u~="bg-page"]{ background:var(--ground); }
[data-u~="fg-system"]{ color:var(--tide); }
[data-u~="fade"]{ transition:opacity var(--dur-slow) var(--ease); }


/* =========================================================================
   Story.

   The only themed surface in the product. That is the whole mechanic: the
   rest of the app is a quiet instrument panel, and Story is the one place
   that changes register. If the theme leaks, both halves lose.

   These live here rather than in a second stylesheet because the audit found
   two disconnected design systems already and a third would be worse than the
   problem it solves.

   Always dark, in both themes. That is the entry signal, and it is why these
   are declared once rather than under a theme selector.
   ========================================================================= */

:root {
  /* The reserved height of the fixed sidebar header on desktop.
     This existed as --rail-head and a cleanup pass mapped it onto a colour
     token, so `height: var(--rail-head)` became `height: <a colour>`, which
     is invalid and drops to auto. The header then had no height, the nav had
     no top padding, and the two overlapped: the logo sat under the first
     navigation item. A length and a colour are not interchangeable and the
     rename did not notice. */
  --rail-head: 128px;
}
