/* =========================================================================
   8-BIT SPARK — main.css
   Single stylesheet. No build step, no framework.

   Brand system implemented from the 8BS palette sheet: five core colours,
   three darkened text-safe variants, off-black body copy, white ground,
   Montserrat, square corners throughout (pixel-art brand — no border-radius).
   ========================================================================= */

/* =========================================================================
   1. TOKENS
   ========================================================================= */

:root {
  /* Tells the browser which themes this page supports, so form controls,
     scrollbars, and the pre-paint canvas follow the OS setting rather than
     staying light under a dark page. */
  color-scheme: light dark;

  /* --- 1a. TIER 1 — core palette (fills) ------------------------------
     Full saturation. Mosaics, ramps, bullet squares, diagram blocks,
     card rules. Never text. */
  --brand-orange:      #f26419;
  --brand-blue:        #33658a;
  --brand-yellow:      #f6ae2d;
  --brand-blue-light:  #86bbd8;
  --brand-blue-dark:   #2f4858;

  /* --- 1b. TIER 2 — mid (large headings, extra fill variety) ----------
     Shared OKLCH lightness 0.662, so they read as one family. Use when a
     layout needs more than five distinct fills, or for display headings
     at 24px+ where 3:1 is the bar. */
  --brand-orange-mid:      #e36a33;   /* 3.29:1 */
  --brand-yellow-mid:      #c2850d;   /* 3.16:1 */
  --brand-blue-light-mid:  #4e9dc5;   /* 3.02:1 */

  /* --- 1c. TIER 3 — deep (text) ---------------------------------------
     Shared OKLCH lightness 0.535, chroma tuned per hue. All clear 4.5:1.

     The cool member of this trio is brand-blue itself, unmodified. That is
     deliberate: light-blue and blue are only 10 degrees apart in OKLCH hue,
     and forcing light-blue dark enough for AA drops it to lightness ~0.56,
     right beside brand-blue's 0.489. The two then collide (dE 0.076, which
     reads as the same colour). Shifting light-blue's hue toward teal barely
     helps — a 28 degree drift only moves dE from 0.082 to 0.090 — so there
     is no version of a dark light-blue that stays on-brand AND stays
     distinct from brand-blue. Using brand-blue directly solves it: dE 0.189
     from the yellow, zero brand drift, and 6.2:1 for free.

     Set coherence: lightness spread 0.046, chroma spread 0.047 — both less
     than half the original #ce5515/#d19426/#729fb8 set (0.110 and 0.107). */
  --brand-orange-text:  #a85029;   /* 5.46:1 */
  --brand-yellow-text:  #926300;   /* 5.24:1 */
  --brand-blue-text:    #33658a;   /* 6.23:1 — tier 1, untouched */

  /* Conditional fourth. Only use where brand-blue does NOT also appear as
     text in the same block, or the two will read as one colour. */
  --brand-blue-light-text: #2b7ea5;   /* 4.54:1 */

  /* Yellow note: golden yellow cannot survive the trip to 4.5:1. Yellow's
     hue peaks bright, so any AA-safe version reads bronze. #d19426 looked
     better because it was only 2.63:1 — that legibility was the cost.

     Alternatives, same method:
     A (own chroma, most brand character) #c14a00 #9c6a00 #497c97
     C (fully unified chroma, most uniform) #ad5b38 #9c6a00 #007fad
     Regenerate with tools/make_palette.py */

  /* brand-blue and brand-dark-blue need no darkening — 6.2:1 and 9.6:1 already. */

  /* --- 1c-bis. ACTION ORANGE -------------------------------------------
     The call-to-action fill. Same hue (42.9) and same chroma (0.191) as
     brand-orange; only OKLCH lightness moves, 0.674 -> 0.580. That buys a
     WHITE label at 4.63:1, which the full-saturation orange cannot do
     (3.18:1, below AA for a 12.8px label).

     dE from brand-orange is 0.093 — just past the 0.076-0.090 band this
     sheet identifies as "reads as the same colour", so it stays recognisably
     the brand orange without a swatch beside it. Tier 1 orange is still the
     accent everywhere it is not carrying text. */
  --brand-orange-deep: #d14400;   /* 4.63:1 with white */

  /* Dark-theme companions. brand-blue and brand-blue-dark are too dark to
     read as text or as an 8px rule on a dark ground (2.77:1 and 1.80:1), so
     the dark theme substitutes these. Everything else it needs is Tier 1. */
  --brand-blue-lift:  #4780ab;   /* 4.07:1 on dark ground, dE 0.188 from light-blue */
  --brand-spark-deep: #285c75;   /* dimmed light-blue for the spark band */

  /* --- 1d. Neutrals ------------------------------------------------------ */
  --brand-black: #171717;   /* body copy + H3 */
  --brand-grey:  #f4f4f4;   /* panel fills */
  --brand-white: #ffffff;   /* page ground */

  /* --- 1e. Semantic roles ------------------------------------------------
     Components reference these and never the raw brand names, so that the
     dark theme in section 1j can repaint the whole site by overriding this
     block alone. If you find yourself reaching for a --brand-* name in a
     component rule, add a role here instead.

     Roles are grouped: ground and text, then emphasis, then per-component. */

  /* Ground + text */
  --c-ink:         var(--brand-black);
  --c-heading:     var(--brand-blue);
  --c-heading-sub: var(--brand-black);
  --c-paper:       var(--brand-white);
  /* Alternating section fill. Default is a faint wash of the brand light-blue
     rather than neutral grey — it tints the page toward the palette instead of
     reading as generic. Swap to var(--brand-grey) for the #f4f4f4 from the
     palette sheet; both are on-brand. */
  --c-tint:        #f1f7fa;
  --c-panel:       var(--c-tint);
  --c-rule:        #d8d8d8;
  --c-muted:       #5d5d5d;
  --c-link:        var(--brand-blue);
  --c-link-hover:  var(--brand-orange-text);
  --c-accent:      var(--brand-orange);
  --c-focus:       var(--brand-orange-text);
  --c-on-dark:     var(--brand-white);
  --c-eyebrow:     var(--brand-blue);

  /* Emphasis text — the .h-* helpers, .statement em, altitude labels.
     Tier 3 on light; Tier 1 on dark, where Tier 1 is the legible tier. */
  --c-emph-orange:     var(--brand-orange-text);
  --c-emph-yellow:     var(--brand-yellow-text);
  --c-emph-blue:       var(--brand-blue-text);
  --c-emph-blue-light: var(--brand-blue-light-text);

  /* Card accent rotation (8px top rules). Decoration, not text. */
  --c-accent-1: var(--brand-blue);
  --c-accent-2: var(--brand-orange);
  --c-accent-3: var(--brand-yellow);
  --c-accent-4: var(--brand-blue-light);

  --c-select-bg: var(--brand-yellow);
  --c-select-fg: var(--brand-black);

  /* Buttons — primary */
  --c-btn-bg:            var(--brand-orange-deep);
  --c-btn-fg:            var(--brand-white);
  --c-btn-border:        var(--brand-orange-deep);
  --c-btn-bg-hover:      var(--brand-blue-dark);
  --c-btn-fg-hover:      var(--brand-white);
  --c-btn-border-hover:  var(--brand-blue-dark);

  /* Buttons — secondary */
  --c-btn2-fg:           var(--brand-blue-dark);
  --c-btn2-border:       var(--brand-blue-dark);
  --c-btn2-bg-hover:     var(--brand-blue-dark);
  --c-btn2-fg-hover:     var(--brand-white);

  /* Header + nav */
  --c-brand-fg:      var(--brand-blue);
  --c-toggle-fg:     var(--brand-blue-dark);
  --c-nav-fg:        var(--brand-blue-dark);
  --c-nav-fg-hover:  var(--brand-blue);
  --c-nav-bg-hover:  var(--c-panel);
  --c-nav-current:   var(--brand-blue);

  /* Band — feature (mid blue) */
  --c-feature-bg:     var(--brand-blue);
  --c-feature-fg:     var(--brand-white);
  --c-feature-accent: var(--brand-yellow);

  /* Band — spark (one bright statement) */
  --c-spark-bg:     var(--brand-blue-light);
  --c-spark-fg:     var(--brand-black);
  --c-spark-eyebrow:var(--brand-blue);

  /* Band — ink (dark blue) */
  --c-ink-bg:      var(--brand-blue-dark);
  --c-ink-fg:      var(--brand-white);
  --c-ink-link:    var(--brand-yellow);
  --c-ink-eyebrow: var(--brand-blue-light);

  /* Footer */
  --c-footer-bg:     var(--brand-blue-dark);
  --c-footer-fg:     var(--brand-white);
  --c-footer-accent: var(--brand-yellow);

  /* --- 1f. Type ----------------------------------------------------------
     Montserrat throughout. Weight roles from the palette sheet:
       700 Bold      H1
       600 SemiBold  H2, buttons, nav, eyebrows
       500 Medium    H3, H4
       400 Regular   body */
  --f-body: "Montserrat", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --f-mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; /* <code> only */

  /* --- 1g. Type scale ----------------------------------------------------
     Base locked at 1rem = 16px. Never set a px font-size on body; that
     overrides a visitor's own browser text-size preference.

     ACTIVE: fluid hybrid. Major third (1.250) at 320px easing to perfect
     fourth (1.333) at 1280px. Reasoning in the README. Two static
     alternatives below — swap the block, change nothing else. */

  --t-xs:   0.75rem;   /* 12px  — fixed floor, never scales */
  --t-sm:   0.8rem;    /* 12.8px — one major-third step down */
  --t-base: 1rem;      /* 16px  — locked */

  --t-lg:  clamp(1.250rem, 1.222rem + 0.139vw, 1.333rem);  /* H4   20.0 -> 21.3 */
  --t-xl:  clamp(1.563rem, 1.491rem + 0.357vw, 1.777rem);  /* H3   25.0 -> 28.4 */
  --t-2xl: clamp(1.953rem, 1.815rem + 0.693vw, 2.369rem);  /* H2   31.3 -> 37.9 */
  --t-3xl: clamp(2.441rem, 2.203rem + 1.192vw, 3.157rem);  /* H1   39.1 -> 50.5 */
  --t-4xl: clamp(3.052rem, 2.667rem + 1.924vw, 4.206rem);  /* hero 48.8 -> 67.3 */

  /* --- OPTION B: static major third (1.250) — matches the current sites
  --t-lg: 1.250rem; --t-xl: 1.563rem; --t-2xl: 1.953rem;
  --t-3xl: 2.441rem; --t-4xl: 3.052rem;
  */

  /* --- OPTION C: static perfect fourth (1.333) — matches the palette sheet
  --t-lg: 1.333rem; --t-xl: 1.777rem; --t-2xl: 2.369rem;
  --t-3xl: 3.157rem; --t-4xl: 4.209rem;
  */

  --lh-tight: 1.15;
  --lh-snug:  1.35;
  --lh-body:  1.6;

  /* --- 1h. Space --------------------------------------------------------- */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;
  --s-4: 1rem;     --s-5: 1.5rem;   --s-6: 2rem;
  --s-7: 3rem;     --s-8: 4rem;     --s-9: 6rem;
  --s-section: clamp(3.5rem, 2rem + 6vw, 7rem);

  /* --- 1i. Layout -------------------------------------------------------- */
  --w-page:   76rem;
  --w-text:   42rem;
  --w-gutter: clamp(1.15rem, 4vw, 3rem);

  /* Pixel-art brand: everything is square. Kept as a token so there is
     exactly one place to change your mind. */
  --radius: 0;
  --border: 1px solid var(--c-rule);

  /* Header separator strip. Fixed px, never scaled — the squares must stay
     square at every viewport width, so the tile repeats rather than stretches. */
  --strip-h: 16px;

  /* Optional resolution ramp (still available as <hr class="pixel-rule">) */
  --ramp-h: clamp(20px, 2.2vw, 32px);

  /* Pixel unit — the module bullet squares, rules, and markers are built from */
  --px: 8px;

  --ease: cubic-bezier(0.2, 0, 0.1, 1);
  --dur:  160ms;
}

/* =========================================================================
   1j. DARK THEME
   Follows the operating system. No toggle, no JavaScript, no persistence —
   the visitor has already stated a preference and this respects it.

   The palette needs almost no new colour. Tier 3 was computed against white,
   so every member of it inverts on a dark ground and fails:

       orange-text  #a85029   5.46:1 on white -> 3.16:1 on dark
       yellow-text  #926300   5.24:1 on white -> 3.29:1 on dark
       blue-light-text #2b7ea5 4.54:1 on white -> 3.81:1 on dark
       blue         #33658a   6.23:1 on white -> 2.77:1 on dark

   And Tier 1 — the tier this sheet says is "never text" — passes:

       orange       #f26419   3.18:1 on white -> 5.44:1 on dark
       yellow       #f6ae2d   1.91:1 on white -> 9.05:1 on dark
       blue-light   #86bbd8   2.08:1 on white -> 8.32:1 on dark

   So on dark, Tier 1 IS the text tier. The golden yellow finally reads as
   type rather than bronze, which it never could on white.

   The blue collision documented in section 1c recurs here, mirrored: any
   light-blue lifted far enough to sit beside brand-blue on a dark ground
   collides with brand-blue-light, ten degrees away. The mirror of the
   original fix resolves it — where Tier 3 used brand-blue unmodified, dark
   uses brand-blue-light unmodified. Zero brand drift, 8.3:1 for free.

   Grounds are built on the brand-blue-dark hue (237) at chroma 0.02, on the
   same reasoning as --c-tint: tint toward the palette rather than read as
   generic charcoal.
   ========================================================================= */

@media (prefers-color-scheme: dark) {
  :root {
    /* Ground + text */
    --c-paper:       #121c23;   /* L 0.22 */
    --c-tint:        #1f2b33;   /* L 0.28 */
    --c-rule:        #38444d;
    --c-ink:         #e3e9ed;   /* 14.1:1 — not #fff, which buzzes at body size */
    --c-muted:       #a7afb5;   /*  7.8:1 */
    --c-heading:     var(--brand-blue-light);   /* 8.3:1 */
    --c-heading-sub: var(--c-ink);
    --c-link:        var(--brand-blue-light);
    --c-link-hover:  var(--brand-yellow);       /* 9.1:1 */
    --c-accent:      var(--brand-orange);       /* 5.4:1 — fine as a mark on dark */
    --c-focus:       var(--brand-orange);
    --c-on-dark:     var(--c-ink);
    --c-eyebrow:     var(--brand-blue-light);

    /* Emphasis — Tier 1 replaces Tier 3 wholesale */
    --c-emph-orange:     var(--brand-orange);
    --c-emph-yellow:     var(--brand-yellow);
    --c-emph-blue:       var(--brand-blue-lift);
    --c-emph-blue-light: var(--brand-blue-light);

    /* Card rotation. brand-blue is only 2.77:1 against this ground, too faint
       for an 8px rule, so slot 1 takes the lifted blue. */
    --c-accent-1: var(--brand-blue-lift);
    --c-accent-2: var(--brand-orange);
    --c-accent-3: var(--brand-yellow);
    --c-accent-4: var(--brand-blue-light);

    --c-select-bg: var(--brand-yellow);
    --c-select-fg: var(--brand-black);

    /* Buttons. The action orange holds in both themes: 4.63:1 with its white
       label, 3.73:1 against this ground and 3.13:1 against the tint band, so
       the shape itself stays perceivable. Hover lifts to light-blue rather
       than dropping to dark-blue, which would vanish here (1.80:1). */
    --c-btn-bg-hover:     var(--brand-blue-light);
    --c-btn-fg-hover:     var(--c-paper);
    --c-btn-border-hover: var(--brand-blue-light);

    --c-btn2-fg:       var(--c-ink);
    --c-btn2-border:   var(--c-ink);
    --c-btn2-bg-hover: var(--c-ink);
    --c-btn2-fg-hover: var(--c-paper);

    /* Header + nav */
    --c-brand-fg:     var(--brand-blue-light);
    --c-toggle-fg:    var(--c-ink);
    --c-nav-fg:       var(--c-ink);
    --c-nav-fg-hover: var(--brand-blue-light);
    --c-nav-current:  var(--brand-blue-light);

    /* Bands. Feature and ink both sit slightly above the page rather than
       below it — on a dark ground these read as a lift, which is the same
       separation job done in the other direction. Spark is dimmed; the full
       light-blue at 8.3:1 against the page is a headlight. */
    --c-spark-bg:      var(--brand-spark-deep);
    --c-spark-fg:      var(--c-ink);
    --c-spark-eyebrow: var(--brand-blue-light);

    --c-ink-fg: var(--c-ink);

    --c-feature-fg: var(--c-ink);

    /* Footer keeps brand-blue-dark, which now lifts off the page instead of
       sinking into it. Text on it is 7.8:1. */
    --c-footer-fg: var(--c-ink);
  }

  /* Diagrams and post thumbnails carry dark ink baked into a transparent
     ground. Where a dark-theme source exists it is swapped in the markup with
     <picture>; this is the fallback plaque for anything that has not been
     redrawn yet. Never use filter: invert() — it destroys the palette. */
  .theme-plaque img {
    background: var(--brand-white);
    padding: var(--s-5);
  }
}

/* =========================================================================
   2. RESET + BASE
   ========================================================================= */

*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

/* The UA sheet's [hidden] rule is a normal declaration, so ANY author
   `display` on the same element beats it and the attribute silently stops
   working. Re-assert it here so `hidden` means hidden everywhere. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(5.6rem + var(--strip-h) + var(--s-4));
}

body {
  background: var(--c-paper);
  color: var(--c-ink);
  font-family: var(--f-body);
  font-size: var(--t-base);
  font-weight: 400;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
}

img, picture, svg, video { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
p, li { text-wrap: pretty; }

h1, h2, h3, h4 {
  font-family: var(--f-body);
  line-height: var(--lh-tight);
  text-wrap: balance;
}

h1 { font-size: var(--t-3xl); font-weight: 700; color: var(--c-heading);     letter-spacing: -0.02em; }
h2 { font-size: var(--t-2xl); font-weight: 600; color: var(--c-heading);     letter-spacing: -0.015em; }
h3 { font-size: var(--t-xl);  font-weight: 500; color: var(--c-heading-sub); letter-spacing: -0.01em; }
h4 { font-size: var(--t-lg);  font-weight: 500; color: var(--c-heading-sub); }

/* Accent heading modifiers — the rotating colour treatment on the current
   site's "What makes 8-Bit Spark unique?" subheads. Note that .h-yellow and
   .h-lblue do not clear 3:1 on white; see 1e. */
.h-blue   { color: var(--c-emph-blue); }
.h-orange { color: var(--c-emph-orange); }
.h-yellow { color: var(--c-emph-yellow); }
.h-lblue  { color: var(--c-emph-blue-light); }

a {
  color: var(--c-link);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: color var(--dur) var(--ease);
}
a:hover { color: var(--c-link-hover); }

:focus-visible {
  outline: 3px solid var(--c-focus);
  outline-offset: 2px;
}

::selection { background: var(--c-select-bg); color: var(--c-select-fg); }

hr { border: 0; border-top: var(--border); margin-block: var(--s-6); }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: var(--s-4);
  top: -4rem;
  z-index: 100;
  background: var(--c-ink-bg);
  color: var(--c-on-dark);
  padding: var(--s-3) var(--s-5);
  border-radius: var(--radius);
  transition: top var(--dur) var(--ease);
}
.skip-link:focus { top: var(--s-4); color: var(--c-on-dark); }

/* =========================================================================
   3. LAYOUT PRIMITIVES
   ========================================================================= */

.wrap {
  width: 100%;
  max-width: var(--w-page);
  margin-inline: auto;
  padding-inline: var(--w-gutter);
}
.wrap--text { max-width: calc(var(--w-text) + var(--w-gutter) * 2); }

.band { padding-block: var(--s-section); }
.band--tight { padding-block: calc(var(--s-section) * 0.6); }
.band--panel { background: var(--c-panel); }
.band--tint  { background: var(--c-tint); }

/* Feature band — the closing call to action. Mid blue rather than the dark
   dusky blue, which was too heavy to end a page on. White text is 6.2:1. */
.band--feature {
  background: var(--c-feature-bg);
  color: var(--c-feature-fg);
}
.band--feature h1, .band--feature h2, .band--feature h3 { color: var(--c-feature-fg); }
.band--feature .lede { color: var(--c-feature-fg); }
.band--feature .eyebrow { color: var(--c-feature-accent); }
.band--feature .eyebrow::before { background: var(--c-feature-accent); }
.band--feature a { color: var(--c-feature-accent); }
.band--feature .btn {
  background: var(--c-feature-fg);
  border-color: var(--c-feature-fg);
  color: var(--c-feature-bg);
}
.band--feature .btn:hover {
  background: var(--c-btn-bg);
  border-color: var(--c-btn-bg);
  color: var(--c-btn-fg);
}
.band--feature .btn--secondary {
  background: transparent;
  border-color: var(--c-feature-fg);
  color: var(--c-feature-fg);
}
.band--feature .btn--secondary:hover { background: var(--c-feature-fg); color: var(--c-feature-bg); }

/* Spark band — one bright statement, dark text on brand light-blue (8.4:1) */
.band--spark {
  background: var(--c-spark-bg);
  color: var(--c-spark-fg);
}
.band--spark h2, .band--spark .statement { color: var(--c-spark-fg); }
.band--spark .eyebrow { color: var(--c-spark-eyebrow); }
.band--spark .eyebrow::before { background: var(--c-accent); }

.band--ink {
  background: var(--c-ink-bg);
  color: var(--c-ink-fg);
}
.band--ink h1, .band--ink h2, .band--ink h3, .band--ink h4 { color: var(--c-ink-fg); }
.band--ink a { color: var(--c-ink-link); }   /* 5.03:1 on dark-blue */
.band--ink a:hover { color: var(--c-ink-fg); }

.grid {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr));
}
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr)); }

/* Quantity query: a grid holding exactly four items squares off to 2x2 at wide
   widths instead of leaving a row of three and an orphan. auto-fit alone can't
   see how many children there are, so it fits as many columns as will physically
   go — which is three at this page width.

   :has() does the counting: "the fourth child is also the last child" is only
   true when there are exactly four. Browsers without :has() (pre-2023) just
   ignore the rule and keep the 3+1 behaviour, so nothing breaks. */
@media (min-width: 56rem) {
  .grid:not(.grid--2):has(> :nth-child(4):last-child) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.stack > * + * { margin-top: var(--s-4); }
.stack--loose > * + * { margin-top: var(--s-5); }
.flow > p + p, .flow > p + ul, .flow > ul + p { margin-top: var(--s-4); }

/* =========================================================================
   4. TYPOGRAPHIC DEVICES
   ========================================================================= */

/* Eyebrow — small uppercase label above a heading, Montserrat SemiBold.
   The leading mark is a hard-edged pixel block, not a hairline rule. */
.eyebrow {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-eyebrow);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-3);
}
.eyebrow::before {
  content: "";
  width: calc(var(--px) * 2);
  height: var(--px);
  background: var(--c-accent);
  flex: none;
}
.band--ink .eyebrow { color: var(--c-ink-eyebrow); }

.lede {
  font-size: var(--t-lg);
  font-weight: 400;
  line-height: var(--lh-snug);
}
.band--ink .lede { color: var(--c-ink-fg); }

.muted { color: var(--c-muted); }

/* Inline italic emphasis, matching the current site's highlight treatment */
.emph { font-style: italic; font-weight: 600; }

/* Square bullets — the core pixel-brand device */
.ticks {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--s-3);
}
.ticks li { position: relative; padding-left: var(--s-6); }
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: var(--px);
  height: var(--px);
  background: var(--c-accent);
}

/* Ordinary lists get square markers too */
ul { list-style-type: square; }

/* =========================================================================
   5. BUTTONS
   Primary   = orange fill, white label   (per the palette sheet)
   Secondary = white fill, dark-blue rule + label
   ========================================================================= */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  font-size: var(--t-sm);
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  padding: 0.9em 1.8em;
  border: 2px solid var(--c-btn-border);
  border-radius: var(--radius);
  background: var(--c-btn-bg);
  color: var(--c-btn-fg);
  cursor: pointer;
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
}
.btn:hover {
  background: var(--c-btn-bg-hover);
  border-color: var(--c-btn-border-hover);
  color: var(--c-btn-fg-hover);
}

.btn--secondary {
  background: transparent;
  border-color: var(--c-btn2-border);
  color: var(--c-btn2-fg);
}
.btn--secondary:hover {
  background: var(--c-btn2-bg-hover);
  border-color: var(--c-btn2-bg-hover);
  color: var(--c-btn2-fg-hover);
}

.band--ink .btn {
  background: var(--c-btn-bg);
  border-color: var(--c-btn-bg);
  color: var(--c-btn-fg);
}
.band--ink .btn:hover {
  background: var(--c-ink-fg);
  border-color: var(--c-ink-fg);
  color: var(--c-ink-bg);
}
.band--ink .btn--secondary {
  background: transparent;
  border-color: var(--c-ink-fg);
  color: var(--c-ink-fg);
}
.band--ink .btn--secondary:hover {
  background: var(--c-ink-fg);
  color: var(--c-ink-bg);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  margin-top: var(--s-5);
}

/* =========================================================================
   6. HEADER + COLLAPSIBLE NAV
   ========================================================================= */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--c-paper);
  padding-bottom: var(--strip-h);
}

/* Brand-colour squares separating header from body. background-repeat, not
   background-size — that's what keeps them square when the window narrows. */
.site-header::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--strip-h);
  background-image: url("/assets/img/mosaic-strip.svg");
  background-repeat: repeat-x;
  background-position: left center;
  background-size: auto var(--strip-h);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0 var(--s-4);
  min-height: 5.6rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s-3);
  text-decoration: none;
  color: var(--c-brand-fg);
  font-weight: 700;
  letter-spacing: -0.02em;
  font-size: var(--t-lg);
  flex: none;
}
.brand img { height: clamp(2.4rem, 12vw, 3.4rem); width: auto; }
.brand:hover { color: var(--c-brand-fg); }

/* The button does nothing without JS, so it is absent by default and revealed
   only inside the mobile block below, and only when the .js class confirms the
   script that operates it has run. */
.nav-toggle {
  display: none;
  align-items: center;
  gap: var(--s-3);
  background: none;
  border: 2px solid var(--c-toggle-fg);
  border-radius: var(--radius);
  color: var(--c-toggle-fg);
  padding: var(--s-2) var(--s-3);
  cursor: pointer;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

/* Three stacked pixel bars */
.nav-toggle__bars {
  position: relative;
  width: 20px;
  height: 14px;
  flex: none;
}
.nav-toggle__bars span {
  position: absolute;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--c-toggle-fg);
  transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.nav-toggle__bars span:nth-child(1) { top: 0; }
.nav-toggle__bars span:nth-child(2) { top: 5.5px; }
.nav-toggle__bars span:nth-child(3) { top: 11px; }

.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle__bars span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* Mobile-first collapsible drawer.

   The default state is OPEN — a plain stacked list under the brand. Collapsing
   is progressive enhancement, applied only under .js, so a visitor whose script
   failed gets the full link list rather than an invisible nav behind a dead
   button. */
.site-nav {
  flex: 0 0 100%;
  min-width: 0;
  border-top: var(--border);
  padding-block: var(--s-4);
}

.site-nav ul {
  list-style: none;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
}

.site-nav a {
  display: block;
  padding: var(--s-3);
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--c-nav-fg);
  border-radius: var(--radius);
}
.site-nav a:hover { background: var(--c-nav-bg-hover); color: var(--c-nav-fg-hover); }

/* Active page marked by a solid pixel block, not a rounded pill */
.site-nav a[aria-current="page"] {
  color: var(--c-nav-current);
  box-shadow: inset var(--px) 0 0 var(--c-accent);
}

/* Below the desktop breakpoint, and only with JS, the list becomes a drawer.
   The toggle reveal has to live in here rather than at the top level: a
   top-level `.js .nav-toggle` would outrank a plain `.nav-toggle { display:none }`
   in the desktop block on specificity and strand the button at wide widths.

   The height cap matters because the drawer sits inside a sticky header. Five
   links on a short landscape viewport otherwise run past the bottom of the
   screen with no way to scroll to them. */
@media (max-width: 59.999rem) {
  .js .nav-toggle { display: inline-flex; }

  .js .site-nav { display: none; }
  .js .site-nav[data-open="true"] {
    display: block;
    max-height: calc(100svh - 5.6rem - var(--strip-h));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

@media (min-width: 60rem) {
  .site-nav {
    display: block;
    flex: 0 1 auto;
    border-top: 0;
    padding-block: 0;
  }
  .site-nav ul { flex-direction: row; align-items: center; }
  .site-nav a { padding: var(--s-2) var(--s-4); }
  .site-nav a[aria-current="page"] {
    box-shadow: inset 0 calc(var(--px) * -0.5) 0 var(--c-accent);
  }
}

/* =========================================================================
   7. MASTHEAD
   Every page opens the same way: eyebrow, h1, lede, buttons. The heading is
   always the first thing rendered and always above the fold — an image, when
   there is one, sits beside the words rather than above them. Pages without
   art use the identical structure minus the media column, so they read as
   composed rather than as a page that lost its picture.
   ========================================================================= */

.masthead {
  position: relative;
}

.masthead__inner {
  display: grid;
  gap: var(--s-6);
  align-items: center;
  padding-block: var(--s-7);
}

@media (min-width: 56rem) {
  .masthead__inner--split {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    gap: var(--s-8);
  }
}

.masthead__body { max-width: 34rem; }
.masthead__body h1 { margin-bottom: var(--s-4); }

.masthead__media img {
  width: 100%;
  border-radius: var(--radius);
}

/* A standalone ramp usable as a section divider anywhere */
.pixel-rule {
  height: var(--ramp-h);
  background-image: url("/assets/img/pixel-ramp.svg");
  background-size: 100% 100%;
  background-repeat: no-repeat;
  border: 0;
  margin: 0;
}
.pixel-rule--flip { transform: scaleX(-1); }

/* =========================================================================
   8. CARDS + PROFILES
   ========================================================================= */

.card {
  border: var(--border);
  border-radius: var(--radius);
  border-top: var(--px) solid var(--card-accent, var(--c-accent));
  background: var(--c-paper);
  padding: var(--s-6);
  height: 100%;
}
.card .eyebrow::before { background: var(--card-accent, var(--c-accent)); }
.card h3 { margin-bottom: var(--s-3); }

/* Rotating accent across a run of cards, mirroring the current site's four
   coloured subheads. Order: blue, orange, yellow, light-blue. */
.grid > .card:nth-child(4n+1) { --card-accent: var(--c-accent-1); }
.grid > .card:nth-child(4n+2) { --card-accent: var(--c-accent-2); }
.grid > .card:nth-child(4n+3) { --card-accent: var(--c-accent-3); }
.grid > .card:nth-child(4n+4) { --card-accent: var(--c-accent-4); }

.profile {
  display: grid;
  gap: var(--s-6);
  align-items: start;
  grid-template-columns: 1fr;
}
@media (min-width: 48rem) {
  .profile { grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); }
  .profile--flip { grid-template-columns: minmax(0, 1fr) minmax(0, 15rem); }
  .profile--flip .profile__media { order: 2; }
}

.profile__media img {
  width: 100%;
  border-radius: var(--radius);
  background: var(--c-panel);
}

.profile__head {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin-bottom: var(--s-4);
}
.profile__head h3 { margin: 0; color: var(--c-heading); font-weight: 600; }
.profile__head img { height: 1.4rem; width: auto; }

.figure { margin: 0; }
.figure img { border-radius: var(--radius); }
.figure figcaption {
  margin-top: var(--s-3);
  font-size: var(--t-sm);
  color: var(--c-muted);
}

/* =========================================================================
   9. BLOG
   ========================================================================= */

.post-list {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--s-6);
  grid-template-columns: repeat(auto-fill, minmax(min(20rem, 100%), 1fr));
}

.post-card {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
  border: var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--c-paper);
  transition: border-color var(--dur) var(--ease);
}
.post-card:hover { border-color: var(--c-accent); }

.post-card__media { aspect-ratio: 16 / 9; background: var(--c-panel); }
.post-card__media img { width: 100%; height: 100%; object-fit: cover; }

.post-card__body {
  padding: var(--s-5);
  display: flex;
  flex-direction: column;
  gap: var(--s-2);
  flex: 1;
}

.post-card__title { font-size: var(--t-lg); font-weight: 600; }
.post-card__title a { text-decoration: none; color: var(--c-heading); }
.post-card__title a::after { content: ""; position: absolute; inset: 0; }
.post-card__title a:hover { color: var(--c-link-hover); }

.post-meta {
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-muted);
  margin-bottom: 6px;
}
.post-meta time {font-weight: 700;}

.post-header { padding-block: var(--s-7) var(--s-5); }
.post-header h1 { margin-block: var(--s-3); }

.prose { max-width: var(--w-text); }
.prose > * + * { margin-top: var(--s-4); }
.prose h2 { margin-top: var(--s-7); font-size: var(--t-xl); }
.prose h3 { margin-top: var(--s-6); font-size: var(--t-lg); }
.prose ul, .prose ol { padding-left: var(--s-5); display: grid; gap: var(--s-2); }
.prose figure { margin-block: var(--s-6); }
.prose blockquote {
  border-left: var(--px) solid var(--c-accent);
  padding-left: var(--s-5);
  font-size: var(--t-lg);
  line-height: var(--lh-snug);
  font-weight: 500;
}
.prose code {
  font-family: var(--f-mono);
  font-size: 0.9em;
  background: var(--c-panel);
  padding: 0.15em 0.4em;
}

/* =========================================================================
   10. FOOTER — pixel mosaic
   ========================================================================= */

.site-footer {
  position: relative;
  margin-top: auto;
  background: var(--c-footer-bg);
  color: var(--c-footer-fg);
  padding-block: var(--s-8) var(--s-6);
  font-size: var(--t-sm);
  isolation: isolate;
}

/* Mosaic behind, dark-blue scrim over it. The scrim is what lets white text
   sit safely on top; it costs some saturation, which is the trade. No strip
   above the footer — the colour change already separates it from the page. */
.site-footer::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  background-image: url("/assets/img/pixel-mosaic.svg");
  background-size: 480px 480px;
  background-position: center;
}
.site-footer::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: color-mix(in srgb, var(--c-footer-bg) 70%, transparent);
}

.site-footer h2, .site-footer h3 { color: var(--c-footer-fg); }
.site-footer a { color: var(--c-footer-fg); }
.site-footer a:hover { color: var(--c-footer-accent); }
.site-footer .eyebrow { color: var(--c-footer-accent); }
.site-footer .eyebrow::before { background: var(--c-accent); }

.site-footer__grid {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
}

.site-footer__brand img { height: 2.4rem; width: auto; margin-bottom: var(--s-4); }

.footer-nav {
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--s-2);
}
.footer-nav a {
  text-decoration: none;
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.site-footer__fine {
  margin-top: var(--s-7);
  padding-top: var(--s-5);
  border-top: 2px solid color-mix(in srgb, var(--c-footer-fg) 28%, transparent);
  display: grid;
  gap: var(--s-4);
  font-size: var(--t-xs);
  line-height: 1.7;
  text-align: center;
}

/* Runs the full width of the rule above it, rather than stopping short */
.land-ack { max-width: none; }

/* =========================================================================
   11. MOTION PREFERENCES
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* =========================================================================
   12. PRINT
   ========================================================================= */

@media print {
  /* Force the light theme on paper. Without this, a visitor whose OS is set
     to dark prints white type on a white sheet, because the dark block above
     also matches when the print stylesheet is composed. */
  :root {
    color-scheme: light;
    --c-paper: #ffffff;
    --c-tint: #ffffff;
    --c-ink: #000000;
    --c-muted: #333333;
    --c-rule: #999999;
    --c-heading: var(--brand-blue);
    --c-heading-sub: #000000;
    --c-link: var(--brand-blue);
    --c-eyebrow: var(--brand-blue);
    --c-emph-orange: var(--brand-orange-text);
    --c-emph-yellow: var(--brand-yellow-text);
    --c-emph-blue: var(--brand-blue-text);
    --c-emph-blue-light: var(--brand-blue-light-text);
    --c-accent-1: var(--brand-blue);
    --c-spark-bg: #ffffff;
    --c-spark-fg: #000000;
  }
  .site-header, .site-footer, .btn-row, .nav-toggle { display: none; }
  body { color: #000; background: #fff; }
  .theme-plaque img { background: none; padding: 0; }
  a::after { content: " (" attr(href) ")"; font-size: 0.85em; }
}

/* =========================================================================
   13. STATEMENT BAND
   One idea, set large, no image, no card. Used where the page needs to stop
   and say the thing rather than list features.
   ========================================================================= */

.statement {
  font-size: var(--t-2xl);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.015em;
  max-width: 34ch;
}
.statement em { font-style: normal; color: var(--c-emph-orange); }

/* Numbered altitude list — encodes the three levels of "dual fluency".
   The marker carries real information (board / build / business), so it is
   a label, not decoration. */
.altitudes {
  list-style: none;
  padding: 0;
  display: grid;
  gap: 0;
  border-top: 2px solid var(--c-rule);
}
.altitudes > li {
  display: grid;
  gap: var(--s-2) var(--s-5);
  padding-block: var(--s-5);
  border-bottom: 2px solid var(--c-rule);
}
@media (min-width: 44rem) {
  .altitudes > li { grid-template-columns: minmax(0, 13rem) minmax(0, 1fr); }
}
.altitudes__label {
  font-size: var(--t-sm);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-emph-orange);
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
}
.altitudes__label::before {
  content: "";
  width: var(--px);
  height: var(--px);
  background: var(--c-accent);
  flex: none;
}
.altitudes > li:nth-child(2) .altitudes__label { color: var(--c-emph-yellow); }
.altitudes > li:nth-child(2) .altitudes__label::before { background: var(--c-accent-3); }
.altitudes > li:nth-child(3) .altitudes__label { color: var(--c-emph-blue-light); }
.altitudes > li:nth-child(3) .altitudes__label::before { background: var(--c-accent-4); }
