/* Dale Stuart — Art Quilts · site stylesheet
   A cinematic gallery with a dark/light toggle: a data-theme on <html> flips the
   token sets below (dark is the default, remembered per visitor via the header
   toggle). The full-bleed hero stays dark-imaged in BOTH skins — a dark cinematic
   band over whichever page ground — so its ink + type use fixed --hero-* tokens. */

:root {
  /* fonts + structure — theme-independent */
  /* Adobe Fonts (kit azi4eex), loaded from use.typekit.net — NOT self-hosted. Adobe's
     licence covers serving from their CDN via a web project, not redistributing the woff2
     files, so the prototype's local copies were removed when the site went public.
     The family names are Adobe's own, lowercase-hyphenated — "New Spirit" would silently
     fall through to Georgia. Kit carries new-spirit 300/300i/400/500/600/700 and
     freight-neo-pro 400/700, which is exactly what this stylesheet asks for. */
  --serif: "new-spirit", georgia, serif;
  --text: "freight-neo-pro", georgia, serif;

  /* ---- DARK skin (default) — warm gallery, borrowed from P6's stage ---- */
  --ink: #160f14;
  --ink-2: #241b21;
  --ivory: #f2ede4;
  --ivory-dim: #b8b2a6;
  --line: rgba(242, 237, 228, 0.14);
  --num-stroke: rgba(242, 237, 228, 0.18);
  --surface: rgba(22, 15, 20, 0.86);      /* header / pill glass */
  --faint: rgba(242, 237, 228, 0.4);      /* footer fine-print */
  --prose: #cfc9bd;                        /* statement body */
  --sel: rgba(224, 145, 47, 0.4);          /* text selection */

  /* the hero is dark-imaged in every skin, so its ground + type never follow the
     theme — fixed dark ink, fixed light text */
  --hero-ink: #160f14;
  --hero-text: #f2ede4;
  --hero-text-dim: #b8b2a6;

  --burgundy: #6d1f36;                     /* light-skin chrome accent (from P9) */
  --gold: #e0912f;                         /* brand gold = the dark drop-cap / --p1 */
  --title: var(--gold);                    /* quilt-title colour — gold on dark (matches the drop cap) */
  --underline: #d4541f;                    /* uniform hover-underline — rust-orange (quilt 01's), set apart from the gold titles */
}

/* ---- LIGHT skin — warm linen / burgundy (values borrowed from the shared
   theme.css so this and P9 stay one family). Chrome goes uniform burgundy; the
   per-accent glow becomes a neutral grounding shadow (see .row-stage img /
   .hero-fig img overrides below). ---- */
[data-theme="light"] {
  --ink: #f4ede6;
  --ink-2: #ede1d7;
  --ivory: #241a1c;
  --ivory-dim: #6f5a5e;
  --line: rgba(36, 24, 27, 0.14);
  --num-stroke: rgba(36, 24, 27, 0.30);    /* darker ghost numerals (was 0.16 — too faint) */
  --surface: rgba(249, 245, 240, 0.9);
  --faint: rgba(36, 24, 27, 0.45);
  --prose: #40332f;
  --sel: rgba(109, 31, 54, 0.18);
  --title: var(--burgundy);                /* quilt-title colour — burgundy on light (matches the drop cap) */
  --underline: var(--gold);                /* hover-underline — gold, under the burgundy titles */
}

* { margin: 0; padding: 0; box-sizing: border-box; }
/* Lenis drives scrolling now — native smooth-behavior must stay off */
html { scroll-behavior: auto; }

/* ---- Lenis baseline (recommended) ---- */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }

body {
  background-color: var(--ink);   /* flat — no gradient, so no banding to dither */
  color: var(--ivory);
  font-family: var(--text);
  font-size: 17px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
}

img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--sel); }

/* ---------- header ---------- */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 1.5rem clamp(1.5rem, 5vw, 4rem);
  transition: background 0.4s, padding 0.4s;
}
.site-header.scrolled {
  background: var(--surface);
  backdrop-filter: blur(10px);
  padding-top: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}
/* Light skin: header type is dark, so it can't float transparent over the dark
   hero — give it the glass surface from the top, on every page. */
[data-theme="light"] .site-header {
  background: var(--surface);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.logo { font-family: var(--serif); font-weight: 600; font-size: 1.5rem; }
.logo em {
  font-weight: 300;
  font-style: italic;
  font-size: 0.98rem;
  color: var(--ivory-dim);
  margin-left: 0.5rem;
}
nav { display: flex; gap: 1.9rem; align-items: center; }
nav a {
  font-family: var(--serif);
  font-size: 0.95rem;
  color: var(--ivory-dim);
  letter-spacing: 0.04em;
  transition: color 0.3s;
}
nav a:hover, nav a.active { color: var(--ivory); }
/* skin toggle — a quiet pill in the nav */
.theme-toggle {
  font-family: var(--serif);
  font-size: 0.86rem;
  letter-spacing: 0.04em;
  color: var(--ivory-dim);
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.28rem 0.9rem;
  cursor: pointer;
  transition: color 0.3s, border-color 0.3s;
}
.theme-toggle:hover { color: var(--ivory); border-color: var(--ivory-dim); }

/* Narrow screens: keep the header to ONE line.
   At full size the logo, three nav links and the skin toggle need ~505px of a phone's
   390px, so the flex row wrapped to two or three lines — and because the header is
   FIXED, the extra lines silently covered the top of every page: the "About Me" heading
   and the project pages' back-link + index number all disappeared underneath it. (Home
   got away with it only because its full-bleed hero has nothing up there to hide.)
   So the type shrinks, the decorative tagline goes, and the gaps tighten — then the two
   text-first pages get a top padding that clears the shortened bar. Sizes are stepped
   twice because "one line" has to hold down to a 320px iPhone SE. */
@media (max-width: 720px) {
  .site-header { padding: 0.85rem 1.1rem; gap: 0.6rem; }
  /* nowrap + no-shrink: without it the flex row breaks the logo itself, stacking
     "Dale" over "Stuart" and putting the bar back to two lines at ~320px */
  .logo { font-size: 1.1rem; white-space: nowrap; flex: 0 0 auto; }
  .logo em { display: none; }        /* "art quilts" — decorative here; the pages say it */
  nav { flex: 0 1 auto; }
  nav { gap: 1rem; }
  nav a { font-size: 0.85rem; letter-spacing: 0.02em; }
  .theme-toggle { font-size: 0.76rem; padding: 0.22rem 0.66rem; letter-spacing: 0.02em; }
  /* clear the one-line bar (~52px) with room to breathe */
  .project { padding-top: 4.4rem; }
  .about   { padding-top: 4.8rem; }
  .contact { padding-top: 4.8rem; }
  .about-head,
  .contact-head { padding-bottom: 2rem; }
}
/* iPhone-SE class. Sized so logo + nav + toggle still leave the toggle clear of the
   right edge at 320px — at the 720px step's sizes the row filled the full width and the
   pill sat flush against the bezel. */
@media (max-width: 400px) {
  .site-header { padding: 0.8rem 0.8rem; gap: 0.5rem; }
  .logo { font-size: 0.98rem; }
  nav { gap: 0.56rem; }
  nav a { font-size: 0.74rem; letter-spacing: 0; }
  .theme-toggle { font-size: 0.68rem; padding: 0.18rem 0.46rem; }
}

/* palette ribbon — a thin bar pinned to the very top edge. JS fills it with
   equal WHOLE colour segments (flex:1, so they divide the width exactly and
   nothing clips), in the CENTERED quilt's palette. Its intensity is driven
   per-frame by how centred the quilt is: on DARK it fades through black
   (filter:brightness), on LIGHT it fades opacity (no black strip) — so it's at
   full colour only when a quilt is ~centred, and the palette swap is hidden in
   the dip. No CSS transition — the smoothness comes from the per-frame drive. */
.palette-ribbon {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 4px;
  z-index: 60;
  display: flex;
  opacity: 0;
  pointer-events: none;
}
.palette-ribbon i { flex: 1 1 0; }

/* ---------- hero (dark-imaged in both skins) ---------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  isolation: isolate;
}
.hero .bg {
  position: absolute;
  inset: -4%;
  background: url("assets/img/cover-what-was-earth-like.jpg") center 58% / 116% auto no-repeat;
  background-color: #16100e;
  z-index: -2;
  transform: scale(1.04);
  animation: heroDrift 22s ease-in-out infinite alternate;
}
@keyframes heroDrift { to { transform: scale(1.12) translateY(-1.5%); } }
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  /* dark scrim only — keeps the hero text legible; fixed dark (not --ink) so the
     hero stays a dark band even under the light skin */
  background:
    linear-gradient(180deg, rgba(22,15,20,0.92) 0%, rgba(22,15,20,0.15) 34%, rgba(22,15,20,0.82) 78%, var(--hero-ink) 100%);
}
.hero-inner {
  padding: 0 clamp(1.5rem, 5vw, 4rem) clamp(3.5rem, 9vh, 6rem);
  max-width: 1100px;
  color: var(--hero-text);
}
.hero .eyebrow {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.4em;
  text-transform: uppercase;
  color: var(--hero-text-dim);
  margin-bottom: 1.2rem;
}
.hero h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(2.6rem, 7vw, 5.4rem);
  line-height: 1.04;
  max-width: 16ch;
  text-shadow: 0 4px 40px rgba(0,0,0,0.5);
}
.hero h1 em { font-weight: 300; color: #e8a04c; }
.hero .sub {
  font-family: var(--serif);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(1.05rem, 2vw, 1.4rem);
  color: var(--hero-text-dim);
  margin-top: 1.3rem;
  max-width: 52ch;
}
.chips i {
  width: 13px; height: 13px;
  border-radius: 50%;
  display: inline-block;
  border: 1px solid rgba(242,237,228,0.25);
}
[data-theme="light"] .chips i { border-color: rgba(36,24,27,0.2); }

/* scroll cue — invites the first scroll that starts the reveal */
.scroll-cue {
  position: absolute;
  left: 50%; bottom: 1.6rem;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.66rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--hero-text-dim);
  z-index: 1;
  animation: cueFade 3s ease-in-out infinite;
}
.scroll-cue i {
  width: 1px; height: 32px;
  background: linear-gradient(var(--hero-text-dim), transparent);
}
@keyframes cueFade { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.9; } }

/* ---------- collection ---------- */
.collection {
  max-width: 1280px;
  margin: 0 auto;
  padding: clamp(3rem, 7vh, 4.5rem) clamp(1.5rem, 5vw, 4rem) 4rem;
}
.collection-head {
  display: flex;
  align-items: baseline;
  gap: 1.4rem;
  margin-bottom: clamp(1.6rem, 3.5vh, 2.6rem);
}
.collection-head h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.7rem, 3.4vw, 2.5rem);
}
.collection-head .rule { flex: 1; border-top: 1px solid var(--line); }
.collection-head .count {
  font-size: 0.82rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ivory-dim);
}

/* The article is now just a spacing slot. Generous vertical room means only
   one quilt sits near the viewport's centre at a time, so each gets its own
   moment to grow in and recede. No dividing rules — each quilt floats.
   --chrome = the accent that leads this row's chrome: the per-quilt accent on
   dark, a uniform burgundy on light (see the light override just below). */
.quilt-row {
  --accent: #e8a04c;
  --chrome: var(--accent);
  padding: clamp(5rem, 15vh, 11rem) 0;
}
[data-theme="light"] .quilt-row { --chrome: var(--burgundy); }

/* The stage is the grid that actually moves: scale + drift + fade are driven
   per-frame from JS via these custom properties (defaults keep it visible
   before/without JS). */
.row-stage {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: 1.5rem 3rem;
  align-items: center;
  position: relative;
  transform-origin: center center;
  transform: translateY(var(--ty, 0px)) scale(var(--s, 1));
  opacity: var(--o, 1);
  will-change: transform, opacity;
}

.row-stage .visual { grid-column: 1 / span 7; position: relative; }
.row-stage .info   { grid-column: 8 / span 5; }
.quilt-row:nth-of-type(even) .visual { grid-column: 6 / span 7; order: 2; }
.quilt-row:nth-of-type(even) .info   { grid-column: 1 / span 5; order: 1; }
@media (max-width: 820px) {
  /* Collapse to ONE column. Leaving the 12-track grid in place here was a real bug: the
     tracks can shrink to zero but the GAPS cannot, so the row carried a hard floor of
     11 × 3rem = 528px. On a 375px phone that forced the whole document to ~515px wide,
     the layout viewport grew to match, and every quilt was cut off on the right. Both
     children already span the full row at this width, so one track is equivalent. */
  .row-stage { grid-template-columns: 1fr; column-gap: 0; }
  .row-stage .visual, .quilt-row:nth-of-type(even) .visual { grid-column: 1 / -1; order: 1; }
  .row-stage .info, .quilt-row:nth-of-type(even) .info { grid-column: 1 / -1; order: 2; }
  /* single-column: the text now sits BELOW the image, so left-align every ghost
     numeral with the image/text instead of leaving it stranded on the wide-layout
     outer edge (odd rows no longer cling to the right) */
  .quilt-row:nth-of-type(odd) .num,
  .quilt-row:nth-of-type(even) .num { left: 0; right: auto; }
}

/* The hero cover is a 2400² square with white mat borders baked in — the top ~17%
   and bottom ~16% are pure white (they read as grey through the dark scrim). A
   landscape viewport crops those mats out of frame; a portrait/square one lets them
   intrude, and once the image stops covering they even float toward the middle.
   So on any non-landscape viewport: (1) force the image to COVER, pinning the mats
   to the very top/bottom edges at every aspect, and (2) darken both ends to opaque
   --hero-ink — a vignette that masks the mats and lets the subtitle sit on solid
   dark, while the clear middle keeps the child's face visible. Gated at 4/3 (not
   1/1): just past square, the barely-covering image still leaves a bottom-mat sliver
   that centre-cropping can't remove, so the fix has to reach into mildly-landscape
   too; only genuinely wide windows crop both mats out and keep the original scrim. */
@media (max-aspect-ratio: 4/3) {
  .hero .bg { background-size: cover; background-position: center; }
  .hero::before {
    background: linear-gradient(180deg,
      var(--hero-ink) 0%,
      var(--hero-ink) 20%,
      rgba(22,15,20,0.22) 34%,
      rgba(22,15,20,0.1) 50%,
      rgba(22,15,20,0.5) 66%,
      var(--hero-ink) 80%,
      var(--hero-ink) 100%);
  }
}

.row-stage .num {
  position: absolute;
  /* sit in the dark gap ABOVE the image (so the image can't clip it), floating
     ~1/3 of its own height clear of the top-outer corner — legible over the flat
     bg at any width/aspect */
  bottom: 100%;
  margin-bottom: 0.3em; /* lift ≈ 1/3 numeral height; scales with font-size */
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(4rem, 9vw, 7rem);
  line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1px var(--num-stroke);
  z-index: 2;
  pointer-events: none;
}
.quilt-row:nth-of-type(odd) .num { right: 0; }
.quilt-row:nth-of-type(even) .num { left: 0; }

.row-stage .img-link { display: block; position: relative; z-index: 1; }
.row-stage .img-wrap {
  /* shrink-wrap the cut-out; no clip/box — the AVIF is transparent, so its
     glow is a drop-shadow on the image itself (below), tracing the silhouette */
  width: fit-content;
  margin-inline: auto;
  transform-origin: center center;
}
/* The quilt is a transparent AVIF cut-out: its per-accent glow is a drop-shadow
   that traces the silhouette (not a rectangle behind a box). The quilt itself
   holds still on hover — no growth; only the title underline + chips respond. */
.row-stage img {
  /* height-capped so each row stays compact; the real aspect comes from the
     cut-out's width/height attributes — no crop, no forced square */
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(56vh, 520px);
  /* dark skin: one centered accent halo, even all around (no downward pool) */
  filter: drop-shadow(0 0 30px color-mix(in srgb, var(--accent) 42%, transparent));
  /* give the glow its own layer, sized to include its ink-overflow — else it gets
     clipped to the transformed .row-stage's layer bounds on first paint, and only
     a window resize repaints it correctly (both skins) */
  will-change: transform;
}
/* light skin: the coloured halo would muddy the cream — swap it for a soft,
   neutral grounding shadow so each cut-out sits on the paper */
[data-theme="light"] .row-stage img {
  filter: drop-shadow(0 20px 32px rgba(40,22,26,0.30)) drop-shadow(0 6px 12px rgba(40,22,26,0.18));
}

.info h3 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.6rem, 3vw, 2.3rem);
  line-height: 1.15;
  color: var(--title);          /* gold on dark, burgundy on light — matches the drop cap */
}
/* hover-underline is a uniform colour (--underline) so it reads as a set: a
   rust-orange on dark that stands apart from the gold titles, gold on light
   under the burgundy titles */
.info h3 a { background-image: linear-gradient(var(--underline), var(--underline));
  background-repeat: no-repeat;
  background-size: 0% 2px;
  background-position: 0 100%;
  transition: background-size 0.5s ease;
  padding-bottom: 4px;
}
.row-stage:hover .info h3 a { background-size: 100% 2px; }
.info .size {
  margin-top: 0.7rem;
  font-size: 0.85rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ivory-dim);
}
.info .chips { display: flex; gap: 8px; margin-top: 1.3rem; }
.info .chips i {
  width: 17px; height: 17px;
  transition: transform 0.3s ease;
}
.row-stage:hover .chips i { transform: translateY(-6px); }
.row-stage:hover .chips i:nth-child(2) { transition-delay: 0.05s; }
.row-stage:hover .chips i:nth-child(3) { transition-delay: 0.1s; }
.row-stage:hover .chips i:nth-child(4) { transition-delay: 0.15s; }
/* bead-rule mode: JS repeats the palette across the title's widest line and
   justifies it flush to both edges (space-between); the container width is set
   inline per row, and the sequence ends on a partial group (1–4 colours). */
.info .chips.beads { justify-content: space-between; gap: 0; }
.info .chips.beads i { flex: 0 0 auto; }
/* hover lift ripples left→right across every bead — each bead's --d delay is set
   in JS from its index; on leave they fall back together (base delay is 0) */
.row-stage:hover .chips.beads i { transform: translateY(-6px); transition-delay: var(--d, 0s); }
/* the "→" call to action: on a quilt row, and again at the foot of the About page */
.info .view,
.about-cta .view {
  display: inline-block;
  margin-top: 1.6rem;
  font-size: 0.84rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--chrome);
}
.info .view .arr,
.about-cta .view .arr { display: inline-block; transition: transform 0.35s ease; }
.info .view:hover .arr,
.about-cta .view:hover .arr { transform: translateX(6px); }
.info .view.disabled { color: var(--ivory-dim); opacity: 0.55; }

/* ---------- project page (palette-tinted) ---------- */
.project-page {
  --p1: #e0912f; --p2: #7da14a; --p3: #9a6fb0; --p4: #a9c2d1;   /* per-quilt palette, set inline from content.js — drives the palette-bar + hero glow */
  --chrome: var(--title);         /* uniform gold (dark) / burgundy (light) so the index + drop cap match the title on every quilt */
  background: var(--ink);         /* flat — palette tints live only in the accents */
}
.project {
  max-width: 1180px;
  margin: 0 auto;
  padding: 5.5rem clamp(1.5rem, 5vw, 3rem) 4rem;
}
/* top row: back-link (left) and the quilt's index number (right) share one line */
.project-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}
.backlink {
  font-size: 0.82rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--ivory-dim);
  transition: color 0.3s;
}
.backlink:hover { color: var(--chrome); }
.project-index {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--chrome);
}

.project-head {
  text-align: center;
  padding: 1.4rem 0 2rem;
}
.project-head h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(2.3rem, 5.4vw, 4rem);
  color: var(--title);          /* gold on dark, burgundy on light — matches the drop cap */
}

/* the quilt's own palette as a design element */
.palette-bar {
  display: flex;
  justify-content: center;
  gap: 0;
  width: min(320px, 60%);
  height: 7px;
  margin: 1.8rem auto 0;
  border-radius: 4px;
  overflow: hidden;
}
.palette-bar i { flex: 1; }
.palette-bar i:nth-child(1) { background: var(--p1); }
.palette-bar i:nth-child(2) { background: var(--p2); }
.palette-bar i:nth-child(3) { background: var(--p3); }
.palette-bar i:nth-child(4) { background: var(--p4); }

/* below the image: dimensions, then the palette bar — which doubles as a slim
   separator between the plate and the statement text */
.project-meta {
  text-align: center;
}
.project-meta .size {
  font-size: 0.9rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--ivory-dim);
}

.statement {
  max-width: 62ch;
  margin: 2.4rem auto 3.6rem;
  color: var(--prose);
  font-size: 1.12rem;
}
.statement p + p { margin-top: 1.15em; }
/* the drop cap opens the artist statement on a project page and the bio on About —
   same chrome, same measure, so both pages start the same way */
.statement p:first-child::first-letter,
.about-intro p:first-child::first-letter {
  font-family: var(--serif);
  font-weight: 500;
  /* sized so the cap spans two lines — its baseline lands on the 2nd line's baseline
     (3.9em verified against New Spirit's metrics at the statement's size/leading) */
  font-size: 3.9em;
  float: left;
  line-height: 0.72;
  padding: 0.02em 0.14em 0 0;
  color: var(--chrome);
}

.hero-fig {
  width: fit-content;
  margin: 0 auto 2.6rem;
  cursor: zoom-in;
}
.hero-fig img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(78vh, 720px);
  /* dark skin: a whisper of the quilt's primary lifts the black-bordered plate */
  filter: drop-shadow(0 0 40px color-mix(in srgb, var(--p1) 34%, transparent));
  will-change: transform;   /* own layer, so the glow isn't clipped on first paint */
}
/* light skin: neutral grounding shadow instead of the coloured glow */
[data-theme="light"] .hero-fig img {
  filter: drop-shadow(0 26px 40px rgba(40,22,26,0.30)) drop-shadow(0 8px 15px rgba(40,22,26,0.18));
}

.details-head,
.record-head {
  display: flex;
  align-items: baseline;
  gap: 1.3rem;
  margin-bottom: 2.4rem;
}
.record-head { margin-top: 4.4rem; }
.details-head h2,
.record-head h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.6rem;
}
.details-head .rule,
.record-head .rule { flex: 1; border-top: 1px solid var(--line); }

/* Detail crops in justified rows: script.js measures the container and scales each
   row to a common height, so every row's edges land flush and nothing is cropped.
   Because a row shares ONE height, a portrait crop comes out narrower than the
   landscape beside it — less area, not more. The old 2-column masonry did the
   opposite: equal widths meant a portrait got up to 2.3x the ink purely for being
   portrait, and left up to 393px of ragged column bottom. Figures are sized inline
   in px by the builder; only their look lives here. */
.detail-rows { display: flex; flex-direction: column; gap: 1.7rem; overflow: hidden; }
.detail-row { display: flex; gap: 1.7rem; }
.detail-row figure {
  position: relative;
  /* never shrink: flex items default to flex-shrink:1, which would squash a crop's
     width while the builder's inline height stayed put — distorting the artwork.
     Between a resize and the rebuild a row may run wide instead; .detail-rows clips
     that rather than letting it push the page sideways. */
  flex: 0 0 auto;
  overflow: hidden;
  border-radius: 4px;
  cursor: zoom-in;
}
.detail-row img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1.2s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.detail-row figure::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 4px;
  box-shadow: inset 0 0 0 0 color-mix(in srgb, var(--chrome) 70%, transparent);
  transition: box-shadow 0.4s ease;
  pointer-events: none;
}
.detail-row figure:hover::after { box-shadow: inset 0 0 0 3px color-mix(in srgb, var(--chrome) 70%, transparent); }
.detail-row figure:hover img { transform: scale(1.04); }

/* ---------- exhibitions + awards record ----------
   One row per show, newest first, with the award riding the row that earned it.
   Three columns — run dates | show + division | award — collapsing to a stacked
   block on narrow screens. Awards take --chrome (uniform gold / burgundy), the
   same chrome as the index and drop cap, so the quilt's own palette stays with
   the artwork rather than colouring the record. */
.record .rec-collective {
  max-width: 62ch;              /* prose, so it keeps the statement's measure */
  color: var(--prose);
  margin-bottom: 2.4rem;
  padding-left: 0.9rem;
  border-left: 2px solid color-mix(in srgb, var(--chrome) 55%, transparent);
}
.record .rec-collective em { font-style: italic; color: var(--ivory); }
.rec-group-label {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ivory-dim);
  margin-bottom: 0.9rem;
}
.rec-list + .rec-group-label { margin-top: 2.6rem; }
/* ONE grid for the whole list, with each row's <li> going display:contents, so the
   date and award columns size themselves to the longest entry and stay aligned
   down the list. Per-row grids can't do that — a fixed date column has to guess,
   and "October 30 – November 3, 2024" already overruns a 12.5rem guess. Cells
   stretch (not baseline) so every row's rule is one straight line. */
.rec-list {
  list-style: none;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) max-content;
  column-gap: 2rem;
}
.rec { display: contents; }
.rec-date,
.rec-body,
.rec-award {
  padding: 0.95rem 0;
  border-top: 1px solid var(--line);
}
.rec:last-child .rec-date,
.rec:last-child .rec-body,
.rec:last-child .rec-award { border-bottom: 1px solid var(--line); }
.rec-date {
  font-size: 0.83rem;
  line-height: 1.45;
  padding-top: calc(0.95rem + 3px);   /* sits the date on the show name's baseline */
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ivory-dim);
  white-space: nowrap;
}
.rec-show { font-family: var(--serif); font-size: 1.12rem; line-height: 1.35; }
.rec-show .sep { color: var(--ivory-dim); }
.rec-div {
  font-size: 0.86rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ivory-dim);
  margin-top: 0.15rem;
}
.rec-exhibit { text-transform: none; letter-spacing: 0.02em; font-style: italic; }
.rec-award {
  font-family: var(--serif);
  font-size: 1.02rem;
  line-height: 1.35;
  color: var(--chrome);
  text-align: right;
  white-space: nowrap;
}
/* the trophy — QD's emoji, used as-is. A colour glyph, so it ignores --chrome and
   stays gold in both skins. That is the intent: a trophy is a gold object, and real
   trophies don't change colour — the one mark here depicting a thing rather than
   decorating one. Sized a touch under the text so it reads as a mark beside the
   placement, not as a bullet. */
.rec-award .aw-icon {
  font-size: 0.92em;
  margin-right: 0.34em;
  vertical-align: 0.02em;
  font-style: normal;
  line-height: 1;
}
/* Narrow screens: one column, so each show becomes a small stacked block —
   date, show, division, award — instead of three columns fighting for room. */
@media (max-width: 860px) {
  .rec-list { display: block; }
  .rec { display: block; padding: 0.95rem 0; border-top: 1px solid var(--line); }
  .rec:last-child { border-bottom: 1px solid var(--line); }
  .rec-date,
  .rec-body,
  .rec-award { padding: 0; border: 0; }
  .rec:last-child .rec-date,
  .rec:last-child .rec-body,
  .rec:last-child .rec-award { border-bottom: 0; }
  .rec-date { margin-bottom: 0.15rem; white-space: normal; }
  .rec-award { text-align: left; margin-top: 0.3rem; white-space: normal; }
}

/* lightbox — stays dark in both skins (photos read best on a dark ground) */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(12, 9, 11, 0.94);
  display: grid;
  place-items: center;
  padding: 3vmin;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease;
  cursor: zoom-out;
}
.lightbox.open { opacity: 1; pointer-events: auto; }
.lightbox img {
  max-width: 94vw;
  max-height: 92vh;
  width: auto; height: auto;
  border-radius: 3px;
  box-shadow: 0 40px 120px -30px rgba(0, 0, 0, 0.8);
  transform: scale(0.96);
  transition: transform 0.35s ease;
  cursor: default;          /* only the backdrop dismisses — see the click handler */
}
.lightbox.open img { transform: scale(1); }

/* prev/next. The lightbox stays dark in both skins, so these use the fixed
   --hero-* ink rather than theme tokens. The button is deliberately much larger
   than its chevron: everything around it is backdrop, and backdrop closes, so a
   near-miss would dismiss the lightbox mid-sequence. */
.lb-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--hero-text);
  opacity: 0.6;
  cursor: pointer;
  transition: opacity 0.25s ease;
}
/* Narrow screens: the image is 94vw, so the arrows sit ON it rather than clear of
   it — and with no swipe gesture they're the only way to page. A disc keeps them
   legible over a pale crop; on desktop they're over backdrop and need no chrome. */
@media (max-width: 820px) {
  .lb-nav { background: rgba(12, 9, 11, 0.55); border-radius: 50%; opacity: 0.85; }
}
.lb-nav:hover, .lb-nav:focus-visible { opacity: 1; }
.lb-nav:focus-visible { outline: 2px solid var(--hero-text); outline-offset: 2px; border-radius: 3px; }
.lb-nav.prev { left: max(0.75rem, 2vw); }
.lb-nav.next { right: max(0.75rem, 2vw); }
/* .lb-nav's display would otherwise beat the UA's [hidden] { display: none } */
.lb-nav[hidden] { display: none; }
.lb-nav svg {
  width: 30px; height: 30px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* reveal (project page only) */
.reveal {
  opacity: 0;
  transform: translateY(44px);
  transition: opacity 1s cubic-bezier(0.2, 0.7, 0.2, 1), transform 1s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.reveal.in { opacity: 1; transform: none; }

.pager {
  display: flex;
  justify-content: space-between;
  margin-top: 3.6rem;
  font-family: var(--serif);
  color: var(--ivory-dim);
}
.pager .disabled { opacity: 0.5; }

/* ---------- about page ----------
   Text-led, so it borrows the project page's typographic kit rather than inventing
   one: the same uniform --chrome, the same drop cap opening the prose, the same
   letterspaced "→" call to action. No palette ribbon and no per-quilt accent — there
   is no quilt on this page to lend its colours, so every mark here is chrome. */
.about-page { --chrome: var(--title); }   /* gold on dark, burgundy on light */
.about {
  max-width: 1180px;
  margin: 0 auto;
  padding: 6.5rem clamp(1.5rem, 5vw, 3rem) 4rem;
}
.about-head,
.contact-head {
  text-align: center;
  padding-bottom: clamp(2.4rem, 6vh, 3.6rem);
}
.about-head h1,
.contact-head h1 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(2.3rem, 5.4vw, 4rem);
  color: var(--title);
}

/* portrait beside the opening prose. The text column is capped at the statement's
   reading measure and the pair is centred as a unit, so the two stay a deliberate
   couple instead of stretching to the container. */
.about-lead {
  display: grid;
  grid-template-columns: minmax(0, 340px) minmax(0, 60ch);
  justify-content: center;
  gap: clamp(2rem, 5vw, 3.6rem);
  align-items: start;
}
/* A rectangular photograph, not a cut-out — so it takes the detail crops' rounded
   frame and a hairline, never the silhouette-tracing drop-shadow the quilts get. */
.portrait img {
  width: 100%;
  height: auto;
  border-radius: 4px;
  border: 1px solid var(--line);
}
/* light skin: the same grounding shadow the quilts trade their glow for */
[data-theme="light"] .portrait img {
  box-shadow: 0 22px 36px -18px rgba(40, 22, 26, 0.35);
}
.about-intro {
  color: var(--prose);
  font-size: 1.12rem;
}
.about-intro p + p { margin-top: 1.15em; }
/* The one inline link in the prose (Soul Stories). Everything else on the site links
   from a title or a letterspaced "→", which read as links by position; this one sits
   mid-sentence, so it needs to be findable standing still — hence a hairline underline
   at rest that firms up on hover, not a hover-only reveal (there is no hover on touch).
   text-decoration rather than a border, so it skips the descenders. */
.about-intro a {
  color: var(--chrome);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: color-mix(in srgb, var(--chrome) 45%, transparent);
  transition: text-decoration-color 0.3s;
}
.about-intro a:hover { text-decoration-color: var(--chrome); }

.about-bridge {
  max-width: 62ch;
  margin: clamp(2.8rem, 6vh, 4rem) auto 0;
  color: var(--prose);
  font-size: 1.12rem;
}
/* Dale's own hand-off line into the list below. It ends on a colon and does the work
   a section heading would, so the adventures carry no "Other Adventures" rule above
   them — her sentence is the heading. */
.about-bridge .glimpse {
  margin-top: 1.5em;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.26rem;
  color: var(--ivory);
}

.adventures {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.8rem, 4vw, 3rem);
  margin-top: clamp(2.4rem, 5vh, 3.4rem);
  padding-top: clamp(2.4rem, 5vh, 3.4rem);
  border-top: 1px solid var(--line);
}
/* a short chrome bar tops each one — the palette-bar's vocabulary, in chrome */
.adv::before {
  content: "";
  display: block;
  width: 40px;
  height: 2px;
  border-radius: 2px;
  background: var(--chrome);
  margin-bottom: 1.1rem;
}
.adv h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 1.35rem;
  line-height: 1.25;
  color: var(--title);
}
.adv p {
  margin-top: 0.8rem;
  color: var(--prose);
}
.adv-link {
  display: inline-block;
  margin-top: 1.1rem;
  font-size: 0.78rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--chrome);
}
.adv-link .arr { display: inline-block; transition: transform 0.35s ease; }
.adv-link:hover .arr { transform: translateX(6px); }

/* the closer, set as a coda: centred, italic display, on its own above the footer */
.about-close {
  max-width: 46ch;
  margin: clamp(3.4rem, 8vh, 5rem) auto 0;
  padding-top: clamp(2.4rem, 5vh, 3.4rem);
  border-top: 1px solid var(--line);
  text-align: center;
  font-family: var(--serif);
  font-weight: 300;
  font-style: italic;
  font-size: clamp(1.2rem, 2.4vw, 1.55rem);
  line-height: 1.45;
  color: var(--ivory);
}
.about-cta { text-align: center; }

@media (max-width: 900px) {
  .adventures { grid-template-columns: 1fr; gap: 2.6rem; }
}
@media (max-width: 820px) {
  /* single column: the portrait sits above the prose, capped so it stays a portrait
     rather than a full-width banner */
  .about-lead { grid-template-columns: minmax(0, 1fr); }
  .portrait { max-width: 300px; margin-inline: auto; }
}

/* ---------- contact page ----------
   Same kit again: uniform --chrome, the site's letterspaced micro-labels, the "→" call to
   action reused as the submit button. The form is the site's first interactive surface, so
   the only new vocabulary here is the field itself — kept as quiet as possible (a hairline
   box on the page ground) so it reads as part of the gallery rather than an app. */
.contact-page { --chrome: var(--title); }
.contact {
  max-width: 1180px;
  margin: 0 auto;
  padding: 6.5rem clamp(1.5rem, 5vw, 3rem) 4rem;
}
.contact-intro {
  max-width: 54ch;
  margin: 0 auto clamp(2.4rem, 5vh, 3.2rem);
  text-align: center;
  color: var(--prose);
  font-size: 1.12rem;
}
.contact-form {
  max-width: 34rem;      /* a comfortable single-column form measure */
  margin: 0 auto;
}
/* the honeypot: off-screen rather than display:none, and out of the tab order + the a11y
   tree (aria-hidden on the wrapper, tabindex -1 on the input), so no keyboard or screen
   reader user can ever land in it — only a bot filling every field it finds */
.hp {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.field { margin-bottom: 1.5rem; }
.field label {
  display: block;
  margin-bottom: 0.5rem;
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ivory-dim);
}
.field input,
.field select,
.field textarea {
  width: 100%;
  font-family: var(--text);
  font-size: 1.02rem;
  line-height: 1.6;
  color: var(--ivory);
  background: color-mix(in srgb, var(--ivory) 4%, transparent);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.7rem 0.85rem;
  transition: border-color 0.25s, background 0.25s;
}
.field textarea { resize: vertical; min-height: 8rem; }
/* the select needs its own arrow — appearance:none strips the native one, and the native
   control ignores the theme tokens on some platforms */
.field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ivory-dim) 50%),
                    linear-gradient(135deg, var(--ivory-dim) 50%, transparent 50%);
  background-position: calc(100% - 1.1rem) 55%, calc(100% - 0.75rem) 55%;
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
  padding-right: 2.4rem;
}
.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--ivory-dim); }
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: none;
  border-color: var(--chrome);
  background: color-mix(in srgb, var(--ivory) 7%, transparent);
}
/* the browser's own "please fill this in" state, but only AFTER a submit attempt —
   :invalid alone would paint every empty required field red on arrival */
.contact-form.submitted .field input:invalid,
.contact-form.submitted .field textarea:invalid { border-color: #c2492f; }

/* submit — the quilt rows' letterspaced call to action, given a button's box */
.submit {
  font-family: var(--text);
  font-size: 0.84rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--chrome);
  background: none;
  border: 1px solid color-mix(in srgb, var(--chrome) 45%, transparent);
  border-radius: 999px;
  padding: 0.8rem 1.9rem;
  cursor: pointer;
  transition: border-color 0.3s, background 0.3s;
}
.submit:hover { border-color: var(--chrome); background: color-mix(in srgb, var(--chrome) 10%, transparent); }
.submit:focus-visible { outline: 2px solid var(--chrome); outline-offset: 3px; }
.submit[disabled] { opacity: 0.55; cursor: default; }
.submit .arr { display: inline-block; transition: transform 0.35s ease; }
.submit:hover:not([disabled]) .arr { transform: translateX(6px); }

.form-status {
  margin-top: 1.2rem;
  font-size: 0.98rem;
  color: var(--prose);
  min-height: 1.5em;          /* reserve the line so the button doesn't jump on send */
}
.form-status.ok { color: var(--chrome); font-family: var(--serif); font-size: 1.1rem; }
.form-status.err { color: #c2492f; }
/* the sent state: the form is replaced by its confirmation, so there's nothing to re-submit */
.contact-form.sent .field,
.contact-form.sent .submit { display: none; }

/* ---------- footer ---------- */
.site-footer {
  border-top: 1px solid var(--line);
  margin-top: 3rem;
  padding: 2.6rem clamp(1.5rem, 5vw, 4rem) 3.2rem;
  text-align: center;
}
.site-footer .motto {
  font-family: var(--serif);
  font-weight: 300;
  font-style: italic;
  font-size: 1.12rem;
  color: var(--ivory-dim);
}
.site-footer .fine {
  margin-top: 0.8rem;
  font-size: 0.78rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .reveal { opacity: 1; transform: none; }
  .row-stage { opacity: 1 !important; transform: none !important; }
  .scroll-cue { display: none; }
}

