/* ==========================================================================
   KARBASBAF ARCHIVE — MASTER STYLE ENGINE, v3
   The Phi-Based Resonating Sheet
   φ = 1.618 — every spatial, typographic, and structural interval derives
   from this ratio. The book-cover showcase grid below is not decorated
   with the golden ratio — it is CONSTRUCTED from the same Fibonacci square
   spiral (1, 1, 2, 3, 5) used in "The Golden Method" (Arts, 2017) for
   lute soundhole placement. Form and subject are the same geometry.

   Type system: Fraunces (display) + Source Serif 4 (reading matter) +
   Inter (utility: nav, caption, labels).
   ========================================================================== */

/* [Pre-launch audit — Bug Fix] The @import that used to sit here has been
   removed. Every page's <head> already loads this exact same Fraunces /
   Source Serif 4 / Inter family string via <link rel="preconnect"> +
   <link rel="stylesheet">, which is the fast path (the browser can start
   the font fetch in parallel with everything else). An @import at the top
   of a stylesheet is a second, redundant request for the identical fonts,
   and — worse — it's render-blocking: the browser must fetch and parse the
   imported sheet before it can finish parsing the rest of THIS file,
   delaying every rule below by a full extra round trip. Removing it costs
   nothing (the <link> tags already do the job) and removes a duplicate
   request plus a render-blocking hop. If a future page is ever added
   without the <link> tags in its <head>, fonts will silently fall back to
   the browser default rather than 404 — no visual break, just a missing
   custom typeface, which is the safer failure mode anyway. */

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

:root {
  /* Palette — gold is a single-pixel tactical accent. Never a fill. */
  --canvas: #FFFFFF;
  --canvas-tint: #FAFAF8;
  --ink: #191A17;
  --ink-90: rgba(25, 26, 23, 0.9);
  /* [Pre-launch audit] Verified, not assumed: --ink-60 flattened onto
     --canvas measures 4.59:1, and onto --canvas-tint measures 4.52:1 —
     both clear WCAG AA passes (≥4.5:1) at the small caption/label sizes
     this token is used at sitewide (.eyebrow, .caption, .doi, .pub-venue,
     .citation-meta, .contact-label, .field label, and others). The margin
     above the 4.5:1 line is real but not large (roughly a tenth of a
     point), so if this token's opacity is ever nudged down for a purely
     visual reason, re-check the ratio before shipping it. */
  --ink-60: rgba(25, 26, 23, 0.6);
  /* [Pre-launch audit] Verified: --ink-35 measures only ~2.2:1 on both
     --canvas and --canvas-tint — a hard WCAG AA fail at any text size
     (needs 4.5:1 for normal text, 3:1 even for large text). Its one call
     site (.citation-status, §19.1) has been moved to --ink-60 instead,
     so nothing in the shipped CSS currently sets text color to this
     token. Left defined here as a documented point on the tonal scale
     for non-text use (a low-contrast fill or hairline, say) — do not
     wire it to a color: declaration without re-verifying contrast first. */
  --ink-35: rgba(25, 26, 23, 0.35);
  --ink-15: rgba(25, 26, 23, 0.15);
  --ink-08: rgba(25, 26, 23, 0.08);
  --gold: #C9974A;
  --gold-soft: rgba(201, 151, 74, 0.35);
  --gold-faint: rgba(201, 151, 74, 0.12);
  /* [Pre-launch audit] Verified, not assumed: --grey-muted measures
     4.59:1 on --canvas — the claim below actually understates it; this
     is a clear AA pass even at normal text size, not just "large text."
     Its one call site (.hero-title .line-secondary) is set against
     --canvas only (the hero carries no background rule, §17.3) and is
     large display type regardless, so it was always safe either way. */
  --grey-muted: #75766F; /* AA-safe on white at large-text sizes */

  /* Type families */
  --font-display: 'Fraunces', 'Iowan Old Style', ui-serif, serif;
  --font-body: 'Source Serif 4', 'Iowan Old Style', ui-serif, serif;
  --font-ui: 'Inter', -apple-system, sans-serif;

  /* Phi = 1.618 — fluid type scale, base 17px */
  --t-caption: clamp(0.6154rem, 0.59rem + 0.13vw, 0.6875rem);
  --t-nav:     clamp(0.8235rem, 0.78rem + 0.22vw, 0.9412rem);
  --t-body:    clamp(0.9412rem, 0.90rem + 0.20vw, 1.0625rem);
  --t-lede:    clamp(1.3529rem, 1.24rem + 0.55vw, 1.6875rem);
  --t-h2:      clamp(1.8235rem, 1.55rem + 1.35vw, 2.75rem);
  --t-h1:      clamp(2.5rem, 1.9rem + 3vw, 4.4375rem);
  --t-hero:    clamp(3.2rem, 1.6rem + 8vw, 7.1875rem);

  /* Phi spacing scale — base unit 17px */
  --s-1: 0.382rem;
  --s-2: 0.618rem;
  --s-3: 1rem;
  --s-4: 1.618rem;
  --s-5: 2.618rem;
  --s-6: 4.236rem;
  --s-7: 6.854rem;
  --s-8: 11.09rem;

  /* Structural grid — rail:bout held at the golden ratio itself */
  --rail-width: minmax(180px, 1fr);
  --bout-width: minmax(0, 1.618fr);
  --content-max: 1440px;
  --gutter: var(--s-5);

  /* Motion */
  --ease-resonant: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-frame: 640ms;
  --dur-quick: 280ms;
}

/* ==========================================================================
   2. RESET
   ========================================================================== */

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

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.78;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body.scroll-locked {
  overflow: hidden;
}

img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, h4, p, figure { margin: 0; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

::selection {
  background: var(--gold-faint);
  color: var(--ink);
}

:focus { outline: none; }

:focus-visible {
  outline: 1.5px solid var(--gold);
  outline-offset: 3px;
  border-radius: 1px;
}

/* ==========================================================================
   3. TYPOGRAPHY SYSTEM
   ========================================================================== */

.eyebrow {
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.25em;
  color: var(--ink-60);
}

.eyebrow--gold { color: var(--gold); }

h1, .h1 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-h1);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.04;
  color: var(--ink);
}

h2, .h2 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-h2);
  font-weight: 480;
  letter-spacing: -0.003em;
  line-height: 1.12;
  color: var(--ink);
}

h3, .h3 {
  font-family: var(--font-ui);
  font-size: var(--t-body);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  line-height: 1.4;
  color: var(--ink);
}

.hero-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-hero);
  letter-spacing: -0.01em;
  line-height: 0.96;
}

.hero-title .line-primary {
  display: block;
  font-weight: 600;
  color: var(--ink);
}

.hero-title .line-secondary {
  display: block;
  font-weight: 340;
  font-style: italic;
  color: var(--grey-muted);
  margin-top: 0.05em;
}

.lede {
  font-family: var(--font-body);
  font-size: var(--t-lede);
  font-weight: 400;
  font-style: italic;
  line-height: 1.5;
  color: var(--ink-90);
}

.body-text {
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.8;
  color: var(--ink-90);
  max-width: 62ch;
}

.body-text + .body-text { margin-top: var(--s-4); }

.caption, .meta {
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-60);
  line-height: 1.6;
}

.doi {
  font-family: var(--font-body);
  font-size: var(--t-caption);
  font-style: italic;
  color: var(--ink-60);
  letter-spacing: 0.01em;
}

.doi a {
  border-bottom: 1px solid var(--gold-soft);
  transition: border-color var(--dur-frame) var(--ease-resonant);
}
.doi a:hover { border-color: var(--gold); }

.dropcap::first-letter {
  font-family: var(--font-display);
  font-size: calc(var(--t-h1) * 0.618);
  font-weight: 600;
  float: left;
  line-height: 0.82;
  padding: 0.06em var(--s-2) 0 0;
  color: var(--ink);
}

/* ==========================================================================
   4. STRUCTURAL GRID — The Lute-Body Asymmetric System
   ========================================================================== */

.wrap {
  max-width: var(--content-max);
  margin: 0 auto;
  padding: 0 var(--s-5);
}

.lute-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
}

@media (min-width: 900px) {
  .lute-grid {
    grid-template-columns: var(--rail-width) var(--bout-width);
    gap: var(--gutter);
    align-items: start;
  }
}

.rail {
  position: relative;
  padding-right: var(--s-4);
}

@media (min-width: 900px) {
  .rail { text-align: right; }
}

.rail > * + * { margin-top: var(--s-3); }

.canvas { min-width: 0; }
.canvas > * + * { margin-top: var(--s-4); }

.section { padding-block: var(--s-7); }
.section--tight { padding-block: var(--s-6); }
.section--tinted { background: var(--canvas-tint); }
.section-head { margin-bottom: var(--s-6); }
.section-head .canvas > .h2 { margin-top: var(--s-2); }

/* ==========================================================================
   5. THE WAVEFORM SPINE — Signature Structural Element
   ========================================================================== */

.rail::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1px;
  width: 1px;
  background: linear-gradient(
    to bottom,
    transparent 0%,
    var(--gold-soft) 8%,
    var(--gold-soft) 92%,
    transparent 100%
  );
  display: none;
}

@media (min-width: 900px) {
  .rail::before { display: block; }
}


.wave-divider {
  width: 100%;
  height: 32px;
  margin: var(--s-6) 0;
  opacity: 0.9;
}

.wave-divider path {
  fill: none;
  stroke: var(--gold);
  stroke-width: 1px;
  stroke-linecap: round;
}

.hr-gold {
  border: none;
  height: 1px;
  background: var(--gold);
  opacity: 0.55;
  margin: var(--s-5) 0;
}

.hr-gold.wrap { max-width: var(--content-max); }

.hr-ink {
  border: none;
  height: 1px;
  background: var(--ink-15);
  margin: var(--s-4) 0;
}

/* ==========================================================================
   6. NAVIGATION
   ========================================================================== */

.site-header {
  position: relative;
  padding: var(--s-5) 0;
  background: var(--canvas);
  z-index: 40;
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-4);
}

.brand {
  font-family: var(--font-display);
  font-size: var(--t-body);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.brand span {
  display: block;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: var(--t-caption);
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-60);
  margin-top: 0.35em;
}

.nav-list {
  display: flex;
  gap: var(--s-6);
  align-items: center;
}

.nav-list a {
  font-family: var(--font-ui);
  font-size: var(--t-nav);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  padding-bottom: 0.5em;
  border-bottom: 1.5px solid transparent;
  transition: color var(--dur-frame) var(--ease-resonant),
              border-color var(--dur-frame) var(--ease-resonant);
}

.nav-list a:hover,
.nav-list a[aria-current="page"] {
  color: var(--ink);
  border-color: var(--gold);
}

.nav-toggle { display: none; }

@media (max-width: 720px) {
  /* The header bar is lifted above the nav panel and its scrim (z-index
     60 vs. the panel's 50 and the scrim's 45) so it stays visible as a
     persistent top strip while the menu is open — the toggle button
     remains exactly where the user tapped it and doubles as the close
     affordance, in addition to the existing Escape-key, tap-a-link, and
     tap-outside close paths already wired in the script below. This
     override lives only inside this ≤720px query; the header's original
     z-index:40 (§6) is untouched everywhere else. */
  .site-header { z-index: 60; }

  .nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    font-family: var(--font-ui);
    font-size: var(--t-caption);
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    border: 1px solid var(--ink-15);
    padding: var(--s-1) var(--s-3);
    transition: border-color var(--dur-quick) var(--ease-resonant);
  }

  .nav-toggle:hover,
  .nav-toggle[aria-expanded="true"] { border-color: var(--gold); }

  /* A true side-drawer, not a full-screen block: 78vw (within the
     requested 75–80% range), anchored to the right edge, sliding in on
     transform only (GPU-composited). It stays a solid, fully legible
     surface for the five links; the translucency lives in the scrim
     below it instead, over the page that's still showing in the
     remaining ~22vw on the left. */
  .nav-list {
    position: fixed !important;
    top: 0;
    right: 0;
    left: auto;
    width: 78vw;
    height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: var(--s-6);
    padding: var(--s-7) var(--s-6);
    background: #FFFFFF;
    border-left: 1px solid var(--ink-15);
    transform: translateX(100%);
    transition: transform var(--dur-frame) var(--ease-resonant);
    visibility: hidden;
    z-index: 50;
  }

  .nav-list[data-open="true"] {
    transform: translateX(0);
    visibility: visible;
  }

  .nav-list a {
    font-size: var(--t-lede);
    letter-spacing: 0.02em;
    font-family: var(--font-display);
    font-weight: 480;
    text-transform: none;
  }

  /* The scrim: a soft, blurred, translucent veil over the exposed left
     ~22vw — the crown and calligraphy stay visually present and
     "beautifully hinted" behind it rather than being cut off by a hard
     edge, giving the drawer real cinematic depth instead of feeling like
     a page swap. Decorative only (pointer-events: none) — the existing
     document-level click listener in the script already closes the nav
     on any outside tap regardless of what intercepts the click. Sits
     below the header (60) and the drawer (50) but above everything else
     on the page. .nav-scrim-active is toggled by the same openNav() /
     closeNav() functions that already drive [data-open] — no script
     changes needed to wire this back up. */
  body.nav-scrim-active::after {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(255, 255, 255, 0.94);
    backdrop-filter: blur(12px) brightness(0.98);
    -webkit-backdrop-filter: blur(12px) brightness(0.98);
    z-index: 45;
    pointer-events: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-list { transition: none; }
}

.site-footer {
  border-top: 1px solid var(--ink-15);
  padding: var(--s-6) 0;
  margin-top: var(--s-8);
}

.site-footer .wrap {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s-4);
}

.site-footer .caption a {
  color: var(--ink-60);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-frame) var(--ease-resonant), color var(--dur-frame) var(--ease-resonant);
}
.site-footer .caption a:hover {
  color: var(--ink);
  border-color: var(--gold);
}
/* Footer seal lockup — the calligraphic mark sits before the name,
   bottom-left of the page, appearing exactly once, sitewide. Purely
   additive: nothing above this line in §6 was touched. */
.footer-lockup {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  min-width: 0; /* allows this flex item to shrink inside .site-footer
                   .wrap's own flex row instead of forcing an overflow */
}

.footer-lockup .caption {
  min-width: 0;
}

.footer-seal {
  height: clamp(2.1rem, 1.8rem + 1.3vw, 2.9rem);
  width: auto;
  flex-shrink: 0;
}

@media (max-width: 640px) {
  .footer-lockup { gap: var(--s-2); }
}

@media (max-width: 340px) {
  .footer-lockup {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-1);
  }
}

/* ==========================================================================
   7. HERO
   ========================================================================== */

.hero {
  padding: var(--s-8) 0 var(--s-7);
  position: relative;
}

.hero-split {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: var(--s-6);
}

@media (min-width: 640px) {
  .hero-split { grid-template-columns: 1.2fr 1fr; }
}

.hero .eyebrow {
  opacity: 0;
  animation: fade-rise 900ms var(--ease-resonant) forwards;
  animation-delay: 80ms;
}

.hero-title {
  margin-top: var(--s-3);
  opacity: 0;
  animation: fade-rise 1100ms var(--ease-resonant) forwards;
  animation-delay: 220ms;
}

.hero .lede {
  margin-top: var(--s-5);
  max-width: 34ch;
  opacity: 0;
  animation: fade-rise 1100ms var(--ease-resonant) forwards;
  animation-delay: 420ms;
}

@keyframes fade-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .hero .eyebrow, .hero-title, .hero .lede {
    animation: none;
    opacity: 1;
  }
}

/* ==========================================================================
   8. THE FULL-VIEWPORT REVEAL SHEET
   ------------------------------------------------------------------------
   [Pre-launch audit — Cleanup] This section originally held three
   subsections: 8.1 (.book-showcase's Fibonacci-spiral mosaic grid), 8.2
   (.book-showcase's paper-grain/raking-light hover choreography), and 8.3
   (.book-reveal, the shared full-viewport reveal sheet). Direct inspection
   of all six pages in this project (index.html, book-designs.html,
   about.html, contact.html, research.html) confirmed .book-showcase was
   referenced nowhere — .cover-gallery (§17.5) and .portfolio-grid (§18.2)
   had fully superseded it. 8.1 and 8.2 have been removed accordingly, at
   explicit request, once that finding was confirmed.

   What remains below (still numbered 8.3, deliberately not renumbered to
   8.1, so this comment continues to explain the gap for anyone reading the
   file later) is NOT exclusive to .book-showcase and was kept: it's the
   shared full-viewport reveal sheet that index.html's own .cover-gallery
   buttons already open via #book-reveal (see the JS at the bottom of
   index.html) — deleting it would have broken the homepage's working
   cover-reveal modal, so it stays exactly as it was.
   ========================================================================== */

/* -------------------------------------------------------------------------
   8.3 — THE REVEAL: full-viewport archival presentation sheet
   Not a lightbox. No dark scrim, no centered floating card. The reveal
   IS the page for a moment — full white canvas, rail/bout layout reused
   at full scale, a close control instead of a modal chrome. JS toggles
   `data-open="true"` on `.book-reveal` and adds `.scroll-locked` to body.
   ------------------------------------------------------------------------- */

.book-reveal {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: var(--canvas);
  opacity: 0;
  visibility: hidden;
  transform: scale(0.985);
  transition: opacity var(--dur-frame) var(--ease-resonant),
              transform var(--dur-frame) var(--ease-resonant),
              visibility 0s linear var(--dur-frame);
  overflow-y: auto;
}

.book-reveal[data-open="true"] {
  opacity: 1;
  visibility: visible;
  transform: scale(1);
  transition: opacity var(--dur-frame) var(--ease-resonant),
              transform var(--dur-frame) var(--ease-resonant),
              visibility 0s linear 0s;
}

@media (prefers-reduced-motion: reduce) {
  .book-reveal {
    transform: none;
    transition: opacity var(--dur-quick) linear,
                visibility 0s linear var(--dur-quick);
  }
  .book-reveal[data-open="true"] {
    transition: opacity var(--dur-quick) linear, visibility 0s linear 0s;
  }
}

.book-reveal .wrap {
  padding-top: var(--s-6);
  padding-bottom: var(--s-7);
  min-height: 100%;
}

.reveal-close {
  position: sticky;
  top: var(--s-4);
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 0.6em;
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--ink-15);
  padding: var(--s-2) var(--s-3);
  background: var(--canvas);
  transition: border-color var(--dur-quick) var(--ease-resonant),
              color var(--dur-quick) var(--ease-resonant);
  z-index: 2;
}

.reveal-close:hover,
.reveal-close:focus-visible {
  border-color: var(--gold);
  color: var(--ink);
}

.reveal-body {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-6);
  margin-top: var(--s-5);
}

@media (min-width: 900px) {
  .reveal-body {
    grid-template-columns: 1.618fr 1fr;
    align-items: start;
    gap: var(--s-7);
  }
}

.reveal-frame {
  position: relative;
  overflow: hidden;
  background: var(--canvas-tint);
  border: 1px solid var(--ink-08);
  aspect-ratio: 3 / 4;
}

.reveal-frame img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: var(--canvas-tint);
}

.reveal-detail .eyebrow { display: block; }

.reveal-detail .h2 {
  margin-top: var(--s-3);
}

.reveal-detail .body-text {
  margin-top: var(--s-4);
  max-width: 48ch;
}

.reveal-detail .doi {
  display: block;
  margin-top: var(--s-5);
}

/* ==========================================================================
   9. GALLERY FRAMES & PLATES — general-purpose (research/architecture pages)
   ========================================================================== */

.plate { position: relative; }

.frame {
  position: relative;
  overflow: hidden;
  background: var(--canvas-tint);
  border: 1px solid var(--ink-08);
}

.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  will-change: transform;
  transition: transform var(--dur-frame) var(--ease-resonant);
  backface-visibility: hidden;
}

.frame:hover img,
.frame:focus-within img {
  transform: scale(1.03);
}

.plate figcaption {
  margin-top: var(--s-3);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-3);
}

.plate-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s-6) var(--s-5);
  align-items: start;
}

.plate--tall .frame { aspect-ratio: 14 / 17; }
.plate--wide .frame { aspect-ratio: 16 / 10; }
.plate--square .frame { aspect-ratio: 1 / 1; }

/* Portrait plate — for vertically-oriented photographs (e.g. the
   about-workshop.jpg documentary photograph, native ratio 33.33:44.44in
   = exactly 3:4). Capped at max-width so a tall aspect-ratio doesn't
   blow the frame up to an oversized height inside the .canvas column. */
.plate--portrait { max-width: 480px; }
.plate--portrait .frame { aspect-ratio: 3 / 4; }

/* Missing-artwork fallback. The site's own script (bottom of index.html)
   already adds .frame--pending and appends a .frame-pending-label span
   whenever a cover image 404s — but until now neither had any CSS at
   all, so the fallback state was raw, unstyled alt text spilling out of
   its frame with no boundary (visible in earlier screenshots as
   overflowing text). Every .frame that isn't loaded yet now reads as a
   deliberate placeholder plate instead of a broken page. */
.frame--pending {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--canvas-tint);
}

.frame--pending img { display: none; }

.frame-pending-label {
  position: absolute;
  inset: var(--s-4);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  text-align: center;
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  letter-spacing: 0.04em;
  line-height: 1.55;
  color: var(--ink-60);
}

/* ==========================================================================
   10. PUBLICATION / RESEARCH BLOCKS
   ========================================================================== */

.pub {
  padding-block: var(--s-6);
  border-top: 1px solid var(--ink-15);
}

.pub:first-of-type { border-top: none; }

.pub-number {
  font-family: var(--font-display);
  font-size: var(--t-lede);
  font-weight: 340;
  font-style: italic;
  color: var(--gold);
  line-height: 1;
}

.pub-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-h2);
  font-weight: 480;
  letter-spacing: -0.002em;
  line-height: 1.16;
  margin-top: var(--s-2);
}

.pub-venue { margin-top: var(--s-2); }

.pub-status {
  display: inline-block;
  margin-top: var(--s-2);
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ink-60);
  border: 1px solid var(--ink-15);
  padding: var(--s-1) var(--s-2);
}

/* ==========================================================================
   11. TIMELINE
   ========================================================================== */

.timeline { position: relative; padding-left: var(--s-4); }

.timeline::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  bottom: 0.5em;
  width: 1px;
  background: var(--ink-15);
}

.timeline-item { position: relative; padding-bottom: var(--s-6); }

.timeline-item::before {
  content: "";
  position: absolute;
  left: calc(var(--s-4) * -1);
  top: 0.4em;
  width: 6px;
  height: 6px;
  border: 1px solid var(--gold);
  background: var(--canvas);
  border-radius: 50%;
  transform: translateX(-50%);
}

.timeline-item:last-child { padding-bottom: 0; }

.timeline-year {
  font-family: var(--font-display);
  font-size: var(--t-lede);
  font-weight: 340;
  color: var(--ink);
}

/* ==========================================================================
   12. PULL QUOTE
   ========================================================================== */

.pull-quote {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-h2);
  font-weight: 400;
  font-style: italic;
  letter-spacing: -0.002em;
  line-height: 1.3;
  max-width: 20ch;
  color: var(--ink);
}

.pull-quote cite {
  display: block;
  margin-top: var(--s-4);
  font-family: var(--font-ui);
  font-style: normal;
  font-size: var(--t-caption);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-60);
}

/* ==========================================================================
   13. FORM ELEMENTS — Contact Page
   ========================================================================== */

.field {
  border-bottom: 1px solid var(--ink-15);
  padding-bottom: var(--s-2);
  transition: border-color var(--dur-frame) var(--ease-resonant);
}

.field:focus-within { border-color: var(--gold); }

.field label {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ink-60);
  margin-bottom: var(--s-2);
}

.field input,
.field textarea {
  width: 100%;
  border: none;
  outline: none;
  background: transparent;
  font-family: var(--font-body);
  font-size: var(--t-body);
  color: var(--ink);
  resize: vertical;
}

.field textarea { min-height: 7em; }

.form-grid { display: grid; gap: var(--s-5); }

.btn-submit {
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  border: 1px solid var(--ink);
  padding: var(--s-3) var(--s-5);
  transition: background var(--dur-frame) var(--ease-resonant),
              color var(--dur-frame) var(--ease-resonant),
              border-color var(--dur-frame) var(--ease-resonant);
  display: inline-block;
}

.btn-submit:hover {
  background: var(--ink);
  color: var(--canvas);
}

/* ==========================================================================
   14. UTILITIES
   ========================================================================== */

.mt-1 { margin-top: var(--s-1); } .mt-2 { margin-top: var(--s-2); }
.mt-3 { margin-top: var(--s-3); } .mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); } .mt-6 { margin-top: var(--s-6); }
.mt-7 { margin-top: var(--s-7); } .mt-8 { margin-top: var(--s-8); }

.text-gold { color: var(--gold); }
.text-ink-60 { color: var(--ink-60); }
.no-transform { text-transform: none; }
.center { text-align: center; }
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}

/* ==========================================================================
   15. RESPONSIVE ADJUSTMENTS
   ========================================================================== */

@media (max-width: 640px) {
  .wrap { padding: 0 var(--s-4); }
  .section { padding-block: var(--s-6); }
  .plate-gallery { gap: var(--s-5) var(--s-4); }
  .reveal-close { top: var(--s-2); }
}

@media (min-width: 900px) and (max-width: 1100px) {
  :root { --rail-width: minmax(220px, 1fr); }
}

/* ==========================================================================
   16. PRINT — The Exhibition Catalog Paradigm
   ========================================================================== */

@media print {
  @page { margin: 2.2cm 1.8cm; }

  * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
    animation: none !important;
    transition: none !important;
  }

  body {
    background: #FFFFFF;
    color: #000000;
    font-size: 11pt;
    line-height: 1.6;
  }

  .site-header, .site-footer, .nav-toggle, .nav-list,
  .btn-submit, .field, form, .book-reveal, .no-print {
    display: none !important;
  }

  .lute-grid { display: block !important; }

  .rail {
    text-align: left;
    padding-right: 0;
    margin-bottom: 0.6em;
  }

  .rail::before { display: none; }

  .hero-title, h1, .h1 { font-size: 26pt; }
  .h2 { font-size: 16pt; }
  .lede { font-size: 12pt; font-style: italic; }

  a { color: #000000; text-decoration: underline; }

  .frame img,
  .cover-gallery .frame img,
  .portfolio-grid .frame img { filter: grayscale(100%); }

  /* [Pre-launch audit — Cleanup] .book-showcase's own print rules used
     to live here (forcing it to display:block, protecting each .plate
     from splitting across a page break, forcing figcaptions visible) —
     removed along with the class itself in §8, confirmed dead across
     all six pages of the project. .cover-gallery (§17.5, index.html)
     and .portfolio-grid (§18.2, book-designs.html) get the equivalent
     print treatment below: multi-column masonry doesn't paginate
     predictably, so print falls back to a simple stacked block list for
     each, every plate protected from splitting across a page break the
     same way .pub / .plate / .cover-card already are further down this
     section. [Pre-launch audit, book-designs.html pass] .portfolio-grid
     didn't have this treatment as of the previous pass — added now. */
  .cover-gallery {
    column-count: 1 !important;
    display: block;
  }
  .cover-gallery .cover-card {
    break-inside: avoid;
    page-break-inside: avoid;
    margin-bottom: 0.6cm;
  }
  .cover-gallery .cover-meta {
    position: static !important;
    opacity: 1 !important;
    transform: none !important;
    background: transparent !important;
    padding: 0.2cm 0 0 !important;
  }

  .portfolio-grid {
    column-count: 1 !important;
    display: block;
  }
  .portfolio-item {
    break-inside: avoid;
    page-break-inside: avoid;
    margin-bottom: 0.6cm;
    opacity: 1 !important;
    transform: none !important;
    pointer-events: auto !important;
  }
  .portfolio-meta {
    position: static !important;
    opacity: 1 !important;
    transform: none !important;
    background: transparent !important;
    padding: 0.2cm 0 0 !important;
  }
  .portfolio-filter { display: none !important; }

  .pub, .plate, .cover-card, .section {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .section {
    padding-block: 0.8cm;
    border-top: 0.5pt solid #999;
  }

  .wrap { max-width: 100%; padding: 0; }

  .hr-gold, .hr-ink { background: #000; opacity: 1; }

  .pull-quote { font-size: 15pt; }

  a[href]::after {
    content: " (" attr(href) ")";
    font-size: 8pt;
    color: #555;
  }

  .doi a::after { content: ""; }
}

/* ==========================================================================
   17. ABOVE-THE-FOLD OVERHAUL — Ceiling Crown & Hero Fusion (index.html only)
   ------------------------------------------------------------------------
   Every rule below is scoped under body.page-home so the shared .site-header,
   .lute-grid, .rail, .frame, and .plate rules used on every other page of
   the archive remain completely untouched. Nothing in §§1–16 above was
   edited to build this section.
   ========================================================================== */

/* -------------------------------------------------------------------------
   17.1 — THE CEILING CROWN: component isolation via flow, not stacking

   Why the crown could "bleed" into the hero at odd zoom levels: it was
   z-index'd to sit *behind* the header and, by design, behind the hero's
   transparent whitespace too — an intentional overlap ("fusion") that
   depended on precise stacking math holding up across every zoom and
   viewport combination. Stacking math is exactly the kind of thing that
   gets fragile under browser zoom, where sub-pixel rounding differs from
   engine to engine. The fix removes the dependency entirely rather than
   tuning it further: on desktop the crown is now a strictly in-flow block
   with position:static, exactly like its already-flawless mobile
   counterpart. In-flow boxes physically reserve their own vertical space
   and cannot be bled into or overlapped by anything after them — not at
   any zoom level, not at any viewport width. There is no z-index left to
   fight.

   The one wrinkle: in-flow order follows DOM order, and the crown's DOM
   position (first child of body, before <header>) is load-bearing for
   the mobile layout that's already signed off — moving the node would
   also silently reorder mobile, which nobody asked for. So the desktop
   reordering is done visually, in CSS only, with flexbox `order` on
   body's direct children. The DOM — and every screen reader's reading
   order — is untouched; only paint order changes, and only at ≥768px.
   Mobile's markup, CSS, and visual order are all byte-for-byte what they
   were when this was signed off as flawless. */

body.page-home {
  --crown-h: clamp(9rem, 40vw, 14rem);
}

/* ASSET IDENTIFIER: assets/images/yazd-crown.png
   Source: the architectural line-drawing sketch of the Yazd skyline
   (wind-towers / badgirs, domes, mud-brick massing) already supplied and
   confirmed rendering correctly as of this pass. No further export
   needed for this asset — filename and path below are exact and final. */
body.page-home .ceiling-crown {
  position: static;
  width: 100%;
  height: var(--crown-h);
  background-image: url('assets/images/yazd-crown.png');
  background-size: contain;
  background-position: top center;
  background-repeat: no-repeat;
  opacity: 1;
  pointer-events: none; /* decorative — never intercepts nav or hero clicks */
}

@media (min-width: 768px) {
  body.page-home {
    --crown-h: clamp(11rem, 20vw, 27rem);
  }

  /* Reorder body's direct children visually — header first, then the
     crown, then the page content — without touching DOM/reading order.
     book-reveal is position:fixed and script has no box, so neither
     participates in flex layout; nothing to assign them. */
  body.page-home {
    display: flex;
    flex-direction: column;
  }

  body.page-home .site-header { order: 1; }

  body.page-home .ceiling-crown {
    order: 2;
    /* No position, no top/left, no z-index, no mask-image — the crown is
       a plain block sitting in normal flow. It cannot overlap anything
       below it because normal flow makes that geometrically impossible. */
    margin-bottom: var(--s-6); /* §17.1b — the definitive safety margin */
  }

  body.page-home main { order: 3; }
  body.page-home .site-footer { order: 4; }
}

/* 17.2 — Zero-clutter header flank.
   The brand lockup has been removed from the markup entirely, leaving the
   left flank as pure negative whitespace. The five nav links (or, on
   mobile, the single menu toggle) are pulled into a tight cluster against
   the right edge — flex-end, not the space-between used site-wide, so
   nothing stretches to fill the gap. */
body.page-home .site-header .wrap {
  justify-content: flex-end;
}

/* 17.3 — Hero canvas fusion.
   The hero carries no background rule — it simply shows body's own white
   (--canvas), and now that the crown lives in its own isolated flow track
   above it with a definitive margin gap, there's no overlap left to
   manage either way. The hero no longer runs through .lute-grid/.rail, so
   the waveform-spine hairline (.rail::before) and the gold eyebrow it
   carried simply don't exist on this section — nothing to override,
   nothing to hide. Left and right sides share one continuous white
   surface. */

/* -------------------------------------------------------------------------
   17.4 — THE ULTIMATE EQUILIBRIUM: a true 50/50 cinematic dialogue

   Two changes work together here. First, the hero's .wrap is allowed to
   exceed the site's standard 1440px content ceiling on genuinely wide
   glass — every other section keeps the original --content-max
   untouched, so the reading measure of body copy elsewhere on the page
   never changes. Second, the calligraphy frame keeps its landscape
   1.618:1 rectangle — φ itself, expressed horizontally, in keeping with
   the file's own "every spatial interval derives from this ratio"
   governing principle — but the grid track ratio is now an exact 1fr 1fr,
   not the earlier 1:1.4 that let the artwork visually outweigh the
   manifesto. The text column stretches and its content is centered
   vertically inside a flex column, so its bounding box matches the
   image's shorter, wider presence exactly. Text functions as graphic art,
   art functions as structural form — one shared horizontal band, equal
   weight on both flanks, generous shared whitespace between them via the
   gap. Scoped to ≥960px, the same threshold already governing the
   2-column split, so tablet keeps its clean single stacked column
   untouched. */
@media (min-width: 640px) and (max-width: 959.98px) {
  body.page-home .hero-split { grid-template-columns: 1fr; }
}

@media (min-width: 960px) {
  body.page-home .hero-split {
    grid-template-columns: 1fr 1fr;
    align-items: stretch;
    gap: var(--s-7);
  }

  body.page-home .hero-copy {
    display: flex;
    flex-direction: column;
    justify-content: center;
  }

  /* The manifesto column is now exactly as wide as the artwork column —
     true 50/50 visual weight — so the paragraph's measure is allowed to
     open up from the site-wide 34ch default (tuned for a much narrower
     column) to something that actually uses the space without running
     past a comfortable reading line length. */
  body.page-home .hero .lede {
    max-width: 46ch;
  }
}

@media (min-width: 1200px) {
  body.page-home .hero .wrap {
    max-width: min(96vw, 1800px);
  }
}

/* -------------------------------------------------------------------------
   17.4b — MOBILE HERO PULL-UP

   .hero's base rule (§7) carries padding-top: var(--s-8) — ~177px, tuned
   for desktop where the hero sits directly under a slim single-row nav.
   On mobile that same 177px stacks on top of a crown that's already
   9–14rem tall and a header row on top of that, pushing "Strings to
   Stars" nearly to the middle of the screen with a dead, unintentional
   gap above it — exactly what the screenshot showed. Mobile gets its own
   much tighter top padding instead, so the manifesto sits directly and
   deliberately beneath the nav line. Bottom padding is untouched — only
   the top has ever been the problem. */
@media (max-width: 767.98px) {
  body.page-home .hero {
    padding-top: var(--s-5);
  }
}

/* The calligraphy plate floats naked: no card, no border, no tint. This
   overrides .frame's general-purpose museum-mount treatment for the hero
   instance only — every other .frame on every other page (covers, the
   workshop portrait) keeps its border and tint exactly as before. The
   1.618:1 cinematic aspect-ratio lives here too, scoped to the hero, so
   the site-wide .plate--tall / .plate--wide / .plate--square utilities in
   §9 stay exactly as originally engineered for every other page. */
body.page-home .hero .plate--cinema .frame {
  aspect-ratio: 1.618 / 1;
}

body.page-home .hero .frame {
  background: transparent;
  border: none;
}

body.page-home .hero .frame:hover img,
body.page-home .hero .frame:focus-within img {
  transform: none;
}

/* -------------------------------------------------------------------------
   17.5 — THE EDITORIAL COVER GALLERY: a complete re-architecture

   The prior mosaic (formerly §8.1/8.2, .book-showcase) staged the five
   covers as a literal 1,1,2,3,5 Fibonacci spiral — a lovely idea on
   paper, but at real viewport sizes four of the five plates rendered
   smaller than a postage stamp, each carrying a permanently-visible
   caption that, on this page, fought the "text functions as graphic
   art" spirit established in the hero above it. That whole approach is
   retired for index.html specifically. [Pre-launch audit — Cleanup]
   .book-showcase has since been confirmed dead across all six pages of
   the project and removed outright from §8 (see that section's own
   comment for the full account) — this section builds an entirely new,
   unrelated component instead: .cover-gallery. */

/* Layout: CSS multi-column ("masonry") rather than a hand-placed grid —
   robust at any viewport with zero row-height arithmetic, and the
   natural column-fill produces the asymmetric, staggered installation
   rhythm on its own once each cover carries a distinct aspect-ratio
   (below): tall, square, and wide plates interleave unevenly down each
   column, the way prints hang salon-style rather than pinned to a table.
   Scales from a single generous column on mobile up to three on wide
   glass — three, not more, so every plate stays large enough to "read
   the fine typography... with the naked eye," per the brief. */
body.page-home .cover-gallery {
  column-count: 1;
  column-gap: var(--s-6);
}

body.page-home .cover-gallery .cover-card {
  display: block;
  width: 100%;
  break-inside: avoid;
  margin-bottom: var(--s-6);
}

@media (min-width: 700px) {
  body.page-home .cover-gallery { column-count: 2; column-gap: var(--s-7); }
  body.page-home .cover-gallery .cover-card { margin-bottom: var(--s-7); }
}

@media (min-width: 1180px) {
  body.page-home .cover-gallery { column-count: 3; }
}

/* Five covers, five distinct proportions — portrait, square, and
   landscape plates interleaved on purpose. Uniform ratios here would
   collapse the multi-column flow straight back into rigid rows; uneven
   heights are what makes it read as a curated installation instead. */
body.page-home .cover-gallery .cover-card:nth-child(1) .frame { aspect-ratio: 3 / 4; }
body.page-home .cover-gallery .cover-card:nth-child(2) .frame { aspect-ratio: 1 / 1; }
body.page-home .cover-gallery .cover-card:nth-child(3) .frame { aspect-ratio: 4 / 5; }
body.page-home .cover-gallery .cover-card:nth-child(4) .frame { aspect-ratio: 5 / 4; }
body.page-home .cover-gallery .cover-card:nth-child(5) .frame { aspect-ratio: 16 / 10; }

/* The plate itself: pristine and captionless at rest. No border, no
   tint panel, no drop shadow — the cover is the only thing on the
   canvas, full-bleed within its own frame. */
body.page-home .cover-gallery .frame {
  position: relative;
  overflow: hidden;
  background: var(--canvas-tint);
  border: none;
  cursor: pointer;
}

body.page-home .cover-gallery .frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  backface-visibility: hidden;
}

@media (hover: hover) and (pointer: fine) {
  body.page-home .cover-gallery .frame img {
    transition: transform 640ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  body.page-home .cover-gallery .frame:hover img,
  body.page-home .cover-gallery .frame:focus-visible img {
    transform: scale(1.045);
  }
}

/* The hover reveal: opaque white, heavy-eased, GPU-composited (opacity +
   transform only, nothing that triggers layout). Title stands beside
   year and medium rather than "author / publisher" — every volume here
   is written and designed by the same hand and carries no commercial
   publisher imprint, so fabricating publisher names would be dishonest;
   this surfaces the real metadata that already lives in the markup
   instead. Gated behind (hover: hover) so touch devices never get a
   metadata panel stuck open after a tap — the full detail is always one
   tap away regardless, via the existing full-viewport reveal (§8.3). */
body.page-home .cover-gallery .cover-meta {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-start;
  padding: var(--s-5);
  background: #FFFFFF;
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  body.page-home .cover-gallery .cover-meta {
    transition: opacity 640ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 640ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  body.page-home .cover-gallery .frame:hover .cover-meta,
  body.page-home .cover-gallery .frame:focus-visible .cover-meta {
    opacity: 1;
    transform: translateY(0);
  }
}

body.page-home .cover-gallery .cover-meta-num {
  font-family: var(--font-display);
  font-size: var(--t-lede);
  font-weight: 340;
  font-style: italic;
  color: var(--gold);
  line-height: 1;
}

body.page-home .cover-gallery .cover-meta-title {
  margin-top: var(--s-2);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-lede);
  font-weight: 480;
  line-height: 1.16;
  color: var(--ink);
}

body.page-home .cover-gallery .cover-meta-sub {
  margin-top: var(--s-2);
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-60);
}

/* Macro spacing: a deeper, more deliberate gap separating the widescreen
   hero above from this dramatically larger gallery, and an equally
   generous close before the gold rule that transitions into the
   supporting academic sections below. Section's site-wide padding-block
   (§3, var(--s-7)) still governs every other section on every other
   page; this bumps only #showcase, only on index.html. */
body.page-home #showcase {
  padding-top: var(--s-8);
  padding-bottom: var(--s-8);
}

/* -------------------------------------------------------------------------
   17.6 — ACADEMIC CITATION PURIFICATION: a quiet institutional title scale

   .pub-title previously borrowed --t-h2 wholesale — the exact size used
   by section headings like "Scholarly Publications" two lines above it —
   so every paper title shouted at the same volume as the section title
   introducing it, with no hierarchy between them. A premium academic
   directory (a journal's own table of contents, a CV, a citation index)
   sets entry titles a notch above body text, never at heading scale. The
   new size sits deliberately between --t-body and --t-lede — large enough
   to anchor each entry, quiet enough to read as reference material rather
   than a headline — with a touch more line-height to keep the now-denser
   type comfortable, and a lighter weight consistent with the rest of the
   page's restrained hand. Scoped to index.html only; research.html (if it
   reuses .pub-title) keeps the original site-wide rule in §10. */
body.page-home .pub-title {
  font-size: clamp(1.05rem, 0.97rem + 0.35vw, 1.3125rem);
  font-weight: 460;
  letter-spacing: -0.001em;
  line-height: 1.34;
}

/* -------------------------------------------------------------------------
   17.7 — THE ARCHIVE GATEWAY: an elevated, unified CTA link

   The "View the complete book design archive →" link previously shared
   the .doi class with the page's actual bibliographic citation links
   (the small italic DOI references under each publication) — so a
   primary call-to-action was styled identically to a footnote. .doi
   itself is untouched (real DOI links stay exactly as quiet as citations
   should be); this link, and its two siblings of the same kind further
   down the page ("Read the full narrative →", "View the full research
   archive →"), now carry a dedicated .archive-cta class instead: set in
   the UI face at nav scale, uppercase and tracked like the header links
   so it reads as a structural wayfinding element rather than a footnote,
   with the same low-latency border-color hover kinetics already used
   throughout the file (--dur-quick / --ease-resonant). Proportional, not
   oversized — one step above body text, one step below the section
   headings around it. */
.archive-cta {
  display: inline-block;
  font-family: var(--font-ui);
  font-size: var(--t-nav);
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
}

.archive-cta a {
  padding-bottom: 0.4em;
  border-bottom: 1.5px solid var(--gold-soft);
  transition: border-color var(--dur-quick) var(--ease-resonant),
              color var(--dur-quick) var(--ease-resonant);
}

.archive-cta a:hover,
.archive-cta a:focus-visible {
  border-color: var(--gold);
  color: var(--ink);
}

/* ==========================================================================
   18. BOOK-DESIGNS.HTML — THE PORTFOLIO PAGE (page-book-designs only)
   ------------------------------------------------------------------------
   Purely additive: nothing above this line was touched to build this
   section. book-designs.html's <body> carries two classes —
   class="page-home page-book-designs". page-home is reused deliberately,
   not by accident: it's what already drives the shared site chrome (the
   ceiling crown's positioning/sizing in §17.1, and the header's
   flex-end link cluster in §17.2), and every one of those selectors
   requires page-home *together with* an element that only exists on the
   homepage (.hero, #showcase, .pub-title, …) — none of which are present
   here, so they simply never match. page-book-designs is the second,
   page-book-designs. class, scoping everything genuinely new below.
   ========================================================================== */

/* -------------------------------------------------------------------------
   18.1 — THE FILTER BAR: a silent typographic index, not a UI widget

   Four plain-text buttons on the bare canvas, tracked and spaced like a
   catalogue's own running head rather than styled as pills or tabs — no
   borders, no background fills at rest. The active state is carried
   entirely by weight, color, and a thin gold underline, consistent with
   the single-pixel-accent rule the whole site already follows for its
   active nav link (§6). Generous horizontal gaps do the "immense
   breathing room" work instead of any visible container chrome. */
.portfolio-filter {
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--s-6);
  row-gap: var(--s-3);
  padding: var(--s-7) 0 var(--s-6);
}

.filter-trigger {
  font-family: var(--font-ui);
  font-size: var(--t-nav);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-60);
  padding-bottom: var(--s-2);
  border-bottom: 1px solid transparent;
  transition: color var(--dur-quick) var(--ease-resonant),
              border-color var(--dur-quick) var(--ease-resonant);
}

.filter-trigger:hover {
  color: var(--ink);
}

.filter-trigger[aria-pressed="true"] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--gold);
}

@media (max-width: 640px) {
  .portfolio-filter {
    column-gap: var(--s-5);
    padding: var(--s-6) 0 var(--s-5);
  }
}

/* -------------------------------------------------------------------------
   18.2 — THE PORTFOLIO GRID: same multi-column technique as .cover-gallery
   (§17.5), an independent component rather than a literal reuse of it —
   this page needs a filterable data-category on every item and a
   center-anchored (not bottom-anchored) hover reveal, so a shared class
   would mean two different interaction contracts fighting over one
   selector. The underlying idea is identical: CSS multi-column masonry,
   varied aspect-ratios per item, organic stagger with zero row-height
   arithmetic, robust at any viewport or zoom level. */
.portfolio-grid {
  column-count: 1;
  column-gap: var(--s-6);
}

.portfolio-item {
  display: block;
  width: 100%;
  break-inside: avoid;
  margin-bottom: var(--s-6);
}

@media (min-width: 700px) {
  .portfolio-grid { column-count: 2; column-gap: var(--s-7); }
  .portfolio-item { margin-bottom: var(--s-7); }
}

@media (min-width: 1180px) {
  .portfolio-grid { column-count: 3; }
}

/* Ten plates, ten proportions — portrait, square, and landscape frames
   interleaved on purpose so the multi-column flow reads as a curated
   installation rather than a uniform table. */
.portfolio-item:nth-child(1)  .frame { aspect-ratio: 3 / 4;  }
.portfolio-item:nth-child(2)  .frame { aspect-ratio: 1 / 1;  }
.portfolio-item:nth-child(3)  .frame { aspect-ratio: 4 / 5;  }
.portfolio-item:nth-child(4)  .frame { aspect-ratio: 5 / 4;  }
.portfolio-item:nth-child(5)  .frame { aspect-ratio: 16 / 10; }
.portfolio-item:nth-child(6)  .frame { aspect-ratio: 4 / 5;  }
.portfolio-item:nth-child(7)  .frame { aspect-ratio: 1 / 1;  }
.portfolio-item:nth-child(8)  .frame { aspect-ratio: 5 / 4;  }
.portfolio-item:nth-child(9)  .frame { aspect-ratio: 3 / 4;  }
.portfolio-item:nth-child(10) .frame { aspect-ratio: 16 / 10; }

/* The plate: pristine and captionless at rest — identical resting-state
   philosophy to .cover-gallery. No border, no tint panel, no shadow. */
.portfolio-grid .frame {
  position: relative;
  overflow: hidden;
  background: var(--canvas-tint);
  border: none;
  cursor: pointer;
}

.portfolio-grid .frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  backface-visibility: hidden;
}

@media (hover: hover) and (pointer: fine) {
  .portfolio-grid .frame img {
    transition: transform 640ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .portfolio-grid .frame:hover img,
  .portfolio-grid .frame:focus-visible img {
    transform: scale(1.045);
  }
}

/* The hover reveal: 100% opaque museum white, Title / Year / Medium
   set dead-center — small-scale, black ink, no gold accent here (gold
   stays reserved for active/selected states per the filter bar above,
   keeping its meaning consistent across the page rather than doubling
   as decoration). Gated behind (hover: hover) so touch devices never
   get a panel stuck open after a tap; full detail is always one tap
   away via the existing full-viewport reveal sheet (§8.3). */
.portfolio-meta {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: var(--s-5);
  background: #FFFFFF;
  opacity: 0;
  transform: scale(0.98);
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .portfolio-meta {
    transition: opacity 640ms cubic-bezier(0.16, 1, 0.3, 1),
                transform 640ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .portfolio-grid .frame:hover .portfolio-meta,
  .portfolio-grid .frame:focus-visible .portfolio-meta {
    opacity: 1;
    transform: scale(1);
  }
}

.portfolio-meta-title {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: var(--t-lede);
  font-weight: 480;
  line-height: 1.16;
  color: var(--ink);
}

.portfolio-meta-sub {
  margin-top: var(--s-2);
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-60);
}

/* -------------------------------------------------------------------------
   18.3 — THE FILTER ENGINE'S VISUAL HALF (the JS half lives in the page's
   own <script>, at the bottom of book-designs.html)

   Filtered-out items fade to transparent and scale down fractionally —
   opacity and transform only, both compositor-driven, so filtering never
   forces a synchronous layout recalculation. They deliberately stay in
   the column flow rather than being removed from it: collapsing a
   multi-column masonry's gaps on every click *is* the layout thrash this
   section exists to avoid, so a held, invisible slot beats a jumping
   grid. pointer-events:none keeps a hidden plate from swallowing clicks
   meant for whatever the reflow has placed near it. */
.portfolio-item {
  transition: opacity 520ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 520ms cubic-bezier(0.16, 1, 0.3, 1);
}

.portfolio-item.is-hidden {
  opacity: 0;
  transform: scale(0.97) translateY(6px);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .portfolio-item,
  .portfolio-meta,
  .portfolio-grid .frame img {
    transition: none !important;
  }
}

/* -------------------------------------------------------------------------
   18.4 — MACRO SPACING for the portfolio page, matching the depth
   established for #showcase on the homepage (§17.5.4) rather than the
   site's default --s-7 section rhythm — the same "definitive, deep, and
   harmonious" gap philosophy, applied consistently on the page whose
   entire purpose is the covers themselves. */
body.page-book-designs main .section:first-of-type {
  padding-top: var(--s-6);
  padding-bottom: var(--s-8);
}

/* ==========================================================================
   19. RESEARCH.HTML — THE SCHOLARLY CITATION DIRECTORY (page-research only)
   ------------------------------------------------------------------------
   Purely additive, same contract as §18: nothing above this line is
   touched. research.html's <body> carries class="page-home page-research"
   — page-home for the same free chrome inheritance explained at the top
   of §18 (crown + header flank), page-research scoping everything below,
   which is new to this page alone.
   ========================================================================== */

/* A second :root block — valid CSS, and the cleanest way to add exactly
   one new custom property without touching the original token block in
   §1. --border-editorial is a semantic alias, not a new color: it points
   at the same --ink-15 hairline value already used for every other
   divider on the site (.hr-ink, .pub, .frame's border), so this page's
   dividers stay in lockstep with the rest of the system by construction,
   not by coincidence. */
:root {
  --border-editorial: var(--ink-15);
}

/* -------------------------------------------------------------------------
   19.1 — THE CITATION INDEX: a printed monograph catalog, not a card list

   No boxes, no shadows, no background fills — a single running column of
   entries separated only by a hairline, the way an Oxford University
   Press catalog folio or a CV's own publication list is actually
   typeset. Generous block padding per entry does the "immensely
   spacious, pure, and quiet" work; the hairline is the only visible
   structure. */
.citation-index {
  max-width: 52rem;
}

.citation-entry {
  padding: var(--s-7) 0;
  border-bottom: 1px solid var(--border-editorial);
}

.citation-entry:first-child {
  padding-top: var(--s-6);
}

.citation-entry:last-child {
  border-bottom: none;
}

.citation-number {
  display: block;
  font-family: var(--font-display);
  font-size: var(--t-lede);
  font-weight: 340;
  font-style: italic;
  color: var(--gold);
  line-height: 1;
}

.citation-title {
  margin-top: var(--s-3);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(1.2rem, 1.05rem + 0.5vw, 1.75rem);
  font-weight: 480;
  line-height: 1.28;
  color: var(--ink);
  max-width: 42ch;
}

.citation-meta {
  margin-top: var(--s-3);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-style: italic;
  color: var(--ink-60);
  line-height: 1.6;
}

.citation-meta em {
  font-style: normal;
  color: var(--ink-90);
}

.citation-entry .doi {
  display: inline-block;
  margin-top: var(--s-3);
}

/* Paper V carries no DOI yet — a quiet, non-interactive status line at
   the same visual weight as .doi so the entry doesn't read as "broken"
   or unfinished, just honestly labeled. No hover kinetics: it isn't a
   link and shouldn't invite a click that goes nowhere.
   [Pre-launch audit — Bug Fix] This rule's color previously read
   --ink-35, which directly contradicted the "same visual weight as
   .doi" sentence above (.doi is --ink-60) and measured only ~2.2:1
   against either background — a hard WCAG AA failure at this
   font-size. Corrected to --ink-60, which now makes the comment above
   literally true and clears AA (~4.5–4.6:1; see the verified figures
   on the --ink-60 token itself in §1). */
.citation-status {
  display: inline-block;
  margin-top: var(--s-3);
  font-family: var(--font-body);
  font-size: var(--t-caption);
  font-style: italic;
  letter-spacing: 0.02em;
  color: var(--ink-60);
}

@media (max-width: 640px) {
  .citation-entry { padding: var(--s-6) 0; }
  .citation-title { max-width: none; }
}

/* -------------------------------------------------------------------------
   19.2 — MACRO SPACING, matching the same "definitive, deep" section
   depth already established for the homepage showcase (§17.5.4) and the
   book-designs archive (§18.4) rather than the site's default --s-7
   rhythm — consistent depth philosophy across all three content pages. */
body.page-research main .section:first-of-type {
  padding-top: var(--s-6);
  padding-bottom: var(--s-8);
}

/* ==========================================================================
   20. ABOUT.HTML — THE NARRATIVE MANUSCRIPT (page-about only)
   ------------------------------------------------------------------------
   Purely additive, same contract as §18 and §19: nothing above this line
   is touched. about.html's <body> carries class="page-home page-about" —
   page-home for the same free chrome inheritance explained at the top of
   §18 (crown + header flank), page-about scoping everything below.

   LAYOUT DECISION: rather than a stacked hero-plus-columns template, the
   portrait and the two biographical chapters share a single two-track
   manuscript grid — the photograph sticks in place in its own column
   while the reader moves through Chapter I and Chapter II beside it, the
   way a museum wall-text keeps one portrait fixed beside a long block of
   didactic copy rather than repeating the image per paragraph. It's the
   one page in the archive built around a scroll-linked relationship
   between image and text instead of a static split, which is deliberate:
   this is the one page that is *about* a person, not an artifact. */
.bio-manuscript {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-7);
}

.bio-frame {
  position: relative;
}

.bio-frame .frame {
  aspect-ratio: 4 / 5;
  border: none;
  background: var(--canvas-tint);
}

.bio-chapters {
  display: flex;
  flex-direction: column;
  gap: var(--s-8);
}

.bio-chapter-number {
  display: block;
  font-family: var(--font-display);
  font-size: var(--t-lede);
  font-weight: 340;
  font-style: italic;
  color: var(--gold);
  line-height: 1;
}

.bio-chapter-title {
  margin-top: var(--s-3);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(1.6rem, 1.35rem + 1vw, 2.375rem);
  font-weight: 480;
  line-height: 1.18;
  color: var(--ink);
}

/* The strict 45–55ch editorial measure this page was asked for — a
   dedicated max-width on the chapter body copy rather than reusing
   .body-text's own site-wide 62ch (§2), which is tuned for the wider
   academic paragraphs elsewhere and would read too long for a two-column
   manuscript layout this narrow. */
.bio-chapter p {
  margin-top: var(--s-4);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.75;
  color: var(--ink-90);
  max-width: 52ch;
}

.bio-chapter p + p {
  margin-top: var(--s-3);
}

@media (min-width: 860px) {
  .bio-manuscript {
    grid-template-columns: 1fr 1.15fr;
    align-items: start;
    gap: var(--s-8);
  }

  /* The sticky offset accounts for the header's own height so the
     portrait settles just beneath the nav line rather than under it —
     var(--s-6) alone would leave it riding partially behind the header
     on shorter viewports. */
  .bio-frame {
    position: sticky;
    top: calc(var(--s-6) + 4.5rem);
  }
}

@media (max-width: 640px) {
  .bio-manuscript { gap: var(--s-6); }
  .bio-chapters { gap: var(--s-7); }
  .bio-chapter p { max-width: none; }
}

/* Macro spacing, matching the same "definitive, deep" section depth
   already established across the homepage showcase, the book-designs
   archive, and the research index. */
body.page-about main .section:first-of-type {
  padding-top: var(--s-6);
  padding-bottom: var(--s-8);
}

/* ==========================================================================
   21. CONTACT.HTML — THE CORRESPONDENCE INDEX (page-contact only)
   ------------------------------------------------------------------------
   Purely additive, same contract as §§18–20: nothing above this line is
   touched. contact.html's <body> carries class="page-home page-contact"
   — page-home for the same free chrome inheritance explained at the top
   of §18 (crown + header flank), page-contact scoping everything below.
   No new tokens needed here: --border-editorial already exists (§19) as
   a genuine shared custom property, not a page-scoped one, so this
   section reuses it directly rather than redeclaring it.
   ========================================================================== */

/* -------------------------------------------------------------------------
   21.1 — THE CORRESPONDENCE INDEX

   Same governing idea as .citation-index (§19.1): a single running
   column, no boxes, no shadows, separated only by a hairline — four
   channels of correspondence typeset like an index rather than a
   contact form. A small tracked label identifies each channel; the
   destination itself is set at display scale, since on a page whose
   entire purpose is "how do I reach this person," the destination is
   the content, not a caption underneath it. */
.contact-index {
  max-width: 40rem;
}

.contact-entry {
  padding: var(--s-6) 0;
  border-bottom: 1px solid var(--border-editorial);
}

.contact-entry:first-child {
  padding-top: var(--s-6);
}

.contact-entry:last-child {
  border-bottom: none;
}

.contact-label {
  display: block;
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-60);
}

.contact-link {
  display: inline-block;
  margin-top: var(--s-2);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(1.35rem, 1.15rem + 1vw, 2.125rem);
  font-weight: 480;
  line-height: 1.2;
  color: var(--ink);
  border-bottom: 1px solid transparent;
  transition: color var(--dur-quick) var(--ease-resonant),
              border-color var(--dur-quick) var(--ease-resonant);
}

.contact-link:hover,
.contact-link:focus-visible {
  color: var(--gold);
  border-bottom-color: var(--gold-soft);
}

.contact-entry-note {
  margin-top: var(--s-2);
  font-family: var(--font-body);
  font-size: var(--t-body);
  font-style: italic;
  color: var(--ink-60);
}

@media (max-width: 640px) {
  .contact-entry { padding: var(--s-5) 0; }
}

/* -------------------------------------------------------------------------
   21.2 — MACRO SPACING, matching the same "definitive, deep" section
   depth established across every other content page in the archive. */
body.page-contact main .section:first-of-type {
  padding-top: var(--s-6);
  padding-bottom: var(--s-8);
}

/* ==========================================================================
   22. SKIP LINK — ACCESSIBILITY UTILITY (site-wide)
   ------------------------------------------------------------------------
   New section, added during the pre-launch audit — purely additive,
   nothing above this line was touched to make room for it.

   One skip-to-content link, meant to be the very first focusable element
   in the DOM on every page (see the corresponding markup note just inside
   <body> in index.html; the same single line should be added to the other
   four pages in their own review pass — none of them currently have it
   either). It sits off-canvas, fully out of the way, until it receives
   keyboard focus, at which point it becomes a real, on-brand control —
   same ink/gold/Inter treatment as every other focusable element on the
   site, not a generic browser-default outline box dropped on top of the
   design. This lets a keyboard or screen-reader user jump straight past
   the identical five-link primary nav repeated at the top of every page
   and land directly in that page's actual content — exactly the kind of
   repetitive boilerplate skip-links exist to solve.
   ========================================================================== */

.skip-link {
  position: fixed;
  top: var(--s-3);
  left: var(--s-3);
  z-index: 200; /* above the book-reveal modal (100) and nav panel (60) —
                   deliberately the topmost layer on the page, since it
                   only ever needs to appear before any other layer has
                   had a chance to open. */
  transform: translateY(-150%);
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--ink);
  transition: transform var(--dur-quick) var(--ease-resonant);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .skip-link { transition: none; }
}
/* ==========================================================================
   23. MOBILE VERTICAL RHYTHM — sitewide spacing patch for small viewports
   ------------------------------------------------------------------------
   .site-footer, #showcase, and the closing padding of the first section
   on four other content pages all use --s-8 (~177px). Their selectors
   either carry higher specificity than the general .section mobile
   override in §15 (an ID, or a multi-class + tag + pseudo-class chain),
   or aren't .section at all (site-footer uses margin, not padding-block).
   Result without this patch: on mobile, these five spots stay locked at
   ~177px while the rest of the site has already dropped to --s-6
   (~68px). Each selector below is repeated at the exact same specificity
   as its original definition; because this block sits at the very end of
   the file, standard cascade rules (equal specificity → last rule in
   source order wins) let it override the desktop value at ≤640px.
   ========================================================================== */

@media (max-width: 640px) {
  .site-footer {
    margin-top: var(--s-6);
  }

  body.page-home #showcase {
    padding-top: var(--s-6);
    padding-bottom: var(--s-6);
  }

  body.page-book-designs main .section:first-of-type,
  body.page-research main .section:first-of-type,
  body.page-about main .section:first-of-type,
  body.page-contact main .section:first-of-type {
    padding-bottom: var(--s-6);
  }
}
/* ==========================================================================
   24. PEER REVIEW LIST — research.html only
   ------------------------------------------------------------------------
   .review-journal reuses the exact clamp/weight already established in
   §17.6 (.pub-title) for "one notch above body text, one notch below a
   section heading" — no new type scale invented, same governing rule
   applied to a second, analogous hierarchy problem.
   ========================================================================== */
.review-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-6);
}

.review-journal {
  margin-top: var(--s-2);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(1.05rem, 0.97rem + 0.35vw, 1.3125rem);
  font-weight: 480;
  letter-spacing: -0.001em;
  line-height: 1.3;
  color: var(--ink);
}

/* ==========================================================================
   25. COVER SYSTEM v2 — real proportions, author captions, full-cover viewer
   Purely additive. Each selector matches (or exceeds) the specificity of the
   rule it refines; later source order wins.
   ========================================================================== */

/* 25.1 — Base fixes: .frame inside a <button> is a <span> (inline), where
   aspect-ratio and overflow do nothing. Buttons also carry UA padding. */
.plate-trigger { padding: 0; text-align: left; }

body.page-home .cover-gallery .frame,
.portfolio-grid .frame {
  display: block;
  width: 100%;
}

/* Hairline edge so pale covers never dissolve into the canvas */
body.page-home .cover-gallery .frame::after,
.portfolio-grid .frame::after {
  content: "";
  position: absolute;
  inset: 0;
  box-shadow: inset 0 0 0 1px var(--ink-08);
  pointer-events: none;
}

/* 25.2 — Hover caption: a compact strip at the foot of the cover (title,
   author, year) instead of an opaque panel that erased the whole cover.
   Keyboard focus triggers it too (focus lives on the <button>). */
body.page-home .cover-gallery .cover-meta {
  inset: auto 0 0 0;
  padding: var(--s-3) var(--s-4);
  background: rgba(255, 255, 255, 0.94);
}

body.page-home .cover-gallery .cover-card:hover .cover-meta,
body.page-home .cover-gallery .cover-card:focus-visible .cover-meta {
  opacity: 1;
  transform: translateY(0);
}

body.page-home .cover-gallery .cover-card:focus-visible .frame img {
  transform: scale(1.045);
}

.portfolio-meta {
  inset: auto 0 0 0;
  align-items: flex-start;
  justify-content: flex-end;
  text-align: left;
  padding: var(--s-3) var(--s-4);
  background: rgba(255, 255, 255, 0.94);
  transform: translateY(8px);
}

.portfolio-grid .portfolio-item:hover .portfolio-meta,
.portfolio-grid .portfolio-item:focus-visible .portfolio-meta {
  opacity: 1;
  transform: translateY(0);
}

.portfolio-grid .portfolio-item:focus-visible .frame img {
  transform: scale(1.045);
}

body.page-home .cover-gallery .cover-meta-title,
.portfolio-meta-title {
  font-size: clamp(1.05rem, 0.97rem + 0.35vw, 1.3125rem);
  margin-top: 0;
}

.cover-meta-author,
.portfolio-meta-author {
  display: block;
  margin-top: var(--s-1);
  font-family: var(--font-body);
  font-size: clamp(0.85rem, 0.82rem + 0.15vw, 0.95rem);
  font-style: italic;
  line-height: 1.3;
  color: var(--ink-60);
}

/* 25.3 — `hidden` must beat the display values set on these components */
.portfolio-item[hidden],
.portfolio-filter[hidden],
.filter-trigger[hidden],
.reveal-views[hidden],
.reveal-step[hidden],
.reveal-count[hidden] { display: none; }

/* 25.4 — The viewer adapts to the image's real shape */
.reveal-frame {
  aspect-ratio: auto;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  background: transparent;
  border: none;
}

.reveal-frame img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: calc(100vh - 9rem);
  max-height: calc(100dvh - 9rem);
  object-fit: contain;
  background: var(--canvas-tint);
  outline: 1px solid var(--ink-08);
  outline-offset: -1px;
  transition: opacity var(--dur-frame) var(--ease-resonant);
}

.reveal-frame[data-loading="true"] img { opacity: 0; }

/* Wide image (full cover): image on top at full width, text beneath */
.book-reveal[data-layout="wide"] .reveal-body {
  grid-template-columns: 1fr;
  gap: var(--s-6);
}
.book-reveal[data-layout="wide"] .reveal-detail { max-width: 62ch; }

.reveal-author {
  margin-top: var(--s-2);
  font-family: var(--font-body);
  font-size: var(--t-lede);
  font-style: italic;
  color: var(--ink-60);
  line-height: 1.3;
}
.reveal-note { font-style: italic; }

/* 25.5 — Full Cover / Front Cover switch */
.reveal-views {
  display: flex;
  gap: var(--s-5);
  margin-top: var(--s-4);
}

.view-trigger {
  font-family: var(--font-ui);
  font-size: var(--t-nav);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-60);
  padding: 0 0 var(--s-2);
  border-bottom: 1px solid transparent;
  transition: color var(--dur-quick) var(--ease-resonant),
              border-color var(--dur-quick) var(--ease-resonant);
}
.view-trigger:hover { color: var(--ink); }
.view-trigger[aria-pressed="true"] {
  color: var(--ink);
  font-weight: 600;
  border-bottom-color: var(--gold);
}

/* 25.6 — Viewer top bar: prev / counter / next left, close right */
.reveal-bar {
  position: sticky;
  top: var(--s-4);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  pointer-events: none;
}
.reveal-bar > * { pointer-events: auto; }
.reveal-bar .reveal-close { position: static; margin-left: 0; }

.reveal-nav {
  display: flex;
  align-items: center;
  gap: var(--s-3);
}

.reveal-step {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  border: 1px solid var(--ink-15);
  padding: var(--s-2) var(--s-3);
  background: var(--canvas);
  transition: border-color var(--dur-quick) var(--ease-resonant);
}
.reveal-step:hover,
.reveal-step:focus-visible { border-color: var(--gold); }

.reveal-count {
  font-family: var(--font-ui);
  font-size: var(--t-caption);
  letter-spacing: 0.18em;
  font-variant-numeric: tabular-nums;
  color: var(--ink-60);
  background: var(--canvas);
  padding: var(--s-1) 0;
}

@media (max-width: 640px) {
  .reveal-bar { top: var(--s-2); }
  .reveal-step { padding: var(--s-2); }
  .reveal-step-label { display: none; }
  .reveal-nav { gap: var(--s-2); }
}

/* 25.7 — Touch devices have no hover: the caption is permanent and sits
   BELOW each cover. The frame stops forcing a ratio and stops clipping. */
@media (hover: none), (pointer: coarse) {
  body.page-home .cover-gallery .frame,
  .portfolio-grid .frame {
    aspect-ratio: auto !important;
    overflow: visible;
    background: transparent;
  }

  body.page-home .cover-gallery .frame img,
  .portfolio-grid .frame img {
    height: auto;
    outline: 1px solid var(--ink-08);
    outline-offset: -1px;
  }

  body.page-home .cover-gallery .frame:hover img,
  .portfolio-grid .frame:hover img { transform: none; }

  body.page-home .cover-gallery .frame::after,
  .portfolio-grid .frame::after { display: none; }

  body.page-home .cover-gallery .cover-meta,
  .portfolio-grid .portfolio-meta {
    position: static;
    inset: auto;
    opacity: 1;
    transform: none;
    background: transparent;
    padding: var(--s-3) 0 0;
  }

  body.page-home .cover-gallery .frame--pending,
  .portfolio-grid .frame--pending { min-height: 12rem; }
}

@media (prefers-reduced-motion: reduce) {
  .reveal-frame img { transition: none; }
}

/* ==========================================================================
   26. SMALL FIXES FROM REVIEW
   ========================================================================== */

/* The browser's default 40px blockquote indent pushed the pull-quote off
   the column. Delete this one rule if you like that indent. */
blockquote { margin: 0; }

/* "Read the full narrative →" and "View the full research archive →" carry
   .archive-cta directly on the <a>, which the §17.7 rule (.archive-cta a)
   never matched — so they had no gold underline or hover. */
a.archive-cta {
  padding-bottom: 0.4em;
  border-bottom: 1.5px solid var(--gold-soft);
  transition: border-color var(--dur-quick) var(--ease-resonant);
}
a.archive-cta:hover,
a.archive-cta:focus-visible { border-color: var(--gold); }
