/* ============================================================================
   DroneWorthy Design System — canonical tokens  ·  v2.1 (round 3)
   Drop-in replacement for part107-tutor/design-system/tokens.css

   ┌──────────────────────────────────────────────────────────────────────┐
   │ SUPERSESSION — RULED 2026-07-25. DO NOT RE-DERIVE FROM v1.0.         │
   │ DRONEWORTHY-DESIGN-SYSTEM.md §2 reserves magenta #C0398F for the     │
   │ primary CTA. That rule is RETIRED. There is now ONE accent, amber    │
   │ --dw-accent, and no brand magenta anywhere in the system.            │
   │ Reason: brand magenta and --faa-chart-magenta on the same screen is  │
   │ a teaching hazard. Removing brand magenta entirely means a learner   │
   │ can never confuse a CTA with Class C. If you are reading v1.0 §2 and │
   │ about to reintroduce #C0398F — don't. This block is the ruling.      │
   └──────────────────────────────────────────────────────────────────────┘

   v2.1 adds the ELEVATION and MOTION scales (§ below). v2.0's colour,
   type, space, radius and FAA blocks are unchanged.

   WHAT CHANGED FROM v1.0 (see CHANGELOG-round-2.md §Tokens for the full list):
   - The app surface is the INSTRUMENT-PANEL dark direction: graphite surfaces,
     ONE amber signal accent. Poppins/Inter -> Sora / IBM Plex Sans / IBM Plex Mono.
   - Added the PAPER PLATE scale. This is the load-bearing idea of round 2:
       CHART SEMANTICS LIVE ON PAPER. APP CHROME LIVES ON GRAPHITE.
     The FAA hexes are untouched; they are simply never asked to sit on graphite,
     because on paper they measure the way the real chart measures.
   - Added an explicit STATE LADDER (neutral / progress / mastered / miss) so an
     un-started subject can never render as a failure.
   - Every text/boundary token below carries its measured contrast ratio.

   HOW THE RATIOS BELOW ARE COMPUTED — one convention, so the numbers are
   reproducible instead of remembered. Standardised 2026-08-08.
     · WCAG 2.x relative luminance on sRGB, ratio (Llighter+0.05)/(Ldarker+0.05).
     · Against the background the comment NAMES, and no other.
     · Printed to 2 decimal places, never rounded up - i.e. TRUNCATED, so a
       printed number can never read higher than the colour actually is.
     · `npm run check:contrast` (scripts/test-a11y-contrast.mjs) is the authority.
       It recomputes every claim on this page and fails when one is wrong.
     · The truncation is the tool's, not the author's - it was rounding to
       nearest when this convention was written, so six claims here were 0.001
       to 0.004 high for no reason but the formatter. Restated 2026-08-08 with
       the tool corrected; the rule and the tool now say the same thing, which
       is the only state in which "paste what it prints" is safe advice.
   Before this, seven claims were 2-3% high and one certified a colour that
   failed. They were not one method applied differently: solving each claim for
   the background it implies gives seven DIFFERENT luminances spread across most
   of the gap between --dw-surface and --dw-bg, so no single reference
   reproduces them. They were hand-entered and generously rounded. If you change
   a colour, run the check and paste what it prints - do not estimate.

   RULE (unchanged from v1.0): every DroneWorthy surface references these
   variables. Do not hardcode a hex anywhere. If a value needs to change, it
   changes HERE and everywhere inherits.
   ============================================================================ */

:root {
  /* ---- Graphite: APP CHROME ONLY -------------------------------------- */
  --dw-bg:            #0E1113;  /* page / deepest well */
  --dw-surface:       #14181B;  /* cards, panels — the reference bg for ratios below */
  --dw-surface-2:     #1B2024;  /* subtle raised / hover fill */
  --dw-surface-3:     #252B31;  /* pressed, inset wells, tab bar */

  --dw-ink:           #F2F5F7;  /* primary text           16.30:1 on --dw-surface */
  --dw-dim:           #9AA7B2;  /* secondary text          7.26:1 on --dw-surface */
  --dw-dim-2:         #7D8B98;  /* tertiary / metadata     5.11:1 on --dw-surface */
  --dw-line:          #5C6772;  /* ALL UI boundaries       3.09:1 on --dw-surface */
  --dw-line-inset:    #2E353C;  /* decorative inset only — never the sole affordance */

  /* ---- The one signal accent ------------------------------------------ */
  --dw-accent:        #F2A31D;  /* amber                   8.52:1 on --dw-surface */
  --dw-accent-quiet:  #C98209;  /* amber, held back (borders on paper, hover fills) */
  /* This one said "9.1:1" with no background named, so the checker's claim regex
     could not see it and it was never verified. Named, and now it is checked. */
  --dw-on-accent:     #0E1113;  /* ink on an amber fill    9.05:1 on --dw-accent */
  /* ADOPTED BY RULING 2026-08-13 00:03Z (`_RULING — three words given on the wave-39 card`);
     the shortfall it recorded was CLOSED 2026-08-13 in LANE DWSHIPFREE, under the wave-40 card.
     Amber as TEXT or a BORDER on a light surface — the same hue held down. --dw-accent-quiet
     was doing that job and measured 2.61:1 on --dw-paper-2, which VISUAL STANDARD §7's amber
     rule forbids outright. Amber as a FILL keeps --dw-on-accent ink; this token is never a fill
     and never appears on a dark surface (3.28:1 there — it is a paper colour).
     🔴 THE DEFECT THIS COMMENT USED TO REPORT IS FIXED HERE, NOT RESTATED. The first adoption
     shipped #9C6300, which cleared the 4.5 floor on --dw-paper (4.55:1) and on the white card
     (5.00:1) but measured 4.16:1 on --dw-paper-2 (#EFEADD) — the background of the very call
     site that produced the complaint (.menu-pop button:hover, .mslesson). #945E00 was named
     there as the one word that closes it, and it is now the shipped value.
     RE-DERIVED IN-REPO, NOT INHERITED, printed with this repo's own truncating formatter
     (Math.floor(v*100)/100 — never rounded up, per test-a11y-contrast.mjs):
         --dw-paper   (#F7F4EC)  4.94:1        --dw-paper-2 (#EFEADD)  4.52:1   ← the wells
         #FFFFFF      (literal)  5.44:1        --dw-surface (#14181B)  3.28:1   ← never used there
     All three light substrates now clear 4.5. The well clears by 0.02, so this token is PINNED
     to the well — darkening that background re-opens the defect, and nothing here would catch it.
     ⚠️ Never write a `--dw-name` followed by a colon in this file's PROSE. The parser above reads
     `(--dw-[a-z0-9-]+)\s*:\s*([^;]+);` over the whole file, so a token-shaped phrase in a comment
     swallows every character up to the next semicolon — including the next real declaration. An
     earlier draft of this very comment wrote one, and it silently deleted a token from the map.
     Only the --dw-paper figure is written as a claim on the line below — test-a11y-contrast.mjs
     reads ONE `N:1 on --dw-token` per line, so a second claim there would be silently unchecked.
     #FFFFFF is a literal, not a token, so it is uncheckable by construction and recorded here. */
  --dw-accent-deep:   #945E00;  /* amber as text on paper  4.94:1 on --dw-paper */
  --dw-accent-wash:   rgba(242,163,29,.13);   /* amber at rest, as a fill */
  --dw-accent-wash-2: rgba(242,163,29,.24);   /* amber engaged */
  --dw-focus-ring:    0 0 0 3px #0E1113, 0 0 0 6px #F2A31D;  /* :focus-visible, everywhere */

  /* ---- State ladder — the ONLY colours allowed to grade a subject ------ */
  --dw-state-neutral: #5C6772;  /* NOT STARTED. Never red. Never a failure colour.  */
  --dw-state-progress:#F2A31D;  /* IN PROGRESS — same amber, one accent             */
  --dw-state-mastered:#5FD48B;  /* MASTERED                9.58:1 on --dw-surface   */
  --dw-state-miss:    #FF8A80;  /* AN ACTUAL RECORDED MISS 7.82:1 on --dw-surface   */
  --dw-state-neutral-fill:  #2E353C;
  --dw-state-mastered-fill: rgba(95,212,139,.16);
  --dw-state-miss-fill:     rgba(255,138,128,.16);

  /* ---- PAPER PLATE: the chart substrate -------------------------------- */
  /* Nothing that is not chart semantics may sit on paper, and no chart
     semantic may sit on graphite. The boundary is drawn with --dw-plate-edge. */
  --dw-paper:         #F7F4EC;  /* the plate — matches sectional stock */
  --dw-paper-2:       #EFEADD;  /* plate wells: legend strip, callout boxes */
  --dw-paper-ink:     #1A1814;  /* chart ink             16.12:1 on --dw-paper */
  --dw-paper-dim:     #5A5648;  /* chart annotation       6.68:1 on --dw-paper */
  /* 🔴 WAS #A8A08A, which measured 2.37:1 while this comment claimed 3.1:1 — under
     the 3:1 WCAG 2.2 SC 1.4.11 floor for a graphical object, on the neatlines a
     learner traces to read airspace. Darkened 2026-08-08 until it clears MEASURED.
     Chosen over #8E8875 (3.22:1) because a hairline also lands in a legend strip:
     on --dw-paper-2 #8E8875 is 2.94:1 and fails, this is 3.10:1 and clears. The
     floor is met on BOTH plate substrates, not only the one the comment names. */
  --dw-paper-line:    #8A8472;  /* chart hairlines / neatlines  3.39:1 on --dw-paper */
  --dw-plate-edge:    #0A0C0D;  /* the deliberate, visible graphite→paper edge */
  --dw-plate-edge-w:  6px;

  /* ---- Semantic (answer feedback — distinct from the state ladder) ----- */
  --dw-good:          #5FD48B;
  --dw-bad:           #FF8A80;
  --dw-caution:       #F2A31D;

  /* ---- Placeholder: content we do NOT have and will not invent --------- */
  /* Anything rendered in these tokens is an empty slot with a label on it. */
  --dw-placeholder-line: #C98209;
  --dw-placeholder-ink:  #F2A31D;
  --dw-placeholder-fill: rgba(242,163,29,.07);

  /* ---- Type ------------------------------------------------------------ */
  --dw-font-display: 'Sora', system-ui, sans-serif;                      /* headings, numerals */
  --dw-font-body:    'IBM Plex Sans', system-ui, sans-serif;             /* body, UI */
  --dw-font-mono:    'IBM Plex Mono', ui-monospace, Consolas, monospace; /* codes, CFR, METAR, chart labels */

  /* 11px is the FLOOR, not a step to reach for. Learners are 35–60. */
  --dw-fs-2xs: 11px; --dw-fs-xs: 12px; --dw-fs-sm: 14px; --dw-fs-md: 16px;
  --dw-fs-lg:  20px; --dw-fs-xl: 26px; --dw-fs-2xl: 34px; --dw-fs-3xl: 46px;
  --dw-lh-tight: 1.2; --dw-lh-snug: 1.35; --dw-lh-normal: 1.55;
  --dw-fw-normal: 400; --dw-fw-medium: 600; --dw-fw-bold: 700;
  --dw-tracking-caps: .09em;   /* the only tracking value in the system */

  /* ---- Space (4-based, unchanged from v1.0) ---------------------------- */
  --dw-space-1: 4px;  --dw-space-2: 8px;  --dw-space-3: 12px; --dw-space-4: 16px;
  --dw-space-5: 24px; --dw-space-6: 32px; --dw-space-7: 48px; --dw-space-8: 64px;

  /* ---- Radius (unchanged from v1.0) ------------------------------------ */
  --dw-radius-xs: 6px; --dw-radius-sm: 8px; --dw-radius-md: 10px;
  --dw-radius-lg: 12px; --dw-radius-pill: 20px; --dw-radius-full: 999px;

  /* ---- Touch ------------------------------------------------------------ */
  --dw-touch-min: 44px;   /* every interactive element, app AND marketing site */

  /* ---- Shell geometry ---------------------------------------------------- */
  --dw-rail-w: 228px;     /* desktop nav rail. App and tool pages agree on it here
                             rather than each hard-coding the same number.       */

  /* ---- Elevation --------------------------------------------------------
     A raised surface in a dark UI is not a shadow — it is a shadow AND a
     top-edge highlight, because light falls from above. Every raised thing
     picks a level; nothing invents its own shadow. Levels are paired, so
     one token carries both halves.                                        */
  --dw-elev-0: inset 0 1px 0 rgba(255,255,255,.045);
  --dw-elev-1: inset 0 1px 0 rgba(255,255,255,.065), 0 1px 2px rgba(0,0,0,.45);
  --dw-elev-2: inset 0 1px 0 rgba(255,255,255,.075), 0 6px 18px rgba(0,0,0,.50), 0 1px 2px rgba(0,0,0,.40);
  --dw-elev-3: inset 0 1px 0 rgba(255,255,255,.090), 0 18px 48px rgba(0,0,0,.60), 0 2px 6px rgba(0,0,0,.45);
  --dw-elev-accent: inset 0 1px 0 rgba(255,255,255,.16), 0 6px 20px rgba(242,163,29,.20);

  /* legacy aliases — v2.0 names, kept so nothing breaks */
  --dw-shadow-sm: 0 1px 3px rgba(0,0,0,.45);
  --dw-shadow-md: 0 10px 28px rgba(0,0,0,.50);
  --dw-shadow-lg: 0 18px 48px rgba(0,0,0,.58);
  --dw-shadow-plate: 0 2px 0 rgba(0,0,0,.55), 0 14px 32px rgba(0,0,0,.55);

  /* ---- Material ---------------------------------------------------------
     Three things stop a dark UI reading as a wireframe: one light source,
     a grain, and translucency where surfaces stack.                       */
  --dw-light: radial-gradient(1100px 560px at 50% -8%, rgba(242,163,29,.075), transparent 72%);
  --dw-light-plate: radial-gradient(560px 300px at 30% -12%, rgba(255,255,255,.05), transparent 70%);
  --dw-grain: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxNDAnIGhlaWdodD0nMTQwJz48ZmlsdGVyIGlkPSduJz48ZmVUdXJidWxlbmNlIHR5cGU9J2ZyYWN0YWxOb2lzZScgYmFzZUZyZXF1ZW5jeT0nLjg1JyBudW1PY3RhdmVzPSczJyBzdGl0Y2hUaWxlcz0nc3RpdGNoJy8+PC9maWx0ZXI+PHJlY3Qgd2lkdGg9JzE0MCcgaGVpZ2h0PScxNDAnIGZpbHRlcj0ndXJsKCNuKScvPjwvc3ZnPg==");
  --dw-grain-opacity: .022;
  --dw-glass:      rgba(20,24,27,.74);
  --dw-glass-2:    rgba(27,32,36,.72);
  --dw-glass-blur: saturate(160%) blur(18px);
  --dw-hairline:   rgba(255,255,255,.07);   /* decorative top edge only — never a sole affordance */
  /* The one celebratory material in the system. Defined once so "earned, not confetti"
     is a value rather than a habit. Amber at .22 — the accent, held to a wash.        */
  --dw-sweep:      linear-gradient(100deg, transparent 30%, rgba(242,163,29,.22) 50%, transparent 70%);

  /* ---- Atmosphere (MARKETING SURFACES ONLY) -----------------------------
     Added 2026-08-12 · LANE DWMOTION. The ambient field behind a public hero:
     a drifting colour field, a ruled measurement grid, over the existing
     --dw-grain. §2.5 band ③ — decoration with no informational job.

     🔒 THESE ARE GRAPHITE, AND THAT IS THE RULE, NOT THE TASTE.
     Both orbs are low-alpha derivations of --dw-dim (#9AA7B2) and --dw-line
     (#5C6772) — no new hues, and deliberately NO AMBER. Amber is this
     system's ONE accent and it is SEMANTIC: it marks the thing you act on.
     An amber wash behind a whole hero spends the only signal colour we have
     on scenery, and then a real amber affordance has nothing left to say.
     ⛔ --dw-light IS AMBER-TINTED and is the app's overhead light, not
     atmosphere — do not reach for it here.
     ⛔ And per §2.5 the whole block is banned outright on any surface showing
     a finding, register, record or result. A hero is none of those.          */
  --dw-atmos-1:   radial-gradient(closest-side, rgba(154,167,178,.16), rgba(154,167,178,0) 70%);
  --dw-atmos-2:   radial-gradient(closest-side, rgba(92,103,114,.20), rgba(92,103,114,0) 70%);
  --dw-grid-line: rgba(255,255,255,.055);   /* the ruled grid — argues precision, carries no data */

  /* ---- Motion -----------------------------------------------------------
     Every animation explains a state change. Durations are tokens so the
     reduced-motion block below can collapse all of them at once.          */
  --dw-ease:        cubic-bezier(.2,.7,.2,1);
  --dw-ease-out:    cubic-bezier(.16,1,.3,1);
  --dw-ease-spring: cubic-bezier(.34,1.4,.64,1);
  --dw-dur-fast:  120ms;
  --dw-dur:       200ms;   /* alias of --dw-dur-base */
  --dw-dur-base:  200ms;
  --dw-dur-moment:300ms;   /* the earned moment — lesson-complete tick + sweep. Between
                              base and slow, and it runs once. Not a celebration loop.  */
  --dw-dur-slow:  320ms;
  --dw-dur-draw:  760ms;   /* rings, bars and routes that draw rather than appear */
  --dw-dur-count: 900ms;   /* debrief count-ups (20 steps × 45ms) */
  --dw-stagger:    60ms;   /* scroll-reveal step */

  /* ---- THE SHARED MOTION SCALE (portfolio-wide) --------------------------
     Added 2026-08-12 · LANE DWMOTION, consuming the block LANE MOTIONTOKENS
     (wave-35 §1) defines. The same seven names carry the same seven values in
     trainworthy and trailhead-and-tripod, so the scale is INHERITED rather
     than reimplemented per property. Do not re-tune a value here alone: this
     block is one of three copies and they are meant to agree.

     ⚠️ PREFIXED --mo-* AND NOT THE SHOWCASE'S --t-*, ON ROBERT'S RULING
     (row 343, LANE MOTIONTOKENS, 2026-08-12). This repo could have taken
     --t-* safely — everything here is --dw-* — but `trainworthy` COULD NOT:
     its --t-* IS its type scale, and --t-base there is 0.875rem, the body-text
     anchor. The ruled --t-base:240ms lands in the same :root block, so
     last-declaration-wins would have silently redefined body text size as a
     duration, and a planted copy left that repo's suite fully green — nothing
     in it could see the collision. The scale was therefore re-prefixed
     SUITE-WIDE so all three repos keep one name for one concept.
     ⛔ Do not "restore" these to --t-* to match the showcase document; the
     showcase is the design reference, and this rename is the later ruling.

     ┌─ 🔴 THE TWO-SIDED LAW. READ THIS BEFORE YOU USE ONE. ────────────────┐
     │ WHICH SIDE ARE YOU ON? Ask what you are styling, not what repo you   │
     │ are in — this one file is read by BOTH sides.                        │
     │                                                                      │
     │  APP CHROME  (app/**, the study app, /app/, any surface INSIDE the   │
     │  product a paying learner uses)                                      │
     │      → --mo-micro and --mo-base ONLY. Ceiling 300ms, per THE VISUAL    │
     │        STANDARD §2⑤. Nothing bouncy, nothing springy.                │
     │      ⛔ --mo-page and --mo-stage ARE FORBIDDEN HERE.                   │
     │                                                                      │
     │  MARKETING / PUBLIC  (landing/**, blog, free tools, anything a       │
     │  visitor sees BEFORE they are a customer)                            │
     │      → all four clocks. §2.5 permits page-scale timing here.         │
     └──────────────────────────────────────────────────────────────────────┘
     The boundary is ONE-WAY: marketing may be more expressive than the app,
     and NOTHING from the marketing vocabulary may leak back into app chrome.
     A reader learns which side they are on from the PATH of the file they are
     editing — that is the test, and the box above is the answer key. */
  --mo-micro:150ms; --mo-base:240ms; --mo-page:520ms; --mo-stage:820ms;
  --mo-ease-out:cubic-bezier(.16,1,.3,1); --mo-ease-in:cubic-bezier(.7,0,.84,0); --mo-ease-move:cubic-bezier(.65,.05,.36,1);
}

/* Every animation in the system opts out here. Non-negotiable.
   🔒 THE REDUCED-MOTION CONTRACT, STATED ONCE FOR THIS REPO:
   the reduced state is a COMPLETE, STATIC, LEGIBLE surface — never a degraded
   one. Collapsing a duration to 1ms is only half of that. The other half is a
   rule every consuming surface owes: anything that starts hidden and is
   revealed by motion must start VISIBLE when this query matches, so nothing
   depends on an animation having run. All content is in the DOM at load;
   reduced motion means it is also all on the SCREEN at load.
   (landing/index.html implements its half by never applying the staged
   start-state at all — see the `dw-motion` gate there.) */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dw-dur-fast: 1ms; --dw-dur: 1ms; --dw-dur-base: 1ms;
    --dw-dur-moment: 1ms; --dw-dur-slow: 1ms; --dw-dur-draw: 1ms;
    --dw-dur-count: 1ms; --dw-stagger: 0ms;
    --mo-micro: 1ms; --mo-base: 1ms; --mo-page: 1ms; --mo-stage: 1ms;
  }
}

/* ============================================================================
   FAA SECTIONAL-CHART PALETTE — FUNCTIONAL, NOT BRAND. DO NOT THEME THESE.
   Verbatim from v1.0. These carry regulatory meaning. They are deliberately
   DIFFERENT hexes from any brand colour and must never be swapped for brand
   colours, recoloured, or "made prettier."

   ROUND-2 NOTE — why they are still exactly these hexes:
   on graphite (#14181B) they measure 2.3:1 and 1.9:1, which is a real
   accessibility failure. The fix is NOT to brighten them. The fix is the
   substrate: on --dw-paper (#F7F4EC) chart-blue measures 4.7:1 and
   chart-magenta 7.9:1 — legible, and identical to the paper a learner will
   actually be handed at the testing centre. So chart semantics only ever
   render on the paper plate. That is what --dw-plate-edge is for.
   ============================================================================ */
:root {
  --faa-chart-blue:    #0071BB;  /* Class B / Class D depiction */
  --faa-chart-magenta: #88264B;  /* Class C / Class E depiction, Mode C veil */

  /* Derived ONLY as low-alpha fills of the two hexes above — no new hues. */
  --faa-chart-blue-fill:    rgba(0,113,187,.10);
  --faa-chart-magenta-fill: rgba(136,38,75,.10);
  --faa-chart-blue-veil:    rgba(0,113,187,.05);
  --faa-chart-magenta-veil: rgba(136,38,75,.05);
}
