/* Cheshire Saxophone Quartet — site stylesheet.

   The design-system components (Panel, Button, SectionHeading, Eyebrow, GoldRule,
   Badge, MemberCard, RepertoireList, NavBar) are React components that write inline
   styles. This file is their hand-translation to plain CSS classes, so the site ships
   as static HTML with no build step and no runtime framework. Component names are kept
   in the class names (.panel, .btn, .member-card …) so the two stay traceable.

   Tokens live in styles/tokens.css, linked alongside this file from the page.
   All values come from the tokens — do not hard-code a colour or a size here. The few
   exceptions are translucent tints of brand colours that have no token (glows, the
   repertoire backlight); each is marked where it appears.

   Hover effects live inside @media (hover: hover) so they never stick after a tap on
   a touch screen. Every one also answers :focus-visible, so keyboard users see them. */


/* ------------------------------------------------------------------ base ---- */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-primary);
  font: var(--type-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: var(--interactive-gold); text-decoration: none; }
a:hover { color: var(--interactive-gold-hover); }

a:focus-visible,
button:focus-visible { outline: none; box-shadow: var(--focus-ring); }

::selection { background: var(--emerald-700); color: var(--parchment); }

img { max-width: 100%; }

/* Visible only when tabbed to, which is the point: the first thing a keyboard user
   meets should be a way past the navigation. */
.skip-link {
  position: absolute; left: var(--space-4); top: var(--space-4);
  z-index: 50;
  padding: var(--space-3) var(--space-5);
  background: var(--surface-deep);
  border: var(--frame-gold);
  border-radius: var(--radius-xs);
  color: var(--gold-300);
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  transform: translateY(-200%);
  transition: transform var(--dur-base) var(--ease-out);
}
.skip-link:focus { transform: translateY(0); }

/* Anchored sections land clear of the sticky bar. site.js measures the bar and sets
   --nav-h; the fallback covers the page before (or without) the script. */
[id] { scroll-margin-top: var(--nav-h, 96px); }

/* ------------------------------------------------------- page decoration ---- */

/* `clip`, not `hidden`: both stop the watercolour bleed from widening the page, but
   `hidden` also turns .page into a scroll container, which quietly stops the sticky
   navigation from sticking. Browsers without `clip` keep `hidden`. */
.page { position: relative; overflow: hidden; overflow: clip; background: var(--surface-page); }

/* The one indulgence: the watercolour ground fades in on first paint. */
.page__veil {
  position: absolute; inset: 0;
  pointer-events: none;
  background: var(--wash-vignette);
  animation: csq-veil var(--dur-veil) var(--ease-out) both;
}

.page__horizon {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 60vh;
  pointer-events: none;
  background: var(--wash-emerald-veil);
}

/* Watercolour splats bleed past the gutters; they never sit behind reading text. */
.splat {
  position: absolute;
  pointer-events: none;
  background: no-repeat center / contain;
}
.splat--soft  { opacity: 0.4; }
.splat--faint { opacity: 0.26; }

.splat--stars { background-image: url("../assets/splat-stars.png"); }
.splat--cloud { background-image: url("../assets/splat-cloud.png"); }
.splat--bloom { background-image: url("../assets/splat-bloom.png"); }
/* WebP for browsers that understand image-set(); the declarations above remain the
   fallback for those that do not, which simply ignore these. */
.splat--stars { background-image: image-set(url("../assets/splat-stars.webp") type("image/webp"), url("../assets/splat-stars.png") type("image/png")); }
.splat--cloud { background-image: image-set(url("../assets/splat-cloud.webp") type("image/webp"), url("../assets/splat-cloud.png") type("image/png")); }
.splat--bloom { background-image: image-set(url("../assets/splat-bloom.webp") type("image/webp"), url("../assets/splat-bloom.png") type("image/png")); }

@keyframes csq-veil { from { opacity: 0; } to { opacity: 1; } }
@keyframes csq-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

/* --------------------------------------------------------------- layout ---- */

.container { max-width: var(--container-max); margin: 0 auto; padding-inline: var(--gutter-lg); }
.container--narrow { max-width: var(--container-narrow); }

.section { position: relative; padding-block: var(--section-y); }
.section--members { padding-top: 0; }
.section--deep {
  position: relative;
  background: var(--surface-deep);
  border-top: var(--frame-hairline);
  border-bottom: var(--frame-hairline);
  overflow: hidden;
}

/* Entrance: a fade plus a rise, as the design system asks. The craft is in the timing,
   not in inventing a new direction — things that belong to one another arrive one after
   another rather than all at once, and the rise is shorter for big type than for cards,
   so a row of four does not move as one slab.

   The resting state is *visible*. site.js arms an element (hiding it) only when it
   starts below the fold, motion is welcome, and animation frames are actually running;
   revealing is just removing the class again. One class, one source of truth — so the
   page reads normally whenever the script is absent, slow, or bows out. */
.reveal {
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
/* Arming must be instant: a delayed fade *out* would show the element flashing first. */
.reveal.is-armed {
  opacity: 0;
  transform: translateY(var(--reveal-rise, 12px));
  transition-duration: 0s;
  transition-delay: 0s;
}

/* Headings travel less than the things they introduce. */
.section-intro.reveal, .about.reveal { --reveal-rise: 8px; }

/* A row arrives left to right, a beat apart. */
.members > li:nth-child(1), .contact > li:nth-child(1) { --reveal-delay: 0ms; }
.members > li:nth-child(2), .contact > li:nth-child(2) { --reveal-delay: 80ms; }
.members > li:nth-child(3) { --reveal-delay: 160ms; }
.members > li:nth-child(4) { --reveal-delay: 240ms; }
.members > li { --reveal-rise: 16px; }

/* A gold hairline that draws in from the centre — the shared hover underline for text
   links. Set on the element; hover rules below widen it. */
.draw-line,
.navbar__link,
.contact__value,
.works__more a,
.footer__top {
  background-image: linear-gradient(var(--gold-300), var(--gold-300));
  background-repeat: no-repeat;
  background-position: 50% 100%;
  background-size: 0 1px;
  transition: var(--transition-control), background-size var(--dur-base) var(--ease-out);
}

/* -------------------------------------------------------------- NavBar ------ */

.navbar {
  position: sticky; top: 0; z-index: 20;
  width: 100%;
  padding: var(--space-5) var(--gutter-lg);
  background: var(--wash-protect-top);
  border-bottom: var(--frame-hairline);
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap;
  gap: var(--space-4) var(--space-8);
  transition: padding var(--dur-base) var(--ease-out);
}
/* Once the page scrolls, the protection gradient firms up into the navy scrim the
   design system prescribes for a fixed bar, so links stay legible over any section. */
.navbar::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: var(--surface-scrim);
  -webkit-backdrop-filter: var(--blur-veil);
  backdrop-filter: var(--blur-veil);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}
.navbar.is-stuck::before,
.navbar.is-open::before { opacity: 1; }
.navbar.is-stuck { padding-block: var(--space-4); }

.navbar__brand {
  display: flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--text-display);
  letter-spacing: var(--tracking-tight);
  transition: var(--transition-control);
}
.navbar__brand:hover { color: var(--gold-200); }

.navbar__nav { display: flex; align-items: center; gap: var(--space-8); flex-wrap: wrap; justify-content: center; }

.navbar__link {
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-primary);
  padding-block: 6px;
}
.navbar__link[aria-current="location"] { color: var(--gold-300); background-size: 100% 1px; }

/* The menu button only appears once site.js has enhanced the bar, on small screens. */
.navbar__toggle {
  display: none;
  align-items: center; gap: var(--space-3);
  height: var(--control-h-sm);
  padding: 0 var(--space-3);
  border: var(--border-hairline) solid var(--line-gold);
  border-radius: var(--radius-xs);
  background: transparent;
  color: var(--gold-300);
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  cursor: pointer;
  transition: var(--transition-control);
}
.navbar__toggle-bars { position: relative; display: block; width: 18px; height: 11px; }
.navbar__toggle-bars > span {
  position: absolute; left: 0; right: 0; height: 1px;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-out), opacity var(--dur-fast) var(--ease-standard), top var(--dur-base) var(--ease-out);
}
.navbar__toggle-bars > span:nth-child(1) { top: 0; }
.navbar__toggle-bars > span:nth-child(2) { top: 5px; }
.navbar__toggle-bars > span:nth-child(3) { top: 10px; }
.navbar.is-open .navbar__toggle-bars > span:nth-child(1) { top: 5px; transform: rotate(45deg); }
.navbar.is-open .navbar__toggle-bars > span:nth-child(2) { opacity: 0; }
.navbar.is-open .navbar__toggle-bars > span:nth-child(3) { top: 5px; transform: rotate(-45deg); }

/* ----------------------------------------------------------- SectionHeading -- */

.heading { display: flex; flex-direction: column; gap: var(--space-3); }
.heading--center { text-align: center; align-items: center; }

.heading__title {
  font: var(--type-h2);
  color: var(--text-display);
  margin: 0;
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}
.heading__title--display { font: var(--type-display); }

.heading__intro {
  font: var(--type-body);
  color: var(--text-primary);
  margin: 0;
  max-width: var(--container-narrow);
  text-wrap: pretty;
}

/* ---------------------------------------------------------------- Eyebrow --- */

.eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  display: block;
  color: var(--text-subtle);
}
.eyebrow--muted { color: var(--text-muted); }

/* --------------------------------------------------------------- GoldRule --- */

.rule        { width: 100%; height: 0; border-top: var(--border-hairline) solid var(--line-gold); }
.rule--short { width: var(--space-16); border-top: var(--border-rule) solid var(--line-gold-strong); }

.rule--double > span {
  display: block; height: 0;
  border-top: var(--border-hairline) solid var(--line-gold-strong);
}
.rule--double > span + span { margin-top: var(--frame-double-offset); }

/* ------------------------------------------------------------------ Panel --- */

.panel {
  padding: var(--card-pad);
  border-radius: var(--radius-xs);
  transition: var(--transition-control);
  background: transparent;
  border: var(--frame-gold);
}
.panel--lg { padding: var(--card-pad-lg); }
.panel--emerald { border: var(--frame-emerald); background: var(--surface-emerald); }

/* Panels brighten their border and gain a glow. They never lift or scale. */
.panel--interactive:focus-visible,
.panel--interactive:focus-within {
  border-color: var(--line-gold-strong);
  box-shadow: var(--glow-gold);
}
a.panel { display: block; color: inherit; }

/* ----------------------------------------------------------------- Button --- */

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  height: var(--control-h);
  padding: 0 var(--control-pad-x);
  border: var(--border-hairline) solid transparent;
  border-radius: var(--radius-xs);
  cursor: pointer;
  text-decoration: none;
  transition: var(--transition-control);
  background: transparent;
  white-space: nowrap;
}
.btn--sm { height: var(--control-h-sm); padding: 0 var(--space-4); }
.btn--lg { height: var(--control-h-lg); padding: 0 var(--space-8); font-size: var(--text-xs); }

.btn--primary { background: var(--interactive-gold); color: var(--text-on-gold); border-color: transparent; }
.btn--primary:active { background: var(--interactive-gold-press); transform: translateY(1px); }

.btn--secondary { background: transparent; color: var(--gold-300); border-color: var(--line-gold); }
.btn--secondary:active { transform: translateY(1px); }

/* Quieter than secondary: no frame at all, for the companion to a gold button. */
.btn--ghost { background: transparent; color: var(--gold-300); border-color: transparent; }
.btn--ghost:active { transform: translateY(1px); }

.btn--primary:focus-visible   { background: var(--interactive-gold-hover); }
.btn--secondary:focus-visible { color: var(--gold-200); border-color: var(--line-gold-strong); }
.btn--ghost:focus-visible     { color: var(--gold-200); }

/* ------------------------------------------------------------------ Badge --- */

/* The design system's "available" tone is --status-available (--emerald-400). On the
   emerald ticket panel that lands at 3.94:1, which is under AA for 12px bold text, so
   the label steps one stop lighter to --emerald-300 (4.84:1). The border keeps the
   status colour, so the chip still reads as the same device. */
.badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  padding: 4px var(--space-3);
  border-radius: var(--radius-xs);
  border: var(--border-hairline) solid;
  background: transparent;
  color: var(--emerald-300);
  border-color: var(--status-available);
}
.badge--gold { color: var(--gold-300); border-color: var(--line-gold); }

/* ------------------------------------------------------------------- hero --- */

.hero { position: relative; padding-block: var(--space-24) var(--space-20); }
.hero__inner { position: relative; display: flex; flex-direction: column; align-items: center; }

/* The hero assembles itself on load, a beat between each part. The title is not delayed:
   it is the largest thing on the page and the one the browser times paint by.

   Gated on .js-motion, which site.js adds only after seeing two real animation frames —
   an entrance that never runs would otherwise leave the hero stranded at opacity 0. */
.js-motion .hero .heading__title { animation: csq-rise var(--dur-slow) var(--ease-out) both; }
.js-motion .hero .rule--double   { animation: csq-rise var(--dur-slow) var(--ease-out) 90ms both; }
.js-motion .hero .heading__intro { animation: csq-rise var(--dur-slow) var(--ease-out) 170ms both; }
.js-motion .hero__billing        { animation: csq-rise var(--dur-slow) var(--ease-out) 280ms both; }
/* The page title, its double rule and the line beneath are one block, but that line
   needs air under the rule or it reads as a caption clinging to it. */
.hero .heading__intro { margin-top: var(--space-6); }

.hero__splat--stars { top: -150px; right: -300px; width: 760px; height: 475px; }
.hero__splat--cloud { top: 210px; left: -320px; width: 560px; height: 270px; }

/* The concert billing: the hero's reason for existing while a concert is on sale.
   Styled as a programme bill — the double gold frame, the date set large in the display
   serif — rather than as a web banner. Scoped to .hero__ so that deleting the block from
   the markup leaves nothing behind. */
.hero__billing {
  position: relative;
  max-width: 620px;
  margin: var(--space-16) auto 0;
  padding: var(--space-8) var(--space-8) var(--space-10);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  text-align: center;
  border: var(--frame-double);
  border-radius: var(--radius-xs);
  background: var(--surface-scrim);
  -webkit-backdrop-filter: var(--blur-veil);
  backdrop-filter: var(--blur-veil);
}
.hero__billing::before {
  content: "";
  position: absolute; inset: var(--frame-double-offset);
  border: var(--border-hairline) solid var(--line-gold);
  pointer-events: none;
}

.hero__billing-label { margin: 0; }

.hero__billing-name {
  margin: 0;
  font: var(--type-h2);
  font-size: var(--text-3xl);
  color: var(--text-display);
  letter-spacing: var(--tracking-tight);
  text-wrap: balance;
}

.hero__billing-when { margin: 0; display: flex; flex-direction: column; gap: var(--space-2); }

/* The date is the loudest thing in the block after the quartet's own name. */
.hero__billing-date {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  line-height: var(--leading-snug);
  color: var(--text-primary);
}
.hero__billing-meta {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
  gap: var(--space-2);
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-muted);
}
/* A drawn dot, so the separator is not a character a screen reader reads out. */
.hero__dot {
  width: 3px; height: 3px;
  border-radius: var(--radius-pill);
  background: var(--line-gold-strong);
}

.hero__billing-taster {
  margin: 0;
  max-width: 46ch;
  font: var(--type-body-sm);
  color: var(--text-primary);
  text-wrap: pretty;
}

.hero__actions {
  margin-top: var(--space-3);
  display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3);
}

/* ------------------------------------------------------- viewport scaling ---- */

/* The design system fixes its type ramp in pixels and its container at 1180px. On a
   large display that leaves a third of the window empty and the small end of the ramp —
   12px eyebrows, 15px sub-labels — looking shrunken, and pixels also mean the page
   ignores a reader who has set a larger default font size.

   The ramp is restated here in rem at exactly the values tokens.css declares (at the
   usual 16px root, 0.75rem *is* 12px), so nothing changes on an ordinary screen. What it
   buys is two things the design system cannot express: the page now honours the reader's
   own font size, and it can grow a step on a large screen.

   Keep these in step with the ramp in tokens.css; the comments are the pixel equivalents. */
:root {
  --text-3xs: 0.6875rem;  /* 11px */
  --text-2xs: 0.75rem;    /* 12px */
  --text-xs:  0.8125rem;  /* 13px */
  --text-sm:  0.9375rem;  /* 15px */
  --text-base: 1.0625rem; /* 17px */
  --text-lg:  1.1875rem;  /* 19px */
  --text-xl:  1.375rem;   /* 22px */
  --text-2xl: 1.6875rem;  /* 27px */
  --text-3xl: 2.125rem;   /* 34px */
  --text-4xl: 2.75rem;    /* 44px */
  --text-5xl: 3.5rem;     /* 56px */
  --text-6xl: 4.5rem;     /* 72px */
  --text-7xl: 5.75rem;    /* 92px */
}

/* Two gentle steps, not a free-flowing scale: the proportions of the design hold, the
   whole thing simply sits better in a big window. Percentages rather than pixels so a
   reader's own font size is still respected, scaled rather than overridden. */
@media (min-width: 1500px) {
  :root { --container-max: 1320px; --container-narrow: 820px; }
  html { font-size: 106.25%; }          /* 17px at a default 16px root */
}
@media (min-width: 1900px) {
  :root { --container-max: 1440px; --container-narrow: 880px; --section-y: var(--space-32); }
  html { font-size: 112.5%; }           /* 18px */
}

/* ------------------------------------------------------------------ about --- */

/* The wash keeps to the right margin; the paragraph sits in a 760px column, so the
   optical centre stays clear. */
.about__splat { top: -110px; right: -360px; width: 560px; height: 270px; }

.about { position: relative; display: flex; flex-direction: column; align-items: center; gap: var(--space-8); }
.about__body {
  margin: 0;
  text-align: center;
  font: var(--type-editorial);
  color: var(--text-primary);
  text-wrap: pretty;
}

/* ---------------------------------------------------------------- members --- */

.section-intro {
  position: relative;
  display: flex; flex-direction: column; align-items: center; gap: var(--space-3);
  margin-bottom: var(--space-16);
}
.section-intro--tight { margin-bottom: var(--space-10); }
.section-intro--mid   { margin-bottom: var(--space-12); }


.members__splat { top: 40px; left: -340px; width: 760px; height: 680px; }

/* One, two or four across — never three, which would strand the fourth player. */
.members {
  position: relative;
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--space-6);
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 560px)  { .members { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .members { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.members > li { display: grid; }
.members .panel { height: 100%; }

.member-card { margin: 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-4); }

.member-card__photo {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  border: var(--frame-double);
  border-radius: var(--radius-none);
  background: var(--surface-raised);
}
.member-card__photo img {
  display: block;
  width: 100%; height: 100%;
  object-fit: cover;
}
/* The inner rule of the programme-cover double frame, drawn in on hover. */
.member-card__photo::after {
  content: "";
  position: absolute; inset: 0;
  border: var(--border-hairline) solid var(--gold-300);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out), inset var(--dur-slow) var(--ease-out);
}

.member-card__caption { text-align: center; }

/* Two lines are reserved for the name so the sub-label, sax drawing and bio share
   one baseline across a row of cards. */
.member-card__name {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  color: var(--text-primary);
  letter-spacing: var(--tracking-tight);
  line-height: 1.2;
  min-height: 2.4em;
  display: flex; align-items: center; justify-content: center;
  text-wrap: balance;
  transition: color var(--dur-base) var(--ease-out);
}
.member-card__instrument {
  margin-top: var(--space-1);
  font: var(--type-body-sm);
  color: var(--text-muted);
}
.member-card__sax {
  height: 210px; width: auto;
  object-fit: contain; display: block;
  opacity: 0.85;
  transition: filter var(--dur-slow) var(--ease-out), opacity var(--dur-base) var(--ease-out);
}

/* ------------------------------------------------------------- repertoire --- */

.repertoire__splat { right: -220px; bottom: -160px; width: 660px; height: 590px; }

/* A soft pool of light that follows the pointer behind the list, a little way behind it:
   site.js eases the position rather than tracking the cursor exactly. Small and faint on
   purpose — it should be noticed only once you have stopped looking for it. The tints are
   gold-300 and emerald-500 at low alpha; there are no tokens for translucent brand
   colours. */
.backlight {
  position: absolute; inset: 0;
  pointer-events: none;
  background: radial-gradient(
    240px circle at var(--mx, 50%) var(--my, 50%),
    rgba(232, 206, 140, 0.07) 0%,
    rgba(46, 152, 115, 0.045) 45%,
    transparent 70%
  );
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.repertoire.is-lit .backlight { opacity: 1; }

.filters {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--space-3);
  margin-bottom: var(--space-10);
}

/* Every box is the same size: equal columns that long titles cannot widen
   (minmax(0, 1fr), not 1fr), and equal rows sized to the tallest box. */
.works {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--space-3);
  grid-template-columns: minmax(0, 1fr);
  grid-auto-rows: 1fr;
}
@media (min-width: 760px) {
  .works { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.works__item {
  display: flex; flex-direction: column; justify-content: center;
  min-width: 0;
  border: var(--border-hairline) solid var(--line-gold);
  border-radius: var(--radius-xs);
  padding: var(--space-4) var(--space-6);
  text-align: center;
  background: transparent;
  transition: var(--transition-control), background-color var(--dur-base) var(--ease-out);
}
.works__item[hidden] { display: none; }

/* Swapping threads: the outgoing set dims together, the incoming set arrives in order.
   Both are driven by site.js, which falls back to an instant swap when frames are not
   running or the reader has asked for less motion. */
.works__item.is-leaving {
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-standard);
}
.works__item.is-entering {
  animation: csq-rise var(--dur-base) var(--ease-out) both;
  animation-delay: var(--enter-delay, 0ms);
}

.works__title { font: var(--type-editorial); color: var(--text-display); transition: color var(--dur-base) var(--ease-out); }
.works__note  { margin-top: var(--space-1); font: var(--type-body-sm); color: var(--text-muted); }

.works__count { margin: var(--space-6) 0 0; text-align: center; font: var(--type-body-sm); color: var(--text-muted); }

/* The lists are a sample, not the library. This says so plainly, under a gold rule so
   it reads as a note rather than as another entry. */
.works__more {
  max-width: 62ch;
  margin: var(--space-5) auto 0;
  padding-top: var(--space-5);
  border-top: var(--border-hairline) solid var(--line-gold);
  text-align: center;
  font: var(--type-editorial);
  color: var(--text-primary);
  text-wrap: pretty;
}
.works__more a { padding-bottom: 2px; }

/* ---------------------------------------------------------------- concert --- */

.concert__splat { top: -40px; right: -280px; width: 620px; height: 400px; }

.concert {
  position: relative;
  display: grid; gap: var(--space-6); align-items: stretch;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
}
.concert__details { display: flex; flex-direction: column; gap: var(--space-6); }
.concert__details > .badge { align-self: flex-start; }
.concert__field   { display: flex; flex-direction: column; gap: var(--space-2); }
.concert__fields  { display: flex; flex-wrap: wrap; gap: var(--space-6) var(--space-12); }

.concert__title { margin: 0; font: var(--type-h3); color: var(--text-display); }
.concert__value { margin: 0; font: var(--type-editorial); color: var(--text-primary); }
.concert__cause { margin: 0; font: var(--type-body); color: var(--text-primary); text-wrap: pretty; }
.concert__aside { margin: 0; font: var(--type-editorial); font-style: italic; color: var(--emerald-300); }

.tickets {
  display: flex; flex-direction: column; justify-content: center; align-items: flex-start;
  gap: var(--space-6); height: 100%;
}
.tickets__title { margin: 0; font: var(--type-h3); color: var(--text-display); letter-spacing: var(--tracking-tight); }
.tickets__price { margin: 0; font: var(--type-editorial); color: var(--text-display); }
.tickets__body  { margin: 0; font: var(--type-body); color: var(--text-primary); max-width: 34ch; text-wrap: pretty; }

/* ------------------------------------------------------------- recordings --- */

.recordings__splat { top: -90px; right: -260px; width: 640px; height: 310px; }

/* A programme-cover double frame: the outer hairline is the border, the inner one sits
   --frame-double-offset inside it. */
.recordings {
  position: relative;
  max-width: 760px; margin: 0 auto;
  padding: var(--space-12) var(--space-8);
  display: flex; flex-direction: column; align-items: center; gap: var(--space-5);
  text-align: center;
  border: var(--frame-double);
  border-radius: var(--radius-xs);
  transition: var(--transition-control);
}
.recordings::before {
  content: "";
  position: absolute; inset: var(--frame-double-offset);
  border: var(--border-hairline) solid var(--line-gold);
  pointer-events: none;
  transition: border-color var(--dur-base) var(--ease-out);
}

.recordings__wave {
  display: block;
  width: min(100%, 440px); height: auto;
  aspect-ratio: 512 / 72;
  margin-bottom: var(--space-3);
  fill: none;
  stroke: var(--gold-500);
  stroke-width: 5.05;
  stroke-linecap: round;
  opacity: 0.5;
  transition: opacity var(--dur-slow) var(--ease-out), filter var(--dur-slow) var(--ease-out);
}

.recordings__title {
  margin: 0;
  font-family: var(--font-display);
  font-style: italic;
  font-size: var(--text-5xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--text-display);
}
.recordings__body {
  margin: 0 0 var(--space-3);
  max-width: 46ch;
  font: var(--type-body);
  color: var(--text-primary);
  text-wrap: pretty;
}

/* ---------------------------------------------------------------- contact --- */

.contact__splat { left: -260px; bottom: -60px; width: 600px; height: 290px; }

.contact {
  position: relative;
  display: grid; gap: var(--space-5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  max-width: 740px; margin: 0 auto;
  list-style: none; padding: 0;
}
.contact > li { display: grid; }
/* Two classes so it outranks `a.panel { display: block }`. */
.panel.contact__card { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }
.contact__inner { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.contact__label {
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--text-muted);
}
.contact__value {
  align-self: flex-start;
  padding-bottom: 2px;
  font-family: var(--font-editorial);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--gold-300);
  overflow-wrap: anywhere;
}
/* A pen-line arrow in the Lucide weight the design system names for small glyphs. */
.contact__arrow {
  flex: none;
  width: 22px; height: 22px;
  fill: none;
  stroke: var(--gold-500);
  stroke-width: 1.5;
  stroke-linecap: round; stroke-linejoin: round;
  opacity: 0.45;
  transform: translateX(-4px);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}

/* ----------------------------------------------------------------- footer --- */

.footer__splat { bottom: -150px; left: -380px; width: 620px; height: 300px; }

.footer { position: relative; padding-block: var(--space-12) var(--space-10); }
.footer__row {
  margin-top: var(--space-8);
  display: flex; flex-wrap: wrap; gap: var(--space-4) var(--space-8);
  align-items: baseline; justify-content: space-between;
}
.footer__name {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  color: var(--text-display);
}
.footer__region,
.footer__top {
  margin: 0;
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--text-muted);
}
.footer__top { padding-bottom: 4px; }
.footer__name { margin-right: auto; }

/* ----------------------------------------------------------------- hover ---- */

/* Pointer-only. Each effect stays inside the brand: text goes lighter, gold hairlines
   draw in, panels brighten their border and glow. Nothing lifts, scales or bounces. */
@media (hover: hover) {
  .navbar__link:hover,
  .contact__card:hover .contact__value,
  .works__more a:hover,
  .footer__top:hover { background-size: 100% 1px; }
  .navbar__link:hover,
  .works__more a:hover { color: var(--gold-200); }
  .footer__top:hover  { color: var(--gold-200); }

  .navbar__toggle:hover { color: var(--gold-200); border-color: var(--line-gold-strong); box-shadow: var(--glow-gold); }

  .btn--primary:hover   { background: var(--interactive-gold-hover); color: var(--text-on-gold); box-shadow: var(--glow-gold); }
  .btn--secondary:hover { color: var(--gold-200); border-color: var(--line-gold-strong); box-shadow: var(--glow-gold); }
  .btn--ghost:hover     { color: var(--gold-200); border-color: var(--line-gold); }

  .panel--interactive:hover { border-color: var(--line-gold-strong); box-shadow: var(--glow-gold); }

  .member:hover .member-card__sax {
    opacity: 1;
    filter: drop-shadow(0 0 18px rgba(201, 168, 96, 0.55)) brightness(1.12); /* gold-500 glow */
  }

  .member:hover .member-card__name { color: var(--gold-300); }
  .member:hover .member-card__photo::after { opacity: 0.85; inset: var(--frame-double-offset); }

  .works__item:hover {
    border-color: var(--line-gold-strong);
    box-shadow: var(--glow-gold);
    background-color: rgba(232, 206, 140, 0.04); /* gold-300 wash */
  }
  .works__item:hover .works__title { color: var(--gold-200); }

  .recordings:hover { border-color: var(--gold-300); box-shadow: var(--glow-gold); }
  .recordings:hover::before { border-color: var(--line-gold-strong); }
  .recordings:hover .recordings__wave { opacity: 0.95; filter: drop-shadow(0 0 10px rgba(201, 168, 96, 0.45)); }

  .contact__card:hover .contact__arrow { opacity: 1; transform: none; }
}

/* Keyboard focus gets the same cues as hover. */
.contact__card:focus-visible .contact__arrow { opacity: 1; transform: none; }
.contact__card:focus-visible .contact__value { background-size: 100% 1px; }
.navbar__link:focus-visible,
.works__more a:focus-visible,
.footer__top:focus-visible { background-size: 100% 1px; }

/* ------------------------------------------------------------ small screens -- */

@media (max-width: 900px) {
  :root { --section-y: var(--section-y-sm); --gutter-lg: var(--gutter); }


  .heading__title--display { font-size: var(--text-4xl); }

  .member-card__sax { height: 150px; }

  .recordings { padding: var(--space-10) var(--space-6); }
  .recordings__title { font-size: var(--text-4xl); }

  .hero__billing { margin-top: var(--space-12); padding: var(--space-6) var(--space-5) var(--space-8); }
  .hero__billing-name { font-size: var(--text-2xl); }
  .hero__billing-date { font-size: var(--text-xl); }
}

/* Without the script the links wrap under the name; with it, they fold into a menu. */
@media (max-width: 820px) {
  .navbar { justify-content: center; padding-block: var(--space-4); }
  .navbar__nav { gap: var(--space-2) var(--space-5); }

  /* Name and menu button share one row; a tighter gap keeps them together on a phone. */
  .navbar--enhanced { justify-content: space-between; column-gap: var(--space-3); }
  .navbar--enhanced .navbar__toggle { display: inline-flex; }
  .navbar--enhanced .navbar__nav {
    display: none;
    width: 100%;
    flex-direction: column; align-items: stretch; gap: 0;
    padding-bottom: var(--space-2);
  }
  .navbar--enhanced.is-open .navbar__nav { display: flex; }
  .navbar--enhanced .navbar__link {
    padding: var(--space-4) 0;
    text-align: center;
    border-top: var(--frame-hairline);
    background-size: 0 0;
  }
  .navbar--enhanced .navbar__link[aria-current="location"] { background: rgba(232, 206, 140, 0.06); } /* gold-300 wash */
}

@media (max-width: 559px) {
  /* One card per row: no neighbour to line up with, so the name needs no reserved line. */
  .member-card__name { min-height: 0; }
}

/* The narrowest phones: the button keeps its name for screen readers but shows only
   the three bars, so it still fits beside the quartet's name. */
@media (max-width: 380px) {
  .navbar__toggle-label {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

@media (max-width: 480px) {
  .navbar__brand { font-size: var(--text-base); }
  .heading__title { font-size: var(--text-3xl); }
  .heading__title--display { font-size: var(--text-3xl); }
  .filters { gap: var(--space-2); }
  /* Side by side the two buttons would each be too narrow to read. */
  .hero__actions { flex-direction: column; align-self: stretch; }
  .hero__actions .btn { width: 100%; }
  .recordings { padding: var(--space-8) var(--space-5); }
  .recordings__title { font-size: var(--text-3xl); }
  .footer__row { flex-direction: column; align-items: center; text-align: center; }
  .footer__name { margin-right: 0; }
}

/* Nothing bounces, nothing springs, nothing loops — and for readers who ask for
   stillness, nothing moves at all. */
@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;
  }
  .reveal, .reveal.is-armed { opacity: 1; transform: none; }
}
