/* ═══════════════════════════════════════════════════════════════
   ELLIOT ANDERSON — VIOLINIST
   Design system & stylesheet

   Structure of this file:
     1. Tokens          — every value the site uses, in one place
     2. Base            — reset, page defaults
     3. Typography      — headings, body, labels
     4. Layout          — containers, sections, rhythm
     5. Components      — nav, hero, bio, the record, the ladder, contact
     6. Motion          — scroll reveals
     7. Responsive      — tablet & phone
     8. Preferences     — reduced motion, focus, print

   TO CHANGE THE LOOK: edit the tokens in section 1. Almost every
   visual decision flows from there — you rarely need to touch
   anything below it.
   ═══════════════════════════════════════════════════════════════ */


/* ═══ 1. TOKENS ══════════════════════════════════════════════ */

:root {

  /* ── 1a. Primitives — raw values. Don't use these directly in
         rules below; use the semantic tokens in 1b instead. ── */

  /* ── The palette is DERIVED. Source: blued steel — the fine tuners, the
     tailpiece wire, the rigging above a stage. Full derivation and
     register clearance (OKLab ΔE 0.0555, clears the 0.05 target) are in
     ../PALETTES.md. Chosen 10 Sep 2026 over a Cremonese varnish brown
     (kept in the register as v3) and over a midnight blue. Do not
     substitute a near-black: an ebony was tested at ΔE 0.0214 from the
     varnish, which is that brown with the colour drained out rather
     than a decision of its own. ── */

  /* Steel — the ground and its lit steps. Hue 236 at chroma 0.024:
     barely chromatic, so it reads as slate rather than as blue, and
     leaves the accents room to be the only colour on the page. */
  --case-900: #0D1A22;   /* steel in the deepest shadow — wells, footer */
  --case-800: #1A2730;   /* THE GROUND */
  --case-700: #233039;   /* steel, lit — raised surfaces */
  --case-600: #2A3841;   /* steel, raised — cards */
  --case-500: #3F4D57;   /* the quieter of the two motif rules */
  --case-400: #52616A;   /* the stronger of the two motif rules */

  /* Spruce — the violin's unvarnished top plate. Hue 87. */
  --spruce-100: #EDE6D6;
  --spruce-300: #D9CEB8;
  --spruce-500: #B5A992;   /* the accessible floor — do not darken */

  /* Rosin — the translucent amber cake a bow is drawn across. Hue 80. */
  --rosin-400: #E0AC4A;
  --rosin-600: #B08130;
  --rosin-700: #8A6633;   /* dimmed, for the motif's carrying voice */

  /* Pearl — the mother-of-pearl inlay in the bow frog. Hue 181, which is
     near-complementary to the varnish at hue 38: the one cool thing on a
     warm instrument, and the reason the page is not monochrome. */
  --pearl-300: #86C9BE;   /* iridescent, not grey — C0.070 */
  --pearl-600: #55807A;   /* dimmed, for the motif's second voice */

  /* Silver — the winding on the lower strings and the fittings on a
     silver-mounted bow. Near-neutral by design: it separates the header
     from the page without adding a fourth hue to argue with the other
     three. Hue 250 at chroma 0.012 — cool enough not to read as beige
     against the varnish, quiet enough to stay a rule and not a colour. */
  --silver-200: #C6CBD1;  /* 9.44:1 on the ground */
  --silver-400: #8C939B;  /* 4.96:1 */

  /* Channels, so component rules never hardcode a colour */
  --ink-rgb:    237, 230, 214;
  --rosin-rgb:  224, 172, 74;
  --ground-rgb:  26,  39,  48;

  /* Spacing — a 4px base, growing in intentional steps */
  --space-1:  0.25rem;   /*   4px */
  --space-2:  0.5rem;    /*   8px */
  --space-3:  0.75rem;   /*  12px */
  --space-4:  1rem;      /*  16px */
  --space-5:  1.5rem;    /*  24px */
  --space-6:  2rem;      /*  32px */
  --space-7:  3rem;      /*  48px */
  --space-8:  4rem;      /*  64px */
  --space-9:  6rem;      /*  96px */
  --space-10: 8rem;      /* 128px */
  --space-11: 11rem;     /* 176px */

  /* Type families */
  --font-display: 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
  --font-text:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  /* Type sizes — fluid, so they scale smoothly between phone and
     desktop instead of jumping at breakpoints */
  --size-display: clamp(3.25rem, 8.5vw, 7rem);
  --size-h1:      clamp(2.1rem, 4.4vw, 3.4rem);
  --size-h2:      clamp(1.45rem, 2.1vw, 1.9rem);
  --size-h3:      clamp(1.05rem, 1.3vw, 1.2rem);
  --size-lede:    clamp(1.05rem, 1.35vw, 1.25rem);
  --size-body:    1.0625rem;
  --size-small:   0.9375rem;
  --size-label:   0.75rem;

  /* Motion — durations and curves, named by feel */
  --dur-micro:     180ms;   /* hover, focus */
  --dur-fast:      320ms;   /* small state changes */
  --dur-base:      560ms;   /* most reveals */
  --dur-slow:      900ms;   /* large elements settling */
  --dur-cinematic: 1400ms;  /* hero only */

  --ease-settle: cubic-bezier(0.16, 1, 0.3, 1);   /* decelerates deeply — things come to rest */
  --ease-soft:   cubic-bezier(0.22, 1, 0.36, 1);  /* gentler, for small moves */
  --ease-inout:  cubic-bezier(0.65, 0, 0.35, 1);  /* symmetrical, for things that travel */

  /* Shape */
  --radius-sm: 2px;
  --radius-md: 4px;
  --radius-lg: 6px;

  /* Elevation */
  --shadow-soft:  0 2px 24px rgba(0, 0, 0, 0.4);
  --shadow-lift:  0 18px 48px rgba(0, 0, 0, 0.55);
  --shadow-frame: 0 32px 80px rgba(0, 0, 0, 0.6);

  /* Layout */
  --measure: 68ch;        /* comfortable reading width */
  --container: 1180px;
  --container-narrow: 940px;
  --nav-height: 72px;


  /* ── 1b. Semantic — what things *mean*. Use these. ── */

  --surface-page:    var(--case-800);
  --surface-raised:  var(--case-700);
  --surface-card:    var(--case-600);
  --surface-well:    var(--case-900);

  --text-primary:    var(--spruce-100);
  --text-secondary:  var(--spruce-300);
  --text-muted:      var(--spruce-500);
  --text-faint:      var(--spruce-500);   /* merged: a L0.27 ground raises the
                                             AA floor to ~L0.67, so a fourth
                                             tier would be indistinguishable */

  /* Two accents, each with a fixed job — never used interchangeably.
     ROSIN marks the artist: the call to action, the eyebrow, group titles,
     festival years, links. PEARL marks the structure: section labels,
     field labels, dates. Warm for him, cool for the scaffolding. */
  --accent:          var(--rosin-400);
  --accent-dim:      var(--rosin-600);
  --accent-cool:     var(--pearl-300);
  /* The third voice was an indigo while the ground was brown. On a steel
     ground a blue accent stops being an accent, so silver took the job —
     the same silver as the rule under the header, which makes the two
     read as one neutral register running through the page. */
  --accent-third:    var(--silver-200);
  --accent-third-dim: var(--silver-400);

  /* The double-stop motif — two voices, unequal weight AND unequal colour.
     One warm, one cool: the two-lineage thesis stated in colour, at 3px. */
  --rule-voice-1:    var(--rosin-700);
  --rule-voice-2:    var(--pearl-600);

  --border-subtle:   rgba(var(--ink-rgb), 0.08);
  --border-strong:   rgba(var(--ink-rgb), 0.16);
  --border-accent:   rgba(var(--rosin-rgb), 0.34);

  /* Stacking order — no arbitrary z-index anywhere else */
  --z-base:    1;
  --z-raised:  10;
  --z-nav:     100;
  --z-overlay: 200;
}


/* ═══ 2. BASE ════════════════════════════════════════════════ */

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

html {
  scroll-behavior: auto;   /* smoothing is handled in JS; CSS smooth would fight it */
  color-scheme: dark;
  scrollbar-width: thin;
  scrollbar-color: rgba(var(--rosin-rgb), 0.22) var(--surface-page);
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-text);
  font-size: var(--size-body);
  line-height: 1.75;
  font-weight: 400;
  color: var(--text-secondary);
  background: var(--surface-page);
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img {
  max-width: 100%;
  display: block;
  height: auto;
}

a {
  color: var(--accent);
  text-decoration: none;
  transition: color var(--dur-micro) var(--ease-soft);
}
a:hover { color: var(--text-primary); }

::selection {
  background: rgba(var(--rosin-rgb), 0.26);
  color: var(--text-primary);
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--surface-page); }
::-webkit-scrollbar-thumb {
  background: rgba(var(--rosin-rgb), 0.2);
  border: 2px solid var(--surface-page);
  border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover { background: rgba(var(--rosin-rgb), 0.38); }

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

/* Skip link — first stop for keyboard and screen-reader users */
.skip-link {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -120%);
  z-index: var(--z-overlay);
  background: var(--surface-card);
  color: var(--text-primary);
  border: 1px solid var(--border-accent);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  padding: var(--space-3) var(--space-5);
  font-size: var(--size-small);
  transition: transform var(--dur-fast) var(--ease-soft);
}
.skip-link:focus { transform: translate(-50%, 0); }

/* Screen-reader-only text. Currently unused, but kept deliberately:
   without it the natural instinct is display:none, which hides content
   from screen readers too. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}


/* ═══ 3. TYPOGRAPHY ══════════════════════════════════════════ */

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.15;
  letter-spacing: -0.01em;
}

/* Eyebrow label — the small tracked-out caps above a section title */
.label {
  display: block;
  font-family: var(--font-text);
  font-size: var(--size-label);
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--accent-cool);
  line-height: 1;
  margin-bottom: var(--space-5);
}

.section-title {
  font-size: var(--size-h1);
  letter-spacing: -0.015em;
  margin-bottom: var(--space-7);
  max-width: 20ch;
}

/* Subheading inside prose */
.bio-text h2 {
  font-size: var(--size-h2);
  color: var(--text-primary);
  margin: var(--space-8) 0 var(--space-4);
  letter-spacing: -0.005em;
}
.bio-text h2:first-child { margin-top: 0; }

.lede {
  font-size: var(--size-lede);
  line-height: 1.65;
  color: var(--text-secondary);
  max-width: 46ch;
}
/* A section intro rather than a hero lede: it sits over a list and wants
   the list's measure, not the hero's narrow column. */
.lede--wide { max-width: 62ch; }

/* The contact section's eyebrow IS its heading. v3 used a <p>, which
   left the section out of the document outline for anyone navigating by
   heading. Same appearance, correct semantics. */
.label--heading {
  font-size: var(--size-label);
  font-weight: 500;
  letter-spacing: 0.24em;
}

p { max-width: var(--measure); }

.bio-text p {
  color: var(--text-muted);
  margin-bottom: var(--space-4);
}

.photo-caption {
  font-size: var(--size-small);
  font-style: italic;
  color: var(--accent-third);
  margin-top: var(--space-3);
  line-height: 1.5;
}


/* ═══ 4. LAYOUT ══════════════════════════════════════════════ */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-6);
}
.container--narrow { max-width: var(--container-narrow); }

/* Sections carry their own vertical rhythm. Alternating the modifier
   creates the dense / open pacing the layout depends on. */
.section {
  position: relative;
  padding-block: var(--space-10);
}
.section--tight { padding-block: var(--space-9); }
.section--loose { padding-block: var(--space-11) var(--space-8); }

section[id] { scroll-margin-top: calc(var(--nav-height) + var(--space-4)); }



/* ═══ 5. COMPONENTS ══════════════════════════════════════════ */

/* ── 5a. Navigation ───────────────────────────────────────── */

.nav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  height: var(--nav-height);
  padding-inline: var(--space-6);
  background: rgba(var(--ground-rgb), 0.72);
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  /* The header is separated by a rule of its own, not by a hairline that
     only appears once you scroll. Three pixels of silver: heavy enough to
     read as a decision, neutral enough not to be mistaken for the gold. */
  border-bottom: 3px solid var(--silver-400);
  transition: border-color var(--dur-fast) var(--ease-soft),
              background var(--dur-fast) var(--ease-soft);
}
.nav.is-scrolled {
  background: rgba(var(--ground-rgb), 0.90);
  border-bottom-color: var(--silver-200);
}

.nav-brand {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-primary);
  white-space: nowrap;
}
.nav-brand:hover { color: var(--text-primary); }

.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  list-style: none;
}

.nav-links a {
  display: block;
  font-size: var(--size-label);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  position: relative;
  transition: color var(--dur-micro) var(--ease-soft);
}
.nav-links a::after {
  content: '';
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: 0.55rem;
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease-settle);
}
.nav-links a:hover,
.nav-links a.is-active { color: var(--text-primary); }
.nav-links a:hover::after,
.nav-links a.is-active::after { transform: scaleX(1); }

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  width: 44px; height: 44px;   /* WCAG 2.5.8 minimum touch target */
  cursor: pointer;
  align-items: center;
  justify-content: center;
  transition: border-color var(--dur-micro) var(--ease-soft);
}
.nav-toggle:hover { border-color: var(--border-accent); }
.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  display: block;
  width: 17px; height: 1px;
  background: currentColor;
  transition: transform var(--dur-fast) var(--ease-soft),
              opacity var(--dur-micro) linear;
}
.nav-toggle span { position: relative; }
.nav-toggle span::before,
.nav-toggle span::after { content: ''; position: absolute; left: 0; }
.nav-toggle span::before { top: -5px; }
.nav-toggle span::after  { top: 5px; }
.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after  { transform: translateY(-5px) rotate(-45deg); }


/* ── 5b. Hero ─────────────────────────────────────────────── */

.hero {
  position: relative;
  min-height: calc(100svh - var(--nav-height));
  display: flex;
  align-items: center;
  /* Symmetric, deliberately: the top and bottom padding are the two
     margins the hero content is centred between when the page is at
     the top, so an uneven pair reads as a photo sitting too low. */
  padding-block: var(--space-8);
  overflow: hidden;
}

/* A single wash of warm light from the upper left — stage light,
   not a glow effect. Replaces the old multi-coloured orbs. */
.hero::before {
  content: '';
  position: absolute;
  top: -20%; left: -10%;
  width: 70%; aspect-ratio: 1;
  background: radial-gradient(
    circle,
    rgba(var(--ink-rgb), 0.055) 0%,
    rgba(var(--ink-rgb), 0.018) 38%,
    transparent 68%
  );
  pointer-events: none;
  z-index: 0;
}

.hero .container { position: relative; z-index: var(--z-base); }

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 0.7fr) minmax(0, 1fr);
  gap: clamp(var(--space-7), 6vw, var(--space-10));
  align-items: center;
}

.hero-portrait {
  position: relative;
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-frame);
  aspect-ratio: 2 / 3;   /* matches the source photograph */

  /* The photo is the first thing on the page, so it has to be whole on
     the first screen — no scrolling to find his head or his feet. The
     height available is the viewport, less the fixed nav, less the
     hero's own padding and a margin of air beneath (9.5rem covers all
     three). At 2:3 the width that fits that height is two thirds of it,
     and 26rem caps it on tall screens so the photo never outgrows the
     text beside it. 15rem is the floor on very short windows. */
  --fit-h: calc(
    100svh
    - var(--nav-height)       /* the bar above */
    - (2 * var(--space-8))    /* the hero's own padding, top and bottom */
    - var(--space-6)          /* clear air under the photo for the cue */
  );
  max-width: max(15rem, min(26rem, calc(var(--fit-h) * 2 / 3)));
}
.hero-portrait img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 22%;
}
/* Grounds the photo into the page rather than letting it float */
.hero-portrait::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.10);
  border-radius: inherit;
  pointer-events: none;
}

.hero-eyebrow {
  font-size: var(--size-label);
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--accent-third);
  margin-bottom: var(--space-5);
}

.hero-name {
  font-family: var(--font-display);
  font-size: var(--size-display);
  font-weight: 400;
  line-height: 0.92;
  letter-spacing: -0.025em;
  color: var(--text-primary);
  margin-bottom: var(--space-6);
}
/* Deliberate line break — the name reads as two stacked words, not
   a wrapped phrase */
.hero-name span { display: block; }
.hero-name .surname { font-style: italic; color: var(--text-secondary); }

.hero-lede {
  font-size: var(--size-lede);
  line-height: 1.6;
  color: var(--text-muted);
  max-width: 38ch;
  margin-bottom: var(--space-7);
}
.hero-lede strong {
  color: var(--text-secondary);
  font-weight: 500;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
}

.hero-scroll {
  position: absolute;
  bottom: var(--space-6);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-base);
  background: none;
  border: 0;
  color: var(--text-faint);
  font-family: var(--font-text);
  font-size: var(--size-label);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  transition: color var(--dur-fast) var(--ease-soft);
}
.hero-scroll:hover { color: var(--text-secondary); }
.hero-scroll::after {
  content: '';
  width: 1px; height: 34px;
  background: linear-gradient(to bottom, var(--border-strong), transparent);
  animation: scrollHint 2.8s var(--ease-inout) infinite;
}
@keyframes scrollHint {
  0%, 100% { transform: scaleY(0.4); transform-origin: top; opacity: 0.4; }
  50%      { transform: scaleY(1);   transform-origin: top; opacity: 1; }
}


/* ── 5c. Buttons ──────────────────────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-text);
  font-size: var(--size-small);
  font-weight: 500;
  letter-spacing: 0.06em;
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-soft),
              border-color var(--dur-fast) var(--ease-soft),
              color var(--dur-fast) var(--ease-soft),
              transform var(--dur-fast) var(--ease-soft);
}
.btn:active { transform: translateY(1px); }

.btn--primary {
  background: var(--accent);
  color: var(--case-900);   /* on the amber button — the deepest lining green */
  font-weight: 600;
}
.btn--primary:hover {
  background: var(--text-primary);
  color: var(--case-900);   /* on the amber button — the deepest lining green */
}

.btn--ghost {
  border-color: var(--border-strong);
  color: var(--text-secondary);
  background: transparent;
}
.btn--ghost:hover {
  border-color: var(--border-accent);
  color: var(--text-primary);
  background: rgba(var(--rosin-rgb), 0.06);
}


/* ── 5d. Biography ────────────────────────────────────────── */

.bio-layout {
  display: grid;
  grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
  gap: clamp(var(--space-6), 5vw, var(--space-9));
  align-items: start;
}

.bio-aside { position: sticky; top: calc(var(--nav-height) + var(--space-6)); }

.bio-photo {
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  position: relative;
}
.bio-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.09);
  border-radius: inherit;
  pointer-events: none;
}

/* Fact list under the sidebar photo — scannable credentials */
.bio-facts {
  margin-top: var(--space-7);
  border-top: 1px solid var(--border-subtle);
}
.bio-facts div {
  padding-block: var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
}
.bio-facts dt {
  font-size: var(--size-label);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-third);
  margin-bottom: var(--space-2);
}
.bio-facts dd {
  font-size: var(--size-small);
  color: var(--text-secondary);
  line-height: 1.55;
}


/* ── 5e. The plate — one full-bleed photograph, pinned to its date ──
   v3 had this as a free-floating "editorial break" between sections.
   It now sits INSIDE the record, immediately after the entries it
   belongs to, and carries its date in the caption. A photograph in a
   chronology has a position in time; this is it.                     */

.rec-plate {
  position: relative;
  margin-block: var(--space-9);
  /* Full escape from the narrow measure the record reads in. The page
     breaks its own measure exactly twice: here, and at the ladder. */
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  /* The mask reveal scales this to 1.04; a full-bleed element would then
     overhang the viewport and create a horizontal scrollbar, and a child
     cannot clip its own transform — so the overhang is clipped here. */
  overflow: hidden;
}
.rec-plate picture {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 21 / 9;
}
.rec-plate img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 38%;
}
/* Feathers the photo into the page top and bottom so it reads as a
   passage in the page, not a pasted-in block */
.rec-plate picture::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    var(--surface-page) 0%,
    transparent 16%,
    transparent 84%,
    var(--surface-page) 100%
  );
  pointer-events: none;
}
.rec-plate figcaption {
  margin-top: var(--space-4);
  text-align: center;
  font-size: var(--size-small);
  font-style: italic;
  color: var(--text-faint);
}
.rec-plate figcaption time {
  font-style: normal;
  font-variant-numeric: lining-nums tabular-nums;
}


/* ── 5f. The record — one dated entry, five facets ──────────
   The v4 argument lives in this one component, and sixty of them appear
   on the page. That volume sets three rules:

     · The DATE is the recurring hero, so it gets a real type system:
       lining tabular figures, so a column of years reads as a grid
       rather than as sixty captions.
     · The YEAR is printed ONCE per group. Rows inside a year carry only
       their day and month. Printing it sixty times is what makes a CV
       look like a CV.
     · The motif marks the YEAR CHANGES, not the rows. At sixty
       repetitions it would be wallpaper.

   One component, five facets, one time direction: earliest first.    */

.facet + .facet { margin-top: var(--space-9); }

.facet-title {
  font-family: var(--font-text);
  font-size: var(--size-label);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  /* The explanatory line that used to sit under each category heading was
     removed on 17 Oct; it carried this rule, so the heading now does. */
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-accent);
}

.rec-list { margin-top: var(--space-5); }

/* A section whose whole body is one list has no category heading above it,
   so the list carries the rule itself. */
.rec-list--ruled {
  border-top: 1px solid var(--border-accent);
  padding-top: var(--space-2);
}

/* `.facet-nav` removed 19 Oct: the three category headings are enough to
   tell the lists apart, and a jump list over three items was ceremony. */

.rec-list { list-style: none; }

.rec-item {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  gap: var(--space-4);
  align-items: baseline;
  /* Compact, deliberately: at sixty rows every extra 8px of padding is
     another half-screen of scrolling for a reader who is scanning. */
  padding-block: var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
  transition: background var(--dur-fast) var(--ease-soft);
}
.rec-item:hover { background: rgba(var(--ink-rgb), 0.03); }

/* Where time moves on — the only place the year rule appears.

   It is the PRECEDING row's rule that strengthens, not a second rule on the
   year row. A border-top here sat 24px below the previous row's
   border-bottom, which drew two hairlines with an empty band between them
   and read as a rendering fault. Caught on a real iPhone, 22 Oct 2026. */
.rec-item--year {
  margin-top: var(--space-5);
}
.rec-item:has(+ .rec-item--year) {
  border-bottom-color: var(--border-strong);
}
/* Safari and Chrome have had :has() since 2022; where it is missing the list
   simply loses the emphasis and keeps one correct hairline per row. */

.rec-date { align-self: start; }

.rec-y {
  display: block;
  font-family: var(--font-display);
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1;
  color: var(--accent-third);          /* silver-200 */
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
}
.rec-d {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-third-dim);      /* silver-400 */
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
  white-space: nowrap;
}
.rec-y + .rec-d { margin-top: var(--space-1); }

.rec-name {
  font-family: var(--font-display);
  font-size: var(--size-h3);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
  letter-spacing: 0;
}
.rec-where {
  font-size: var(--size-small);
  line-height: 1.5;
  color: var(--text-muted);
  max-width: 62ch;
}

/* The outcome sits on the same line as the detail where there is room,
   rather than taking a third line of its own on every award row. */
.rec-body { display: flex; flex-direction: column; gap: 2px; }

/* THE ACCENT RULE for a page this long: gold marks an OUTCOME, and
   almost nothing else — the facet titles, the hero button, the nav
   underline, the diary dates. Used for general emphasis it would be
   costume jewellery by the fortieth row. */
.rec-result {
  display: inline-block;
  margin-top: var(--space-1);
  font-size: var(--size-label);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Festival names link out to each institute. Same underline voice as the
   contact links: a hairline in the accent that fills in on hover. */
.rec-link {
  color: inherit;
  border-bottom: 1px solid var(--border-accent);
  padding-bottom: 2px;
  transition: color var(--dur-micro) var(--ease-soft),
              border-color var(--dur-micro) var(--ease-soft);
}
.rec-link:hover,
.rec-link:focus-visible { color: var(--accent); border-bottom-color: var(--accent); }


/* ── 5f2. The ladder — ten unbroken seasons ─────────────────
   The one moment on the page. The spine is the motif's corner form
   stretched to the full height of the list: one continuous line, no
   gaps, which is literally the claim being made. Delete it and the
   section still lists five orchestras — but it stops saying
   "unbroken", which is the test that makes this language and not
   decoration.                                                       */

.ladder {
  list-style: none;
  position: relative;
  margin-top: var(--space-8);
  padding-inline-start: var(--space-7);
}
.ladder::before {
  content: '';
  position: absolute;
  inset-block: 0.75rem;
  inset-inline-start: 0;
  width: 6px;                   /* 2 + 3 + 1 — the motif's own construction */
  background:
    linear-gradient(var(--rule-voice-1), var(--rule-voice-1)) left  / 2px 100% no-repeat,
    linear-gradient(var(--rule-voice-2), var(--rule-voice-2)) right / 1px 100% no-repeat;
}

.rung {
  position: relative;
  display: grid;
  grid-template-columns: 7rem minmax(0, 1fr);
  gap: var(--space-5);
  padding-block: var(--space-5);
  border-top: 1px solid var(--border-subtle);
}
.rung:first-child { border-top: 0; padding-top: 0; }

/* A node where each seat begins, sitting on the spine */
.rung::before {
  content: '';
  position: absolute;
  inset-block-start: calc(var(--space-5) + 0.45rem);
  inset-inline-start: calc(-1 * var(--space-7));
  width: 9px; height: 9px;
  margin-inline-start: -1px;
  border-radius: 50%;
  background: var(--surface-page);
  border: 2px solid var(--accent-cool);
}
.rung:first-child::before { inset-block-start: 0.5rem; }

.rung-span {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2vw, 1.6rem);
  font-weight: 600;
  line-height: 1.1;
  color: var(--accent-third);
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
  white-space: nowrap;
}

.rung-name {
  font-family: var(--font-display);
  font-size: var(--size-h3);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.25;
  letter-spacing: 0;
}
.rung-role {
  margin-top: var(--space-2);
  font-size: var(--size-label);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
}
.rung-detail {
  margin-top: var(--space-3);
  font-size: var(--size-small);
  color: var(--text-muted);
  max-width: 62ch;
}

/* `.rung-dates` removed 17 Oct with the per-performance sub-lists. The
   rung now carries its span, its seat, its role and its conductor. */


/* ── 5f3. Still to come ─────────────────────────────────────
   Two rows, and the only forward-looking thing on the page.
   Deliberately a different shape from the record so it does not read as
   more history: the date leads at full size instead of sitting in a
   column, and it is the one other place the accent appears.          */

.up-list { list-style: none; margin-top: var(--space-7); }

.up-item {
  display: grid;
  grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
  gap: var(--space-5);
  align-items: baseline;
  padding-block: var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
}
.up-item:first-child { border-top: 1px solid var(--border-strong); }

.up-date {
  font-family: var(--font-display);
  font-size: clamp(1.25rem, 2vw, 1.6rem);
  font-weight: 600;
  line-height: 1.1;
  color: var(--accent);
  font-variant-numeric: lining-nums tabular-nums;
  font-feature-settings: "lnum" 1, "tnum" 1;
}
.up-name {
  font-family: var(--font-display);
  font-size: var(--size-h3);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.35;
}
.up-where {
  font-size: var(--size-small);
  color: var(--text-muted);
  margin-top: var(--space-1);
}


/* ── 5g. Contact ──────────────────────────────────────────── */

.contact-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  gap: clamp(var(--space-7), 6vw, var(--space-10));
  align-items: center;
}

.contact-list { border-top: 1px solid var(--border-subtle); }
.contact-list > div {
  display: grid;
  grid-template-columns: 8rem minmax(0, 1fr);
  gap: var(--space-5);
  padding-block: var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
  align-items: baseline;
}
.contact-list dt {
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-cool);
}
/* Contact is the last thing on the page and the only thing on it that
   asks for an action — it is set larger than body copy, not smaller. */
.contact-list dd {
  color: var(--text-primary);
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 1.7vw, 1.4rem);
  line-height: 1.3;
}
.contact-list a { color: var(--text-primary); border-bottom: 1px solid var(--border-accent); padding-bottom: 1px; }
.contact-list a:hover { color: var(--accent); border-bottom-color: var(--accent); }

.contact-photo {
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-lift);
  position: relative;
}
.contact-photo::after {
  content: '';
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px rgba(var(--ink-rgb), 0.09);
  border-radius: inherit;
  pointer-events: none;
}


/* ── 5i. Footer ───────────────────────────────────────────── */

.footer {
  border-top: 1px solid var(--border-subtle);
  padding-block: var(--space-8);
  /* No margin: the rule above is the separation, and it now sits with
     64px of air on either side of it rather than 176 above and 96 below. */
}
.footer .container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  align-items: center;
  justify-content: space-between;
}
.footer p {
  font-size: var(--size-small);
  color: var(--text-faint);
  margin: 0;
}
.footer-name {
  font-family: var(--font-display);
  font-size: 1.05rem;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}


/* ── 5k. THE DOUBLE-STOP — the site's one graphic motif ─────────

   A double-stop is two notes sounded at once on one instrument: one
   player, two voices. It is the same fact the site is built on — a
   student holding an orchestral lineage and a chamber lineage at the
   same time.

   Drawn as two hairlines, close-set, of UNEQUAL weight, because the two
   voices are not equal. Construction is fixed and every member of the
   family obeys it:

       voice 1   2px   var(--rule-voice-1)   the carrying line
       voice 2   1px   var(--rule-voice-2)   the second string
       gap       3px
       ends      square — a bowed note starts and stops
       axis      horizontal only; strings run one way

   Removal test: delete these and every divider becomes a generic <hr>
   and the site stops restating its own thesis. It is language, not
   decoration — but it carries no information, so it is hidden from
   assistive technology throughout.                                   */

.stop {
  border: 0;
  height: 6px;                 /* 2 + 3 + 1 */
  margin: 0;
  background:
    linear-gradient(var(--rule-voice-1), var(--rule-voice-1)) top    / 100% 2px no-repeat,
    linear-gradient(var(--rule-voice-2), var(--rule-voice-2)) bottom / 100% 1px no-repeat;
}

/* Reduced form — marks a section opener, sits before the label */
.stop--mark {
  width: 28px;
  display: block;
  margin-bottom: var(--space-5);
}

/* Linear form — the full divider between major sections */
.stop--divider {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  opacity: 0.85;
}

/* Corner form — RETIRED in v4, deliberately. It marked a row on an
   eleven-row list; at sixty rows the same gesture on every row becomes
   noise, and the year rule (5f) took over its job of saying "a new thing
   starts here". The family's large member is now the ladder spine (5f2),
   which does more work than the corner form ever did: it is the only
   element on the page that draws "unbroken". Recorded in GRAPHIC.md. */

/* The nav's active underline is the motif at its smallest */
.nav-links a::after {
  height: 3px;
  background:
    linear-gradient(var(--accent), var(--accent))           top    / 100% 2px no-repeat,
    linear-gradient(var(--accent-cool), var(--accent-cool)) bottom / 100% 1px no-repeat;
}

/* The one large moment: the staff. Five lines, once on the page, closing
   the contact section. Sparingly — repeated, it becomes wallpaper. */
.staff {
  height: 17px;                /* 5 lines at 1px on a 4px unit */
  border: 0;
  margin: var(--space-9) auto 0;
  max-width: 260px;
  background: repeating-linear-gradient(
    to bottom,
    var(--accent-third) 0 1px,
    transparent 1px 4px
  );
  opacity: 0.5;
}

/* ═══ 6. MOTION — scroll reveals ═════════════════════════════ */

/* One reveal system, three variants. Elements start displaced and
   settle into place; nothing pops. The stagger index is set inline
   as --i so a list reveals in sequence, not all at once. */

/* Everything below is scoped to .js — a class the page sets on itself
   in the <head>. If scripting is disabled or errors before that runs,
   none of these rules apply and the content is simply visible. The
   site must never depend on JavaScript to be readable. */

.js [data-reveal] {
  opacity: 0;
  transition:
    opacity var(--dur-base) var(--ease-settle),
    transform var(--dur-base) var(--ease-settle),
    clip-path var(--dur-slow) var(--ease-settle);
  transition-delay: calc(var(--i, 0) * 70ms);
}

.js [data-reveal="up"]   { transform: translateY(26px); }
.js [data-reveal="fade"] { transform: none; }
/* Mask reveal — the photograph wipes open upward. Reserved for
   images, where it reads as a curtain rather than a slide. */
.js [data-reveal="mask"] {
  clip-path: inset(0 0 100% 0);
  transform: scale(1.04);
}

.js [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
  clip-path: inset(0 0 0 0);
}


/* ═══ 7. RESPONSIVE ══════════════════════════════════════════ */

/* Short windows: once the hero content is centred in the visible area,
   a window under ~820px tall has no room left beneath it, and the cue
   lands on top of the buttons. Measured: 5px of clearance at 800px
   tall, 36px of overlap at 720. Drop it — the page still scrolls. */
@media (max-height: 820px) {
  .hero-scroll { display: none; }
}

@media (max-width: 1024px) {
  .hero-grid { gap: var(--space-7); }
  .bio-layout { grid-template-columns: minmax(0, 240px) minmax(0, 1fr); }
  .contact-grid { grid-template-columns: 1fr; }
  .contact-photo { max-width: 460px; }
}

@media (max-width: 860px) {
  :root { --nav-height: 64px; }

  /* Nav collapses to a panel */
  .nav-toggle { display: flex; }
  .nav-links {
    position: absolute;
    top: var(--nav-height); left: 0; right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: rgba(var(--ground-rgb), 0.97);
    backdrop-filter: blur(20px);
    -webkit-backdrop-filter: blur(20px);
    border-bottom: 1px solid var(--border-subtle);
    padding: var(--space-3) var(--space-5) var(--space-5);
    display: none;
  }
  .nav-links.is-open { display: flex; }
  .nav-links a { padding: var(--space-4) var(--space-2); font-size: var(--size-small); letter-spacing: 0.12em; }
  .nav-links a::after { display: none; }

  /* Without scripting the toggle cannot open anything, which would
     leave an inert button and no visible navigation at all. Show the
     links inline instead and drop the dead control. */
  html:not(.js) .nav {
    height: auto;
    flex-wrap: wrap;
    padding-block: var(--space-3);
    row-gap: var(--space-2);
  }
  html:not(.js) .nav-toggle { display: none; }
  html:not(.js) .nav-links {
    display: flex;
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    width: 100%;
    gap: var(--space-1) var(--space-5);
    background: none;
    border: 0;
    padding: 0;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  html:not(.js) .nav-links a { padding: var(--space-2) 0; }

  /* Hero stacks, portrait first — the photograph still leads */
  .hero { min-height: auto; padding-block: var(--space-8); }
  .hero-grid { grid-template-columns: 1fr; gap: var(--space-7); }
  .hero-portrait { max-width: 340px; aspect-ratio: 4 / 5; }
  .hero-scroll { display: none; }

  .bio-layout { grid-template-columns: 1fr; gap: var(--space-7); }
  .bio-aside { position: static; max-width: 340px; }

  .section { padding-block: var(--space-9); }
  .section--loose { padding-block: var(--space-9) var(--space-7); }

  .rec-plate picture { aspect-ratio: 4 / 3; }

  /* Below 860px the date stops being a column and becomes one inline
     line above the entry — a 1.7rem year stacked over sixty rows is a
     great deal of vertical space for information read at a glance. */
  .rec-item { grid-template-columns: 1fr; gap: var(--space-2); }
  .rec-date { display: flex; align-items: baseline; gap: var(--space-3); }
  .rec-y { font-size: 1.1rem; }
  .rec-y + .rec-d { margin-top: 0; }

  .ladder { padding-inline-start: var(--space-6); }
  .rung { grid-template-columns: 1fr; gap: var(--space-3); }
  .rung::before { inset-inline-start: calc(-1 * var(--space-6)); }

  .up-item { grid-template-columns: 1fr; gap: var(--space-2); }

  .contact-list > div { grid-template-columns: 1fr; gap: var(--space-1); }
}

@media (max-width: 520px) {
  :root {
    --size-body: 1rem;
    --space-10: 5rem;
    --space-11: 6rem;
  }
  .container { padding-inline: var(--space-5); }
  .rec-name, .up-name { font-size: 1.05rem; }
  .footer .container { flex-direction: column; align-items: flex-start; }
}


/* ═══ 8. PREFERENCES ═════════════════════════════════════════ */

/* Reduced motion keeps the design — it removes the travel, not the
   craft. Content is fully visible and nothing depends on animation. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .js [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }
  .hero-scroll::after { animation: none; opacity: 0.6; transform: scaleY(1); }
}

/* Print / save-as-PDF. This page is plausibly printed or exported for
   auditions and applications, so it has to survive the trip.

   The colour fix is done at the TOKEN level: remapping the semantic
   tokens corrects every heading, label, caption and rule at once.
   Without it the whole palette is near-white on white paper — the
   printout comes out effectively blank. */
@media print {
  :root {
    --surface-page:   #fff;
    --surface-raised: #fff;
    --surface-card:   #fff;
    --surface-well:   #fff;

    --text-primary:   #000;
    --text-secondary: #1a1a1a;
    --text-muted:     #333;
    --text-faint:     #555;

    --accent:         #6b5520;
    --accent-dim:     #6b5520;

    --border-subtle:  #ccc;
    --border-strong:  #999;
    --border-accent:  #999;

    --shadow-soft: none;
    --shadow-lift: none;
    --shadow-frame: none;
  }

  body { background: #fff; color: #000; }

  .nav, .hero-scroll, .skip-link { display: none; }

  .js [data-reveal] { opacity: 1 !important; transform: none !important; clip-path: none !important; }

  /* Keep related content together across page breaks */
  .rec-item, .rung, .up-item, figure { break-inside: avoid; }
  h1, h2, h3, h4 { break-after: avoid; }

  .hero { min-height: 0; }

  /* On paper the full-bleed escape fights the margins, and the spine and
     nodes are ink for nothing. */
  .rec-plate { width: auto; margin-inline: 0; }
  .rec-plate picture::after { display: none; }  /* the page-coloured feather */
  .ladder { padding-inline-start: 0; }
  .ladder::before, .rung::before { display: none; }

  a { text-decoration: underline; }
}
