/* =========================================================================
   GENERATED FROM design/tokens.css — edit that, then run:
     bun design/sync-site-tokens.ts
   ========================================================================= */

/* =========================================================================
   Enereg Design System — canonical tokens
   =========================================================================
   This file IS the live token source. src/styles/app.css is generated from
   it — edit here, then run `bun design/sync-tokens.ts && bun run css:build`.

   Never edit src/index.css — it is Tailwind's compiled output.

   Three rules govern every colour below:
     1. A colour names its JOB, not its look.  (`warning`, not `amber`)
     2. Text needs 4.5:1. UI edges need 3.0:1. Nothing ships below.
     3. Purple means "the system did this for you." Nothing else may.
   ========================================================================= */

:root {
  /* ---------------------------------------------------------------------
     RAMPS — the only place raw hex is allowed.
     Both ramps are lifted verbatim from the live enereg.com stylesheet,
     which is the most complete design artefact the company owns.
     --------------------------------------------------------------------- */

  --color-brand-50:  #E1F5EE;
  --color-brand-100: #B3E5D4;
  --color-brand-200: #80D1B8;
  --color-brand-300: #4DBC9C;
  --color-brand-400: #26AB87;
  --color-brand-500: #0F6E56;  /* the brand. 6.20:1 on white. */
  --color-brand-600: #0C5C48;
  --color-brand-700: #084A3A;
  --color-brand-800: #05382C;
  --color-brand-900: #02261E;
  --color-brand-950: #011A14;

  --color-navy-50:  #F0F4F8;
  --color-navy-100: #D9E2EC;
  --color-navy-200: #BCCCDC;
  --color-navy-300: #9FB3C8;
  --color-navy-400: #829AB1;
  --color-navy-500: #627D98;  /* 4.28:1 — NEVER use as text on white */
  --color-navy-600: #486581;  /* 6.08:1 — the lightest legible body grey */
  --color-navy-700: #334E68;
  --color-navy-800: #243B53;
  --color-navy-900: #102A43;
  --color-navy-950: #0A1929;

  /* ---------------------------------------------------------------------
     SEMANTIC HUES
     Each status hue splits in two, because a colour legible as a 2px
     border is not automatically legible as 11px text. This split is the
     systematic fix for the status-pill contrast failures — not per-pill
     hand-tuning.
       *-fill  → backgrounds, borders, icons, dots   (needs 3.0:1)
       *-text  → the label sitting on that fill      (needs 4.5:1)
     Where one value clears both bars, there is only one token.
     --------------------------------------------------------------------- */

  --color-danger:        #A32D2D;  /* 7.07:1 — clears both */
  --color-danger-light:  #FCEBEB;
  --color-info:          #185FA5;  /* 6.52:1 — clears both. STATUS ONLY. */
  --color-info-light:    #E6F1FB;
  --color-ai:            #534AB7;  /* 6.93:1 — clears both */
  --color-ai-light:      #EEEDFE;

  /* Amber is attention. It is never a call to action. */
  --color-warning-fill:  #BA7517;  /* 3.72:1 — borders/icons/dots only */
  --color-warning-text:  #925C12;  /* 4.87:1 on its own pill. Use for labels. */
  --color-warning-light: #FAEEDA;
  /* Compatibility alias for the `bg-warning` utilities already in the code.
     It resolves to the FILL. Any *label* must use --color-warning-text, which
     is the only amber that clears 4.5:1. */
  --color-warning: var(--color-warning-fill);

  /* Energy. Decorative fill and gradient endpoints ONLY: 2.49:1 means it
     fails even as a UI edge. Any label placed on it must be Ink (7.09:1). */
  --color-signal: #14B8A6;

  /* ---------------------------------------------------------------------
     ROLES — what components actually reference.
     Components must never reach past these into a ramp step directly.
     --------------------------------------------------------------------- */

  --color-brand:       var(--color-brand-500);
  --color-brand-light: var(--color-brand-50);
  /* Hover for solid CTAs. Always moves AWAY from the label's colour — darker
     in light mode (white label), lighter in dark (navy label) — so hovering
     can only raise contrast. An alpha hover (bg-brand/85) made contrast
     depend on the backdrop and grazed 4.5:1; these are solid. */
  --color-brand-hover:  var(--color-brand-600);
  --color-danger-hover: color-mix(in srgb, var(--color-danger) 88%, black);

  /* The label sitting ON any solid semantic fill — a primary button, a toast,
     a filled badge. It must be a token, not a literal white: in dark mode the
     semantic hues all lighten together, and white on them scores 2.3–2.9:1.
     Verified in both modes against brand, danger and info fills. */
  --color-on-solid: #FFFFFF;   /* 6.20 / 7.07 / 6.52 on brand, danger, info */
  --color-on-brand: var(--color-on-solid);  /* kept: the name buttons use */

  --color-surface:           #FFFFFF;
  --color-surface-secondary: var(--color-navy-50);
  --color-surface-tertiary:  var(--color-navy-100);

  /* Border sits BETWEEN navy-100 and navy-200: surface-tertiary is already
     navy-100, and an edge that equals the surface it sits on is invisible
     (the old palette kept them distinct for exactly this reason). */
  --color-border:           color-mix(in srgb, var(--color-navy-100) 55%, var(--color-navy-200));
  --color-border-secondary: var(--color-navy-200);

  --color-text-primary:   var(--color-navy-900);  /* 14.64:1 */
  --color-text-secondary: var(--color-navy-600);  /* 6.08:1  — was navy-500, which failed */

  /* Decorative only: hairlines, dividers, disabled glyphs. 2.91:1.
     There is deliberately no third text colour. Two is enough. */
  --color-hairline: var(--color-navy-400);

  /* Deprecated alias. Was 2.91:1 text and failed. Points at a legible value
     so existing callers become compliant. Prefer --color-text-secondary in
     new code; a gate check for new references is still pending. */
  --color-text-tertiary: var(--color-navy-600);

  --color-focus: var(--color-brand-500);  /* 6.20:1 vs white, 5.61:1 vs page */

  /* Overlay backdrop behind modals/drawers. One token, strength via opacity
     modifiers (bg-scrim/60 for dialogs, /30 for viewers, /10 for drawers) —
     never a stock bg-black/N, so the gate can ban the stock palette outright. */
  --color-scrim: #000000;

  /* ---------------------------------------------------------------------
     MARKETING / DECK surface
     Bone is the slide and marketing-page background — bit-identical to the
     stone-50 the live site already ships, and warm against the teal. The
     product never uses it; product surfaces stay on the navy ramp.
     navy-950 on Bone = 17.7:1.
     --------------------------------------------------------------------- */
  --color-paper: #FAFAF9;

  /* ---------------------------------------------------------------------
     TYPE — settled 2026-07-10. Work Sans · Lora · IBM Plex Mono.
     All three are Google Fonts, served from a single origin.

       Work Sans   variable   50,316 B   (one file)   sans:  chrome, body
       Lora        variable   37,788 B   (one file)   serif: system prose
       Plex Mono   400 + 500  29,596 B   (two files)  mono:  figures, codes
                             ---------
                             117,700 B   across ONE origin

     Against the outgoing 107,872 B across TWO origins. The 9.8 KB buys a
     serif the product did not have and drops a third-party font CDN.
     Work Sans alone is 26 KB lighter than the three static cuts it
     replaces, because one variable file covers every weight.

     Why each:
       Work Sans — reads calmer at 13px than a geometric sans, which matters
         on screens comparing dozens of fields at once.
       Lora — a TEXT serif. Ours carries running prose (the paragraph where
         the system explains itself), not headlines. A display serif thins
         out across several lines at 16px.
       IBM Plex Mono — slashed zero. That is the disambiguation convention
         in engineering and financial documents, which is the register a
         tariff or a title-deed number belongs to.

     This also fixes a bug rather than documenting one: the outgoing sans had
     no 600 weight, so every `font-semibold` in the codebase was a browser-
     synthesised fake. Work Sans is variable.

     THE PRODUCT MUST ACTUALLY LOAD THEM. A family declared here but never
     fetched falls back silently to the system stack, which is how the old
     `--font-sans` came to disagree with `body`. Put this in src/index.html:

       <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
       <link rel="stylesheet" href="https://fonts.googleapis.com/css2?\
family=Work+Sans:wght@400..700&family=Lora:wght@400..600\
&family=IBM+Plex+Mono:wght@400;500&display=swap">

     (Narrowing a variable range does not shrink the file — Google serves the
     same woff2 either way. The ranges state intent; the preconnect is the
     optimisation.)

     The `*-weights` tokens are read by the conformance gate, which rejects
     any font-weight the loaded face cannot render.
     `a..b` is a variable range; `a,b` is a list of static cuts.
     --------------------------------------------------------------------- */

  --font-sans:  "Work Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-serif: "Lora", Georgia, "Times New Roman", serif;
  --font-mono:  "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --font-sans-weights:  400..700;
  --font-serif-weights: 400..600;
  --font-mono-weights:  400,500;

  --font-weight-normal:   400;
  --font-weight-medium:   500;
  --font-weight-semibold: 600;  /* real, not synthesised — Work Sans is variable */
  --font-weight-bold:     700;

  /* ---------------------------------------------------------------------
     ELEVATION — was four hand-written rgba blocks in app.css
     --------------------------------------------------------------------- */

  --shadow-1: 0 1px 2px rgb(16 42 67 / 0.05);
  --shadow-2: 0 1px 3px rgb(16 42 67 / 0.08), 0 1px 2px rgb(16 42 67 / 0.06);
  --shadow-3: 0 4px 12px rgb(16 42 67 / 0.10), 0 2px 4px rgb(16 42 67 / 0.06);
  --shadow-4: 0 12px 32px rgb(16 42 67 / 0.14), 0 4px 8px rgb(16 42 67 / 0.08);

  /* ---------------------------------------------------------------------
     MOTION — was six magic numbers scattered across app.css
     --------------------------------------------------------------------- */

  --duration-fast: 150ms;
  --duration-base: 200ms;
  --duration-slow: 280ms;
  --duration-reveal: 600ms;

  --ease-standard:  cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out:       cubic-bezier(0, 0, 0.2, 1);
  --ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* ---------------------------------------------------------------------
     STACKING — modals previously had no defined layer
     --------------------------------------------------------------------- */

  --z-base:     0;
  --z-dropdown: 10;
  --z-sticky:   20;
  --z-overlay:  30;
  --z-modal:    40;
  --z-toast:    50;
  --z-tooltip:  60;
}

/* ===========================================================================
   DARK MODE
   Dark mode currently scores BETTER than light mode on every contrast pair.
   It is not the problem. These values change only where light mode changed.
   =========================================================================== */

.dark {
  color-scheme: dark;

  /* brand-300 is on-ramp and clears 7.02:1 on the dark surface. The vivid
     mint that used to live here has moved to --color-signal, where its
     luminosity is an asset rather than an unexplained orphan. */
  --color-brand:       var(--color-brand-300);
  --color-brand-light: #0F2A22;
  --color-signal:      #34D9A8;
  /* Labels on solid fills are navy here, so hover moves LIGHTER. */
  --color-brand-hover:  var(--color-brand-200);
  --color-danger-hover: color-mix(in srgb, var(--color-danger) 88%, white);
  /* 7.59 / 6.11 / 7.47 on the dark brand, danger and info fills.
     White here would be 2.34 / 2.90 / 2.37 — all unreadable. */
  --color-on-solid:    var(--color-navy-950);
  --color-on-brand:    var(--color-on-solid);

  --color-danger:        #E87575;
  --color-danger-light:  #2A1515;
  --color-info:          #6BAEE8;
  --color-info-light:    #11223A;
  --color-ai:            #A094F0;
  --color-ai-light:      #1C1832;
  --color-warning-fill:  #E8A838;
  --color-warning-text:  #E8A838;  /* 7.75:1 on its pill — one value clears both */
  --color-warning-light: #2A1F10;

  --color-surface:           #152030;
  --color-surface-secondary: #0D1825;
  --color-surface-tertiary:  #1F2B3D;

  --color-border:           #2A3A50;
  --color-border-secondary: #3C4D66;

  --color-text-primary:   #E4E9F0;  /* 13.44:1 */
  --color-text-secondary: #95A5B8;  /* 6.52:1  */
  --color-hairline:       #6B7C92;  /* 3.84:1  — non-text */
  --color-text-tertiary:  #95A5B8;  /* deprecated alias */

  --color-focus: var(--color-brand-300);

  --shadow-1: 0 1px 2px rgb(0 0 0 / 0.30);
  --shadow-2: 0 1px 3px rgb(0 0 0 / 0.30), 0 1px 2px rgb(0 0 0 / 0.24);
  --shadow-3: 0 4px 12px rgb(0 0 0 / 0.40), 0 2px 4px rgb(0 0 0 / 0.24);
  --shadow-4: 0 12px 32px rgb(0 0 0 / 0.50), 0 4px 8px rgb(0 0 0 / 0.32);
}

/* ===========================================================================
   RETIRED TOKENS — do not reintroduce.

   --color-coral / --color-coral-light
       Defined since the first commit; referenced by zero components. It sat
       between danger-red and warning-amber and would collide with both.
       "County/local approvals" (its intended job) is a category, not a
       status, and categories are carried by labels, not hue.

   #2a9d7a  (the beta badge's private teal)
       An off-by-one of brand-400. Use var(--color-brand-400).

   #34D9A8 as --color-brand in dark mode
       Reassigned to --color-signal. See above.

   Satoshi  (Fontshare)
       Replaced by Work Sans. Three static weights (76 KB) became one
       variable file (50 KB), one fewer third-party origin, and the 600
       weight the codebase already used now genuinely exists.

   JetBrains Mono / Instrument Serif
       Replaced by IBM Plex Mono and Lora per the brand kit. Plex Mono is a
       2 KB saving; the mono swap is the one purely aesthetic change here and
       can be reverted at zero cost.
   =========================================================================== */

/* Serif is not decoration. It marks prose the system wrote — AI narrative
   summaries — and marketing display type. It never appears in the chrome. */

/* ===========================================================================
   BASE
   =========================================================================== */

body {
  font-family: var(--font-sans);
  background-color: var(--color-surface-secondary);
  color: var(--color-text-primary);
  margin: 0;
  -webkit-font-smoothing: antialiased;
  font-size: 16px;
  line-height: 1.6;
  transition: background-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard);
}

/* Focus was previously improvised per-component with ad-hoc ring utilities.
   One visible, consistent, AA-compliant ring. */
:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: 2px;
}

/* No component in the codebase honoured this. Motion is now opt-out by
   default for anyone who has asked their OS to reduce it. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}
