/* ============================================================================
   Lagrange Studios: the committed token file.

   design.md is the contract. This file is the only place its numbers exist as
   code, and every page links it. Before Session 3 there were three token
   vocabularies in this repo: an inline :root in index.html, another in
   daryl.html, and the canvas export under _ds/ that the three case-study pages
   linked. That is how the dark hairline ended up with three different values
   (baseline D10) and the mono label size ended up at 10.5px against an 11px
   floor. One file, linked five times, removes the whole class of bug.

   Provenance: merged from the Claude Design canvas export that used to live
   under _ds/, which was already the most faithful implementation of design.md
   in the repo, with four corrections recorded in design.md 3.1 and 4.2. That
   export was deleted in Session 5 (design.md 12 #11, amendment S5.1). It had
   no reference left in any page and Vercel was serving its pre-correction
   colour values at a public URL, which is the failure mode a superseded token
   vocabulary has: nobody links it, everybody can read it.

   Clause map:  fonts 4.1 · color 3.1 · type 4.2, 4.3, 4.4 · grid 5.1
                space 5.2 · radius 5.4 · frame 5.5 · motion 7.1 · focus 9

   Contrast:    every ratio below is measured, not asserted. Regenerate with
                `python3 tools/contrast.py --md`. Table: docs/contrast.md.
                A pair that is not in that table is not approved.

   Motion:      the timing tokens were committed here in Session 3 and used by
                nothing, so Session 4 would have no reason to invent a
                duration. Session 4 used them. `--ease` drives the scrubs and
                `--ease-out` the time-based moves; the distinction is
                amendment S4.1 and it is load-bearing, not stylistic.
   ========================================================================= */

/* --- 4.1 Three families, seven self-hosted faces, no CDN at render time -----
   All three are SIL Open Font License 1.1. There is no 700 weight in the
   system: 4.3 sets weight 400 at every size above 40px, and monumentality
   comes from scale. Do not add a face to get a bolder heading. */
@font-face{font-family:'Instrument Sans';font-style:normal;font-weight:400;font-display:swap;src:url('/fonts/instrument-sans-latin-400-normal.woff2') format('woff2')}
@font-face{font-family:'Instrument Sans';font-style:normal;font-weight:500;font-display:swap;src:url('/fonts/instrument-sans-latin-500-normal.woff2') format('woff2')}
@font-face{font-family:'Instrument Sans';font-style:normal;font-weight:600;font-display:swap;src:url('/fonts/instrument-sans-latin-600-normal.woff2') format('woff2')}
@font-face{font-family:'Instrument Serif';font-style:normal;font-weight:400;font-display:swap;src:url('/fonts/instrument-serif-latin-400-normal.woff2') format('woff2')}
@font-face{font-family:'Instrument Serif';font-style:italic;font-weight:400;font-display:swap;src:url('/fonts/instrument-serif-latin-400-italic.woff2') format('woff2')}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400;font-display:swap;src:url('/fonts/jetbrains-mono-latin-400-normal.woff2') format('woff2')}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;src:url('/fonts/jetbrains-mono-latin-500-normal.woff2') format('woff2')}

:root{
  /* --- 3.1 Primitives. Twelve colors, two temperatures, no additions. ------
     Never apply a primitive in a component. Use the semantic role below it;
     if a role has no token, add the token rather than borrowing by value. */

  /* Surfaces, cool to warm */
  --void:#061A2B;      /* deepest ground, simulation backdrop */
  --deep:#09243A;      /* secondary dark surface */
  --oat:#EDE8DB;       /* third surface, quiet bands and callouts */
  --bone:#F7F4EC;      /* default page ground */
  --paper:#FBFAF6;     /* lightest surface: plates, cards */

  /* The ink family, for light surfaces */
  --ink:#12253D;       /* body text on light          14.07:1 on bone */
  --slate:#2E4560;     /* baseline figures, structure  8.94:1 on bone */
  --graphite:#4E5D71;  /* mono annotation on light     6.11:1 on bone */
  --peri:#3F63E8;      /* computed values, links       4.59:1 on bone
                          NOT approved on --oat: 4.13:1. Use --ink there. */

  /* The accent. One role set, two surface temperatures (3.2, 3.3). */
  --gold:#F2BC4B;      /* dark grounds only           10.14:1 on void
                          on bone it measures 1.58:1. Never on light. */
  --ochre:#8F5A10;     /* light grounds only           5.25:1 on bone
                          on void it measures 3.06:1. Never on dark. */

  /* Annotation on dark only */
  --sky:#9BC7EA;       /* mono on dark                 9.87:1 on void
                          on bone it measures 1.63:1. Never on light. */

  /* Hairlines. Depth comes from these, never from a shadow (5.4).
     AMENDED S5: the hairline is the opposite ground at low alpha, which is
     why the dark rule is bone-tinted rather than neutral white. See 3.1. */
  --line-on-light:rgba(18,37,61,.16);
  --line-on-light-strong:rgba(18,37,61,.42);
  --line-on-dark:rgba(247,244,236,.18);
  --line-on-dark-strong:rgba(247,244,236,.34);

  /* Registration marks (5.5). Structure, never the accent (3.2). */
  --tick:rgba(18,37,61,.45);
  --tick-dark:rgba(255,255,255,.45);

  /* Contract aliases. design.md 3.1 names these; keep both spellings working
     so a clause citation and the code read the same. */
  --rule:var(--line-on-light);
  --rule-dark:var(--line-on-dark);

  /* --- 3.1 Semantic roles, light ground -----------------------------------
     The only tier a component may reference. */
  --surface-page:var(--bone);
  --surface-card:var(--paper);
  --surface-quiet:var(--oat);
  --text-body:var(--ink);
  --text-display:var(--ink);
  --text-secondary:var(--slate);
  --text-mono:var(--graphite);
  --text-computed:var(--peri);
  --text-link:var(--peri);
  --accent-on-light:var(--ochre);
  --cta-ground:var(--gold);
  --cta-label:var(--ink);
  --focus-ring:var(--peri);
  --hairline:var(--line-on-light);
  --hairline-strong:var(--line-on-light-strong);
  --tick-color:var(--tick);

  /* --- 3.1 Semantic roles, dark ground ------------------------------------ */
  --surface-dark:var(--deep);
  --surface-dark-deep:var(--void);
  --text-body-on-dark:var(--bone);
  --text-mono-on-dark:var(--sky);
  --accent-on-dark:var(--gold);
  --hairline-on-dark:var(--line-on-dark);

  /* --- 4.1 Families ------------------------------------------------------- */
  --font-sans:"Instrument Sans","Helvetica Neue",Arial,sans-serif;
  --font-serif-display:"Instrument Serif",Georgia,serif; /* display: dark only */
  --font-mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;

  /* --- 4.2 The scale. Fluid, tuned to 375px and 1440px endpoints. --------- */
  --t-metric:clamp(64px,13vw,196px);
  --t-metric-sm:clamp(40px,5vw,72px);   /* the same figure inside a rail */
  --t-display:clamp(44px,6.2vw,92px);   /* hero h1 only */
  --t-h2:clamp(32px,4.2vw,62px);
  --t-h3:clamp(21px,2.2vw,28px);
  --t-lede:clamp(17px,1.5vw,21px);
  --t-body:16px;
  --t-body-sm:14px;
  --t-mono:11px;
  --t-mono-sm:9.5px;                    /* ephemeris rail only */

  /* --- 4.3 Weights. 400 and 500 for running text, 600 for nav state only. - */
  --w-regular:400;
  --w-medium:500;
  --w-nav-active:600;

  /* --- 4.3 Tracking and leading ------------------------------------------- */
  --track-display:-.045em;   /* permitted band -.045em to -.055em */
  --track-metric:-.055em;
  --track-mono:.13em;        /* uppercase always; the one exception is code */
  --lh-metric:.9;            /* permitted band .86 to 1.04 */
  --lh-display:.98;
  --lh-h2:1.04;
  --lh-lede:1.45;
  --lh-body:1.65;
  --lh-mono:1.5;

  /* --- 4.3 Measure. Never a full-bleed paragraph, on any surface. --------- */
  --measure:68ch;
  --measure-doc:66ch;
  --measure-principal:48ch;  /* 6.13 PrincipalNote */

  /* --- 4.4 Type floors by surface. Below these is a legibility failure. --- */
  --floor-body-web:16px;      --floor-mono-web:9.5px;
  --floor-body-print:16px;    --floor-mono-print:11px;
  --floor-body-slide:40px;    --floor-mono-slide:28px;
  --floor-body-social:24px;   --floor-mono-social:16px;
  --floor-body-youtube:40px;  --floor-mono-youtube:20px;
  --floor-headline-banner:46px; --floor-mono-banner:11px;

  /* --- Semantic type roles ------------------------------------------------ */
  --type-metric:var(--w-regular) var(--t-metric)/var(--lh-metric) var(--font-sans);
  --type-metric-sm:var(--w-regular) var(--t-metric-sm)/var(--lh-metric) var(--font-sans);
  --type-display:var(--w-regular) var(--t-display)/var(--lh-display) var(--font-sans);
  --type-h2:var(--w-regular) var(--t-h2)/var(--lh-h2) var(--font-sans);
  --type-h3:var(--w-medium) var(--t-h3)/1.2 var(--font-sans);
  --type-lede:var(--w-regular) var(--t-lede)/var(--lh-lede) var(--font-sans);
  --type-body:var(--w-regular) var(--t-body)/var(--lh-body) var(--font-sans);
  --type-body-sm:var(--w-regular) var(--t-body-sm)/1.55 var(--font-sans);
  --type-mono:var(--w-regular) var(--t-mono)/var(--lh-mono) var(--font-mono);
  --type-mono-sm:var(--w-regular) var(--t-mono-sm)/var(--lh-mono) var(--font-mono);
  --type-quote:italic var(--w-regular) clamp(24px,2.6vw,38px)/1.25 var(--font-serif-display);

  /* --- 5.1 Grid ----------------------------------------------------------- */
  --grid-columns:12;
  --grid-gutter:24px;
  --grid-max:1180px;                  /* max CONTENT width, padding excluded */
  --page-pad:clamp(20px,5vw,64px);
  --doc-margin:68px;

  /* --- 5.2 Space. 8px base. Permitted values only. Anything else is a bug. */
  --s-4:4px;    --s-8:8px;     --s-12:12px;  --s-16:16px;
  --s-24:24px;  --s-32:32px;   --s-48:48px;  --s-64:64px;
  --s-96:96px;  --s-128:128px; --s-160:160px;
  --section-pad-y:var(--s-96);

  /* --- 5.4 Radius and depth ----------------------------------------------- */
  --radius:0;              /* every surface, card, plate and image */
  --radius-pill:999px;     /* the one deliberate exception: the CTA */
  --hairline-w:1px;
  --rule-w:1.5px;
  /* There is no shadow token. Do not add one. */

  /* --- 5.5 The registration frame ----------------------------------------- */
  --tick-arm:14px;         --tick-inset:10px;        --tick-w:1px;
  --tick-arm-print:16px;   --tick-inset-print:34px;
  --tick-inset-slide:22px;
  --tick-arm-large:26px;   --tick-inset-large:40px;

  /* --- 7.1 Motion. Committed, unused in Session 3. ------------------------ */
  --m-micro:180ms;
  --m-element:320ms;
  --m-section:700ms;
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  /* Banned: bounce, overshoot, elastic. The instrument concept dies the
     moment something boings (7.1). */

  /* --- 9 Accessibility ----------------------------------------------------- */
  --focus-w:2px;
  --focus-offset:3px;      /* checked against observablehq.com's 6px, kept. S4 */
}

/* ============================================================================
   Base. The shared vocabulary every page inherits.
   ========================================================================= */

*,*::before,*::after{box-sizing:border-box}

/* The hidden attribute is the accessible way to remove an element, but the UA
   rule behind it is display:none at the lowest specificity, so any component
   that sets display wins and the element stays on screen while reporting
   itself hidden. Caught on the capped follow-up control, which could be
   answered twice. Make the attribute mean what it says. */
[hidden]{display:none!important}

html{scroll-behavior:smooth}
@media(prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;
  background:var(--surface-page);
  color:var(--text-body);
  font:var(--type-body);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  /* 4.3: tabular figures everywhere a number can change or align. The page is
     mostly numbers, so this is a root setting rather than a per-cell one. */
  font-variant-numeric:tabular-nums;
}

h1,h2,h3,h4,p,figure,blockquote,ul,ol{margin:0}
h1,h2{font-weight:var(--w-regular);letter-spacing:var(--track-display)}
ul,ol{padding:0;list-style:none}
img,svg{display:block;max-width:100%}
button{font:inherit;color:inherit}

a{color:var(--text-link);text-decoration:none;border-bottom:1px solid currentColor;transition:opacity var(--m-micro) var(--ease)}
/* Gated: a touch tap fires a false :hover and leaves the control stuck in
   its hover state until something else is tapped. Amendment S5.2. */
@media(hover:hover) and (pointer:fine){a:hover{opacity:.66}}

/* Reduced motion drops MOVEMENT, not every transition. A universal
   transition-duration:0 also kills colour and opacity fades, which are not
   movement and are the half a reader who asked for less motion still wants: a
   control that changes state with no transition at all reads as a glitch.
   Restricting transition-property instead keeps each element's authored
   duration and curve and lets only non-moving properties through, so no
   transform or size transition can run whether or not it was enumerated
   anywhere. An element with no authored transition has duration 0 and is
   unaffected. Lives here because every page links this file; it was previously
   a per-page rule on index only. S5.13. */
@media(prefers-reduced-motion:reduce){
  *{animation-duration:0ms!important;animation-iteration-count:1!important;
    transition-property:opacity,color,background-color,border-color,box-shadow,fill,stroke!important}
}

/* 9: an in-page jump, a skip link and a focus scroll all land the target at
   the top of the viewport, which on a page with a sticky header means under
   it. Each page with a sticky header declares its own --sticky-h; pages
   without one fall through to 0. Amendment S5.3. */
html{scroll-padding-top:var(--sticky-h,0px)}

/* 9: visible focus rings on every interactive element, no exceptions. */
a:focus-visible,button:focus-visible,input:focus-visible,
select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{
  outline:var(--focus-w) solid var(--focus-ring);
  outline-offset:var(--focus-offset);
}

/* --- Layout -------------------------------------------------------------- */
/* 5.1: max CONTENT width 1180px. The padding sits outside it, which is why
   this is a calc and not a flat max-width. */
.lg-rail{
  width:100%;
  max-width:calc(var(--grid-max) + var(--page-pad) * 2);
  margin-inline:auto;
  padding-inline:var(--page-pad);
}
.lg-grid{
  display:grid;
  grid-template-columns:repeat(var(--grid-columns),1fr);
  gap:var(--grid-gutter);
  max-width:var(--grid-max);
  margin-inline:auto;
}
.lg-measure{max-width:var(--measure);text-wrap:pretty}

/* --- Mono. Always uppercase, .13em, never below 9.5px, never a headline. -- */
.lg-mono{font:var(--type-mono);letter-spacing:var(--track-mono);text-transform:uppercase;color:var(--text-mono)}
.lg-mono-sm{font:var(--type-mono-sm);letter-spacing:var(--track-mono);text-transform:uppercase;color:var(--text-mono)}
/* 4.3 exception: token names and hex values are code being quoted, not labels. */
.lg-code{font:var(--type-mono);letter-spacing:0;text-transform:none;color:var(--text-mono)}

/* --- Serif italic. Dark surfaces only, complete sentences only (4.1). ----- */
.lg-quote{font:var(--type-quote);color:var(--text-body-on-dark)}

/* --- 5.3 Expose the structure. The grid is the look. --------------------- */
.lg-rule{border:0;border-top:var(--hairline-w) solid var(--hairline);margin:0}
.lg-rule-strong{border:0;border-top:var(--rule-w) solid var(--hairline-strong);margin:0}

/* --- 5.6 A contained dark surface. Never a full-bleed band. -------------- */
.lg-on-dark{
  background:var(--void);
  color:var(--bone);
  --surface-page:var(--void);
  --surface-card:var(--deep);
  --surface-quiet:var(--deep);
  --text-body:var(--bone);
  --text-display:var(--bone);
  --text-secondary:var(--sky);
  --text-mono:var(--sky);
  --text-computed:var(--sky);
  --text-link:var(--sky);
  --accent-on-light:var(--gold);
  --cta-ground:var(--gold);
  --cta-label:var(--ink);
  --hairline:var(--line-on-dark);
  --hairline-strong:var(--line-on-dark-strong);
  --tick-color:var(--tick-dark);
}

/* --- Skip link ----------------------------------------------------------- */
.lg-skip{
  position:absolute;left:-9999px;top:0;z-index:99;
  background:var(--gold);color:var(--ink);
  padding:var(--s-12) var(--s-16);border:0;
  font:var(--type-mono);letter-spacing:var(--track-mono);text-transform:uppercase;
}
.lg-skip:focus{left:var(--s-12);top:var(--s-12)}
