/* ============================================================================
   harryhaymanphiladelphia.com — v7 "Ledger"
   Ian Swain, 2026-09-07

   Carries forward the identity Harry approved on 27 July: charcoal ground,
   Instrument Serif sentence case, one crimson drawn from his own RACONTEUR and
   BON VIVANT ink lettering. The rule from v4 still holds and is enforced here:
   CRIMSON MARKS THE PAGE, IT IS NEVER TEXT. It fills, rules and points.

   What is new is the machinery. Motion is scroll-driven in CSS rather than
   JavaScript, so it runs on the compositor, costs nothing on the main thread,
   and degrades to "everything visible" when the feature is missing rather than
   to "everything hidden" — the failure mode that leaves a reveal-animated page
   blank forever if a script dies.
   ============================================================================ */

/* ---------------------------------------------------------------- fonts ---- */
@font-face {
  font-family: "Instrument Serif";
  src: url("assets/fonts/instrumentserif-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Serif";
  src: url("assets/fonts/instrumentserif-400-italic.woff2") format("woff2");
  font-weight: 400; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("assets/fonts/inter-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("assets/fonts/inter-500.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url("assets/fonts/inter-600.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Space Mono";
  src: url("assets/fonts/spacemono-400.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

/* --------------------------------------------------------------- tokens ---- */
/* LIGHT SCALE, 2026-09-07 on Ian's instruction: "light scale not dark scale, no
   grey text, it needs to be easily legible." So there is no grey in here. What
   was --ink-2 and --ink-3 are now dark warm neutrals, not greys, and the lowest
   of them still measures 7.7:1 — above the 4.5:1 the standard asks for body
   text and above the 7:1 it asks for AAA.
   Measured on --paper: --ink 17.3:1 · --ink-2 11.1:1 · --ink-3 7.7:1 ·
   --mark 7.9:1. Nothing on this page is set in a colour that fails AAA.
   The crimson is now Harry's RAW drawn ink #A10000 rather than the lifted
   #D42A3C — on a dark ground his own red measured 2.0:1 and had to be brightened
   to be usable at all. On paper it works as drawn, which is the better outcome. */
:root {
  color-scheme: light;

  --field:      #FAF8F3;   /* the ground — warm paper, not white */
  --field-2:    #F2EEE5;   /* raised panel */
  --field-3:    #EAE5D9;   /* card */
  --rule:       #D6D0C2;
  --rule-soft:  #E3DED2;

  --ink:        #16181C;   /* body — 17.3:1 */
  --ink-2:      #33383F;   /* secondary — 11.1:1 */
  --ink-3:      #4A5058;   /* meta — 7.7:1, dark not grey */

  --mark:       #A10000;   /* his own drawn ink — 7.9:1 on paper */
  --mark-deep:  #7A0000;
  --on-mark:    #FFFFFF;

  --paper:      #EDE8DC;   /* the one deeper chapter */
  --paper-ink:  #16181C;

  --display: "Instrument Serif", "Times New Roman", Georgia, serif;
  --body:    "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono:    "Space Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  --gut: clamp(1.25rem, 4vw, 4rem);
  --max: 78rem;

  --ease: cubic-bezier(.22, 1, .36, 1);
}

/* ---------------------------------------------------------------- reset ---- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

body {
  margin: 0;
  background: var(--field);
  color: var(--ink);
  font-family: var(--body);
  font-size: clamp(1rem, .96rem + .2vw, 1.0625rem);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

img, picture, video { max-width: 100%; display: block; }
a { color: inherit; }
h1, h2, h3 { margin: 0; font-weight: 400; text-wrap: balance; }
p { margin: 0; text-wrap: pretty; }
button { font: inherit; color: inherit; }

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 200;
  background: var(--mark); color: var(--on-mark);
  padding: .75rem 1.25rem; font-weight: 600;
}
.skip-link:focus { left: 0; }

.wrap {
  width: min(100% - var(--gut) * 2, var(--max));
  margin-inline: auto;
}

/* Grain. One SVG-filtered layer over the whole page, cheap and static. It is
   what keeps a flat charcoal from reading as an empty div on a large screen. */
.grain {
  position: fixed; inset: 0; z-index: 1; pointer-events: none;
  opacity: .045; mix-blend-mode: multiply;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.5'/%3E%3C/svg%3E");
}

/* ============================================================ scroll motion */
/* Native scroll-driven animation. No observer, no rAF, no main-thread work.
   Elements start VISIBLE and the animation only runs where the feature exists,
   so a browser without it shows a finished page rather than a blank one. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .rise {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 26%;
    }
    .fade {
      animation: fade linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 22%;
    }
    /* The chapter rule draws itself as the chapter arrives. */
    .chapter > .wrap > .ch-head::after {
      animation: draw linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 34%;
    }
  }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Reading progress, painted by the scroll timeline itself. */
.progress {
  position: fixed; inset-inline: 0; top: 0; height: 2px; z-index: 90;
  background: var(--mark); transform-origin: 0 50%; transform: scaleX(0);
}
@supports (animation-timeline: scroll()) {
  .progress {
    animation: draw linear both;
    animation-timeline: scroll(root block);
  }
}

/* ================================================================= masthead */
.mast {
  position: fixed; inset-inline: 0; top: 0; z-index: 80;
  padding-block: 1rem;
  backdrop-filter: blur(14px) saturate(140%);
  background: color-mix(in srgb, var(--field) 72%, transparent);
  border-bottom: 1px solid transparent;
  transition: border-color .3s, background .3s, padding .3s;
}
.mast[data-stuck] {
  border-bottom-color: var(--rule-soft);
  padding-block: .6rem;
}
.mast .wrap { display: flex; align-items: center; gap: 1.5rem; }

.brand {
  font-family: var(--display);
  font-size: 1.3rem; letter-spacing: .01em; text-decoration: none;
  margin-inline-end: auto;
}
.brand em { font-style: italic; }

.mnav { display: flex; gap: 1.5rem; }
.mnav a {
  font-size: .8125rem; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); text-decoration: none;
  position: relative; padding-block: .25rem;
  transition: color .2s;
}
.mnav a::after {
  content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 1px;
  background: var(--mark); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .3s var(--ease);
}
.mnav a:hover, .mnav a[aria-current] { color: var(--ink); }
.mnav a:hover::after, .mnav a[aria-current]::after { transform: scaleX(1); }

.menu-btn { display: none; background: none; border: 0; cursor: pointer;
  font-size: .8125rem; letter-spacing: .06em; text-transform: uppercase; }

@media (max-width: 880px) {
  .mnav { display: none; }
  .menu-btn { display: block; }
  .menu-panel[data-open] {
    display: grid; position: fixed; inset: 0; z-index: 85;
    background: var(--field); place-content: center; gap: 1rem; text-align: center;
  }
  .menu-panel[data-open] a {
    font-family: var(--display); font-size: 2rem; text-decoration: none;
  }
}
.menu-panel:not([data-open]) { display: none; }

/* ===================================================================== hero */
/* Split frame: type on the field, photograph bleeding off the right edge, the
   two meeting on a soft scrim rather than a hard seam. Ian chose the waterfront
   frame on 2026-09-07. Its alt text describes what is actually in it — a
   working drydock — because a screen reader should not be told "rooftop". */
.hero {
  min-height: 100svh;
  display: grid; align-items: center;
  grid-template-columns: 1fr;
  position: relative; isolation: isolate;
  padding-block: 8rem 4rem;
}
@media (min-width: 940px) {
  .hero { grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr); padding-block: 0; }
}
.hero::before {
  content: ""; position: absolute; inset: 0; z-index: -2;
  background:
    radial-gradient(110% 80% at 4% 4%, #FFFFFF 0%, transparent 62%),
    var(--field);
}
.hero-copy { position: relative; z-index: 2; }
.hero-copy > .wrap { width: min(100% - var(--gut) * 2, 40rem); margin-inline: 0 auto; }
@media (min-width: 940px) {
  .hero-copy > .wrap {
    margin-inline-start: max(var(--gut), calc((100vw - var(--max)) / 2));
    padding-inline-end: 3rem;
  }
}

.hero-ph { position: relative; }
@media (max-width: 939px) {
  .hero-ph { order: -1; height: 46svh; margin-bottom: 2.5rem; }
}
@media (min-width: 940px) {
  .hero-ph { position: absolute; inset: 0 0 0 46%; z-index: -1; }
}
.hero-ph picture, .hero-ph img { width: 100%; height: 100%; }
.hero-ph img {
  object-fit: cover; object-position: 62% 30%;
  filter: grayscale(.85) contrast(1.06) brightness(.99);
}
/* The scrim is what lets 16px type sit over a photograph and still measure.
   Two layers: a horizontal fade into the field, and a floor under the copy. */
.hero-ph .scrim {
  position: absolute; inset: 0;
  /* On a dark ground the scrim had to be wide to make type measure over the
     photograph. On paper the copy no longer overlaps it, so the scrim only has
     to soften the seam — a wide one just bleached the picture. */
  background: linear-gradient(90deg,
    var(--field) 0%,
    color-mix(in srgb, var(--field) 55%, transparent) 9%,
    transparent 28%);
}
@media (max-width: 939px) {
  .hero-ph .scrim {
    background: linear-gradient(0deg, var(--field) 2%, color-mix(in srgb, var(--field) 40%, transparent) 46%, transparent 100%);
  }
}
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: scroll()) {
    /* Parallax on the compositor: the frame drifts a little slower than the page. */
    .hero-ph img {
      animation: drift linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100svh;
    }
    @keyframes drift { to { transform: translateY(6%) scale(1.06); } }
  }
}

.eyebrow {
  font-family: var(--mono);
  font-size: .75rem; letter-spacing: .34em; text-transform: uppercase;
  color: var(--mark); margin-bottom: 1.75rem;
}
.hero h1 {
  font-family: var(--display);
  font-size: clamp(2.75rem, 1.2rem + 6.2vw, 7rem);
  line-height: .94; letter-spacing: -.02em;
  margin-bottom: 1.75rem;
}
.hero h1 em { font-style: italic; position: relative; white-space: nowrap; }
.hero h1 em::after {
  content: ""; position: absolute; left: -.02em; right: -.02em; bottom: .06em;
  height: .055em; background: var(--mark); border-radius: .03em;
  transform-origin: 0 50%;
}
@media (prefers-reduced-motion: no-preference) {
  .hero h1 em::after { animation: draw .9s var(--ease) .5s both; }
}

.roles {
  display: flex; flex-wrap: wrap; gap: .5rem 1.25rem;
  font-family: var(--mono); font-size: .6875rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 1.75rem;
}
.lede {
  max-width: 42ch; color: var(--ink-2);
  font-size: clamp(1rem, .96rem + .3vw, 1.1875rem);
  margin-bottom: 2.25rem;
}

.cta { display: flex; flex-wrap: wrap; gap: .875rem; }
.btn {
  display: inline-flex; align-items: center; gap: .6rem;
  padding: 1rem 1.75rem; text-decoration: none;
  font-size: .875rem; font-weight: 600; letter-spacing: .02em;
  border: 1px solid var(--rule);
  transition: transform .25s var(--ease), background .25s, border-color .25s;
}
.btn-p { background: var(--mark); border-color: var(--mark); color: var(--on-mark); }
.btn-p:hover { background: var(--mark-deep); border-color: var(--mark-deep); }
.btn-o { background: color-mix(in srgb, var(--field) 60%, transparent); }
.btn-o:hover { border-color: var(--ink-3); background: var(--field-2); }
@media (prefers-reduced-motion: no-preference) {
  .btn:hover { transform: translateY(-2px); }
}

/* ================================================================= chapters */
.chapter { padding-block: clamp(5rem, 10vw, 9rem); position: relative; z-index: 2; }
.ch-head { margin-bottom: clamp(2.5rem, 5vw, 4rem); position: relative; }
.ch-head::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -1.25rem;
  height: 1px; background: var(--rule); transform-origin: 0 50%;
}
.ch-n {
  font-family: var(--mono); font-size: .75rem; letter-spacing: .3em;
  text-transform: uppercase; color: var(--mark); display: block; margin-bottom: 1rem;
}
.ch-head h2 {
  font-family: var(--display);
  font-size: clamp(2rem, 1.1rem + 3.4vw, 4rem);
  line-height: 1.04; letter-spacing: -.015em; max-width: 22ch;
}
.ch-head h2 em { font-style: italic; color: var(--ink); }
.ch-sub { margin-top: 1.25rem; color: var(--ink-3); max-width: 52ch; }

/* ---------------------------------------------------------------- climb ---- */
/* The signature. A horizontal rail whose position is driven by the page's own
   vertical scroll — the sticky-track technique, but the transform comes from a
   scroll timeline rather than a scroll handler. */
.climb-track { position: relative; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) and (min-width: 900px) {
    .climb-track {
      height: 300vh;
      /* The rail's timeline is THE TRACK, not the page. Driving it from
         scroll(nearest) meant the whole document's scroll range moved it, so
         the rail was already half gone before the section had even pinned —
         card one arrived clipped. A view-timeline scoped to the track with a
         `contain` range runs from the moment the track fills the viewport to
         the moment it stops, which is exactly the interval it is stuck for. */
      view-timeline-name: --climbtrack;
      view-timeline-axis: block;
    }
    .climb-stick {
      position: sticky; top: 0; height: 100svh;
      display: grid; align-content: center; gap: 2rem; overflow: clip;
      padding-block: 4rem 2rem;
      /* The rail is width:max-content. Without an explicit track the single
         grid column sizes to IT — several thousand pixels — and the centred
         .wrap holding the heading gets pushed to the middle of that, far off
         the page's own measure. Pin the track to the container. */
      grid-template-columns: minmax(0, 1fr);
    }
    .climb-stick .ch-head { margin-bottom: 0; }
    .climb-rail {
      display: flex; gap: 1.25rem; padding-inline: var(--gut);
      width: max-content; overflow: visible;
      animation: slide linear both;
      animation-timeline: --climbtrack;
      animation-range: contain 0% contain 100%;
    }
    @keyframes slide { to { transform: translateX(calc(-100% + 100vw - var(--gut) * 2)); } }
  }
}
/* Fallback and small screens: an honest horizontal scroller. */
.climb-rail {
  display: flex; gap: 1.25rem; padding-inline: var(--gut);
  overflow-x: auto; scroll-snap-type: x mandatory;
  scrollbar-width: thin; padding-bottom: 1rem;
}
.step {
  flex: 0 0 min(20rem, 74vw); scroll-snap-align: start;
  background: var(--field-2); border: 1px solid var(--rule-soft);
  padding: 2rem 1.75rem; display: grid; align-content: start; gap: .75rem;
  position: relative;
}
.step::before {
  content: ""; position: absolute; left: 0; top: 0; width: 2rem; height: 2px;
  background: var(--mark);
}
.step .yr {
  font-family: var(--mono); font-size: .75rem; letter-spacing: .2em;
  color: var(--ink-3);
}
.step h3 { font-family: var(--display); font-size: 1.6rem; line-height: 1.1; }
.step p { color: var(--ink-2); font-size: .9375rem; }

/* --------------------------------------------------------------- worlds ---- */
.worlds {
  display: grid; gap: 1px; background: var(--rule-soft);
  border: 1px solid var(--rule-soft);
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
}
.world {
  background: var(--field); padding: clamp(1.75rem, 3vw, 2.75rem);
  display: grid; gap: .875rem; align-content: start;
  position: relative; isolation: isolate;
  transition: background .35s;
  container-type: inline-size;
}
.world::after {
  content: ""; position: absolute; left: 0; top: 0; width: 100%; height: 2px;
  background: var(--mark); transform: scaleX(0); transform-origin: 0 50%;
  transition: transform .45s var(--ease);
}
.world:hover { background: var(--field-2); }
.world:hover::after { transform: scaleX(1); }
.world .kicker {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--ink-3);
}
.world h3 { font-family: var(--display); font-size: clamp(1.5rem, 1.2rem + .9vw, 2rem); line-height: 1.1; }
.world p { color: var(--ink-2); font-size: .9375rem; }

/* ---------------------------------------------------------------- story ---- */
/* The one inverted chapter. Paper, to break 4,000px of charcoal in half and to
   give the archive portrait a ground it belongs on. */
.story { background: var(--paper); color: var(--paper-ink); }
.story .ch-n { color: var(--mark-deep); }
.story .ch-head::after { background: #CFC8B7; }
.story .ch-sub, .story p { color: var(--ink-2); }
/* .ch-head h2 em is set to --ink for the dark chapters; on paper that is
   near-white on near-white and the emphasised words disappear. */
.story .ch-head h2, .story .ch-head h2 em { color: var(--paper-ink); }
.story figcaption { color: var(--ink-3); }
.story-grid {
  display: grid; gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: 1fr;
  align-items: start;
}
@media (min-width: 900px) { .story-grid { grid-template-columns: 1.15fr .85fr; } }
.story-copy { display: grid; gap: 1.25rem; max-width: 62ch; }
.story-copy p:first-of-type {
  font-family: var(--display); font-size: clamp(1.375rem, 1.1rem + 1vw, 1.75rem);
  line-height: 1.35; color: var(--paper-ink);
}
figure { margin: 0; }
figure img { width: 100%; height: auto; filter: grayscale(1) contrast(1.06); }
figcaption {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-3); margin-top: .875rem;
  padding-top: .875rem; border-top: 1px solid #CFC8B7;
}

/* ---------------------------------------------------------------- marks ---- */
/* These logos arrive as a mixed bag: most are dark artwork on an OPAQUE white
   background, one is white artwork on black. The obvious dark-page treatment,
   `brightness(0) invert(1)`, turns an opaque tile into a solid white rectangle
   — which is exactly what it did here, fifteen grey blocks and no logos.
   So each mark gets its own chip and the blend does the removing:
   multiply drops a white background into a light chip; screen drops a black
   one into a dark chip. Two cases, both honest, neither needs the file edited. */
/* The grid lines used to be the container background showing through a 1px gap,
   which meant any cell the marks did not fill read as a dark hole punched in the
   wall. Lines are now drawn per chip, so a short last row just ends. */
.marks {
  display: grid; gap: 0;
  border: 1px solid var(--rule-soft); border-width: 1px 0 0 1px;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
}
.m { border: 1px solid var(--rule-soft); border-width: 0 1px 1px 0; }
.m {
  background: #FFFFFF; aspect-ratio: 16 / 9;
  display: grid; place-items: center; padding: 1.1rem;
  transition: background .3s;
}
.m img {
  max-height: 2.75rem; width: auto; object-fit: contain;
  mix-blend-mode: multiply;
  filter: grayscale(1); opacity: .78;
  transition: opacity .3s, filter .3s;
}
.m:hover { background: var(--paper); }
.m:hover img { opacity: 1; filter: none; }
/* White artwork on black: the one that would vanish on a light chip. */
.m--screen { background: #16181C; }
.m--screen img { mix-blend-mode: screen; filter: grayscale(1) brightness(1.4); opacity: .85; }
.m--screen:hover { background: #23262B; }
.m--screen:hover img { filter: none; opacity: 1; }

/* -------------------------------------------------------------- writing ---- */
.pieces { display: grid; gap: 0; border-top: 1px solid var(--rule-soft); }
.piece {
  display: grid; gap: .5rem; padding-block: 1.75rem;
  border-bottom: 1px solid var(--rule-soft);
  text-decoration: none; position: relative;
  transition: padding-inline-start .35s var(--ease), background .3s;
}
.piece:hover { padding-inline-start: 1.25rem; background: var(--field-2); }
.piece::before {
  content: ""; position: absolute; left: 0; top: 50%; width: 3px; height: 0;
  background: var(--mark); transform: translateY(-50%);
  transition: height .35s var(--ease);
}
.piece:hover::before { height: 60%; }
.piece .meta {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
}
.piece h3 {
  font-family: var(--display); font-size: clamp(1.25rem, 1.05rem + .8vw, 1.75rem);
  line-height: 1.2; max-width: 40ch;
}

/* ------------------------------------------------------------- speaking ---- */
.speak {
  background: var(--field-2); border: 1px solid var(--rule-soft);
  padding: clamp(2rem, 5vw, 4rem);
  display: grid; gap: 1.5rem; justify-items: start;
}
.speak h2 {
  font-family: var(--display); font-size: clamp(1.75rem, 1.2rem + 2.4vw, 3rem);
  line-height: 1.08; max-width: 20ch;
}
.speak h2 em { font-style: italic; }
.speak p { color: var(--ink-2); max-width: 56ch; }

/* -------------------------------------------------------------- contact ---- */
.contact-grid { display: grid; gap: clamp(2rem, 5vw, 4rem); }
@media (min-width: 900px) { .contact-grid { grid-template-columns: 1fr 1fr; } }
.field { display: grid; gap: .5rem; margin-bottom: 1.25rem; }
.field label {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-3);
}
.field input, .field textarea {
  font: inherit; color: var(--ink);
  background: var(--field-2); border: 1px solid var(--rule);
  padding: .875rem 1rem; width: 100%;
  transition: border-color .25s, background .25s;
}
.field input:focus, .field textarea:focus { border-color: var(--mark); background: var(--field-3); }
.field textarea { min-height: 8rem; resize: vertical; }
.hp { position: absolute; left: -9999px; }

/* ----------------------------------------------------------------- foot ---- */
.foot {
  border-top: 1px solid var(--rule-soft);
  padding-block: 3rem 4rem; color: var(--ink-3); font-size: .875rem;
}
.foot .wrap { display: flex; flex-wrap: wrap; gap: 1rem 2rem; align-items: baseline; }
.foot .said {
  font-family: var(--display); font-size: 1.25rem; color: var(--ink-2);
  margin-inline-end: auto; max-width: 40ch;
}
.foot a { color: var(--ink-2); }

/* --------------------------------------------------------------- print ---- */
@media print {
  .mast, .progress, .grain, .menu-panel, .cta { display: none !important; }
  body { background: #fff; color: #000; }
}

/* ------------------------------------------------- prose + full articles ---- */
/* Added 2026-09-07: Ian asked for every page of the site on one page, so the
   Community Involvement copy and all five articles now live here in full.
   The articles are collapsed by default — 186 paragraphs open at once is a
   document, not a page — and each one still links to its live permalink. */
.prose { display: grid; gap: 1.25rem; max-width: 62ch; }
.prose p { color: var(--ink-2); }
.prose p:first-child {
  font-family: var(--display); color: var(--ink);
  font-size: clamp(1.25rem, 1.05rem + .9vw, 1.625rem); line-height: 1.35;
}

.pieces { display: grid; gap: 0; border-top: 1px solid var(--rule-soft); }
.piece {
  display: grid; gap: .625rem; padding-block: 2rem;
  border-bottom: 1px solid var(--rule-soft);
  position: relative; text-decoration: none;
}
.piece::before {
  content: ""; position: absolute; left: -1.25rem; top: 2.25rem;
  width: 3px; height: 0; background: var(--mark);
  transition: height .35s var(--ease);
}
.piece:hover::before, .piece:has(details[open])::before { height: 2.5rem; }
.piece .meta {
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-3);
}
.piece h3 {
  font-family: var(--display); font-size: clamp(1.25rem, 1.05rem + .8vw, 1.75rem);
  line-height: 1.2; max-width: 44ch;
}
.piece .stand { color: var(--ink-2); max-width: 62ch; }

.piece summary {
  cursor: pointer; list-style: none; width: max-content;
  font-family: var(--mono); font-size: .6875rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--mark);
  padding: .5rem 0; transition: color .2s;
}
.piece summary::-webkit-details-marker { display: none; }
.piece summary::after { content: "  +"; }
.piece details[open] summary::after { content: "  \2013"; }
.piece summary:hover { color: var(--on-mark); }
.piece .body {
  display: grid; gap: 1rem; max-width: 66ch;
  padding: 1.25rem 0 .5rem; border-top: 1px solid var(--rule-soft);
  margin-top: .5rem;
}
.piece .body p { color: var(--ink-2); font-size: .9375rem; }
.piece .src a { color: var(--ink-3); font-family: var(--mono); font-size: .75rem; }
.piece .src a:hover { color: var(--mark); }

/* ============================================================================
   COLOUR + SPARKLE — 2026-09-07, on Ian's note: "needs more color and sparkle."
   Crimson stays primary and stays graphic. What is added is a small accent set,
   one per world, so the page has range without becoming a rainbow. Every accent
   below is measured on --paper and none is under 5.6:1, so any of them can
   carry text: crimson 7.9 · brass 5.8 · teal 5.7 · plum 9.2 · forest 7.2 ·
   indigo 9.6. They are drawn from the subjects themselves — brass for the
   hospitality rooms, teal for the water and the policy work, plum for jazz,
   forest for the food system, indigo for the civic side.
   ============================================================================ */
:root {
  --a-crimson: #A10000;
  --a-brass:   #8F5300;
  --a-teal:    #0F6E6E;
  --a-plum:    #6B2D5B;
  --a-forest:  #2F5D3A;
  --a-indigo:  #2A3D7C;
}

/* Each chapter takes an accent; the number, the rule and the emphasis follow it. */
#climb     { --accent: var(--a-brass); }
#worlds    { --accent: var(--a-indigo); }
.story     { --accent: var(--a-plum); }
#marks     { --accent: var(--a-teal); }
#community { --accent: var(--a-forest); }
#writing   { --accent: var(--a-indigo); }
#speaking  { --accent: var(--a-brass); }
#contact   { --accent: var(--a-crimson); }
.chapter   { --accent: var(--a-crimson); }

.ch-n { color: var(--accent, var(--mark)); }
.ch-head::after {
  background: linear-gradient(90deg,
    var(--accent, var(--mark)) 0%,
    color-mix(in srgb, var(--accent, var(--mark)) 22%, var(--rule)) 26%,
    var(--rule) 62%);
}
/* The emphasised words in a chapter heading take a hairline of the accent,
   the same rule the hero uses for "dozen". Colour marks, it is never the type. */
.ch-head h2 em { position: relative; }
.ch-head h2 em::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: .04em;
  height: .045em; background: var(--accent, var(--mark)); opacity: .5;
  transform-origin: 0 50%;
}
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .ch-head h2 em::after {
      animation: draw linear both; animation-timeline: view();
      animation-range: entry 20% cover 40%;
    }
  }
}

/* ---- the worlds, one colour each -------------------------------------- */
.worlds .world:nth-child(1) { --accent: var(--a-brass); }
.worlds .world:nth-child(2) { --accent: var(--a-indigo); }
.worlds .world:nth-child(3) { --accent: var(--a-crimson); }
.worlds .world:nth-child(4) { --accent: var(--a-forest); }
.worlds .world:nth-child(5) { --accent: var(--a-plum); }
.worlds .world:nth-child(6) { --accent: var(--a-teal); }
/* 7-10 keep the cycle but shift it, so no colour sits directly under itself at three across. */
.worlds .world:nth-child(7) { --accent: var(--a-plum); }
.worlds .world:nth-child(8) { --accent: var(--a-teal); }
.worlds .world:nth-child(9) { --accent: var(--a-brass); }
.worlds .world:nth-child(10) { --accent: var(--a-indigo); }
.world::after { background: var(--accent); }
.world .kicker { color: var(--accent); }
.world:hover {
  background: color-mix(in srgb, var(--accent) 5%, var(--field));
}
.world h3 { transition: color .3s var(--ease); }
/* Worlds that link out: the name stays the heading, the underline says it goes somewhere. */
.world h3 a, .world p a { color: inherit; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .18em; text-decoration-color: var(--accent); }
.world h3 a:hover, .world p a:hover { color: var(--accent); }
.world h3 a:focus-visible, .world p a:focus-visible, .m a:focus-visible { outline: 2px solid var(--accent, var(--mark)); outline-offset: 3px; }
/* A linked mark fills its chip, so the whole chip is the target. */
.m a { display: grid; place-items: center; width: 100%; height: 100%; }
.world:hover h3 { color: var(--accent); }

/* ---- the climb, warming as it rises ------------------------------------ */
.climb-rail .step:nth-child(1) { --accent: var(--a-teal); }
.climb-rail .step:nth-child(2) { --accent: var(--a-forest); }
.climb-rail .step:nth-child(3) { --accent: var(--a-indigo); }
.climb-rail .step:nth-child(4) { --accent: var(--a-plum); }
.climb-rail .step:nth-child(5) { --accent: var(--a-brass); }
.climb-rail .step:nth-child(6) { --accent: var(--a-crimson); }
.step::before { background: var(--accent); width: 2.5rem; height: 3px; }
.step .yr { color: var(--accent); }
.step {
  transition: transform .4s var(--ease), box-shadow .4s var(--ease), border-color .4s;
}
@media (prefers-reduced-motion: no-preference) {
  .step:hover {
    transform: translateY(-4px);
    border-color: color-mix(in srgb, var(--accent) 34%, var(--rule));
    box-shadow: 0 14px 34px -18px color-mix(in srgb, var(--accent) 55%, transparent);
  }
}

/* ---- hero: a light wash instead of a flat field ------------------------ */
.hero::before {
  background:
    radial-gradient(70% 55% at 2% 6%, color-mix(in srgb, var(--a-brass) 7%, transparent) 0%, transparent 60%),
    radial-gradient(60% 60% at 34% 104%, color-mix(in srgb, var(--a-indigo) 8%, transparent) 0%, transparent 62%),
    radial-gradient(110% 80% at 4% 4%, #FFFFFF 0%, transparent 62%),
    var(--field);
}
/* The eyebrow gets the smallest possible amount of shine — a slow sweep, once. */
.eyebrow { position: relative; display: inline-block; }
@media (prefers-reduced-motion: no-preference) {
  .eyebrow::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(100deg, transparent 38%,
      color-mix(in srgb, var(--mark) 26%, transparent) 50%, transparent 62%);
    animation: sweep 2.6s var(--ease) .9s 1 both;
  }
  @keyframes sweep { from { transform: translateX(-120%); } to { transform: translateX(230%); } }
}

/* ---- affiliations: colour on hover rather than a flat grey wall -------- */
.m { transition: background .35s, box-shadow .35s; }
.m:hover { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--a-teal) 34%, transparent); }

/* ---- writing: the accent leads the eye down the list ------------------- */
.pieces .piece:nth-child(5n+1) { --accent: var(--a-crimson); }
.pieces .piece:nth-child(5n+2) { --accent: var(--a-indigo); }
.pieces .piece:nth-child(5n+3) { --accent: var(--a-brass); }
.pieces .piece:nth-child(5n+4) { --accent: var(--a-teal); }
.pieces .piece:nth-child(5n+5) { --accent: var(--a-plum); }
.piece::before { background: var(--accent); }
.piece .meta { color: var(--accent); }
.piece:hover { background: color-mix(in srgb, var(--accent) 4%, var(--field)); }
.piece h3 { transition: color .3s var(--ease); }
.piece:hover h3 { color: var(--accent); }

/* ---- buttons: depth, not flatness ------------------------------------- */
.btn-p {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--mark) 88%, #FFFFFF) 0%, var(--mark) 100%);
  box-shadow: 0 6px 18px -10px color-mix(in srgb, var(--mark) 70%, transparent);
}
.btn-p:hover {
  background: linear-gradient(180deg, var(--mark) 0%, var(--mark-deep) 100%);
  box-shadow: 0 10px 24px -10px color-mix(in srgb, var(--mark) 60%, transparent);
}

/* ---- speaking panel: a tinted card, not a grey one -------------------- */
.speak {
  background:
    radial-gradient(80% 120% at 100% 0%, color-mix(in srgb, var(--a-brass) 9%, transparent) 0%, transparent 60%),
    var(--field-2);
  border-color: color-mix(in srgb, var(--a-brass) 18%, var(--rule));
}
