/* ════════════════════════════════════════════════════════════════════════════
   NOOSH — the one authored token file.

   AUTHORED HERE. scripts/sync_tokens.sh copies this file byte-for-byte to
   pos-app/frontend/css/n-tokens.css and website/assets/css/n-tokens.css.
   Edit this file; never edit a copy (design plan §9 D3).

   ── The palette has two halves ───────────────────────────────────────────
   CHROME  — Noosh's own identity: the paper, the ink, the flame, the
             semantic colours (ok / warn / danger / void), the rules and
             hairlines. Two schemes, light and dark, hand-tuned hexes.
             A tenant never changes these.
   TENANT  — ONE hue+saturation+lightness per restaurant (--n-accent-h/s/l).
             Every other accent value is DERIVED from those three numbers so
             that no choice of hue can break a contrast ratio: the fill's
             lightness is nudged until ink or paper reaches 4.5:1 on it, the
             ink on the fill is whichever of the two reads better, hover and
             active move AWAY from that ink, and the text and line tones are
             mapped until they clear 4.5:1 and 3:1 on every ground.

   PROOF   — `python3 scripts/contrast_check.py --gate` is the proof. It
             holds the same chrome hexes and the same derivation maths as
             this file, and exits non-zero if any pair fails (chrome, both
             sample tenants, all ten presets in both schemes).
             `python3 scripts/contrast_check.py --json` prints the derived
             values; pos-app/frontend/js/00-accent.js mirrors that maths at
             runtime and pos-app/frontend/tests/accent.test.mjs proves the
             two agree.

   Every value below is a custom property, grouped and commented, so the
   whole product can be tuned from this file without opening a component.
   ════════════════════════════════════════════════════════════════════════ */


/* ════════════════════════════════════════════════════════════════════════
   0. FACES  (self-hosted, latin subset — no CDN, ever; design plan §9 D5)

   The two woff2 files are in the repo (site batch 5). Both are the upstream
   VARIABLE fonts, subset to latin with fontTools and served from /fonts/ on
   each surface's own origin — website/fonts/ for the marketing site,
   pos-app/frontend/fonts/ for the till — with the OFL beside them. Nothing is
   fetched from a CDN, by any page, ever.

   The four numbers on the two Fallback faces below were measured off those
   files by scripts/font_metrics.py: re-run it (it patches this file and syncs
   the copies) whenever a woff2 is replaced, so that a font swap still moves
   nothing. Both woff2 files are VARIABLE, so the script instances each one at
   the weight a page actually renders text in — wght 400, and for Fraunces
   opsz 14, the body size — before it reads a single advance. The numbers are
   therefore the ratio between Arial/Georgia and the real face AS DRAWN: how
   much wider to set the fallback (size-adjust) and where to put its baseline
   inside that box (ascent/descent/line-gap-override), so the line the visitor
   sees before the woff2 arrives occupies the same box as the line after.
   ════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: "Noosh Sans";            /* self-hosted Libre Franklin, under its OWN family
                                           name: a same-name @font-face would shadow any
                                           locally installed "Libre Franklin" instead of
                                           sitting beside it in the stack below. */
  src: url("/fonts/libre-franklin-latin.woff2") format("woff2");
  font-weight: 400 700;                 /* variable: covers --n-w-regular … --n-w-bold */
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Noosh Serif";           /* self-hosted Fraunces — same reasoning */
  src: url("/fonts/fraunces-latin.woff2") format("woff2");
  font-weight: 300 900;                 /* variable: wght 300–900 (design plan §4) */
  font-style: normal;
  font-display: swap;
}

/* Metric-tuned fallbacks: a font swap must move nothing (CLS budget < 0.05).
   Every number here is measured — never hand-typed — by
   `python3 scripts/font_metrics.py`. */
@font-face {
  font-family: "Libre Franklin Fallback";
  src: local("Arial");
  size-adjust: 107.44%;
  font-display: swap;
  ascent-override: 89.91%;
  descent-override: 22.90%;
  line-gap-override: 0.00%;
}

@font-face {
  font-family: "Fraunces Fallback";
  src: local("Georgia");
  size-adjust: 107.14%;
  font-display: swap;
  ascent-override: 91.28%;
  descent-override: 23.80%;
  line-gap-override: 0.00%;
}


:root {

  /* ══════════════════════════════════════════════════════════════════════
     1. CHROME — LIGHT SCHEME  (mirrors CHROME["light"] in contrast_check.py)
     ══════════════════════════════════════════════════════════════════════ */

  /* -- grounds --------------------------------------------------------- */
  --n-bg: #F7F5F0;                      /* paper: the page ground */
  --n-surface: #FFFFFF;                 /* cards, sheets, table bodies */
  --n-surface-raised: #FFFFFF;          /* menus, popovers, the lifted row */
  --n-surface-sunken: #EFECE4;          /* inset panels, table headers, wells of light */
  --n-surface-well: #E5E1D6;            /* the deepest inset (search fields, code) */

  /* -- ink ------------------------------------------------------------- */
  --n-text: #171512;                    /* the only black */
  --n-text-2: #4A463F;                  /* secondary body */
  --n-text-muted: #655F57;              /* 5.6:1 on white, 4.6:1 on sunken */
  --n-text-faint: #8A857D;              /* 3:1 — UI furniture only, never body text */

  /* -- constants (identical in both schemes; the two poles the maths uses) */
  --n-ink: #171512;
  --n-paper: #F7F5F0;

  /* -- rules, borders, inversion --------------------------------------- */
  --n-rule: #171512;                    /* the editorial rule: full-strength ink */
  --n-border: rgba(23, 21, 18, .14);    /* hairline */
  --n-border-soft: rgba(23, 21, 18, .07); /* the quieter hairline */
  --n-inverse-bg: #171512;              /* inverted chips/toasts */
  --n-inverse-text: #F7F5F0;
  --n-track: #E4E0D4;                   /* meters, sliders, progress troughs */

  /* -- map (floor plan / local market) --------------------------------- */
  --n-map-base: #EDEAE2;
  --n-map-street: #2A2722;
  --n-map-water: #44413B;
  --n-map-tiles: light;                 /* read by the tile layer chooser */

  color-scheme: light;

  /* ══════════════════════════════════════════════════════════════════════
     2. SEMANTIC — LIGHT
     `-fill` is the block of colour; the bare name is the TEXT tone (4.5:1 on
     every ground); `-soft` is the wash behind a chip or a row.
     ══════════════════════════════════════════════════════════════════════ */

  --n-ok: #276A3C;                      /* moss — text tone */
  --n-ok-fill: #2E7D46;
  --n-ok-soft: rgba(46, 125, 70, .13);

  --n-warn: #7A5104;                    /* amber — text tone */
  --n-warn-fill: #B26F12;
  --n-warn-soft: rgba(178, 111, 18, .14);

  --n-danger: #9C2F45;                  /* pomegranate — text tone */
  --n-danger-fill: #B0455A;
  --n-danger-soft: rgba(176, 69, 90, .12);
  --n-danger-ink: var(--n-paper);       /* ink or paper on the danger GROUND,
                                           whichever reads: 6.5:1 here */

  /* void is a TREATMENT, not a fifth hue: muted ink + strikethrough + hatch */
  --n-void: #6E6A66;
  --n-void-hatch: repeating-linear-gradient(45deg, transparent 0 6px, var(--n-border-soft) 6px 7px);

  /* ══════════════════════════════════════════════════════════════════════
     3. BRAND
     ══════════════════════════════════════════════════════════════════════ */

  --n-flame-glow: #F4B355;              /* the flame's inner tongue — lighter saffron, mark only */
  --n-flame: #E8A13A;                   /* saffron. CHROME, not tenant: the brand mark only.
                                           Never a button, never a state. */

  /* ══════════════════════════════════════════════════════════════════════
     3b. CATEGORY COLOURS — a categorical palette (menu categories are
     distinct hues regardless of tenant). Decorative bars and dots only;
     each clears 3:1 against every ground of its scheme. catColor() hashes
     a category name to an index, so a category keeps its colour on every
     device with no setup.
     ══════════════════════════════════════════════════════════════════════ */

  --n-cat-1: #C42B35;
  --n-cat-2: #3A67CB;
  --n-cat-3: #A85A24;
  --n-cat-4: #3F7754;
  --n-cat-5: #8A5F8A;
  --n-cat-6: #197B7B;
  --n-cat-7: #B93A6B;
  --n-cat-8: #7A7104;
  --n-cat-9: #5E6C97;
  --n-cat-10: #8E5A3A;

  /* ══════════════════════════════════════════════════════════════════════
     4. TENANT ACCENT

     INPUTS — the only three numbers a restaurant owns:
     ══════════════════════════════════════════════════════════════════════ */

  --n-accent-h: 36;                     /* the saffron preset = today's default accent, */
  --n-accent-s: 79%;                    /* so a restaurant that never opens Appearance   */
  --n-accent-l: 57%;                    /* looks exactly as it does now.                 */

  /* DERIVED — the values below are the exact output of
     `scripts/contrast_check.py --json` for (36, 79, 57) on the LIGHT scheme.
     They are written out as literals so a page paints correctly BEFORE any
     JS runs; pos-app/frontend/js/00-accent.js recomputes and OVERWRITES all
     of them on documentElement for any other tenant hue. Do not hand-edit:
     regenerate with `python3 scripts/contrast_check.py --accent 36 79 57`. */
  --n-accent: #E8A33B;                  /* fill — the tenant's colour */
  --n-accent-ink: #171512;              /* ink or paper, whichever reads better on the fill */
  --n-accent-hover: #EBB056;            /* lightness +6, away from the ink */
  --n-accent-active: #EEBC72;           /* lightness +12, away from the ink */
  --n-accent-soft: rgba(232, 163, 59, .14); /* the wash: 14% light / 20% dark */
  --n-accent-text: #925E11;             /* tone-mapped to 4.5:1 on every ground and the wash */
  --n-accent-line: #C47F17;             /* tone-mapped to 3:1 on bg and surface */
  --n-accent-ring: rgba(196, 127, 23, .35); /* the line at 35% — focus rings */

  /* ══════════════════════════════════════════════════════════════════════
     5. TYPE  (design plan §4)
     ══════════════════════════════════════════════════════════════════════ */

  /* Libre Franklin: everything interactive, every label, all three surfaces. */
  --n-font-ui: "Noosh Sans", "Libre Franklin", "Libre Franklin Fallback", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;

  /* The same family, set tabular: every price, total, quantity, time and
     percentage. Numbers must never shift horizontally as they change.
     A font-family value cannot carry numeric features, so the two companion
     tokens below travel with it — apply all three together. */
  --n-font-num: var(--n-font-ui);
  --n-font-num-variant: tabular-nums lining-nums;   /* → font-variant-numeric */
  --n-font-num-features: "tnum";                     /* → font-feature-settings */

  /* Fraunces: office page titles and the ONE headline number per screen;
     site headlines and pull-quotes. Never on the floor. */
  --n-font-display: "Noosh Serif", "Fraunces", "Fraunces Fallback", "Iowan Old Style", Palatino, Georgia, serif;

  /* The character grid. One surface needs it: the office's bill preview,
     where the layout being chosen IS "42 characters to the line", so a
     proportional face would draw a preview that lies about where the paper
     wraps. Never body text, never a label. */
  --n-font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* -- scale (px) — nothing below step −2; the floor's minimum is step 1 -- */
  --n-step--2: 11px;                    /* captions of last resort */
  --n-step--1: 12.5px;                  /* captions */
  --n-step-0: 14px;                     /* office body, table cells */
  --n-step-1: 16px;                     /* floor minimum, site body */
  --n-step-2: 19px;                     /* tile price */
  --n-step-3: 23px;
  --n-step-4: 28px;
  --n-step-5: 34px;                     /* the total; the table number on the map */
  --n-step-6: 44px;
  --n-step-7: 56px;
  --n-step-8: 72px;                     /* site hero */

  /* -- line heights ---------------------------------------------------- */
  --n-lh-display: 1.25;
  --n-lh-ui: 1.4;
  --n-lh-prose: 1.55;

  /* -- tracking ---------------------------------------------------------- */
  --n-track-tight: -0.02em;             /* display sizes (step 6 and up) */
  --n-track-snug: -0.01em;              /* headings (steps 3–5) */
  --n-track-wide: 0.06em;               /* small labels that must read at a glance */

  /* -- weights --------------------------------------------------------- */
  --n-w-regular: 400;
  --n-w-medium: 500;
  --n-w-semibold: 600;
  --n-w-bold: 700;

  /* ══════════════════════════════════════════════════════════════════════
     6. SPACE  (design plan §5)
     ══════════════════════════════════════════════════════════════════════ */

  --n-space-1: 4px;
  --n-space-2: 8px;
  --n-space-3: 12px;
  --n-space-4: 16px;
  --n-space-5: 24px;
  --n-space-6: 32px;
  --n-space-7: 48px;
  --n-space-8: 64px;
  --n-space-9: 96px;                    /* section bands on the site — the macro gap the scale stopped short of */

  /* ══════════════════════════════════════════════════════════════════════
     7. RADIUS — tiles md, sheets lg, chips pill; tables and rules stay 0
     ══════════════════════════════════════════════════════════════════════ */

  --n-radius-sm: 6px;
  --n-radius-md: 10px;
  --n-radius-lg: 16px;
  --n-radius-pill: 999px;

  /* ══════════════════════════════════════════════════════════════════════
     8. ELEVATION — one shadow vocabulary (light scheme; dark lifts with a
     surface token instead, see §12)
     ══════════════════════════════════════════════════════════════════════ */

  --n-shadow-1: 0 1px 2px rgba(23, 21, 18, .06);                                  /* hairline lift */
  --n-shadow-2: 0 4px 14px rgba(23, 21, 18, .09), 0 1px 3px rgba(23, 21, 18, .05); /* menu / popover */
  --n-shadow-e: 0 18px 44px -18px rgba(23, 21, 18, .25);                          /* sheet */
  --n-scrim: rgba(23, 21, 18, .45);     /* behind a sheet: dims the surface it covers, so one primary is live */

  /* ══════════════════════════════════════════════════════════════════════
     9. MOTION — answers a user action, never loops
     ══════════════════════════════════════════════════════════════════════ */

  --n-dur-fast: 120ms;                  /* press, hover */
  --n-dur-base: 200ms;                  /* reveal in place */
  --n-dur-slow: 320ms;                  /* sheets */
  --n-ease: cubic-bezier(.2, .7, .3, 1);
  --n-dur-undo: 8000ms;                 /* how long an undo toast stays */
  --n-dur-longpress: 500ms;             /* press-and-hold before it counts */
  --n-dur-voice-max: 90000ms;           /* ceiling on one voice clip: recorder → local model → answer */
  --n-bump-scale: 1.06;                 /* the money bump: how far a changed total grows */

  /* -- breakpoints (a custom property cannot live inside @media, so these are
        the ONE place the numbers are written; components repeat them only in
        @media prelude and read them from here in JS via --n-bp-*) --------- */
  --n-bp-phone: 600px;                  /* ≤ phone: category strip, ticket sheet */
  --n-bp-tablet: 900px;                 /* ≤ tablet portrait: bottom tab bar */
  --n-bp-inspector: 1200px;             /* < this the office inspector is a sheet */

  /* ══════════════════════════════════════════════════════════════════════
     10. HIT TARGETS  (the floor raises the minimum, see §13)
     ══════════════════════════════════════════════════════════════════════ */

  --n-hit-min: 44px;                    /* default minimum interactive target */
  --n-hit-office: 36px;                 /* office table rows */
  --n-hit-button: 44px;                 /* office buttons */
  --n-hit-site: 48px;                   /* site buttons, and links that stand alone as a door */
  --n-tick: 16px;                       /* checkbox / radio box — one size on every surface */

  /* ══════════════════════════════════════════════════════════════════════
     11. FOCUS
     ══════════════════════════════════════════════════════════════════════ */

  /* a halo: a surface-coloured gap then the accent LINE (tone-mapped to 3:1
     against bg and surface), so the ring meets WCAG 2.2 focus appearance on
     any ground. Components that clip their box (overflow) use the inset form. */
  --n-ring-w: 2px;                      /* the focus line itself */
  --n-ring-gap: 2px;                    /* the surface-coloured gap under the halo */
  --n-focus-ring: 0 0 0 var(--n-ring-gap) var(--n-surface), 0 0 0 calc(var(--n-ring-gap) + var(--n-ring-w)) var(--n-accent-line);
  --n-focus-ring-inset: inset 0 0 0 calc(var(--n-ring-w) + var(--n-ring-gap) / 2) var(--n-accent-line);
}


/* ════════════════════════════════════════════════════════════════════════
   12. CHROME — DARK SCHEME  (mirrors CHROME["dark"] in contrast_check.py)
   The device may override the SCHEME (glare on the floor); it never
   overrides the tenant's hue (design plan §9 D2).
   ════════════════════════════════════════════════════════════════════════ */

:root[data-scheme="dark"] {

  /* -- grounds: ink, lowered ------------------------------------------- */
  --n-bg: #12100C;
  --n-surface: #1B1813;
  --n-surface-raised: #242019;          /* dark elevation is a lighter surface, not a shadow */
  --n-surface-sunken: #0B0A07;
  --n-surface-well: #0B0A07;

  /* -- ink: paper, lowered --------------------------------------------- */
  --n-text: #F3EFE7;
  --n-text-2: #CFC8BA;
  --n-text-muted: #A79F91;
  --n-text-faint: #7E776C;

  /* -- rules, borders, inversion --------------------------------------- */
  --n-rule: #F3EFE7;
  --n-border: rgba(243, 239, 231, .17);
  --n-border-soft: rgba(243, 239, 231, .08);
  --n-inverse-bg: #F3EFE7;
  --n-inverse-text: #12100C;
  --n-track: #2B261E;

  /* -- map -------------------------------------------------------------- */
  --n-map-base: #1B1813;
  --n-map-street: #B8B0A0;
  --n-map-water: #0A0908;
  --n-map-tiles: dark;

  color-scheme: dark;

  /* -- semantic --------------------------------------------------------- */
  --n-ok: #8FD3A4;
  --n-ok-fill: #3C9159;
  --n-ok-soft: rgba(76, 163, 102, .16);

  --n-warn: #EDB668;
  --n-warn-fill: #D9952F;
  --n-warn-soft: rgba(217, 149, 47, .15);

  --n-danger: #F0A0B0;
  --n-danger-fill: #C25B70;
  --n-danger-soft: rgba(194, 91, 112, .17);
  --n-danger-ink: var(--n-ink);         /* the dark scheme's danger is a PALE
                                           pomegranate; ink reads on it, 9.1:1 */

  --n-void: #9A9284;

  /* -- tenant accent, dark defaults -------------------------------------
     Exact output of `scripts/contrast_check.py --json` for (36, 79, 57) on
     the DARK scheme. 00-accent.js overwrites these for any other tenant. */
  --n-accent: #E8A33B;
  --n-accent-ink: #171512;
  --n-accent-hover: #EBB056;
  --n-accent-active: #EEBC72;
  --n-accent-soft: rgba(232, 163, 59, .20);
  --n-accent-text: #E8A33B;
  --n-accent-line: #E8A33B;
  --n-accent-ring: rgba(232, 163, 59, .35);

  /* -- category colours: three nudged 1–2 L to clear 3:1 on the dark grounds */
  --n-cat-1: #CC2D37;
  --n-cat-7: #BD3B6D;
  --n-cat-10: #955F3D;

  /* -- elevation: near-zero on dark; the hairline does the lifting ------- */
  --n-shadow-1: 0 0 0 1px var(--n-border);
  --n-shadow-2: 0 0 0 1px var(--n-border);
  --n-shadow-e: 0 0 0 1px var(--n-border);
  --n-scrim: rgba(0, 0, 0, .6);

  /* legacy alias, dark: the old "surface-2" was a shade ABOVE the surface
     (#242019) — elevation by tone — so it maps to raised here, not sunken */
  --surface-2: var(--n-surface-raised);
}


/* ════════════════════════════════════════════════════════════════════════
   13. REDUCED MOTION — every duration collapses to 1ms
   ════════════════════════════════════════════════════════════════════════ */

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


/* ════════════════════════════════════════════════════════════════════════
   14. POS OVERRIDES — the floor is an instrument panel

   Applied by [data-surface="floor"] on the till shell. Legibility and hit
   size are FIXED (glare, standing, one-handed — design plan §9 D10); only
   spacing scales, via the owner's single density dial:
     0.9 cramped counter · 1 default · 1.15 glare / fat-finger.
   ════════════════════════════════════════════════════════════════════════ */

[data-surface="floor"] {
  --n-hit-min: 56px;                    /* EVERY interactive target, including line-remove */
  --n-pos-density: 1;                   /* the one dial the owner has */
  --n-text-min: var(--n-step-1);        /* 16px floor minimum — never scaled */

  --n-space-1: calc(4px * var(--n-pos-density));
  --n-space-2: calc(8px * var(--n-pos-density));
  --n-space-3: calc(12px * var(--n-pos-density));
  --n-space-4: calc(16px * var(--n-pos-density));
  --n-space-5: calc(24px * var(--n-pos-density));
  --n-space-6: calc(32px * var(--n-pos-density));
  --n-space-7: calc(48px * var(--n-pos-density));
  --n-space-8: calc(64px * var(--n-pos-density));
  --n-space-9: calc(96px * var(--n-pos-density));
}


/* ════════════════════════════════════════════════════════════════════════
   15. LEGACY ALIASES — the strangling period (design plan §9 D4)

   The old screens speak --bg / --acc / --ok…; they keep working while the
   new ones are written. DELETE THIS BLOCK WHEN THE LAST OLD SCREEN IS
   SWAPPED. Nothing new may use a name from this block.
   ════════════════════════════════════════════════════════════════════════ */

:root {
  /* grounds and ink */
  --bg: var(--n-bg);
  --surface: var(--n-surface);
  --surface-2: var(--n-surface-sunken);   /* light: a shade below the surface */
  --well: var(--n-surface-well);
  --text: var(--n-text);
  --text-2: var(--n-text-2);
  --mut: var(--n-text-muted);
  --faint: var(--n-text-faint);

  /* rules, borders, inversion */
  --rule: var(--n-rule);
  --hair: var(--n-border);
  --hair-soft: var(--n-border-soft);
  --inv-bg: var(--n-inverse-bg);
  --inv-text: var(--n-inverse-text);
  --track: var(--n-track);

  /* map */
  --map-base: var(--n-map-base);
  --map-street: var(--n-map-street);
  --map-water: var(--n-map-water);
  --map-tiles: var(--n-map-tiles);

  /* elevation */
  --shadow-e: var(--n-shadow-e);

  /* accent */
  --acc: var(--n-accent);
  --acc-h: var(--n-accent-hover);
  --acc-d: var(--n-accent-active);
  --on-acc: var(--n-accent-ink);
  --acc-t: var(--n-accent-text);
  --acc-soft: var(--n-accent-soft);

  /* semantic */
  --ok: var(--n-ok-fill);
  --ok-t: var(--n-ok);
  --ok-soft: var(--n-ok-soft);
  --warn: var(--n-warn-fill);
  --warn-t: var(--n-warn);
  --warn-soft: var(--n-warn-soft);
  --bad: var(--n-danger-fill);
  --bad-t: var(--n-danger);
  --bad-soft: var(--n-danger-soft);
}
