/* ============================================================
   tokens.css — design tokens (the single source of visual truth)
   Everything references these, so re-skinning never touches components.
   Type scale, spacing and radii are all on a strict grid (the difference
   between "designed" and "amateur"). Text colors meet WCAG AA.
   ============================================================ */
:root {
  /* brand — one accent (indigo). Teal is used only for categorical data viz. */
  --brand-50:#eef2ff; --brand-100:#e0e7ff; --brand-200:#c7d2fe; --brand-300:#a5b4fc;
  --brand-400:#818cf8; --brand-500:#6366f1; --brand-600:#4f46e5; --brand-700:#4338ca;
  --accent-500:#0d9aae; --accent-600:#0e7d8e;

  /* FILL vs INK — two jobs one token was doing badly.
     --brand-600 is used BOTH as ink on a light tint (.nav a.active: brand-600 text on
     brand-50) AND as a solid fill under white text (.btn-primary, .kpi.hero,
     .step.active .num). Dark mode has to LIGHTEN it for the first job — and doing so
     dropped white-on-fill to 1.99:1 on the primary button, the single most-clicked
     control in the app. One token cannot be both light enough to read on a dark tint
     and dark enough to carry white text.
     So: --brand-fill is what you FILL with, --on-brand is the text that sits on it, and
     both hold their contrast in either theme. The -500/-600/-700 ramp goes back to being
     purely an ink/tint ramp. */
  --brand-fill:#4f46e5; --brand-fill-hover:#4338ca; --on-brand:#ffffff;

  /* FILLABLE FIELD — the company olive, used for one job only: marking the inputs the
     estimator is meant to type into. It is deliberately NOT the brand indigo, because
     indigo already means "primary action / current nav" everywhere else; giving fields
     their own hue means "you can fill this" never competes with "click this". Kept as a
     token trio so the affordance can be retuned in one place across both themes. */
  --field-line:#A9BA7C;    /* resting border — olive, readable but not shouting */
  --field-tint:#F6F9EE;    /* resting fill  — barely-there olive wash */
  --field-focus:#7C8F45;   /* focus/active border, and the focus ring hue */

  /* COSTING TIER — one hue per tier, so which set of numbers is on screen is legible from
     anywhere. Chosen for MEANING, not decoration:
       direct   red-brown — your raw cost, the one that must never reach a client
       internal amber     — internal review figure, not for issue either
       client   olive     — the brand colour, and the only tier meant to leave the office
     Deliberately three clearly different hues rather than three shades of one, because the
     mistake being prevented is confusing them at a glance. */
  --tier-direct:#A63D2F; --tier-direct-tint:#FBEEEC;
  --tier-internal:#B45309; --tier-internal-tint:#FFF7E8;
  --tier-client:#7C8F45; --tier-client-tint:#F6F9EE;

  /* semantic — 500 is for fills/dots (reads by hue, not contrast); the 700s
     below are darker "text-on-tinted-fill" variants for pill labels, which at
     500 measured 3.1-3.6:1 on their own -50 tint (fails WCAG AA's 4.5:1 floor). */
  --ok-500:#059669; --ok-50:#ecfdf5; --ok-100:#d1fae5; --ok-600:#047857; --ok-700:#047857;
  --warn-500:#d97706; --warn-50:#fffbeb; --warn-100:#fef3c7; --warn-600:#b45309; --warn-700:#b45309;
  --danger-500:#dc2626; --danger-50:#fef2f2; --danger-100:#fee2e2; --danger-600:#b91c1c; --danger-700:#b91c1c;
  --info-500:#2563eb; --info-50:#eff6ff; --info-600:#1d4ed8; --info-700:#1d4ed8;

  /* neutrals (light) — text colors verified ≥4.5:1 on surfaces */
  --bg:#f5f6f9;
  --bg-elev:#ffffff;
  --surface:#ffffff;
  --surface-2:#f7f8fb;
  --surface-3:#eef0f5;
  --border:#e5e7ee;
  --border-strong:#cdd2de;
  --text:#0f1422;        /* ~16:1  */
  --text-2:#475069;      /* ~7.2:1 */
  --text-3:#636a7e;      /* ~4.8:1 on app bg — AA for body text */
  --text-disabled:#8a90a0; /* ~4.5:1 — for disabled labels a user still needs to read (nav locked pre-project); opacity:.4 on --text-2 fell to ~2.9:1 */
  --text-inv:#ffffff;

  /* layered, low-opacity shadows (real depth, not a hard drop) */
  --shadow-sm:0 1px 2px rgba(15,20,34,.05), 0 1px 1px rgba(15,20,34,.04);
  --shadow-md:0 2px 4px rgba(15,20,34,.05), 0 6px 16px rgba(15,20,34,.08);
  --shadow-lg:0 4px 8px rgba(15,20,34,.06), 0 16px 32px rgba(15,20,34,.12);
  --shadow-pop:0 8px 16px rgba(15,20,34,.10), 0 24px 56px rgba(15,20,34,.18);

  /* radius — clean step scale */
  --r-xs:6px; --r-sm:8px; --r-md:10px; --r-lg:14px; --r-xl:20px; --r-full:999px;

  /* spacing — 4px base grid */
  --sp-1:4px; --sp-2:8px; --sp-3:12px; --sp-4:16px; --sp-5:20px; --sp-6:24px; --sp-8:32px; --sp-10:40px; --sp-12:48px;

  /* type — strict scale, no half-pixels; base 14 for comfortable reading */
  --font:'Inter','Inter var',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
  --mono:'SF Mono',ui-monospace,'Cascadia Code','JetBrains Mono',Consolas,monospace;
  --fs-2xs:11px; --fs-xs:12px; --fs-sm:13px; --fs-base:14px; --fs-md:16px; --fs-lg:18px; --fs-xl:22px; --fs-2xl:28px; --fs-3xl:34px;
  --lh-tight:1.25; --lh:1.55;
  /* weight scale — collapses ten ad-hoc literals (600/640/650/680/700/720/740/760/780/800)
     scattered across components.css/layout.css onto five named roles. */
  --fw-regular:450; --fw-medium:500; --fw-semibold:600; --fw-bold:680; --fw-heavy:760;

  --sidebar-w:252px; --topbar-h:60px;
  --ring:0 0 0 3px rgba(99,102,241,.30);
}

[data-theme="dark"] {
  --bg:#0b0d17;
  --bg-elev:#12152a;
  --surface:#161a30;
  --surface-2:#1b2039;
  --surface-3:#232a47;
  --border:#283050;
  --border-strong:#39426a;
  --text:#eef0f8;        /* ~15:1 on surface */
  --text-2:#aeb6d3;      /* ~7:1  */
  --text-3:#8a93b6;      /* ~4.8:1 — AA on dark surface */
  --text-disabled:#6b7398;
  --text-inv:#0b0d17;
  --brand-50:#20264f; --brand-100:#2a3164;
  /* Mid-tones used as TEXT/ICON color on a tinted fill (e.g. .nav a.active's
     brand-600 on brand-50) previously kept their light-mode value here, which
     reads at ~1.6:1 on the now-dark -50 tint — the single most-used nav state
     was nearly invisible in dark mode. Every mid-tone gets its own dark value. */
  --brand-500:#818cf8; --brand-600:#a5b4fc; --brand-700:#c7d2fe;
  /* Deliberately NOT lightened: this is a fill, and white has to stay legible on it.
     Nudged slightly brighter than the light-theme value so it still reads as a live
     control against the darker surfaces, while keeping white above 4.5:1. */
  --brand-fill:#5b52ea; --brand-fill-hover:#6d64f0; --on-brand:#ffffff;
  /* Fillable-field olive, dark theme. The tint has to sit ABOVE the surface it lands on
     rather than below it — a dark-mode field reads as recessed if it is darker than its
     card — so this is a lifted olive-charcoal, not a darkened version of the light tint. */
  --field-line:#6E8046; --field-tint:#1C2417; --field-focus:#9DB463;
  /* Tier hues lifted for dark ground; the tints become deep washes rather than pale ones,
     so the tier banner still reads as a tinted surface instead of a black hole. */
  --tier-direct:#E88374; --tier-direct-tint:#2B1714;
  --tier-internal:#F0A64B; --tier-internal-tint:#2A1F0E;
  --tier-client:#A8C06B; --tier-client-tint:#1C2417;
  --accent-500:#22c3d6; --accent-600:#3ad2e4;
  --ok-500:#34d399; --ok-600:#6ee7b7; --ok-700:#6ee7b7;
  --warn-500:#f59e0b; --warn-600:#fbbf24; --warn-700:#fbbf24;
  --danger-500:#f87171; --danger-600:#fca5a5; --danger-700:#fca5a5;
  --info-500:#60a5fa; --info-50:#0e2036; --info-600:#93c5fd; --info-700:#93c5fd;
  --ok-50:#0e2a22; --ok-100:#123626; --warn-50:#2a210e; --warn-100:#352a12; --danger-50:#2c1416; --danger-100:#391a1d;
  --shadow-sm:0 1px 2px rgba(0,0,0,.45);
  --shadow-md:0 6px 16px rgba(0,0,0,.5);
  --shadow-lg:0 16px 40px rgba(0,0,0,.6);
  --shadow-pop:0 24px 56px rgba(0,0,0,.7);
  --ring:0 0 0 3px rgba(129,140,248,.4);
}
