/* earl.ee — design tokens
   Single source of truth for colour, type, space, shape and motion.
   Import once, globally. Never write a raw hex or an arbitrary px value in a
   component; if no token fits, add one here first.

   This file contains NO url(). That is a canon rule, not an accident: a
   document may inline it byte-for-byte into a <style> in its <head> (see
   DESIGN-SYSTEM.md, "Inlining"), and an inlined url() would resolve against
   the document's address instead of the stylesheet's. The @font-face blocks
   live in fonts.css, which is linked beside this file.
*/

:root {
  /* ── surfaces ───────────────────────────────── */
  --paper:        #FBFAF6;   /* page background          */
  --raised:       #FFFFFF;   /* cards, panels            */
  --vellum:       #F3F0E9;   /* quiet blocks, wells      */
  --ink:          #14171F;   /* text, night sections     */

  /* ── ink scale ──────────────────────────────── */
  --ink-70:       #3A3F4C;   /* lede                     */
  --ink-50:       #5C6270;   /* secondary                */
  --ink-30:       #8A8578;   /* meta, captions           */
  --line:         #E6E2D9;   /* every border             */
  --line-strong:  #D8D3C7;   /* control borders          */

  /* ── accent: dawn gold — ONE element per screen ── */
  --accent:       #C7A15A;   /* fills: primary button    */
  --accent-deep:  #9A7430;   /* accent text on paper     */
  --accent-wash:  #F6EEDC;   /* badge / tint background  */
  --accent-hover: #B8914A;   /* primary button hover     */
  --accent-night: #C7A15A;   /* accent on ink surfaces   */
  --on-accent:    #14171F;   /* text on an accent fill   */

  /* ── functional, never decorative ───────────── */
  /* Grounds stay quiet, text and borders are strong: in this system
     confidence is contrast, not saturation. Text on its own wash clears
     7:1 — mono 12px badge text needs the headroom.
     Note the lightness gap between good and fault: under red-green
     deficiency the hue collapses, the lightness difference does not. */
  --signal:       #38598C;   /* focus rings, information */
  --signal-wash:  #E9EFF8;
  --good:         #3C6B45;
  --good-wash:    #E7F0E9;
  --fault:        #78261F;   /* deliberately darker than good */
  --fault-wash:   #FBECEA;

  /* calendar marks — a fixed row of four, same weight as --good/--signal.
     NOT a per-calendar property: a calendar is colourless, and a PERSON marks
     the two or three they need daily. The mark belongs to the person, never to
     the project, and never leaves my. It works because it is rare — twenty
     coloured calendars are unreadable, three marked ones jump out. Carried by
     the EDGE (2px left rule of a block, the dot in the narrow month, the dot
     in the filter list) — never by a ground. Identity is the calendar's
     SHORTCODE, not its colour. */
  --cal-1:        #5A4E8C;
  --cal-2:        #8C5A3A;
  --cal-3:        #2F7A72;
  --cal-4:        #8C3A6B;

  /* ── night: theme-STABLE canvas constants ─────
     Canvases (graphs, topology, terminals) are media, not chrome — they stay
     dark in BOTH themes (rule 09). These mirror the dark-theme palette but
     never flip with it; they are not overridden in the dark block below. */
  --night-signal:      #9DB8F5;   /* semantic colours ON a night ground: the */
  --night-good:        #86C79B;   /* light cuts, so they hold on dark in     */
  --night-fault:       #E8A097;   /* BOTH themes                             */
  --night-surface:     #14171F;
  --night-raised:      #1B1F27;
  --night-line:        #2A2E38;
  --night-line-strong: #3A3F4C;
  --night-ink:         #EDEAE3;
  --night-muted:       #8B8F9C;
  --night-faint:       #6B7080;
  --night-accent:      #DDBB79;   /* gold lifted for night surfaces */

  /* ══ Instrument Panel — the canvas subsystem ═══════════════════
     Adopted from the code-state-viewer handoff (specs 1c/4a/5a/7a/7b/8a/8b/9a).
     Theme-STABLE: never flipped by prefers-color-scheme.

     NAMESPACED --ip-* on purpose. The subsystem's own names (--bg-canvas,
     --text-primary, --border, --accent-selected, --font-mono …) collide with
     earl.ee tokens of the same name and different values; unprefixed, whichever
     stylesheet loads second wins silently. After the prefix the names are the
     subsystem's, 1:1 with theme.rs and styles.css.

     Canvas physics, not chrome physics: area fills on a dark ground need
     luminance and hue separation, where chrome needs contrast at 12px on paper.
     Same semantics, deliberately different values. ── */

  /* canvas ground — radial: LIGHTER at the centre, darker at the rim,
     so the eye settles where the data is */
  --ip-bg-canvas:        #0B0F14;   /* rim                              */
  --ip-bg-canvas-center: #131A24;   /* centre                           */
  --ip-node-fill-base:   #0E1319;   /* mix base for fills, chip ground  */

  /* instrument frame — the chrome AROUND a canvas, cooler than --night-*
     because a warm frame beside a cool canvas reads as two mismatched blacks */
  --ip-bg-app:         #10141A;
  --ip-bg-panel:       #131A23;
  --ip-bg-panel-alt:   #161D27;
  --ip-bg-hover-row:   #1B2534;
  --ip-border:         #1D2530;
  --ip-border-strong:  #232B36;
  --ip-border-stronger:#2A3541;

  --ip-text-primary:   #E8EDF2;
  --ip-text-secondary: #C3D2E0;
  --ip-text-muted:     #71829A;
  --ip-text-muted-alt: #8496A8;
  --ip-text-faint:     #48566A;
  --ip-text-faint-alt: #5A6C7E;

  /* depth = hierarchy depth relative to the current view root, NOT type.
     Cycles at ≥5 (--ip-depth-N-ring, N = depth % 5). Ring always 1.5px.
     Fills are PRECOMPUTED opaque mixes (ring at 18% over --ip-node-fill-base),
     not alpha: a 6–18% translucent disc lets crossing edges shine through at
     near-full strength. Translucency here is a rendering decision, not a colour. */
  --ip-depth-0-ring: #5B8DEF;  --ip-depth-0-fill: #243B63;
  --ip-depth-1-ring: #4CC38A;  --ip-depth-1-fill: #1D3A2C;
  --ip-depth-2-ring: #F5A623;  --ip-depth-2-fill: #4A3413;
  --ip-depth-3-ring: #E0699E;  --ip-depth-3-fill: #47203A;
  --ip-depth-4-ring: #A678E8;  --ip-depth-4-fill: #322047;

  --ip-node-ring:      1.5px;
  --ip-node-r-1:         6px;   /* ≤3 children   */
  --ip-node-r-2:         8px;   /* ≤10           */
  --ip-node-r-3:        10px;   /* ≤30           */
  --ip-node-r-4:        13px;   /* >30           */
  --ip-hover-ring:        rgba(255,255,255,0.7);   /* additive, on leave it goes */
  --ip-hover-ring-stroke:   1px;
  --ip-hover-ring-gap:      3px;   /* clear of the depth ring */
  --ip-dim-opacity:     0.22;
  --ip-container-tint:  0.06;   /* depth colour over the canvas centre    */
  --ip-container-cover: 0.85;   /* how opaque that tint is drawn          */
  --ip-container-ring:  0.40;   /* stage ring alpha                       */
  --ip-container-ring-stroke: 1px;
  --ip-ancestor-ring:   0.25;   /* quieter ring for ancestors             */
  --ip-stage-fraction:  0.70;   /* the expanded leaf owns ~70% of the area */

  /* the dawn gold at canvas luminance — one decision, four steps:
     paper #9A7430 · chrome #C7A15A · night #DDBB79 · canvas #FFD9A0 */
  --ip-accent-selected: #FFD9A0;   /* selection ring, loud label, breadcrumb leaf */
  --ip-accent-call:     #D9A441;   /* the selection's call arrows, number chips    */
  --ip-selected-halo:   rgba(255,217,160,0.25);
  --ip-selected-ring-stroke: 2px;   /* REPLACES the depth ring, not additive */
  --ip-selected-halo-stroke: 5px;   /* width of the halo band                */

  /* edges — three contrast tiers, NEVER hidden. These three do separate by
     lightness, and must: here colour is the only channel. */
  --ip-edge-selected: #D9A441;   /* the selection's calls, numbered  */
  --ip-edge-hover:    #8A9DB5;   /* the tapped node's edges          */
  --ip-edge-base:     #44566E;   /* all the rest, ALWAYS drawn       */
  --ip-edge-selected-stroke: 1.5px;
  --ip-edge-hover-stroke:      1px;
  --ip-edge-base-stroke:       1px;
  --ip-edge-dash:     3px;   --ip-edge-gap: 4px;
  --ip-bundle-1:      1px;   --ip-bundle-2: 1.5px;  --ip-bundle-3: 2.5px;

  /* labels — two volumes, all always visible */
  --ip-label-loud:    10.5px;
  --ip-label-quiet:      9px;
  --ip-label-quiet-mix:  0.62;   /* depth ring darkened for quiet labels */
  --ip-label-shrink-start:  2;      /* camera scale where quiet labels start shrinking */
  --ip-label-shrink-min: 0.65;      /* floor of that shrink — 9px → ~5.9px, never removed */
  --ip-label-chip-border: #2B3A4D;
  --ip-chip-r:           7px;  --ip-chip-text: 8px;
  --ip-chip-ring-stroke: 1px;
  --ip-leader:        rgba(72,86,106,0.6);   /* collision leader line */
  --ip-leader-stroke: 0.75px;
  --ip-breadcrumb-sep:   #33404F;

  /* semantic chips — tinted ground + luminous text.
     The chrome badge doctrine (solid ground) exists for paper; on a dark
     ground a tint already carries substance, and solid pills would out-shout
     the data they annotate. */
  --ip-verb:  #9DB8F5;  --ip-verb-bg:  #1B2A45;
  --ip-io:    #F5A623;  --ip-io-bg:    #2E2413;
  --ip-pure:  #7EE0AC;  --ip-pure-bg:  #14291F;
  --ip-drift: #E09DA6;  --ip-drift-bg: #2A1A1D;  --ip-drift-border: #4A2B30;
  --ip-stat-drift: #F0883E;   /* drift counter — maps to --fault in chrome */

  /* shell details (spec 1c) */
  --ip-card-border:   #212B38;
  --ip-indent-guide:  #223040;
  --ip-search-bg:     #10161E;
  --ip-search-border: #232F3D;
  --ip-btn-primary-border: #2B4270;
  --ip-nav-marker-off: #33404F;
  --ip-nav-active-text: #EAF0F6;

  /* code colouring (spec 9a) — no new hues, all drawn from the above.
     Names stay brightest; keywords and types are supporting cast. */
  --ip-code-struct:   #9FB2C4;
  --ip-tok-keyword:   var(--ip-verb);
  --ip-tok-fn-name:   var(--ip-accent-selected);
  --ip-tok-type:      var(--ip-pure);
  --ip-tok-string:    #C47A9D;
  --ip-tok-comment:   #5A8A6E;
  --ip-tok-call:      var(--ip-accent-call);
  --ip-tok-punct:     var(--ip-text-muted);
  --ip-comment-chip-bg: #14291F;
  --ip-count-chip-fg: #C3D2E0;
  --ip-count-chip-bg: #1B2534;

  /* environments ON A CANVAS — the chrome rule inverts: live is the most
     present thing on a graph, not the unmarked one. */
  --ip-env-live:     #E8E3D8;   /* near-white: production is loudest */
  --ip-env-live-fill: #4F5152;  /* the ring at the same ~30% over
                                   --ip-node-fill-base that the depth fills
                                   measure — stored, like every other fill */
  --ip-env-preview:  #5B8DEF;
  --ip-env-build:    #A678E8;
  --ip-env-platform: #4CC38A;

  /* ── environments: coded, never decorative ────
     Live is deliberately uncoloured — production is the default
     reality; the other environments are the ones that get marked.
     Markers only (badge, dot). Never a fill, never a button. */
  --env-preview:       #3F6B94;
  --env-preview-wash:  #E8EEF5;
  --env-build:         #7A5D8A;
  --env-build-wash:    #F0EAF3;
  --env-platform:      #37746E;
  --env-platform-wash: #E6EFED;
  --env-live:          var(--ink);
  --env-live-wash:     var(--vellum);

  /* ── type ───────────────────────────────────── */
  --font-display: "Newsreader", Georgia, serif;
  --font-sans:    "Instrument Sans", system-ui, sans-serif;
  --font-mono:    "JetBrains Mono", ui-monospace, monospace;

  --text-display: 300 64px/1.05 var(--font-display);

  /* The one fluid step, and it has exactly one consumer: the large wordmark on
     www, which is sized to the viewport rather than to the scale — it tops out
     at the display step and shrinks with the window instead of wrapping.
     Anything else that wants to be fluid is a layout problem in a type costume.
     Not for the small mark above a hero eyebrow: that one is --text-app-title.
     Tracking follows size, not role: above 40px it takes --track-tight, below
     it takes none. */
  --text-wordmark: 300 clamp(44px, 9vw, 64px)/1.0 var(--font-display);

  --text-h2:      300 40px/1.15 var(--font-display);
  --text-h3:      400 24px/1.25 var(--font-display);
  --text-app-title: 400 20px/1.20 var(--font-display);  /* app shell only — the serif floor */
  --text-lede:    400 19px/1.62 var(--font-sans);
  --text-body:    400 17px/1.60 var(--font-sans);
  --text-small:   400 15px/1.50 var(--font-sans);
  --text-stat:    600 22px/1.20 var(--font-sans);  /* the numeral on a stat tile;
                                                      set font-variant-numeric:
                                                      tabular-nums with it */
  --text-meta:    400 12px/1.40 var(--font-mono);
  /* Same step, emphasised. The system says "mono 12 uppercase weight 500" in a
     dozen places; that sentence is this token. Eyebrows and section labels take
     it instead of overriding --text-meta's weight. */
  --text-label:   500 12px/1.40 var(--font-mono);
  /* For the two cases that are body copy asked to lead: a card heading, a legal
     section title. Not a type step — the step is right, only the weight leans. */
  --weight-medium: 500;
  --weight-semi:   600;
  --track-tight:  -0.025em;
  /* Three roles, not one value with drift. The eyebrow is wider than the meta
     label on purpose — it is read as a marker, not as text. Status labels are
     tighter because a coloured word has to stay a word. */
  --track-eyebrow: 0.16em;  /* eyebrows, section numbers  */
  --track-meta:    0.14em;  /* the generic meta label     */
  --track-status:  0.12em;  /* labels in semantic colours */

  /* ── space (multiples of 4) ─────────────────── */
  --space-xs:  4px;   --space-sm:  8px;   --space-2:  12px;
  --space-md: 16px;   --space-4:  24px;   --space-lg: 32px;
  --space-6:  48px;   --space-xl: 72px;   --space-8: 104px;
  --space-2xl:160px;

  --container:    1120px;   /* page width               */
  --measure:       720px;   /* prose width              */
  /* The hero pair. Two wrap widths, not two suggestions: the claim breaks
     early and stacks, the lede runs longer under it. Tokens because the
     relationship is the design — a second hero must inherit the ratio, not
     guess at it again. */
  --measure-claim:  16ch;
  --measure-lede:   52ch;
  --gutter:         48px;   /* 24px below 40rem — set in the media query at the end of this block */
  /* Breakpoints are not tokens — custom properties do not resolve inside a
     media query, so a --bp-* would be a name that cannot be used where it is
     needed. The two thresholds are named here as prose and written literally at
     the query: 40rem (gutters fall to 24) and 60rem (three columns fit). */

  /* ── shape & depth ──────────────────────────── */
  /* The accent rule above a feature title. Geometry that lives in a sentence is
     geometry nobody can change without hunting for it. */
  --rule-accent-w:  28px;
  --rule-accent-h:   2px;
  --radius-control:   6px;   /* buttons, chips          */
  --radius-input:    10px;   /* fields, selects         */
  --radius-card:     14px;   /* cards, panels           */
  --radius-pill:    999px;   /* badges, avatars         */
  --shadow-lift: 0 12px 32px -12px rgba(20,23,31,0.22); /* floating only */
  --shadow-lift-up: 0 -12px 32px -12px rgba(20,23,31,0.22); /* the same lift, mirrored — floats anchored to the bottom edge (bottom sheets) */
  --focus-ring:  0 0 0 3px rgba(56,89,140,0.20);

  /* ── motion ─────────────────────────────────── */
  --ease:        cubic-bezier(0.2, 0, 0, 1);
  --dur-state:   160ms;   /* hover, focus, toggle       */
  --dur-enter:   280ms;   /* entrances                  */
  --dur-spin:    900ms;   /* spinner ring, one turn     */
  --dur-rail:   1600ms;   /* rail segment, one pass     */
}

/* Below 40rem the gutter falls to 24 — the one promise the doc makes about
   this token. The threshold is written literally per the breakpoint rule. */
@media (max-width: 40rem) {
  :root { --gutter: 24px; }
}

/* ── night: derived, not designed ───────────────
   Only surfaces, ink, line and accent change. No dark-only
   component, no dark-only layout. Canvases (graphs, topology,
   terminals) stay dark in BOTH themes — they are media, not chrome. */
@media (prefers-color-scheme: dark) {
  :root {
    --paper:       #14171F;
    --raised:      #1B1F27;
    --vellum:      #21262F;
    --ink:         #EDEAE3;   /* warm off-white, never #FFF */
    --ink-70:      #B9BCC5;
    --ink-50:      #8B8F9C;
    --ink-30:      #6B7080;
    --line:        #2A2E38;
    --line-strong: #3A3F4C;

    --accent-deep: #DDBB79;   /* gold text needs lifting on dark */
    --accent-wash: #2A2418;

    --signal:      #9BBAE4;
    --signal-wash: #1A2230;
    --good:        #85BE8E;
    --good-wash:   #16241A;
    --fault:       #E8A097;   /* still the lighter-hue/darker-ground pair */
    --fault-wash:  #2B1815;

    --cal-1:       #A89BD9;
    --cal-2:       #D9A585;
    --cal-3:       #7FC4BB;
    --cal-4:       #D98BB8;

    --env-preview:       #8FB0D6;
    --env-preview-wash:  #1C2530;
    --env-build:         #B99BC6;
    --env-build-wash:    #26202B;
    --env-platform:      #79B0A8;
    --env-platform-wash: #1A2725;

    --shadow-lift: 0 12px 32px -12px rgba(0,0,0,0.55);
    --shadow-lift-up: 0 -12px 32px -12px rgba(0,0,0,0.55);
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ── base ───────────────────────────────────────
   The only global rules. Everything else is a component. */
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: var(--text-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

* { box-sizing: border-box; }

a {
  color: var(--ink);
  text-decoration-color: var(--accent);
  text-underline-offset: 3px;
  transition: color var(--dur-state) ease-out;
}
a:hover { color: var(--accent-deep); }

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-color: var(--signal);
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 300;
  letter-spacing: var(--track-tight);
  text-wrap: pretty;
  margin: 0;
}
h3 { font-weight: 400; letter-spacing: 0; }

p { text-wrap: pretty; margin: 0; }

code, kbd, samp, pre { font-family: var(--font-mono); }
