/* ============================================================
   ERAS photo — Editorial alternative (v2).
   Fashion-magazine layout on a scroll-driven photo foundation.
   Text lives in TL + BR corners on the photo spreads.
   ============================================================ */

/* ── Tokens ────────────────────────────────────────────────── */
:root {
  --bone:        #F5F0E8;
  --bone-deep:   #ECE5D8;
  --cream:       #FAF6EF;
  --ink:         #1A1814;
  --ink-soft:    #2E2A23;
  --mute:        #6B6258;
  --mute-soft:   #948A7E;
  --hair:        rgba(26, 24, 20, 0.18);
  --hair-strong: rgba(26, 24, 20, 0.35);
  --accent:      #B86A3A;
  --accent-soft: #C58A5F;

  --on-photo:        #FBF7EE;
  --on-photo-soft:   rgba(251, 247, 238, 0.78);
  --on-photo-hair:   rgba(251, 247, 238, 0.55);

  /* Inverted (dark) band — used by the closing slide seam and the
     trust + pricing sections so they read as black with cream type.
     Warm near-black to match the evening-sky climax (rgb(22,12,6)). */
  --noir:        #100C08;
  --noir-line:   rgba(250, 246, 239, 0.14);
  --noir-line-2: rgba(250, 246, 239, 0.30);

  --serif:   "Instrument Serif", "Fraunces", Georgia, "Times New Roman", serif;
  --serif-2: "Fraunces", "Instrument Serif", Georgia, serif;
  --sans:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --max:    1320px;
  --gutter: clamp(24px, 4vw, 64px);

  --t-fast: 240ms;
  --t:      420ms;
  --t-slow: 900ms;
  --ease:   cubic-bezier(0.2, 0.7, 0.2, 1);
}

* { box-sizing: border-box; }

html, body {
  margin: 0; padding: 0;
  background: var(--bone);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

html {
  scroll-behavior: smooth;
  /* Fixed nav covers the top ~110px of the viewport on desktop. Without
     this padding, anchor jumps (e.g. hitting "Book" in the nav → #book)
     would land the target row directly under the nav and hide the
     section header. With it, the browser leaves room for the nav so
     "The Sessions" folio + title + deck sit fully visible above the
     New York / Miami toggle. Applies to `scrollIntoView` calls too,
     not just plain href anchors. */
  scroll-padding-top: 110px;
}
@media (max-width: 780px) {
  html { scroll-padding-top: 84px; }
}

img, svg { display: block; max-width: 100%; }
button { font: inherit; cursor: pointer; }
a { color: inherit; text-decoration: none; }

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

/* ── Scroll-driven photo stage (fixed bg) ─────────────────── */
.bg-stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  background: var(--ink);
  transition: opacity var(--t-slow) var(--ease);
}

.bg-stage.is-bone { opacity: 0; }

.bg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 32%;
  opacity: 0;
  z-index: 1;
  /* Only opacity transitions on is-active toggle. Transform is driven by an
     infinite-alternate animation on the layer itself (see below), so it never
     snaps when is-active is removed and never couples to the crossfade. */
  transition: opacity 600ms ease-out;
  will-change: opacity;
  /* GPU promotion — keeps transitions buttery in Safari & Chrome. */
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
  -webkit-transform: translateZ(0);
          transform: translateZ(0);
  /* Continuous, gentle Ken Burns. Always running on every layer regardless of
     activation state. Different `animation-delay` per layer (set in JS) so the
     layers are at different points in the cycle when they're shown. */
  animation: bgDrift 48s infinite alternate ease-in-out;
}
.bg-img.is-active {
  opacity: 1;
  /* Active layer paints on top during the crossfade. */
  z-index: 2;
}
/* NOTE: the intro slideshow (Cover = data-idx 0, Impression = data-idx 6) is
   severed from the tier slideshow in JS, not CSS — see `setPhoto` in main.js.
   A blunt CSS `transition:none` on these layers (tried earlier) killed the
   Cover→Impression crossfade too, leaving a dark flash. setPhoto now snaps the
   outgoing layer out instantly ONLY when crossing OUT of the intro group into
   the tiers (Impression → ERAS Basic, across the carousel), and keeps the
   normal 600ms crossfade for Cover↔Impression and everything else. */
@keyframes bgDrift {
  from { transform: translateZ(0) scale(1.05); }
  to   { transform: translateZ(0) scale(1.11); }
}

/* ── Scroll-driven 3D map journey (NY → Miami chapters) ─────────
   A full-bleed canvas painted by main.js (mapJourney). It replaces
   the portrait photos on the New York + Miami spreads. Sits above
   the photo layers but below the vignette. Hidden (opacity 0) until
   main.js adds `.is-map` to the stage as those spreads enter view,
   at which point the photo layers + vignette fade out and the cream
   map shows. The canvas itself is sized to the device pixel ratio in
   JS; CSS only controls layout + the crossfade. */
.bg-map {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  z-index: 3;            /* above .bg-img (1/2), below .bg-vignette (default) */
  opacity: 0;
  pointer-events: none;
  transition: opacity 600ms ease-out;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
}
/* When the journey is active: reveal the map, mute the photo crossfade
   underneath, swap the dark stage backing to the brand cream, and drop
   the photographic vignette (the map has its own soft vignette baked in). */
.bg-stage.is-map { background: var(--bone); }
.bg-stage.is-map .bg-map { opacity: 1; }
/* Hide the photo layers INSTANTLY (override their 600ms opacity transition) so
   no headshot lingers under the map as it appears. The stage is cream behind. */
.bg-stage.is-map .bg-img { opacity: 0; transition: none; }
.bg-stage.is-map .bg-vignette { opacity: 0; transition: opacity 600ms ease-out; }

/* Empty map spreads still need to occupy a full viewport each so the
   scroll engine has range to drive the journey. (`.spread` is already
   100vh, but these carry no corner copy.) */
.spread-map { pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  /* Map still renders (it's the chapter background), but main.js skips
     the eased camera interpolation and snaps per-spread instead. */
  .bg-map { transition: none; }
}

/* Frame vignette.
   A classic photographic ring vignette is the load-bearing layer: the
   center ~28% of the frame is held completely clear (faces always land
   there), and darkening builds smoothly through 58% → 82% → 100% to all
   four edges + corners. On top of that we layer:
     • A heavier BR corner radial — every spread's headline lives in BR.
     • A softer TL corner radial — supports the nav brand + tier-display
       labels on spreads 02–04.
     • Two thin edge washes for the very top (nav band) and very bottom
       (meta line / footer).
   Stops are tuned so the composite darkening at the corners is strong
   enough to carry serif type, while the face zone stays untouched. */
.bg-vignette {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    /* Top edge wash — nav band legibility on white photo tops. */
    linear-gradient(to bottom, rgba(8, 6, 4, 0.55) 0%, rgba(8, 6, 4, 0) 20%),
    /* Bottom edge wash — meta line + sticky CTA support. Heavier than the
       top because the entire BR text block (lede included) sits in this zone. */
    linear-gradient(to top,    rgba(8, 6, 4, 0.68) 0%, rgba(8, 6, 4, 0) 34%),
    /* BR corner anchor — every spread's headline + the cover lede live here.
       Pushed harder than the symmetric TL anchor because BR carries the most copy. */
    radial-gradient(
      ellipse 72% 64% at 100% 100%,
      rgba(8, 6, 4, 0.7)  0%,
      rgba(8, 6, 4, 0.46) 28%,
      rgba(8, 6, 4, 0.2)  56%,
      rgba(8, 6, 4, 0.04) 82%,
      rgba(8, 6, 4, 0)    92%
    ),
    /* TL corner anchor — tier-display headlines on spreads 02–04. Softer
       than BR because the nav also gets its own backdrop-blur when scrolled. */
    radial-gradient(
      ellipse 54% 50% at 0% 0%,
      rgba(8, 6, 4, 0.45) 0%,
      rgba(8, 6, 4, 0.24) 32%,
      rgba(8, 6, 4, 0.08) 62%,
      rgba(8, 6, 4, 0)    86%
    ),
    /* PAGE-WIDE RING VIGNETTE — the dominant darkening layer. Clear in the
       center face zone, then a smooth roll-off through the mid-frame out
       to the corners. Stronger outer values than round 20. */
    radial-gradient(
      ellipse 95% 90% at 50% 50%,
      rgba(8, 6, 4, 0)    0%,
      rgba(8, 6, 4, 0)    26%,
      rgba(8, 6, 4, 0.26) 56%,
      rgba(8, 6, 4, 0.58) 80%,
      rgba(8, 6, 4, 0.82) 100%
    );
}
@media (max-width: 880px) {
  /* Mobile is taller and narrower, so the ring tightens vertically and the
     edge washes carry more weight. */
  .bg-vignette {
    background:
      linear-gradient(to bottom, rgba(8, 6, 4, 0.58) 0%, rgba(8, 6, 4, 0) 22%),
      linear-gradient(to top,    rgba(8, 6, 4, 0.58) 0%, rgba(8, 6, 4, 0) 30%),
      radial-gradient(
        ellipse 90% 50% at 100% 100%,
        rgba(8, 6, 4, 0.6)  0%,
        rgba(8, 6, 4, 0.36) 32%,
        rgba(8, 6, 4, 0.12) 62%,
        rgba(8, 6, 4, 0)    86%
      ),
      radial-gradient(
        ellipse 85% 45% at 0% 0%,
        rgba(8, 6, 4, 0.48) 0%,
        rgba(8, 6, 4, 0.24) 34%,
        rgba(8, 6, 4, 0.08) 64%,
        rgba(8, 6, 4, 0)    88%
      ),
      radial-gradient(
        ellipse 110% 80% at 50% 50%,
        rgba(8, 6, 4, 0)    0%,
        rgba(8, 6, 4, 0)    24%,
        rgba(8, 6, 4, 0.26) 56%,
        rgba(8, 6, 4, 0.58) 82%,
        rgba(8, 6, 4, 0.8)  100%
      );
  }
}

/* ── Nav ───────────────────────────────────────────────────── */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 22px var(--gutter);
  color: var(--on-photo);
  /* --ni (nav-invert, 0 → 1) is written by main.js as the closing slide
     darkens. The is-light rules below interpolate every color against
     it so the bar morphs gradually from the light frosted theme into a
     dark frosted one over the inverted band. */
  --ni: 0;
  transition: color var(--t) var(--ease), background var(--t) var(--ease), backdrop-filter var(--t) var(--ease);
}
.nav.is-light {
  /* Light → dark frosted, interpolated by --ni. At --ni:0 these resolve
     to exactly the previous light values; at 1, a dark frosted bar. */
  color: color-mix(in srgb, var(--ink), var(--cream) calc(var(--ni) * 100%));
  background: color-mix(in srgb, rgba(245, 240, 232, 0.72), rgba(16, 12, 8, 0.58) calc(var(--ni) * 100%));
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid;
  border-bottom-color: color-mix(in srgb, var(--hair), var(--noir-line) calc(var(--ni) * 100%));
}
/* Brand wordmark — normally "ink always", but over the inverted band it
   has to lift too: ERAS (inherits .brand) ink → cream, and "photo"
   mute → light-mute, both driven by --ni. */
.nav.is-light .brand {
  color: color-mix(in srgb, var(--ink), var(--cream) calc(var(--ni) * 100%));
}
.nav.is-light .brand-photo {
  color: color-mix(in srgb, var(--mute), rgba(250, 246, 239, 0.74) calc(var(--ni) * 100%));
}

/* v1 logo — iridescent spinning mark + italic eras·photo wordmark with a
   gradient-fill dot. The mark stays full-color across both nav states; the
   word uses currentColor so it switches from cream (over photo) to ink
   (over bone) automatically. */
/* v10a logo lockup — iridescent disk + italic Instrument Serif ERAS caps + photo
   lowercase, sized so the two words feel like equal visual objects. The disk
   is lowered slightly so its center aligns with the horizontal middle of the
   ERAS cap forms. The first letter "E" carries an extra +0.03em kerning so
   the E↔R pair feels balanced against the visually wider R↔A and A↔S gaps. */
.brand {
  display: inline-flex;
  align-items: baseline;
  gap: 30px;
  font-family: var(--serif);
  font-style: italic;
  line-height: 1;
  letter-spacing: 0;
  text-decoration: none;
  /* Ink always — the brand sits at the top-left of every photo (which has a
     white studio backdrop) and over the bone canvas in Part Two, so ink reads
     in both states. Bypasses the nav's color state inheritance so the brand
     doesn't pick up the cream + soft-shadow treatment meant for nav-links. */
  color: var(--ink);
  text-shadow: none;
}
.brand-mark {
  --mark-y: 9px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  flex-shrink: 0;
  align-self: center;
  transform: translateY(var(--mark-y)) rotate(0deg);
  background: conic-gradient(from 0deg, #B49AFF, #79D3E5, #FFB29A, #FFA9D2, #B49AFF);
  /* No shadow / no inset highlight — the iridescent disk stands on its
     own color alone (per user note, v2 round 68: strip every shadow
     and glow out of the top menu). Applies to home nav (.brand-mark)
     and every sub-page nav (.nav-sub .brand-mark) — they share this
     declaration. */
  box-shadow: none;
  animation: spinmark 14s linear infinite;
}
@keyframes spinmark {
  from { transform: translateY(var(--mark-y)) rotate(0deg); }
  to   { transform: translateY(var(--mark-y)) rotate(360deg); }
}
.brand-word {
  display: inline-flex;
  align-items: baseline;
  gap: 12px;
}
.brand-eras {
  font-size: 54px;
  letter-spacing: -0.04em;
}
.brand-e {
  letter-spacing: 0.03em;
}
.brand-photo {
  font-size: 68px;
  letter-spacing: -0.035em;
  /* Two-tone wordmark: ERAS in ink, "photo" in muted warm gray. Overrides
     the ink color inherited from .brand. Applies on every page that uses
     the brand lockup — home nav, sub-page nav, anywhere else it surfaces. */
  color: var(--mute);
}
@media (prefers-reduced-motion: reduce) {
  .brand-mark { animation: none; }
}

.nav-links {
  display: flex;
  gap: 28px;
  /* Slightly larger nav labels with a warm dark-gray tint instead
     of full ink — softer, more editorial than a hard black. */
  font-size: 15px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.nav-links a {
  position: relative;
  padding-bottom: 4px;
  color: var(--mute);
  transition: opacity var(--t) var(--ease), color var(--t) var(--ease);
}
.nav-links a:hover { opacity: 1; color: var(--ink); }

/* No glow on nav links over photo spreads. v2 round 68: user asked to
   strip every shadow / glow from the top menu, so the soft dark drop
   that previously sat behind these labels (for legibility over white
   studio backdrops) is gone. The text now relies on its own ink + the
   light backdrop for contrast. */
.nav:not(.is-light) .nav-links {
  text-shadow: none;
}
/* Nav links interpolate from mute (light theme) to light-cream (dark
   band) with --ni, matching the bar behind them. */
.nav.is-light .nav-links a {
  color: color-mix(in srgb, var(--mute), rgba(250, 246, 239, 0.74) calc(var(--ni) * 100%));
}
.nav.is-light .nav-links a:hover {
  color: color-mix(in srgb, var(--ink), var(--cream) calc(var(--ni) * 100%));
}

/* Book button — solid ink pill. Used in two places, with identical visuals:
   1. The top nav (`.nav .nav-cta`) — hidden on load, fades in via scroll
      progress as the cover Book CTA approaches the nav band.
   2. The cover BR (`.cover-row .nav-cta`) — visible immediately, scrolls
      off naturally with the cover spread.
   Same class, same look → the user perceives a single button traveling. */
.nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 12px 22px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bone);
  border: 1px solid var(--ink);
  text-shadow: none;
  /* No drop-shadow / no inset highlight. v2 round 68: top menu is
     shadow-free; the ink pill carries its own contrast against any
     backdrop and doesn't need a lifted look. */
  box-shadow: none;
  transition:
    background var(--t) var(--ease),
    color      var(--t) var(--ease),
    border     var(--t) var(--ease);
}
.nav-cta:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bone);
  transform: translateY(-1px);
  box-shadow: none;
}
.nav-cta-arrow { transition: transform var(--t) var(--ease); }
.nav-cta:hover .nav-cta-arrow { transform: translateX(3px); }

/* Nav-scoped: hidden by default, opacity + translate driven by a scroll
   progress custom property (--nav-cta-progress, 0 → 1) set in main.js.
   The transition smooths out any rAF jitter without lagging behind scroll.
   Opacity is additionally scaled by (1 - --ni): as the nav darkens into
   the booking band the Book pill fades out — by then the visitor is
   already inside the booking section, so the nav CTA is redundant. */
.nav .nav-cta {
  opacity: calc(var(--nav-cta-progress, 0) * (1 - var(--ni, 0)));
  transform: translateY(calc((1 - var(--nav-cta-progress, 0)) * -6px));
  pointer-events: none;
  transition:
    opacity   140ms var(--ease),
    transform 140ms var(--ease),
    background var(--t) var(--ease),
    color      var(--t) var(--ease),
    border     var(--t) var(--ease);
}
.nav.nav-cta-active .nav-cta {
  pointer-events: auto;
}
/* Once the bar is mostly dark the pill is invisible — drop its hit
   target so it can't catch clicks over the booking section. */
.nav.nav-cta-hidden .nav-cta {
  pointer-events: none;
}
/* Light-state Book pill: shadow-free, and its fill/text/border
   interpolate with --ni so it inverts (ink pill → cream pill) over the
   dark band. Hover (below) still jumps to the accent fill. */
.nav.is-light .nav-cta {
  box-shadow: none;
  background: color-mix(in srgb, var(--ink), var(--cream) calc(var(--ni) * 100%));
  color: color-mix(in srgb, var(--bone), var(--ink) calc(var(--ni) * 100%));
  border-color: color-mix(in srgb, var(--ink), var(--cream) calc(var(--ni) * 100%));
}
.nav.is-light .nav-cta:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bone);
  box-shadow: none;
}

/* ── Hamburger + mobile slide-out menu ───────────────────────────
   Right-side action cluster: Book pill, then the burger pinned to the
   far right. Burger is display:none on desktop (the .nav-links + footer
   carry navigation there) and only appears at the mobile breakpoint.
   The .mobile-menu panel stays in the DOM at every width so its links
   are crawled under mobile-first indexing; it's just not openable until
   the burger shows. */
.nav-right {
  display: flex;
  align-items: center;
  gap: clamp(8px, 2.4vw, 14px);
}
.nav-burger {
  display: none;            /* shown only at ≤780px (below) */
  width: 44px; height: 44px;
  align-items: center; justify-content: center;
  padding: 0; margin: 0;
  background: none; border: 0;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.nav-burger-box {
  display: inline-flex; flex-direction: column;
  gap: 5px; width: 24px;
}
/* Lines track the nav-link color, NOT the nav's white `currentColor`:
   dark gray (--mute) over photos and the light frosted bar, lifting to
   cream/white only over the dark band — driven by --ni, exactly like
   `.nav-links a`. */
.nav-burger-line {
  height: 2px; width: 100%;
  background: var(--mute); border-radius: 2px;
  transition: transform 220ms var(--ease), opacity 160ms var(--ease),
              background var(--t) var(--ease);
}
.nav.is-light .nav-burger-line {
  background: color-mix(in srgb, var(--mute), var(--cream) calc(var(--ni) * 100%));
}
/* Burger → X when the menu is open (driven off aria-expanded). */
.nav-burger[aria-expanded="true"] .nav-burger-line:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-burger[aria-expanded="true"] .nav-burger-line:nth-child(2) { opacity: 0; }
.nav-burger[aria-expanded="true"] .nav-burger-line:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.mobile-menu {
  position: fixed; inset: 0;
  z-index: 60;
  visibility: hidden;
  pointer-events: none;
}
.mobile-menu::before {              /* scrim */
  content: "";
  position: absolute; inset: 0;
  background: rgba(16, 12, 8, 0.5);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity 260ms var(--ease);
}
.mobile-menu.is-open {
  visibility: visible;
  pointer-events: auto;
}
.mobile-menu.is-open::before { opacity: 1; }

.mobile-menu-panel {
  position: absolute; top: 0; right: 0;
  width: min(90vw, 400px); height: 100%;
  display: flex; flex-direction: column;
  background: #1C1813;            /* very dark warm gray */
  color: var(--cream);
  box-shadow: -20px 0 60px rgba(0, 0, 0, 0.35);
  padding: clamp(80px, 17vw, 100px) clamp(26px, 7vw, 38px) 44px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  transform: translateX(100%);
  transition: transform 320ms var(--ease);
}
.mobile-menu.is-open .mobile-menu-panel { transform: translateX(0); }

.mm-book {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 15px 22px; margin-bottom: 10px;
  background: var(--cream); color: var(--ink);
  border-radius: 999px;
  font-family: var(--sans); font-size: 13px; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.mm-book:hover { background: var(--accent); color: var(--bone); }

/* Section labels — small cream-muted sans caps, generous top space to
   separate groups now that the dividing rules are gone. */
.mm-group {
  margin: 30px 0 8px;
  font-family: var(--sans); font-size: 11px; font-weight: 600;
  letter-spacing: 0.2em; text-transform: uppercase;
  color: rgba(250, 246, 239, 0.42);
}
/* Link items — large Instrument Serif, cream, no dividers; spacing comes
   from line-height + padding rhythm rather than hairlines. */
.mobile-menu-panel > a:not(.mm-book) {
  padding: 7px 0;
  font-family: var(--serif);
  font-size: clamp(26px, 7.5vw, 34px);
  line-height: 1.12; letter-spacing: 0.01em;
  color: var(--cream);
  transition: color var(--t) var(--ease), padding-left var(--t) var(--ease);
}
.mobile-menu-panel > a:not(.mm-book):hover {
  color: var(--accent);
  padding-left: 8px;
}
.mm-legal {
  display: flex; flex-wrap: wrap; gap: 10px 20px;
  margin-top: 34px;
}
.mm-legal a {
  font-family: var(--sans);
  font-size: 13px; color: rgba(250, 246, 239, 0.5);
  transition: color var(--t) var(--ease);
}
.mm-legal a:hover { color: var(--cream); }

body.menu-open { overflow: hidden; }

@media (max-width: 780px) {
  .nav-links { display: none; }
  .nav-burger { display: inline-flex; }
  .nav { padding: 14px 18px; gap: clamp(10px, 3vw, 24px); }
  /* Brand scales DOWN with viewport on narrow phones. Each clamp's MAX equals
     the old fixed size, so wider phones (≳400px) are unchanged; below that the
     wordmark shrinks so the Book pill on the right always keeps its space.
     Previously the fixed 36/46px wordmark + 36px mark pushed Book off the right
     edge at ≤~375px — clipping it on 360px (the most common Android width) and
     320px (iPhone SE / older small phones). */
  .brand { gap: clamp(11px, 3.6vw, 20px); min-width: 0; }
  .brand-mark { --mark-y: 6px; width: clamp(25px, 8.3vw, 36px); height: clamp(25px, 8.3vw, 36px); }
  .brand-eras { font-size: clamp(23px, 8.9vw, 36px); }
  .brand-photo { font-size: clamp(28px, 11.5vw, 46px); }
  .brand-word { gap: clamp(5px, 1.9vw, 8px); }
  /* Book pill: never shrink (so it can't be compressed or clipped). Height,
     text + padding scale DOWN with the viewport so the pill stays in
     proportion with the shrinking wordmark instead of looking oversized on the
     narrowest phones. The ~36px floor keeps it comfortably tappable; the caps
     (44 / 11.5 / 18px) match the wider-phone sizing. */
  .nav-cta {
    flex-shrink: 0;
    min-height: clamp(36px, 11vw, 44px);
    padding: clamp(8px, 2.5vw, 11px) clamp(14px, 4.6vw, 18px);
    font-size: clamp(10px, 2.95vw, 11.5px);
    box-sizing: border-box;
    letter-spacing: 0.18em;
  }
}

/* ── Spread (fullscreen photo panel with corner text) ─────── */
/* Spread = full viewport panel. The corner blocks are absolutely positioned
   so they truly live in their corners. The middle ~40% of the frame is kept
   completely empty so the face (center of every photo) is never crossed by
   text. */
.spread {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  min-height: 100svh;
  padding: clamp(96px, 14vh, 140px) var(--gutter) clamp(56px, 8vh, 96px);
  color: var(--on-photo);
}

/* Cover spread also acts as a flex column so the sticky Book button lands
   naturally at the bottom-right as its only in-flow child. TL + BR are
   absolutely positioned so they're outside the flex flow. */
/* Cover has no `.spread-tl` block. On desktop the `.spread-br` is
   absolutely positioned at the bottom-right so this doesn't matter, but on
   mobile (≤880 px) the spread switches to a flex column and `.spread-br`
   becomes static — without a flex justify the BR block would float to the
   top of the empty spread. Anchor it to the bottom-right on the cover. */
.spread[data-photo="0"] {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: flex-end;
}

.spread-tl {
  position: absolute;
  top: clamp(104px, 14vh, 150px);
  left: var(--gutter);
  max-width: 30ch;
  /* JS (see main.js section 2b) sets `opacity` + `transform` per scroll
     frame so the corner copy fades + lifts away before it can reach the
     fixed nav band. Hint the compositor so the per-frame updates stay
     cheap. */
  will-change: opacity, transform;
}

.spread-br {
  position: absolute;
  bottom: clamp(72px, 10vh, 112px);
  right: var(--gutter);
  text-align: right;
  max-width: 40ch;
  /* Same fade hook as .spread-tl — see main.js section 2b. */
  will-change: opacity, transform;
}

/* Mobile: drop absolute positioning, stack TL → (face gap) → BR.
   BR + the CTA stay right-aligned to match desktop. */
@media (max-width: 880px) {
  .spread {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    gap: 24px;
    padding-top: clamp(100px, 18vh, 140px);
    padding-bottom: clamp(80px, 14vh, 120px);
  }
  /* The consolidated tier spreads (cover + Basic/Plus/Besties) carry a
     SINGLE child — `.spread-br` — now that the top-left block is merged
     in. Under the default `space-between` a lone child sticks to the top
     of the frame, which made the header appear in the upper third instead
     of resting at the bottom and sliding up. Pin them to the bottom like
     the cover. (Added 2026-05-29.) */
  .spread.spread-tag {
    justify-content: flex-end;
  }
  .spread-tl,
  .spread-br {
    position: static;
    max-width: 100%;
  }
  .spread-br {
    align-self: flex-end;
    text-align: right;
    max-width: 36ch;
  }
}

/* ── Masthead / folio / kicker (small editorial labels) ───── */
.masthead {
  margin: 0;
  font-family: var(--serif-2);
  font-weight: 400;
  font-size: clamp(18px, 1.5vw, 22px);
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.masthead-bullet {
  display: inline-block;
  margin: 0 6px;
  opacity: 0.55;
}
.folio {
  margin: 10px 0 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  letter-spacing: 0.04em;
  color: var(--on-photo-soft);
}

/* Top-left display headlines on the photo spreads.
   `.tier-display` is used on spreads 02–04 ("ERAS Basic" / "ERAS Plus" /
   "ERAS Besties"). `.chapter-name` is used on spreads 05–06 ("New York." /
   "Miami."). v2 round 73: both share one type treatment now — same size,
   same terracotta color, same typographic system — so the top-left rail
   reads as one consistent display level across all five photo spreads.
   Shading was removed at the same time: the previous soft dark glow (which
   anchored the type visually against the white studio backdrops) is now
   gone for a flatter, cleaner look. The photos themselves all carry near-
   white backgrounds in this top-left zone, so contrast holds without the
   shadow. */
.chapter-name,
.tier-display {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-style: normal;
  font-size: clamp(52px, 6.5vw, 92px);
  line-height: 0.94;
  letter-spacing: -0.018em;
  color: var(--accent);
  /* No shadow at this display level — overrides the soft dark glow
     inherited from `.spread, .spread-tl, .spread-br` below. */
  text-shadow: none;
}
.tier-display em {
  font-style: italic;
  /* Slightly lighter weight feel via opacity, so "ERAS" reads as a prefix
     and the tier word ("Basic" / "Plus" / "Besties") is the punchline. */
  opacity: 0.92;
}

/* ── Solid masthead-tag treatment ─────────────────────────────
   Header legibility option 5. Applied via `.spread-tag` to the cover
   + the three tier spreads (Basic / Plus / Besties) only — the NY/Miami
   map spreads and the gallery are intentionally excluded.

   The whole header is consolidated into ONE block in the bottom-right
   on a single solid dark plate. Content hierarchy (top → bottom), tuned
   2026-05-29 to be direct with zero repeated words:
     1. `.tier-display` — the SERVICE NAME, now the large cream title
        (it used to be a tiny eyebrow above a marketing headline; the
        headline was cut because it repeated the button's wording).
     2. `.tier-sub`     — one short value line in muted light-gray that
        carries the per-tier difference (same-day / + LinkedIn / for two).
     3. `.btn-glass`    — the action + price only ("Book · $225").
   Brightness alone separates the cream title from the gray sub — no
   accent hue. Padding stays tight so the plate sits lightly on the
   portrait. */
.spread-tag .tier-display {
  /* Service name as the headline — overrides the giant clamp(52–92px)
     display size the tier label uses elsewhere, but stays large + cream
     so it's the clear anchor of the plate. */
  font-size: clamp(42px, 5.4vw, 70px);
  line-height: 0.96;
  margin: 0;
  letter-spacing: -0.01em;
  color: var(--on-photo);
  text-shadow: none;
}
.spread-tag .tier-display em {
  font-style: italic;
  /* "ERAS" prefix sits dimmer so the tier word is the punchline. */
  opacity: 0.74;
}
.spread-tag .tier-sub {
  /* One tight value line under the name — sans, muted light-gray, sized
     well below the title so the hierarchy reads at a glance. */
  margin: 6px 0 0;
  font-family: var(--sans);
  font-size: clamp(13.5px, 1.05vw, 15.5px);
  line-height: 1.25;
  color: #C7C4BC;
  text-shadow: none;
}

.spread-tag .spread-br {
  /* Shrink-to-fit so the plate hugs the headline + CTA instead of
     spanning the full 40ch column. `fit-content` also keeps it tight on
     mobile, where .spread-br switches to static flow. */
  width: -webkit-fit-content;
  width: fit-content;
  max-width: 100%;
  background: rgba(20, 17, 14, 0.78);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  padding: 9px 16px 13px;
  border-radius: 12px;
}
@media (max-width: 880px) {
  /* Keep the plate right-aligned and content-hugging in the stacked
     mobile layout. */
  .spread-tag .spread-br { align-self: flex-end; }
  /* The desktop clamp's 5.6vw preferred value collapses to the 40px floor
     on phones (it only clears 40px above ~714px wide), so the hero
     statement looked undersized next to the label + Book pill. Give the
     statement a mobile-only floor that scales with phone width. */
  .spread-tag .spread-br .cover { font-size: clamp(48px, 13vw, 66px); }
  /* Same trap on the tier service names (Basic/Plus/Besties): their
     5.4vw preferred value collapses to its 42px floor on phones. Match the
     hero's mobile floor so the service name stays the plate's anchor. */
  .spread-tag .tier-display { font-size: clamp(48px, 13vw, 66px); }
}
.spread-tag .spread-br .cover,
.spread-tag .spread-br .headline {
  /* On a solid plate the type carries itself — drop the on-photo glow. */
  text-shadow: none;
}
.spread-tag .btn-glass {
  /* Tighter gap inside the plate than the default 28px so the plate
     stays compact. Slightly darker fill so the pill reads as a distinct
     control sitting on the plate. */
  margin-top: 14px;
  background: rgba(20, 17, 14, 0.5);
}

/* Italic serif kicker — contextual subtitle on the inner spreads
   (Application photo · $225 / AAMC · MyERAS · Fellowship / etc.). Inherits
   the on-photo text-shadow from `.spread-tl, .spread-br`. */
.kicker {
  margin: 0 0 14px;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(14px, 1.2vw, 17px);
  color: var(--on-photo-soft);
  letter-spacing: 0.01em;
}

/* ── Glass pill button (on-photo CTA) ──────────────────────
   Used on the cover (BOOK) and every tier spread (02–06) as the section CTA.
   The label carries the meta info that used to live in the small italic
   kicker above the headline (Application photo · $225 / etc.), so one
   element does two jobs — the meta line AND the CTA — instead of a kicker
   plus a separate "Choose Basic" button.

   Treatment: sans serif body, medium weight, slightly tracked — reads as
   a button. Glass fill is opaque enough to lift off any photo backdrop;
   subtle drop-shadow adds extra separation. */
.btn-glass {
  display: inline-flex;
  align-items: center;
  /* Standardized gap between the text block above and the glass pill.
     Same value on the cover (lede → pill) and every tier spread (body → pill)
     so the whole sequence feels rhythmically consistent. */
  margin-top: 28px;
  padding: 13px 24px;
  border-radius: 999px;
  background: rgba(20, 17, 14, 0.68);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  backdrop-filter: blur(16px) saturate(140%);
  border: 1px solid rgba(251, 247, 238, 0.24);
  font-family: var(--sans);
  font-style: normal;
  font-weight: 500;
  font-size: clamp(13.5px, 0.95vw, 15px);
  letter-spacing: 0.02em;
  color: var(--on-photo);
  text-decoration: none;
  text-shadow: none;
  white-space: nowrap;
  box-shadow: 0 6px 20px rgba(8, 6, 4, 0.32);
  transition:
    background var(--t) var(--ease),
    border-color var(--t) var(--ease),
    box-shadow var(--t) var(--ease),
    transform var(--t) var(--ease);
  cursor: pointer;
}
.btn-glass:hover {
  background: rgba(20, 17, 14, 0.82);
  border-color: rgba(251, 247, 238, 0.4);
  box-shadow: 0 10px 26px rgba(8, 6, 4, 0.42);
  transform: translateY(-1px);
}
.btn-glass:focus-visible {
  outline: 2px solid var(--on-photo);
  outline-offset: 3px;
}
@media (max-width: 880px) {
  .btn-glass {
    padding: 11px 20px;
    font-size: 13px;
    white-space: normal;
    text-align: center;
  }
}

/* ── In-article CTA block ─────────────────────────────────────
   Wrapper used on every secondary editorial page (Guide, Timeline,
   Residency, Fellowship, Examples, Spec checker, Studio + Specialty
   sub-pages) to surface the booking pill mid-article and at the end.
   Goal: bookings. Visible against the bone background, but not loud —
   the btn-glass dark fill carries the contrast on its own.

   The default-state styling matches the home-page spread btn-glass
   exactly. Hover INVERTS — cream fill + dark ink text — so the pill
   reads as a flipped color version of itself on rollover. This is
   a sub-page-only treatment; the home-page spreads keep their
   original "slightly darker on hover" behavior. */
.article-cta {
  /* Generous vertical breathing room — these blocks are deliberate
     "stop and consider" moments separate from the paragraph rhythm. */
  margin: 64px auto 72px;
  padding: 24px 16px;
  text-align: center;
}
/* DEFAULT state — CREAM pill with dark-gray text + dark-gray border.
   Different from the home-page spread btn-glass (which sits over photos
   and needs to be dark-with-light-text). On a sub-page bone background,
   the cream fill sits in the palette (slightly brighter than --bone)
   while the dark border defines a clear primary CTA shape. */
.article-cta .btn-glass {
  margin-top: 0;
  /* Internal padding inherited from the home-page btn-glass (13px 24px)
     — we don't want the sub-page pills any taller than the spread ones.
     All extra breathing room lives OUTSIDE the pill (wrapper margin +
     margin-top on .article-cta-tag below). */
  padding: 13px 26px;
  font-size: clamp(14px, 1vw, 16px);
  background: var(--cream);                  /* cream pill (#FAF6EF) */
  color: var(--ink);                         /* dark ink text */
  border: 1.5px solid rgba(20, 17, 14, 0.78);/* dark gray border */
  /* Solid cream doesn't need backdrop-filter — reset cleanly. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  /* Sub-pages don't sit over photography, so we don't need the photo-
     overlay text-shadow that .spread carries. Reset it cleanly. */
  text-shadow: none;
  /* No drop-shadow — keeps the pill crisp. The earlier soft halo was
     reading as dirty/smudged against the bone background. The border
     does all the work of defining the shape. */
  box-shadow: none;
  transition:
    background var(--t) var(--ease),
    color var(--t) var(--ease),
    border-color var(--t) var(--ease),
    box-shadow var(--t) var(--ease),
    transform var(--t) var(--ease);
}
/* HOVER + FOCUS — INVERTED. Pill flips to warm dark gray, text + border
   flip to white/light. The whole color scheme inverts in place, giving
   a clean rollover signal that matches the home-page slideshow style. */
.article-cta .btn-glass:hover,
.article-cta .btn-glass:focus-visible {
  background: rgba(20, 17, 14, 0.78);       /* warm dark gray */
  color: #FFFFFF;                            /* white text */
  border-color: rgba(20, 17, 14, 0.55);     /* matching gray border */
  /* No shadow on hover either — keep the pill clean. */
  box-shadow: none;
  transform: translateY(-1px);
}
.article-cta .btn-glass:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
.article-cta-tag {
  /* Generous negative-space break between the pill and the tag line.
     Applied globally — every .article-cta block on every sub-page picks
     this up automatically, so all CTAs get the same breathing room.
     Bumped to 96px desktop / 64px mobile — the previous 64/44 still read
     as too tight against the pill border on Residency / Fellowship and
     the rest of the sub-pages. */
  margin: 96px 0 0;
  font-family: var(--sans);
  font-size: 12.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(20, 17, 14, 0.55);
}
@media (max-width: 880px) {
  .article-cta {
    margin: 48px auto 56px;
    padding: 16px 12px;
  }
  .article-cta .btn-glass {
    padding: 11px 22px;
    font-size: 14px;
  }
  .article-cta-tag {
    margin-top: 64px;
    font-size: 11.5px;
    letter-spacing: 0.05em;
  }
}

/* ── Big display type ─────────────────────────────────────── */
/* Subtle dark glow on every piece of text that sits on a photo —
   keeps cream type readable whether it lands on a white studio
   backdrop, dark clothing, or skin. */
.spread,
.spread-tl,
.spread-br {
  text-shadow: 0 1px 2px rgba(8, 6, 4, 0.55), 0 2px 24px rgba(8, 6, 4, 0.35);
}

/* Headline sizes are deliberately conservative so the BR corner block fits in
   the bottom-right without growing upward across the face. */
.cover, .headline {
  margin: 0 0 18px;
  font-family: var(--serif);
  font-weight: 400;
  font-style: normal;
  letter-spacing: -0.015em;
  line-height: 0.98;
  color: var(--on-photo);
}
.cover {
  font-size: clamp(40px, 5.6vw, 78px);
}
.cover em {
  font-style: italic;
  font-family: var(--serif);
}
/* Kicker — small all-caps sans eyebrow that sits ABOVE the large serif
   statement. Subject as a quiet label; the benefit below is the hero line.
   Both slides share this label-over-statement rhythm. */
.cover-kick {
  display: block;
  margin: 0 0 0.5em;
  font-family: var(--sans);
  font-size: clamp(13px, 1.4vw, 18px);
  font-weight: 600;
  font-style: normal;
  letter-spacing: 0.24em;
  line-height: 1.1;
  text-transform: uppercase;
}
.headline {
  font-size: clamp(32px, 4.4vw, 62px);
}
.headline em {
  font-style: italic;
}

.lede {
  margin: 0 0 20px;
  font-size: clamp(14px, 1.05vw, 16px);
  line-height: 1.55;
  /* Lede sat in `--on-photo-soft` (cream at 0.78 alpha) which was washing out
     against the light backdrop. Lift to full-opacity cream + a slightly
     heavier text-shadow for crisp readability on every spread. */
  color: var(--on-photo);
  max-width: 38ch;
  margin-left: auto;
  text-shadow:
    0 1px 2px rgba(8, 6, 4, 0.7),
    0 2px 16px rgba(8, 6, 4, 0.55);
}
.spread-tl .lede { margin-left: 0; }
.body {
  margin: 0;
  font-size: clamp(13px, 0.95vw, 15px);
  line-height: 1.6;
  color: var(--on-photo-soft);
  max-width: 36ch;
  margin-left: auto;
}
.spread-br .body { margin-left: auto; }
@media (max-width: 880px) {
  .lede, .body { max-width: 100%; }
  .spread-tl .lede, .spread-tl .body { margin-left: 0; }
}

/* ── Buttons ──────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 22px;
  border-radius: 999px;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 500;
  background: var(--on-photo);
  color: var(--ink);
  border: 1px solid var(--on-photo);
  text-shadow: none; /* buttons should be crisp, not glowing */
  transition: background var(--t) var(--ease), color var(--t) var(--ease), transform var(--t) var(--ease);
}
.btn:hover { background: var(--ink); color: var(--on-photo); border-color: var(--ink); }
.btn-arrow { transition: transform var(--t) var(--ease); }
.btn:hover .btn-arrow { transform: translateX(4px); }

.btn-ghost {
  background: transparent;
  color: var(--on-photo);
  border: 1px solid var(--on-photo-hair);
}
.btn-ghost:hover { background: var(--on-photo); color: var(--ink); }

.btn-on-photo { margin-top: 18px; }

.hairline-note {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: var(--on-photo-soft);
  display: inline-flex;
  align-items: center;
  gap: 10px;
}
.check {
  display: inline-grid;
  place-items: center;
  width: 18px; height: 18px;
  border: 1px solid var(--on-photo-hair);
  border-radius: 999px;
  font-size: 11px;
}

/* ── Bone curtain — covers fixed bg as we enter part two ──── */
.bone-curtain {
  position: relative;
  z-index: 3;
  height: 0;
}

/* ── Editorial content sections (bone canvas) ─────────────── */
.ed-section,
.ed-section-quiet,
.trust,
.foot {
  position: relative;
  z-index: 4;
  background: var(--bone);
}

.ed-section {
  padding: clamp(80px, 12vh, 140px) var(--gutter);
}
.ed-section-quiet {
  padding: clamp(40px, 6vh, 64px) var(--gutter) clamp(60px, 9vh, 96px);
  background: var(--bone-deep);
}

.ed-section-head {
  max-width: var(--max);
  margin: 0 auto clamp(48px, 7vh, 84px);
}

.ed-folio {
  margin: 0 0 18px;
  font-size: 11px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
}
.ed-title {
  margin: 0 0 22px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(40px, 6vw, 84px);
  line-height: 0.96;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.ed-title em { font-style: italic; }
.ed-deck {
  margin: 0;
  font-size: clamp(15px, 1.1vw, 18px);
  line-height: 1.55;
  color: var(--mute);
  max-width: 56ch;
}

.link-arrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 0;
  font-size: 13px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  border-bottom: 1px solid var(--hair-strong);
  transition: border-color var(--t) var(--ease), color var(--t) var(--ease);
}
.link-arrow:hover { color: var(--accent); border-color: var(--accent); }

/* ── Trust marquee ────────────────────────────────────────── */
.trust {
  padding: 38px var(--gutter);
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  overflow: hidden;
  text-align: center;
}
.trust-label {
  margin: 0 0 26px;
  font-family: var(--sans);
  font-size: 9.5px;
  font-weight: 400;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  color: var(--mute-soft);
}
@media (max-width: 780px) {
  .trust { padding: 28px var(--gutter); }
  .trust-label { margin-bottom: 18px; font-size: 9px; letter-spacing: 0.3em; }
}

.marquee {
  position: relative;
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
}
.marquee-track {
  display: inline-flex;
  align-items: center;
  gap: 24px;
  white-space: nowrap;
  animation: marquee 80s linear infinite;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(22px, 2.2vw, 32px);
  color: var(--ink-soft);
}
.marquee-track .dot {
  font-style: normal;
  color: var(--mute-soft);
  font-family: var(--sans);
}
@keyframes marquee {
  to { transform: translateX(-50%); }
}

/* ── Spec list ────────────────────────────────────────────── */
.ed-grid {
  max-width: var(--max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: clamp(40px, 6vw, 96px);
  align-items: start;
}
@media (max-width: 880px) {
  .ed-grid { grid-template-columns: 1fr; }
}

.spec-list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--hair);
}
.spec-list li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 24px;
  padding: 22px 0;
  border-bottom: 1px solid var(--hair);
  font-size: 15px;
}
.sl-k {
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
}
.sl-v {
  font-family: var(--serif-2);
  font-size: clamp(20px, 2.2vw, 28px);
  color: var(--ink);
  font-weight: 400;
}

/* ── Subsection blocks (inside an ed-section) ────────────── */
/* Used for the rejection-reasons grid + the comparison table that sit
   below the main spec list inside #requirements. Each subsection gets
   editorial breathing room from the block above. */
.ed-subsection {
  max-width: var(--max);
  margin: clamp(64px, 9vh, 110px) auto 0;
}
.ed-subsection .ed-section-head {
  margin-bottom: clamp(32px, 5vh, 56px);
}
.ed-title-sm {
  margin: 0 0 18px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(28px, 3.4vw, 44px);
  line-height: 1.0;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.ed-title-sm em { font-style: italic; }

/* ── Comparison table (ERAS vs AMCAS vs LinkedIn vs Passport) ─── */
.compare-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}
.compare-table {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  font-family: var(--sans);
  font-size: 14px;
}
.compare-table thead th {
  text-align: left;
  padding: 18px 18px;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
  border-bottom: 1px solid var(--hair);
  background: var(--bone);
}
.compare-table thead th:first-child { background: transparent; }
.compare-table tbody th {
  text-align: left;
  padding: 16px 18px;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mute);
  border-bottom: 1px solid var(--hair);
  vertical-align: top;
  white-space: nowrap;
}
.compare-table tbody td {
  padding: 16px 18px;
  font-family: var(--serif-2);
  font-size: clamp(15px, 1.2vw, 17px);
  color: var(--ink);
  border-bottom: 1px solid var(--hair);
  vertical-align: top;
}
.compare-table tbody td:first-of-type {
  background: var(--cream);
}
.compare-table tbody tr:last-child th,
.compare-table tbody tr:last-child td { border-bottom: 0; }

/* ── Pricing tiers ────────────────────────────────────────── */
.tiers {
  max-width: var(--max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
}
@media (max-width: 980px) {
  .tiers {
    grid-template-columns: 1fr;
  }
}

.tier {
  position: relative;
  padding: clamp(36px, 4vw, 48px) clamp(28px, 3.4vw, 40px);
  display: flex;
  flex-direction: column;
  gap: 22px;
  border-right: 1px solid var(--hair);
  background: var(--bone);
  transition: background var(--t) var(--ease);
}
.tier:last-child { border-right: none; }
.tier:hover { background: var(--cream); }
@media (max-width: 980px) {
  .tier { border-right: none; border-bottom: 1px solid var(--hair); }
  .tier:last-child { border-bottom: none; }
}

.tier-featured {
  background: var(--ink);
  color: var(--on-photo);
}
.tier-featured:hover { background: var(--ink-soft); }
.tier-featured .tier-tag { color: rgba(251, 247, 238, 0.65); }
.tier-featured .tier-list li { color: rgba(251, 247, 238, 0.85); border-bottom-color: rgba(251, 247, 238, 0.15); }
.tier-featured .tier-cta { color: var(--on-photo); border-color: rgba(251, 247, 238, 0.4); }
.tier-featured .tier-cta:hover { background: var(--on-photo); color: var(--ink); border-color: var(--on-photo); }

.tier-head { display: flex; flex-direction: column; gap: 4px; }
.tier-tag {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--mute);
}
.tier-name {
  margin: 8px 0 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(40px, 4.2vw, 60px);
  line-height: 1;
  letter-spacing: -0.01em;
}
.tier-name em {
  font-style: italic;
  margin-right: 8px;
  color: var(--accent);
}
.tier-featured .tier-name em { color: var(--accent-soft); }

.tier-price {
  margin: 0;
  font-family: var(--serif);
  display: inline-flex;
  align-items: baseline;
}
.tp-cur {
  font-size: 18px;
  margin-right: 4px;
  opacity: 0.7;
}
.tp-num {
  font-size: clamp(56px, 6vw, 80px);
  line-height: 1;
  letter-spacing: -0.02em;
}

.tier-list {
  margin: 0;
  padding: 0;
  list-style: none;
  flex: 1 1 auto;
}
.tier-list li {
  padding: 12px 0;
  border-bottom: 1px solid var(--hair);
  font-size: 14px;
  color: var(--ink-soft);
}
.tier-list li:last-child { border-bottom: none; }

.tier-cta {
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 14px 18px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  font-size: 12px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.tier-cta:hover { background: var(--ink); color: var(--bone); }

.pricing-foot {
  max-width: var(--max);
  margin: clamp(28px, 4vh, 40px) auto 0;
  font-family: var(--serif);
  font-style: italic;
  color: var(--mute);
  text-align: center;
}

/* ── Extras (single retouching card) ──────────────────────── */
.extras-grid {
  max-width: 760px;
  margin: 0 auto;
}
.extra {
  padding: clamp(40px, 5vw, 64px);
  background: var(--cream);
  border: 1px solid var(--hair);
}
.extra-tag {
  margin: 0 0 16px;
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
}
.extra-title {
  margin: 0 0 12px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(32px, 3.6vw, 52px);
  line-height: 1;
}
.extra-price {
  margin: 0 0 16px;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(18px, 1.6vw, 24px);
  color: var(--ink);
}
.extra-price strong { font-style: normal; font-weight: 500; }
.extra-body {
  margin: 0 0 20px;
  color: var(--mute);
  font-size: 15px;
  line-height: 1.55;
  max-width: 50ch;
}

/* ── Prep ─────────────────────────────────────────────────── */
.prep-grid {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0;
  list-style: none;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
}
@media (max-width: 980px) {
  .prep-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 640px) {
  .prep-grid { grid-template-columns: 1fr; }
}

.prep-card {
  padding: clamp(28px, 3vw, 40px);
  border-right: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  background: var(--bone);
  display: flex;
  flex-direction: column;
  gap: 14px;
  transition: background var(--t) var(--ease);
}
.prep-card:hover { background: var(--cream); }
.prep-card h3 {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(22px, 2vw, 28px);
  line-height: 1.1;
}
.prep-card p {
  margin: 0;
  color: var(--mute);
  font-size: 14px;
  line-height: 1.55;
}
.prep-card em { font-style: italic; color: var(--ink); }

.prep-foot {
  max-width: 720px;
  margin: clamp(36px, 5vh, 56px) auto 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(16px, 1.3vw, 20px);
  color: var(--mute);
  text-align: center;
  line-height: 1.55;
}

/* ── Booking ──────────────────────────────────────────────── */
/* v2 round 71: the home-page booking shell now ships frameless —
   no cream background, no hairline border, no horizontal padding.
   The picker grid runs full-width inside its parent `.ed-section`,
   which provides the only horizontal indent from the viewport edge
   (via its own `var(--gutter)` padding). This maximises the
   usable width for the three tier cards — especially on mobile,
   where every extra pixel helps keep all three cards readable in
   a portrait viewport. Vertical padding is preserved so the shell
   still has breathing room above and below the section header /
   trust strip. The `/book/` page has its own higher-specificity
   override (`.book-page .book-shell`) so it remains unchanged. */
.book-shell {
  max-width: var(--max);
  margin: 0 auto;
  background: transparent;
  border: 0;
  padding: clamp(28px, 4vw, 56px) 0;
}
@media (max-width: 640px) {
  .book-shell {
    padding: clamp(20px, 4vw, 32px) 0;
  }
}

/* v2 round 72: home-page tier picker breaks out of the parent
   `.ed-section`'s `var(--gutter)` so the three cards run flush
   with the section's content edge (and, on mobile, flush with
   the viewport — gutter collapses to 24 px there, fully canceled
   by this breakout). Only the picker-grid escapes — `.loc-toggle`
   (NY / Miami pills + studio prompt) and `.picker-foot` (the
   "Not sure which one?" line) stay at the shell's normal width
   so their text isn't pushed against the screen edge. Scoped to
   `#pricing` so the `/book/` page (no `#pricing` ancestor) is
   untouched. */
#pricing .picker-grid {
  margin-left: calc(-1 * var(--gutter));
  margin-right: calc(-1 * var(--gutter));
}
.book-step {
  display: none;
}
.book-step.is-active { display: block; }

/* ── Location pill toggle (city + studio) ────────────────── */
/* Lives at the top of step-pick (and, duplicated, inside step-book's
   .bs-studio-pick chooser). Two rows: large city pills (NY / Miami),
   then a sub-row of studio pills (NY → Times Square + Wall Street /
   Miami → Brickell). Nothing is selected by default; the sub-row
   reserves height from the start so the price grid never jumps when
   a city is picked. The booking flow no longer surfaces a "please pick
   a studio first" warning anywhere — if a tier is clicked without a
   studio, the user advances to step-book and the in-step .bs-studio-pick
   chooser takes the iframe's place until they pick. */
.loc-toggle {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  /* No horizontal padding — on narrow (mobile) viewports the side
     padding ate into the already-tight width and pushed the open
     city pill past the screen edge, clipping it. The pill row below
     manages its own breathing room. */
  padding: clamp(8px, 1.4vh, 14px) 0 clamp(22px, 3vh, 32px);
  margin-bottom: clamp(24px, 3vh, 36px);
  border-bottom: 1px solid var(--hair);
  border-radius: 6px;
  transition: background var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.loc-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
}
/* Studio sub-row is always present in the layout — its height is
   reserved upfront so the price grid below the toggle doesn't shift
   when a city is picked and the pills appear. Individual studio pills
   stay hidden via the HTML `hidden` attribute (`.loc-studio[hidden]`
   resolves to `display: none` further down) until the matching city
   is selected, so at rest the row sits empty but height-reserved.
   Reserved height fits the two-line pill (name + italic address) plus
   a comfortable margin. */
.loc-studios {
  align-items: center;
  min-height: 56px;
}

.loc-pill {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 1px solid var(--hair-strong);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  padding: 11px 22px;
  border-radius: 999px;
  cursor: pointer;
  transition: background var(--t) var(--ease), color var(--t) var(--ease),
              border-color var(--t) var(--ease), transform var(--t) var(--ease);
}
.loc-pill:hover {
  border-color: var(--ink);
}
.loc-pill.is-active {
  background: var(--ink);
  color: var(--bone);
  border-color: var(--ink);
}
.loc-pill-lg {
  font-size: 13.5px;
  letter-spacing: 0.2em;
  padding: 13px 28px;
}
/* Sub-row studio pills sit slightly smaller and use the accent tint when
   active so they read as a secondary choice under the city pills. Each
   pill carries two stacked lines: an uppercase letter-spaced sans
   studio name (.ls-name) and a smaller italic-serif address line
   (.ls-addr) below it. The pill auto-grows in height to fit the second
   line; padding is tightened vertically so the pill doesn't read as
   overly tall when both lines are present. */
.loc-studio {
  font-size: 11.5px;
  letter-spacing: 0.18em;
  /* Asymmetric vertical padding pulls the two-line stack visually
     upward: more space at the top, less at the bottom, so the italic
     address sits lower inside the pill (closer to the bottom edge)
     for an editorial caption feel. */
  padding: 8px 18px 5px;
  color: var(--mute);
  border-color: var(--hair-strong);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.2;
  gap: 4px;
}
.loc-studio.is-active {
  background: var(--accent);
  color: var(--bone);
  border-color: var(--accent);
}
/* Restore the browser's default `[hidden] { display: none }` behavior
   on studio pills. We set `display: inline-flex` on .loc-studio above
   (so the two-line name + address layout works), which has higher
   specificity than the user-agent stylesheet's `[hidden]` rule and
   was silently making hidden pills (e.g. Brickell when NY is picked)
   still render. */
.loc-studio[hidden] { display: none; }
/* Studio name — inherits the pill's letter-spaced uppercase treatment.
   Wrapped in a span so it sits on its own line above the address. */
.ls-name {
  display: block;
}
/* Address line — small italic serif, normal case, sits below the
   uppercase studio name. On rest pills it reads in --mute-soft; on
   the active (accent) pill it reads in --bone with reduced opacity
   so it stays legible without competing with the studio name. */
.ls-addr {
  display: block;
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0.01em;
  /* Bumped from 10.5px → 12px for legibility; reads as a deliberate
     italic caption rather than fine print. */
  font-size: 12px;
  color: var(--mute-soft);
  /* Extra top margin (on top of the parent's `gap: 4px`) shifts the
     address slightly lower in the pill so the visual weight sits
     toward the bottom edge. */
  margin-top: 2px;
  white-space: nowrap;
}
.loc-studio.is-active .ls-addr {
  color: var(--bone);
  opacity: 0.85;
}

.loc-current {
  margin: 6px auto 0;
  font-family: var(--serif);
  font-style: italic;
  /* Bumped from 18 px so the selected studio address reads as a
     deliberate moment of confirmation, not a tiny caption. */
  font-size: 26px;
  color: var(--mute);
  text-align: center;
  /* Stacked layout: small light-gray label on top, large italic
     serif name below — same column structure at every state (rest /
     city-picked / studio-locked) so position is stable as the user
     transitions between them. */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  max-width: 56ch;
  line-height: 1.35;
  /* Height reserved up-front to fit the largest state: label (1 line)
     + name (up to 2 lines when the locked address wraps on a narrower
     viewport). Centers content vertically when shorter copy occupies
     the space so the visual weight stays in the same band as the
     prompt swaps between "Pick a city…", "Pick a studio…", and the
     studio address. */
  min-height: 90px;
}
.lc-current-label {
  font-family: var(--sans);
  font-style: normal;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute-soft);
  white-space: nowrap;
}
.lc-current-name { color: var(--ink); }

/* The label and name always keep their own styling — light-gray
   uppercase letter-spaced sans on top, large black italic serif
   below. The previous "studio-locked" CSS override (hide label,
   restyle name) was retired because the user's preferred locked
   layout writes the studio name *into* the label slot and the full
   street address *into* the name slot, so the visual rhythm is the
   same at every state. */

@media (max-width: 640px) {
  .loc-pill-lg { font-size: 12.5px; padding: 11px 22px; letter-spacing: 0.16em; }
  .loc-studio  { font-size: 11px;   padding: 8px 16px;  letter-spacing: 0.14em; }
  /* Mobile: address still reads big but a touch smaller than desktop
     so a long line like "168 SE 1st Street, 10th Floor · Brickell,
     Miami" doesn't wrap awkwardly on a 375 px phone. min-height is
     bumped a touch because the longer locked address may wrap to two
     lines on narrow screens — we reserve for that case so the layout
     stays stable through the transition. */
  .loc-current { font-size: 20px; line-height: 1.3; min-height: 80px; }
}

/* ──────────────────────────────────────────────────────────────
   New booking picker (v7) — one expanding pill per city.

   Layout: a flex row (.loc-row-cities) holds two .city-pill
   containers (NY, Miami). Each city pill is a rounded outline
   containing a city button on the left + an inline
   .cp-studios-wrap that grows in width to reveal the studios
   inside. Internal dividers are thin SVG `)` curves; the active
   studio fill is a 3-piece leaf (.cp-fill-l wedges + .cp-fill-m
   rectangle + .cp-fill-r lens) bounded by those curves rather
   than its own pill outline. The old `.loc-row.loc-cities` /
   `.loc-row.loc-studios` pair lives on inside `.bs-studio-pick`
   (the in-step studio chooser inside step-book), so those legacy
   rules above this block are kept intact.

   Animation: symmetric s-curve in both directions (slower-faster-
   slower), open ~580ms, close ~460ms. main.js sequences city
   switches with a 320ms delay so the row never has to wrap. */
:root {
  --pill-h: 48px;
  --div-w: 24px;                              /* = pill-h / 2 — true half-circle radius */
  --t-open: 580ms;
  --t-close: 460ms;
  --ease-anim: cubic-bezier(.7, 0, .3, 1);    /* slower-faster-slower s-curve */
}

.loc-row-cities {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 12px;
  width: 100%;
}

/* The expanding pill — single rounded outline, isolated stacking
   context so the active-fill ::before/SVG layers don't bleed past
   the rounded border. */
.city-pill {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--hair-strong);
  border-radius: 999px;
  background: transparent;
  overflow: hidden;
  isolation: isolate;
  position: relative;
  transition: border-color var(--t-close) var(--ease-anim);
  -webkit-tap-highlight-color: transparent;
  max-width: 100%;
}
.city-pill.is-open {
  border-color: var(--ink);
  transition: border-color var(--t-open) var(--ease-anim);
}

/* City button — always visible. When the pill opens, the ::before
   fills with ink and extends `right: -[divider width]` past the
   button so its rounded right end coincides with the first
   separator's curve (same half-circle radius, they overlay
   perfectly). */
.cp-city-btn {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 13px 28px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  min-height: var(--pill-h);
  transition: color var(--t-close) var(--ease-anim),
              transform 140ms var(--ease);
  touch-action: manipulation;
  z-index: 1;
}
.city-pill.is-open .cp-city-btn {
  color: var(--bone);
  transition: color var(--t-open) var(--ease-anim),
              transform 140ms var(--ease);
}
.cp-city-btn::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: calc(var(--div-w) * -1);
  background: var(--ink);
  border-radius: 999px;
  z-index: -1;
  opacity: 0;
  transition: opacity var(--t-close) var(--ease-anim);
  pointer-events: none;
}
.city-pill.is-open .cp-city-btn::before {
  opacity: 1;
  transition: opacity var(--t-open) var(--ease-anim);
}
.cp-city-btn:active { transform: scale(.96); }

/* Studios wrap — animates max-width 0 → 720px. The 720 is a
   ceiling; the actual width is bounded by the content. Different
   transitions for open vs close (CSS picks the destination-state
   transition automatically). */
.cp-studios-wrap {
  display: inline-flex;
  align-items: stretch;
  overflow: hidden;
  max-width: 0;
  transition: max-width var(--t-close) var(--ease-anim);
}
.city-pill.is-open .cp-studios-wrap {
  max-width: 720px;
  transition: max-width var(--t-open) var(--ease-anim);
}

/* Separator — thin SVG arc of radius = pill-h / 2, rendered full
   height so the hairline touches the top and bottom of the outer
   pill outline. */
.cp-divider {
  display: inline-flex;
  align-items: stretch;
  justify-content: center;
  flex: 0 0 var(--div-w);
  width: var(--div-w);
  color: var(--hair-strong);
  opacity: 0;
  transition: opacity 200ms var(--ease-anim);
  pointer-events: none;
  position: relative;
  z-index: 0;
}
/* The hairline separator arc has a near-vertical tangent at its
   rightmost extent, so as a thin stroke it reads as a stray vertical
   line rather than a rounded curve — especially on narrow viewports
   where --div-w is small and the arc is squeezed tall and thin. The
   divider element is kept (its width spaces the sections so the
   leaf-shaped active fill lands in the right gap), but its visible
   hairline stays hidden in every state. Section boundaries are
   carried instead by the rounded city-button fill and, once a studio
   is chosen, the rounded leaf fill. */
.city-pill.is-open .cp-divider {
  opacity: 0;
}
.cp-divider svg {
  width: 100%;
  height: 100%;
  display: block;
}

/* Studio section. On open: opacity + transform both transition.
   On close: only opacity (the small translateX(8px) snaps under
   the fade, which is barely noticeable). Reverse stagger on close
   so the rightmost studio fades first, matching the wrap
   collapsing rightward. */
.cp-studio {
  position: relative;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  color: var(--mute);
  font-family: var(--sans);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 8px 22px 5px;
  cursor: pointer;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  line-height: 1.2;
  gap: 4px;
  white-space: nowrap;
  min-height: var(--pill-h);
  opacity: 0;
  transform: translateX(8px);
  transition: opacity 280ms var(--ease-anim),
              color 240ms var(--ease);
  z-index: 2;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
/* Close stagger — rightmost studio leaves first */
.cp-studio:nth-of-type(1) { transition-delay: 100ms; }
.cp-studio:nth-of-type(2) { transition-delay: 0ms; }

.city-pill.is-open .cp-studio {
  opacity: 1;
  transform: translateX(0);
  transition: opacity 320ms var(--ease-anim),
              transform 480ms var(--ease-anim),
              color 240ms var(--ease);
}
/* Open stagger — leftmost studio arrives first */
.city-pill.is-open .cp-studio:nth-of-type(1) { transition-delay: 120ms; }
.city-pill.is-open .cp-studio:nth-of-type(2) { transition-delay: 240ms; }

.cp-studio:active { transform: scale(.96); }
.cp-studio:hover { color: var(--ink); }
.cp-studio.is-active { color: var(--bone); }
.cp-studio.is-active .ls-addr { color: var(--bone); opacity: 0.85; }

/* Leaf-shaped active fill — three positioned pieces. .cp-fill-l
   in the left divider area is clipped (via inline SVG path) to
   the "right-of-curve" wedges shape. .cp-fill-m is a plain
   rectangle filling the section. .cp-fill-r in the right divider
   area is clipped to the "left-of-curve" lens shape. Last-child
   studios drop .cp-fill-r and let the outer pill's
   overflow:hidden + border-radius:999px clip the middle
   rectangle's right edge into the same `)` shape. */
.cp-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: -1;
  opacity: 0;
  transition: opacity 280ms var(--ease-anim);
  pointer-events: none;
  color: var(--accent);     /* SVG <path fill="currentColor"> picks this up */
}
.cp-fill svg { width: 100%; height: 100%; display: block; }
.cp-fill-l { left: calc(var(--div-w) * -1); width: var(--div-w); }
.cp-fill-m { left: 0; right: 0; background: var(--accent); }
.cp-fill-r { right: calc(var(--div-w) * -1); width: var(--div-w); }
.cp-studio.is-active .cp-fill { opacity: 1; }
.cp-studios-wrap > .cp-studio:last-child .cp-fill-r { display: none; }

/* Prompt fade — coordinates with the picker open/close. main.js
   toggles .is-changing around text swaps for a 220ms fade out
   and back in. */
.loc-current { transition: opacity 220ms var(--ease-anim); }
.loc-current.is-changing { opacity: 0; }

/* Mobile — keep the two city pills on a single inline row. The
   pill dimensions, font sizes, and padding all shrink so an
   expanded city (with its studio sub-pills) plus the other city
   collapsed fits horizontally on most phones. If a very narrow
   viewport can't fit even the shrunken picker, the row falls
   back to native horizontal scroll instead of wrapping. */
@media (max-width: 640px) {
  :root {
    --pill-h: 40px;
    --div-w: 20px;
  }
  .loc-row-cities {
    flex-wrap: nowrap;
    gap: 8px;
    overflow-x: auto;
    overflow-y: visible;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 0;
    /* `safe center` centers the pills when they fit, but falls back to
       start-alignment the moment the row overflows — so an opened city
       pill is never clipped with its left edge stranded off-screen and
       unreachable (which is what plain `center` does on overflow). */
    justify-content: safe center;
  }
  .loc-row-cities::-webkit-scrollbar { display: none; }
  /* `flex-shrink: 0` prevents pills from being squeezed when the row
     overflows — they keep their natural width and the row scrolls. */
  .city-pill { flex-shrink: 0; }
  .cp-city-btn {
    font-size: 11.5px;
    letter-spacing: 0.16em;
    padding: 10px 18px;
  }
  .cp-studio {
    font-size: 10.5px;
    letter-spacing: 0.14em;
    padding: 7px 14px 5px;
  }
  .cp-studio .ls-addr {
    font-size: 11px;
  }
}

/* Narrower phones — shrink further so the picker fits on a 375 px
   viewport (iPhone SE / mini class). */
@media (max-width: 420px) {
  :root {
    --pill-h: 36px;
    --div-w: 18px;
  }
  .cp-city-btn {
    font-size: 10px;
    letter-spacing: 0.14em;
    padding: 8px 14px;
  }
  .cp-studio {
    font-size: 9.5px;
    letter-spacing: 0.12em;
    padding: 6px 10px 4px;
  }
  .cp-studio .ls-addr {
    font-size: 10px;
  }
}

/* Reduced-motion + hostile-browser fallbacks. */
@media (prefers-reduced-motion: reduce) {
  .city-pill, .cp-studios-wrap, .cp-studio, .cp-divider, .cp-city-btn,
  .cp-city-btn::before, .cp-fill, .loc-current {
    transition: none !important;
  }
  .city-pill.is-open .cp-studio { transform: none; }
}
@supports not (transition: max-width 1ms) {
  .cp-studios-wrap { transition: none; }
  .city-pill.is-open .cp-studio { transform: none; }
}
.no-anim .city-pill *,
.no-anim .city-pill *::before,
.no-anim .loc-current {
  transition: none !important;
  animation: none !important;
}
.no-anim .city-pill.is-open .cp-studio { transform: none; }

/* (Studio map styles removed on 2026-05-18 — to be reintroduced as
   its own dedicated treatment later. The booking shell no longer
   renders any `.loc-map` DOM.) */

/* ── Picker grid: row-aligned cards via subgrid ──────────────
   The picker grid defines nine shared rows. Each .picker-card
   uses `grid-template-rows: subgrid` to map its own children
   onto those rows, so the same row position (e.g., "session
   duration") sits at the same Y across all three cards and
   stays vertically comparable.

   Row map:
     1. .pk-head
     2. .pk-price
     3. common feature: session duration
     4. common feature: image count
     5. common feature: outfit / background
     6. common feature: AAMC spec format
     7. common feature: same-day delivery
     8. unique items block (Plus's outdoors, Besties's "for two")
     9. CTA button

   Row 8 is `1fr` so empty cards (Basic has no unique row) push
   the button to the same baseline as cards with unique content. */
.picker-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows:
    auto    /* 1. head: flag */
    auto    /* 2. head: tag */
    auto    /* 3. head: name  ← all three names share this row, so they
                                line up no matter how many lines the flag
                                or tag above them wrap to */
    auto    /* 4. price */
    auto    /* 5. common: session */
    auto    /* 6. common: images */
    auto    /* 7. common: outfit */
    auto    /* 8. common: AAMC */
    auto    /* 9. common: delivery */
    1fr     /* 10. unique block — fills extra space when empty */
    auto;   /* 11. button */
  column-gap: 1px;
  /* row-gap drives the vertical rhythm inside every card via subgrid
     inheritance — head→price→list→unique→button all share this gap.
     The picker stays comparison-table-narrow on mobile, so the only
     dimension we have left to spend on breathing room is vertical. */
  row-gap: 15px;
  background: var(--hair);
}
/* All three tiers stay side-by-side at every viewport size — no
   column drop on mobile. Card content compacts below 640 px (see
   the `.picker-card` mobile override block) so each card stays
   legible at ~120 px wide on a phone. */

.picker-card {
  /* Subgrid: inherit the parent's row tracks 1-11 so each card's
     internal sections line up with the other cards. */
  display: grid;
  grid-template-rows: subgrid;
  grid-row: 1 / -1;
  position: relative;
  background: var(--cream);
  /* Tighter padding so the card holds together at narrow widths and
     the price + feature list aren't pushed against the cell wall. */
  padding: clamp(16px, 2.2vw, 28px);
  cursor: pointer;
  transition: background var(--t) var(--ease);
}
/* Explicit row assignment for the card's direct children. Pinning each
   to a named track keeps Basic (no unique block) from collapsing the
   button up, and lets the head map onto the flag/tag/name tracks. */
/* The head is itself a subgrid spanning the parent's flag/tag/name
   tracks (rows 1-3). Because the tag track sizes to the TALLEST tag
   across all three cards, a 1-line tag ("Bring a friend") reserves the
   same height as a 2-line tag ("Application photo") — so every "ERAS …"
   name drops onto the shared row 3 and the names align across cards at
   any screen size, regardless of how flag/tag wrap. */
.picker-card > .pk-head {
  grid-row: 1 / span 3;
  display: grid;
  grid-template-rows: subgrid;
  row-gap: 4px;                /* tight head spacing, overrides parent gap */
}
.picker-card > .pk-head > .pk-flag { grid-row: 1; align-self: start; }
.picker-card > .pk-head > .pk-tag  { grid-row: 2; align-self: start; margin: 0; }
.picker-card > .pk-head > .pk-name { grid-row: 3; align-self: start; margin: 0; }
.picker-card > .pk-price { grid-row: 4; }
.picker-card > .pk-list-common {
  /* Second-level subgrid: each of the 5 <li> common feature rows
     drops into one of the parent's tracks 5-9 so feature rows align
     across cards. */
  grid-row: 5 / span 5;
  display: grid;
  grid-template-rows: subgrid;
  margin: 0;
  padding: 0;
  list-style: none;
}
.picker-card > .pk-list-unique {
  grid-row: 10;
  /* Items stack at the bottom of row 10 (just above the button) so
     when a card has 0 or 1 unique items, they sit close to the CTA
     rather than floating in the middle. */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 11px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.picker-card > .picker-cta {
  grid-row: -1;
  align-self: end;
  /* 50 px of empty space above and below the button (outside the
     pill, not inside it). The button's row track grows to fit the button
     plus these margins, so the empty space sits between the unique
     items / common list above and the bottom of the card below. */
  margin: 50px 0;
}
/* Mobile compaction. At ~390 px viewport each card is ~127 px wide,
   so the picker has to drop font sizes, padding, and gaps significantly
   to keep the price + feature list readable without truncation. The
   feature-list text wraps to multiple lines — that's fine, each card
   just grows taller. The grid stretches all three to match the tallest
   card's height. */
@media (max-width: 640px) {
  /* Mobile row-gap stays generous so each description item gets
     breathing room — we lose horizontal space at narrow widths and
     have to make it up vertically. */
  .picker-grid { row-gap: 11px; }
  .picker-card { padding: 12px 8px; }
  .picker-card > .pk-list-unique { gap: 11px; }
  .picker-card .pk-flag {
    font-size: 9px;
    letter-spacing: 0.16em;
  }
  /* Mobile bump for tag + name in line with the desktop ~65% bump
     above. Held back a bit from a strict 65 % so "ERAS Besties"
     still fits inside a ~110 px content column on a 375 px phone. */
  .picker-card .pk-tag { font-size: 15px; line-height: 1.2; }
  /* .pk-name intentionally has no mobile font-size override — the base
     clamp(30px, 9vw, 74px) scales it fluidly with the screen and fills
     the column at every width. */
  .picker-card .pk-cur  { font-size: 12px; margin-right: 2px; }
  .picker-card .pk-num  { font-size: 30px; }
  .picker-card .pk-each {
    /* "total · $150 each" is too long for a 120 px card. Drop down
       to a 1-line label below the price instead of inline. */
    display: block;
    margin: 4px 0 0;
    font-size: 8.5px;
    letter-spacing: 0.16em;
  }
  .picker-card .pk-list {
    font-size: 11px;
    line-height: 1.35;
    gap: 7px;
  }
  .picker-card .pk-list li { gap: 7px; }
  .picker-card .pk-icon { width: 12px; height: 12px; margin-top: 2px; }
  .picker-card .picker-cta {
    /* Modest padding INSIDE the pill on mobile — the big 75 px
       breathing room lives outside, via the margin on
       `.picker-card > .picker-cta`. Horizontal padding stays tight
       so "BOOK BESTIES" still fits in a ~110 px column on a 375 px
       phone. */
    padding: 11px 10px;
    font-size: 9.5px;
    letter-spacing: 0.12em;
    gap: 4px;
  }
  /* Mobile price block gets a smaller bottom margin since the
     overall card spacing is already tighter. */
  .picker-card .pk-price { margin-bottom: 6px; }
}
.picker-card:hover { background: var(--bone); }
.picker-featured {
  background: var(--ink);
  color: var(--on-photo);
}
.picker-featured:hover { background: var(--ink-soft); }
.picker-featured .pk-tag,
.picker-featured .pk-flag,
.picker-featured .pk-list li { color: rgba(251, 247, 238, 0.75); }

.pk-head { display: flex; flex-direction: column; gap: 4px; }
.pk-flag {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--accent);
}
.picker-featured .pk-flag { color: var(--accent-soft); }
.pk-tag {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  /* ~65% larger than the previous 14 px so the tagline reads as
     deliberate editorial copy, not a fine-print label. */
  font-size: 23px;
  line-height: 1.15;
  color: var(--mute);
}
.pk-name {
  margin: 6px 0 0;
  font-family: var(--serif);
  font-weight: 400;
  /* Slideshow treatment: italic "ERAS" stacked over the upright tier
     word, sized to fill the column and scale up with the screen. The
     fluid clamp is driven by viewport width (the columns widen with the
     screen, so the type tracks the column), floored so it stays legible
     on a ~110 px phone column and capped so it doesn't overwhelm on
     desktop. Wraps to two lines via the `em` block rule below. */
  font-size: clamp(30px, 9vw, 74px);
  line-height: 0.95;
  letter-spacing: -0.01em;
}
/* Force the slideshow stack: "ERAS" on its own line (italic), the tier
   word beneath it — at every width, so the names read as a consistent
   two-line lockup that fills the column. */
.pk-name em {
  display: block;
  opacity: 0.8;
  margin: 0;
}

.pk-price {
  /* Extra breathing room below the price block so the feature list
     reads as its own section, not glued to the price. The card's
     flex `gap` already provides 14 px between items; the margin
     stacks on top to add another ~10 px specifically here. */
  margin: 0 0 10px;
  font-family: var(--serif);
  display: inline-flex;
  align-items: baseline;
}
.pk-cur { font-size: 16px; opacity: 0.7; margin-right: 4px; }
.pk-num { font-size: clamp(40px, 4vw, 56px); line-height: 1; letter-spacing: -0.02em; }

.pk-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 11px;
  font-size: 13.5px;
  line-height: 1.4;
}
.pk-list li {
  color: var(--mute);
  display: flex;
  align-items: flex-start;
  gap: 11px;
}
.picker-featured .pk-list li { color: rgba(251, 247, 238, 0.82); }

/* Inline icon glyph used in feature lists. Sits inline with the body type,
   slight top nudge so the optical baseline lines up with first text line. */
.pk-icon {
  flex-shrink: 0;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  color: var(--accent);
}
.picker-featured .pk-icon { color: var(--accent-soft); }
.pk-list li sup {
  font-size: 0.65em;
  vertical-align: super;
}

/* Defs-only SVG — declared at the top of body to register the symbol library.
   Must not take any layout space. */
.icon-defs {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Inline span used to expose a secondary anchor target inside a section
   (e.g. id="book" inside the id="pricing" section). Zero footprint. */
.anchor-jump {
  display: block;
  height: 0;
  width: 0;
  overflow: hidden;
}

/* Visually hide an element while keeping it in the accessibility tree
   and search-engine HTML. Used on /book/ for the page's h1 — the
   visible title was removed to keep the iframe high on screen, but
   we still want one canonical h1 per page for SEO + screen readers. */
.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;
}

/* "each" tag on the Besties price — small uppercase suffix sitting next to $300 */
.pk-each {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--mute);
  margin-left: 8px;
  align-self: baseline;
}
.picker-featured .pk-each { color: rgba(251, 247, 238, 0.65); }

.picker-cta {
  /* In the new grid layout: `align-self: end` (set in the card-scoped
     rule below) pins the button to the bottom of its row track, and
     `justify-self: center` here centers it horizontally within the
     card column. */
  justify-self: center;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* Modest vertical padding inside the pill itself — the real
     breathing room lives OUTSIDE the button (see margin in the
     `.picker-card > .picker-cta` rule below). */
  padding: 14px 24px;
  border: 1px solid var(--ink);
  border-radius: 999px;
  background: transparent;
  color: var(--ink);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-align: center;
  cursor: pointer;
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
}
.picker-cta:hover { background: var(--ink); color: var(--bone); }
.picker-featured .picker-cta { color: var(--on-photo); border-color: rgba(251, 247, 238, 0.4); }
.picker-featured .picker-cta:hover { background: var(--on-photo); color: var(--ink); border-color: var(--on-photo); }

/* ── Pricing palette: Pewter → Graphite → Onyx ─────────────────
   A quiet, masculine, gallery-cold gradient across the three
   tier cards. Cool desaturated grays in place of the previous
   cream / black / cream alternation. ID selectors (specificity
   0,1,0) override the .picker-card and .picker-featured class
   rules above without touching them, so existing layout,
   typography, and accent colors carry forward unchanged.
   Pewter stays light enough to keep its existing dark type;
   Graphite + Onyx invert text to the cream "on-photo" palette
   already used by .picker-featured. */
/* Palette tuned to the reference swatch image the user pinned:
   all three cards live in the dark half of the spectrum — dark
   pewter / charcoal graphite / near-black onyx. Cream "on-photo"
   text inversion applies to all three (already in place below). */
#tier-basic    { background: #46484C; color: var(--on-photo); }
#tier-basic:hover { background: #51545A; }
#tier-basic .pk-tag,
#tier-basic .pk-list li { color: rgba(251, 247, 238, 0.82); }
#tier-basic .pk-flag { color: var(--accent-soft); }
#tier-basic .pk-icon { color: var(--accent-soft); }
#tier-basic .pk-each { color: rgba(251, 247, 238, 0.65); }
#tier-basic .picker-cta { color: var(--on-photo); border-color: rgba(251, 247, 238, 0.4); }
#tier-basic .picker-cta:hover { background: var(--on-photo); color: var(--ink); border-color: var(--on-photo); }

#tier-plus    { background: #2A2B2E; }
#tier-plus:hover { background: #34363A; }

#tier-besties { background: #0E1013; color: var(--on-photo); }
#tier-besties:hover { background: #181A1E; }
#tier-besties .pk-tag,
#tier-besties .pk-list li { color: rgba(251, 247, 238, 0.78); }
#tier-besties .pk-flag { color: var(--accent-soft); }
#tier-besties .pk-icon { color: var(--accent-soft); }
#tier-besties .pk-each { color: rgba(251, 247, 238, 0.65); }
#tier-besties .picker-cta { color: var(--on-photo); border-color: rgba(251, 247, 238, 0.4); }
#tier-besties .picker-cta:hover { background: var(--on-photo); color: var(--ink); border-color: var(--on-photo); }

.picker-foot {
  margin: clamp(20px, 3vh, 28px) 0 0;
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: var(--mute);
}
.picker-foot a { border-bottom: 1px solid var(--hair-strong); }
.picker-foot a:hover { color: var(--accent); border-color: var(--accent); }

/* ── Orbit attention cue (city pills + Book CTAs) ─────────────────
   A warm-orange "comet" (bright cream core + peach mid-gradient +
   blurred terracotta halo) orbits the pill's outer border, driven
   by main.js via a rAF loop along a live stadium path. Used to
   draw the eye to the next required choice in the booking flow:

     state A — no city picked     → orbit both .city-pill elements
     state B — a city pill opened → orbit ONLY that pill (its
                                     outer outline tracks the
                                     expand animation, so the
                                     comet follows the growing
                                     stadium continuously)
     state C — a studio is locked → orbit hands off to the three
                                     .picker-cta Book buttons

   JS injects an absolutely-positioned SVG (.orbit-svg) into each
   target. The pill's own overflow:hidden clips the halo to the
   pill's rounded perimeter, so the visible part of the comet
   rides the inside of the rim. .picker-cta has overflow:visible
   so the comet's halo glows outward.

   The .orbit-cue class toggled by JS controls visibility via
   opacity — that lets the SVG fade in/out cleanly when state
   transitions, without yanking the DOM. */
.city-pill,
.picker-cta {
  /* overflow & isolation already set on these in their own rules;
     position:relative is the only addition needed for the absolute
     SVG inside them to anchor correctly. */
  position: relative;
}
.picker-cta { overflow: visible; isolation: isolate; }

.orbit-svg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  z-index: 0;
  opacity: 0;
  transition: opacity 360ms cubic-bezier(.6,.02,.3,1);
}
/* Two selectors cover both DOM strategies:
   – `.orbit-cue .orbit-svg` matches when the SVG is a CHILD of
     the cueing element (city-pill case — SVG lives inside the
     pill so the pill's overflow:hidden clips the rim).
   – `.orbit-cue ~ .orbit-svg` matches when the SVG is a SIBLING
     after the cueing element (picker-cta case — SVG lives in
     the parent .picker-card so the button's intrinsic size
     isn't inflated by an inline-flex SVG child). */
.orbit-cue .orbit-svg,
.orbit-cue ~ .orbit-svg { opacity: 1; }

/* Keep button labels above the orbit SVG. .picker-cta's <span>
   child already exists; .cp-city-btn / .cp-studio carry their
   own z-index in the pill picker rules above. */
.picker-cta > * { position: relative; z-index: 1; }

/* Reduced-motion fallback — drop the orbit entirely, replace
   with a static 1-px gray outline so the visual cue remains
   for users who avoid motion. Color tuned to each surface:
   ink on the bone-bg pills, cream on the dark-bg book CTAs. */
@media (prefers-reduced-motion: reduce) {
  .orbit-svg { display: none; }
  .city-pill.orbit-cue {
    box-shadow: 0 0 0 1px rgba(26, 24, 20, 0.55);
  }
  .picker-cta.orbit-cue {
    box-shadow: 0 0 0 1px rgba(250, 246, 239, 0.65);
  }
}

/* Step 2 — schedule
   Compact single-row header: [ERAS Basic $225] [← Change session]
   [Times Square · 260 W 44th St]. The bs-info wrapper and bs-eyebrow
   are gone; flexbox keeps name + back button tight together and
   pushes the location info to the right edge. On narrow screens
   bs-meta wraps to a new line below. */
.bs-head {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: clamp(12px, 2vh, 20px);
  border-bottom: 1px solid var(--hair);
  margin-bottom: clamp(14px, 2vh, 22px);
}
.bs-back {
  /* ~50% smaller than the previous version — tighter padding,
     smaller font, narrower letter-spacing. The arrow glyph is also
     gone from the markup; this is now a small text-only pill that
     sits unobtrusively next to the session name. */
  display: inline-flex;
  align-items: center;
  padding: 4px 9px;
  border: 1px solid var(--ink);
  background: transparent;
  border-radius: 999px;
  font-size: 8.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink);
  transition: background var(--t) var(--ease), color var(--t) var(--ease);
  cursor: pointer;
}
.bs-back:hover { background: var(--ink); color: var(--bone); }

.bs-name {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(24px, 2.4vw, 32px);
  line-height: 1;
}
.bs-name em { font-style: italic; opacity: 0.85; margin-right: 4px; }
.bs-price {
  font-size: 0.7em;
  margin-left: 12px;
  color: var(--mute);
}
.bs-meta {
  /* Pushed to the right edge of the flex row on desktop. On mobile
     (when bs-meta wraps to a new line), `margin-left: 0` from the
     wrap behavior keeps it left-aligned under the name. */
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--serif);
  font-style: italic;
  font-size: 15px;
  color: var(--mute);
}
.bs-meta-dot { color: var(--mute-soft); }
@media (max-width: 640px) {
  .bs-head { gap: 10px; }
  .bs-meta { margin-left: 0; flex-basis: 100%; font-size: 13.5px; }
}

.book-frame { display: none; }
.book-frame.is-active { display: block; }

/* ── In-step studio chooser (.bs-studio-pick) ───────────────
   Lives inside #step-book, sits between .bs-head and .book-frames.
   Hidden by default; surfaces only when #step-book carries the
   `.needs-studio` class, which JS toggles when the user lands on
   step-book without a locked studio (e.g. they clicked a tier card
   first without picking a city). When visible, .book-frames stays
   hidden and .bs-meta also fades out — the user sees a calm
   invitation to pick a studio rather than a half-rendered iframe
   header. Once a studio is locked, JS drops .needs-studio and the
   matching iframe takes its place. No warning copy, no shake/pulse.

   Designed to feel like a continuation of the flow, not a roadblock
   — same pill language as step-pick, soft eyebrow + italic serif
   line, a quiet foot caption reassuring the user that location
   doesn't change the deliverable. */
.bs-studio-pick {
  display: none;
}
#step-book.needs-studio .bs-studio-pick { display: block; }
#step-book.needs-studio .book-frames    { display: none; }
#step-book.needs-studio .bs-meta        { display: none; }

.bs-studio-prompt {
  margin: clamp(12px, 2.5vh, 28px) auto clamp(18px, 2.6vh, 28px);
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: clamp(6px, 1vh, 10px);
  max-width: 56ch;
}
.bs-studio-line {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(24px, 2.4vw, 32px);
  color: var(--ink);
  line-height: 1.2;
}
.bs-studio-foot {
  margin: clamp(14px, 2vh, 22px) auto 0;
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 14px;
  color: var(--mute);
  max-width: 56ch;
}
/* The .loc-toggle inside step-book reuses the same pill language as
   step-pick but doesn't need the bottom hairline (we're not separating
   from another section here — the chooser stands alone). The
   universal `.loc-toggle:not(:has(.loc-city.is-active)) .loc-studios`
   rule above already hides the studio row at rest, so no extra
   collapse rules are needed here. */
.bs-loc-toggle {
  border-bottom: 0;
  margin-bottom: 0;
}

/* Schedulista iframe sizing.
   The iframes ship with `height="900"` in HTML — fine for desktop,
   but on mobile (≤ 640 px) the calendar + selected-date + AM/PM
   time-slot grid stack vertically and need more room. We set a
   generous `min-height` so the last row of time slots isn't clipped
   below the iframe edge. Schedulista's widget.js can still grow the
   iframe further via postMessage; `min-height` just sets a floor. */
.book-frame iframe {
  display: block;
  width: 100%;
  border: 0;
  min-height: 1000px;
}
/* Side gutters so the Schedulista time-slot buttons don't sit flush
   against the screen edge on narrow phones (the rightmost column —
   e.g. "2:00 pm" — was getting clipped/hard to tap). The container
   is content-box, so the width:100% iframe simply insets within the
   padding; no horizontal overflow. */
.book-frame {
  box-sizing: border-box;
  padding-left: 12px;
  padding-right: 12px;
}
@media (max-width: 640px) {
  .book-frame iframe { min-height: 1500px; }
  .book-frame {
    padding-left: 16px;
    padding-right: 16px;
  }
}

.book-placeholder {
  padding: clamp(40px, 6vw, 72px);
  background: var(--bone);
  border: 1px dashed var(--hair-strong);
  text-align: center;
}
.book-placeholder h3 {
  margin: 0 0 10px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(22px, 2vw, 30px);
}
.book-placeholder p {
  margin: 0 0 20px;
  color: var(--mute);
  font-size: 14px;
  max-width: 56ch;
  margin-left: auto;
  margin-right: auto;
}
.book-placeholder code {
  font-family: ui-monospace, Menlo, Consolas, monospace;
  background: var(--bone-deep);
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 12px;
}
.book-placeholder .btn { background: var(--ink); color: var(--bone); border-color: var(--ink); }
.book-placeholder .btn:hover { background: var(--bone); color: var(--ink); }

/* ── FAQ ──────────────────────────────────────────────────── */
.faq-list {
  max-width: 820px;
  margin: 0 auto;
  border-top: 1px solid var(--hair);
}
.faq-list details {
  border-bottom: 1px solid var(--hair);
  padding: 0;
}
.faq-list summary {
  list-style: none;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  padding: 22px 0;
  cursor: pointer;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-q {
  font-family: var(--serif);
  font-size: clamp(18px, 1.6vw, 22px);
  color: var(--ink);
}
.faq-chev {
  font-family: var(--serif-2);
  font-size: 22px;
  color: var(--mute);
  transition: transform var(--t) var(--ease), color var(--t) var(--ease);
}
.faq-list details[open] .faq-chev { transform: rotate(45deg); color: var(--accent); }
.faq-list details p {
  margin: 0 0 22px;
  color: var(--mute);
  font-size: 15px;
  line-height: 1.65;
  max-width: 64ch;
}

/* ── FAQ extras (CTAs, maps, in-answer enrichments) ────────────
   Some FAQ answers carry additional content past the answer
   paragraph: a small CTA pill ("Add retouching", "Book Besties"),
   a row of pills, or — in the case of the location question — a
   three-up grid of studio cards with embedded Google Maps. The
   wrappers below keep that content vertically aligned with the
   collapsing chevron animation and bottom-bordered by the next
   <details>. The map grid spans the full 820px FAQ column even
   though answer prose is capped at 64ch — maps want the width. */

/* Spacing wrapper for anything that sits BELOW the answer paragraph
   inside a <details>. Keeps a tidy gap between the last paragraph
   and the enrichment block, and reserves bottom space before the
   FAQ row border. */
.faq-extras {
  margin: -6px 0 22px;     /* -6px tightens to the trailing <p> margin */
  max-width: 100%;
}

/* Inline CTA row used at the end of an FAQ answer. Renders one or
   more small pills under the prose. Wraps cleanly on mobile. */
.faq-cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 6px;
}

/* Small FAQ CTA pill — visually consistent with the article-cta
   .btn-glass treatment (cream fill, dark ink, dark border) but
   tighter so it reads as an inline action inside an answer rather
   than a "stop and consider" full-block CTA. */
.faq-cta {
  display: inline-flex;
  align-items: center;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--cream);
  color: var(--ink);
  border: 1.4px solid rgba(20, 17, 14, 0.78);
  font-family: var(--sans);
  font-style: normal;
  font-weight: 500;
  font-size: 13px;
  letter-spacing: 0.02em;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--t) var(--ease),
    color var(--t) var(--ease),
    border-color var(--t) var(--ease),
    transform var(--t) var(--ease),
    box-shadow var(--t) var(--ease);
}
.faq-cta:hover {
  background: rgba(20, 17, 14, 0.92);
  color: var(--on-photo);
  border-color: rgba(20, 17, 14, 0.92);
  transform: translateY(-1px);
  box-shadow: 0 6px 16px rgba(8, 6, 4, 0.18);
}
.faq-cta:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}
.faq-cta .arr {
  margin-left: 8px;
  font-family: var(--serif-2);
  font-style: normal;
}
@media (max-width: 560px) {
  .faq-cta {
    width: 100%;
    justify-content: center;
    white-space: normal;
  }
}

/* Studio map grid — three cards side by side at desktop, stacks
   on tablet/mobile. Each card carries a Google Maps embed, the
   address, and a link to the studio's own page. */
.faq-map-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin-top: 10px;
}
.faq-map-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--hair);
  border-radius: 10px;
  overflow: hidden;
  background: var(--cream);
}
.faq-map-card .faq-map-frame {
  position: relative;
  width: 100%;
  /* 4:3 map aspect ratio — tall enough to read the street grid
     without dominating the FAQ row. */
  padding-top: 75%;
}
.faq-map-card .faq-map-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  /* Render the embedded Google Map in black & white. This is a color
     transform, not a positional overlay — it doesn't cover or hide any
     of Google's UI elements, just desaturates the entire iframe. */
  filter: grayscale(1) contrast(1.05);
  -webkit-filter: grayscale(1) contrast(1.05);
}
.faq-map-card .faq-map-body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.faq-map-card .faq-map-city {
  font-family: var(--serif);
  font-size: 15px;
  color: var(--ink);
  line-height: 1.25;
}
.faq-map-card .faq-map-addr {
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--mute);
  line-height: 1.45;
  margin: 0;
}
/* Studio locations — big serif name, highlighted address on its own line */
.faq-loc-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.faq-loc {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.faq-loc-name {
  font-family: var(--serif);
  font-size: 24px;
  line-height: 1.15;
  color: var(--ink);
}
.faq-loc-addr {
  align-self: flex-start;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.35;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  padding: 4px 10px;
  border-radius: 7px;
}

.faq-map-card .faq-map-link {
  margin-top: 4px;
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hair);
  align-self: flex-start;
  padding-bottom: 1px;
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.faq-map-card .faq-map-link:hover {
  color: var(--accent);
  border-color: var(--accent);
}
.faq-map-card .faq-map-link .arr {
  font-family: var(--serif-2);
  margin-left: 6px;
}
@media (max-width: 880px) {
  .faq-map-grid {
    grid-template-columns: 1fr;
    gap: 14px;
  }
}

/* ── FAQ inline data blocks (tables + turnaround timeline) ────
   Used inside individual <details> answers to lay out information
   that reads better as a chart than as prose: the AAMC spec table,
   the session-tier comparison, studio opening hours, and the
   same-day delivery timeline. Visual language matches .spec-card
   (hairline borders on bone, sans uppercase keys, serif values)
   so these slot into the editorial system without inventing a
   new aesthetic. */

/* Scroll wrapper — kicks in on narrow viewports so the 4-column
   tables (tier compare, hours) can scroll horizontally instead of
   squishing or wrapping. Desktop: no scroll, no visual change. */
.faq-table-scroll {
  margin: 14px 0 4px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* The table itself. Real <table> markup for screen readers and SEO;
   styled to look like the rest of the site's editorial tables. */
.faq-table {
  width: 100%;
  border-collapse: collapse;
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
  font-family: var(--sans);
  background: var(--bone);
}
.faq-table th,
.faq-table td {
  padding: 11px 14px;
  border-right: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  text-align: left;
  vertical-align: top;
  line-height: 1.4;
}
.faq-table thead th {
  font-family: var(--sans);
  font-size: 10.5px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
  background: var(--cream);
  white-space: nowrap;
}
.faq-table tbody th {
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
  background: var(--cream);
  white-space: nowrap;
}
.faq-table tbody td {
  font-family: var(--serif-2);
  font-size: 15px;
  color: var(--ink);
}
.faq-table tbody td .num {
  font-family: var(--serif-2);
  font-size: clamp(17px, 1.4vw, 21px);
  letter-spacing: -0.005em;
}
.faq-table tbody td strong {
  color: var(--ink);
  font-weight: 600;
}
.faq-table tbody td.dash {
  color: var(--mute-soft);
}
.faq-table-foot {
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--mute);
  margin: 8px 0 4px;
  line-height: 1.55;
}

/* Tighter on phones — keep the 4-column tier-compare readable. */
@media (max-width: 640px) {
  .faq-table th,
  .faq-table td { padding: 9px 11px; }
  .faq-table tbody td { font-size: 13.5px; }
  .faq-table thead th,
  .faq-table tbody th { font-size: 10px; letter-spacing: 0.18em; }
}

/* ── Same-day delivery timeline chart ─────────────────────────
   A four-step horizontal bar that visualises how same-day delivery
   actually plays out — session, selection, AAMC-spec JPEG, full
   gallery. Built with CSS grid + pseudo-element bars; no chart lib,
   no JS. The bar opacity ramps up across steps to imply progression
   from "in studio" to "files in your inbox". On phones it collapses
   to a vertical list with dot markers. */
.faq-timeline {
  margin: 16px 0 4px;
  padding: 18px 18px 14px;
  background: var(--cream);
  border: 1px solid var(--hair);
}
.faq-timeline-bar {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
  align-items: end;
}
.faq-timeline-step {
  text-align: left;
}
.faq-timeline-step::before {
  content: "";
  display: block;
  height: 6px;
  border-radius: 3px;
  background: var(--accent);
  margin: 0 0 10px;
}
.faq-timeline-step:nth-child(1)::before { opacity: 0.30; }
.faq-timeline-step:nth-child(2)::before { opacity: 0.55; }
.faq-timeline-step:nth-child(3)::before { opacity: 0.80; }
.faq-timeline-step:nth-child(4)::before { opacity: 1.00; }
.faq-timeline-time {
  display: block;
  font-family: var(--sans);
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mute);
  margin-bottom: 3px;
}
.faq-timeline-label {
  display: block;
  font-family: var(--serif-2);
  font-size: 14.5px;
  color: var(--ink);
  line-height: 1.3;
}
.faq-timeline-foot {
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--mute);
  margin: 12px 0 0;
  line-height: 1.55;
}
@media (max-width: 640px) {
  .faq-timeline-bar {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .faq-timeline-step {
    position: relative;
    padding-left: 16px;
  }
  .faq-timeline-step::before {
    position: absolute;
    left: 0;
    top: 6px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    margin: 0;
  }
}

/* ── Reviews (compact dual-platform slider) ───────────────── */
/* Two tiles up top (Google + Yelp), each linking out to that
   platform's review page. Below: a horizontal scroll-snap track
   of compact review cards mixing both platforms. Native swipe on
   mobile; arrow buttons + wheel/drag on desktop. No review count
   is shown anywhere — five stars on both platforms is the proof.
   Stars use the body serif so they sit inline with the body type. */

/* Score bar */
.reviews-bar {
  max-width: var(--max);
  margin: 0 auto clamp(28px, 4vh, 44px);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}
@media (max-width: 640px) {
  .reviews-bar { grid-template-columns: 1fr; }
}
.rv-platform {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 20px;
  border: 1px solid var(--hair);
  background: var(--cream);
  text-decoration: none;
  color: var(--ink);
  transition:
    background var(--t) var(--ease),
    border-color var(--t) var(--ease);
}
.rv-platform:hover {
  background: var(--bone);
  border-color: var(--ink-soft);
}
.rv-dot {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 14px;
  color: #fff;
  flex-shrink: 0;
  letter-spacing: 0;
}
.rv-dot--g { background: #1a73e8; }   /* Google blue */
.rv-dot--y { background: #d32323; }   /* Yelp red   */
.rv-stack {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}
.rv-stars {
  font-family: var(--serif);
  font-size: clamp(16px, 1.5vw, 20px);
  letter-spacing: 0.08em;
  color: var(--accent);
  line-height: 1;
}
.rv-label {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--mute);
}
.rv-arrow {
  font-family: var(--sans);
  color: var(--mute);
  font-size: 16px;
  transition: transform var(--t) var(--ease), color var(--t) var(--ease);
}
.rv-platform:hover .rv-arrow {
  transform: translateX(3px);
  color: var(--ink);
}

/* Auto-marquee shell. Overflow is hidden; the track inside is animated
   by a compositor-thread CSS keyframe (see below). We isolate paint so
   the wrapper has its own layer, and we use absolutely-positioned
   gradient overlays at each edge instead of `mask-image`. Reason:
   `mask-image` combined with a transform-animating child is a known
   stutter pattern on iOS Safari and some Android browsers — it doesn't
   always promote cleanly to a compositor layer, so the masked content
   re-rasterises per frame. The overlay approach is pure paint with no
   compositing interaction with the moving track. The color matches the
   section background (--bone) so cards fade in/out cleanly. */
.reviews-marquee {
  position: relative;
  max-width: var(--max);
  margin: 0 auto;
  overflow: hidden;
  contain: paint;
}
.reviews-marquee::before,
.reviews-marquee::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 64px;
  z-index: 2;
  pointer-events: none;
}
.reviews-marquee::before {
  left: 0;
  background: linear-gradient(to right, var(--bone) 0%, rgba(245, 240, 232, 0) 100%);
}
.reviews-marquee::after {
  right: 0;
  background: linear-gradient(to left,  var(--bone) 0%, rgba(245, 240, 232, 0) 100%);
}

.rv-marquee-track {
  list-style: none;
  margin: 0;
  padding: 2px 0 8px;
  display: flex;
  gap: 14px;
  width: max-content;
  /* Own compositor layer — promotes the track so the keyframe animation
     runs on the GPU and never re-rasterises the cards mid-cycle. */
  will-change: transform;
  transform: translate3d(0, 0, 0);
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
/* The animation is gated on `.is-ready` — added by JS only after clones
   are appended and the loop distance is measured. Before that, the
   `from` keyframe (transform: none) holds, so the un-cloned originals
   sit in place without animating to a wrong stop point. */
.rv-marquee-track.is-ready {
  animation: rv-marquee-scroll var(--rv-duration, 60s) linear infinite;
}
@keyframes rv-marquee-scroll {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(var(--rv-shift, -50%), 0, 0); }
}
/* CSS-only pause states. Hover/focus-within pause for readability;
   data-rv-paused is set by JS when the section is offscreen or the
   tab is hidden (prevents the loop from "running invisibly" across
   tab switches, which would otherwise cause a visible jump on return).
   No JS in the render loop. */
.reviews-marquee:hover .rv-marquee-track,
.reviews-marquee:focus-within .rv-marquee-track,
.reviews-marquee[data-rv-paused="true"] .rv-marquee-track {
  animation-play-state: paused;
}

.rv-marquee-track > .rv-card {
  flex: 0 0 320px;
}
@media (max-width: 640px) {
  .reviews-marquee::before,
  .reviews-marquee::after { width: 36px; }
  .rv-marquee-track > .rv-card { flex-basis: 78vw; }
}

/* Honor reduced-motion: hold the track in place rather than scrolling. */
@media (prefers-reduced-motion: reduce) {
  .rv-marquee-track.is-ready { animation: none; }
}

/* Card */
.rv-card {
  background: var(--cream);
  border: 1px solid var(--hair);
  padding: 20px 22px 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 280px;
  transition: background var(--t) var(--ease);
}
.rv-card:hover { background: var(--bone); }

.rv-card-top {
  display: flex;
  align-items: center;
  gap: 12px;
}
.rv-card .rv-dot {
  width: 26px;
  height: 26px;
  font-size: 12px;
}
.rv-card-stars {
  font-family: var(--serif);
  font-size: 15px;
  letter-spacing: 0.1em;
  color: var(--accent);
  line-height: 1;
}

.rv-body {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 15px;
  line-height: 1.5;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.rv-body sup {
  font-size: 0.6em;
  font-family: var(--sans);
  font-weight: 400;
  vertical-align: super;
}

.rv-foot {
  margin-top: auto;
  padding-top: 10px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  border-top: 1px solid var(--hair);
}
.rv-name {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 13px;
  letter-spacing: -0.005em;
  color: var(--ink);
}
.rv-meta {
  font-family: var(--sans);
  font-weight: 400;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--mute);
}
.rv-meta sup {
  font-size: 0.7em;
  vertical-align: super;
}

.reviews-foot {
  max-width: 640px;
  margin: clamp(28px, 4vh, 48px) auto 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(13px, 1vw, 15px);
  color: var(--mute);
  text-align: center;
  line-height: 1.55;
}

@media (max-width: 640px) {
  .rv-platform { padding: 14px 16px; gap: 12px; }
  .rv-dot { width: 28px; height: 28px; font-size: 12px; }
}

/* ── Footer ───────────────────────────────────────────────── */
.foot {
  padding: clamp(56px, 8vh, 96px) var(--gutter) clamp(24px, 4vh, 36px);
  background: var(--ink);
  color: rgba(251, 247, 238, 0.85);
}
.foot-top {
  max-width: var(--max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 2fr);
  gap: clamp(32px, 5vw, 80px);
  padding-bottom: clamp(40px, 6vh, 64px);
  border-bottom: 1px solid rgba(251, 247, 238, 0.14);
}
@media (max-width: 820px) {
  .foot-top { grid-template-columns: 1fr; }
}

.foot-brand { max-width: 32ch; }
.foot-mark {
  font-family: var(--serif-2);
  font-size: 22px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.foot-mark span { margin: 0 6px; opacity: 0.55; }
.foot-tag {
  margin: 14px 0 0;
  font-family: var(--serif);
  font-style: italic;
  color: rgba(251, 247, 238, 0.65);
  font-size: 16px;
}

.foot-cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(24px, 3vw, 48px);
}
@media (max-width: 640px) {
  .foot-cols { grid-template-columns: 1fr 1fr; }
}
.foot-col h4 {
  margin: 0 0 14px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: rgba(251, 247, 238, 0.55);
  font-weight: 500;
}
.foot-col p,
.foot-col a {
  display: block;
  margin: 0 0 8px;
  font-size: 14px;
  color: rgba(251, 247, 238, 0.78);
  transition: color var(--t) var(--ease);
}
.foot-col a:hover { color: var(--on-photo); }

/* Footer studio block — three stacked addresses with a small tracked tag */
.foot-col-studios .foot-studio {
  margin: 0 0 16px;
  font-size: 13.5px;
  line-height: 1.5;
  color: rgba(251, 247, 238, 0.78);
}
.foot-studio-tag {
  display: block;
  margin-bottom: 4px;
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--accent-soft);
}
.foot-contact {
  margin: 18px 0 0 !important;
  padding-top: 14px;
  border-top: 1px solid rgba(251, 247, 238, 0.14);
  font-size: 13.5px !important;
  line-height: 1.7;
}
.foot-contact a {
  display: inline !important;
  margin: 0 !important;
}

.foot-base {
  max-width: var(--max);
  margin: 0 auto;
  padding-top: clamp(28px, 4vh, 36px);
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 12px;
  color: rgba(251, 247, 238, 0.55);
}
.foot-fine { margin: 0; }

.foot-tm {
  max-width: var(--max);
  margin: clamp(20px, 3vh, 28px) auto 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: 12px;
  color: rgba(251, 247, 238, 0.45);
  line-height: 1.55;
}

/* ── Reveal-on-scroll ─────────────────────────────────────── */
.reveal {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 800ms var(--ease), transform 800ms var(--ease);
}
.reveal.is-in {
  opacity: 1;
  transform: none;
}

/* ── Reduced motion ───────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Disable Ken Burns and lock all background layers at a neutral scale. */
  .bg-img {
    animation: none !important;
    transform: none !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   SUB-PAGES (added v2 round 43)
   The site moved from single-page to hub-and-spoke. Sub-pages share
   the same design language but live on a clean bone canvas with no
   3D scroll stage, no fullscreen photo spreads, no booking flow.
   Each sub-page is a long-form article + a slim header/footer.
   ═══════════════════════════════════════════════════════════════════ */

/* The body class. Drop this on every sub-page <body>. */
body.sub-page {
  background: var(--bone);
  color: var(--ink);
}
body.sub-page::before,
body.sub-page::after { display: none; }
/* `.bg-stage` lives outside the Swup container on the home page, so it
   persists in the DOM when navigating to a sub-page via Swup. Hide it
   there so the homepage backdrop doesn't bleed through behind sub-page
   content. The Body Class plugin syncs `.sub-page` on each swap, so this
   toggles automatically. */
body.sub-page .bg-stage { display: none; }

/* Sub-page nav — full match to the home page nav (light-state).
   Same iridescent .brand-mark, same large serif wordmark, same uppercase
   letter-spaced nav links, same ink Book pill with arrow. Position sticky so
   the bar stays at the top as the reader scrolls through long content.
   Always-visible CTA (no scroll-progress dependency — that's home-only). */
.nav-sub {
  position: sticky;
  top: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 22px var(--gutter);
  color: var(--ink);
  background: rgba(245, 240, 232, 0.72);
  backdrop-filter: saturate(140%) blur(14px);
  -webkit-backdrop-filter: saturate(140%) blur(14px);
  border-bottom: 1px solid var(--hair);
  transition: background var(--t) var(--ease);
}

/* Brand lockup — same as home: iridescent disk + italic Instrument Serif
   ERAS caps + italic photo lowercase. The disk is lowered slightly so its
   center aligns with the horizontal middle of the cap forms. */
.nav-sub .brand {
  display: inline-flex;
  align-items: baseline;
  gap: 30px;
  font-family: var(--serif);
  font-style: italic;
  line-height: 1;
  letter-spacing: 0;
  text-decoration: none;
  color: var(--ink);
  text-shadow: none;
}
.nav-sub .brand-mark {
  --mark-y: 9px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  flex-shrink: 0;
  align-self: center;
  transform: translateY(var(--mark-y)) rotate(0deg);
  background: conic-gradient(from 0deg, #B49AFF, #79D3E5, #FFB29A, #FFA9D2, #B49AFF);
  /* No shadow / no inset highlight — the iridescent disk stands on its
     own color alone (per user note, v2 round 68: strip every shadow
     and glow out of the top menu). Applies to home nav (.brand-mark)
     and every sub-page nav (.nav-sub .brand-mark) — they share this
     declaration. */
  box-shadow: none;
  animation: spinmark 14s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .nav-sub .brand-mark { animation: none; }
}
.nav-sub .brand-word {
  display: inline-flex;
  align-items: baseline;
  gap: 12px;
  font-family: var(--serif);
  font-style: italic;
}
.nav-sub .brand-eras {
  font-size: 54px;
  letter-spacing: -0.04em;
  font-weight: 400;
  color: var(--ink);
}
.nav-sub .brand-e {
  letter-spacing: 0.03em;
}
.nav-sub .brand-photo {
  font-size: 68px;
  letter-spacing: -0.035em;
  /* Same two-tone treatment as the home nav — muted gray "photo" against
     the dark "ERAS". The global .brand-photo rule (above) sets var(--mute);
     this redeclaration keeps the size override and re-asserts the color so
     specificity doesn't trip anyone editing the home nav block later. */
  color: var(--mute);
  margin-left: 0;
}

.nav-sub .nav-links {
  display: flex;
  gap: 28px;
  font-family: var(--sans);
  /* Slightly larger nav labels with a warm dark-gray tint — matches
     the home-page nav treatment. */
  font-size: 15px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.nav-sub .nav-links a {
  position: relative;
  padding-bottom: 4px;
  color: var(--mute);
  text-decoration: none;
  transition: opacity var(--t) var(--ease), color var(--t) var(--ease);
}
.nav-sub .nav-links a:hover { color: var(--ink); }

.nav-sub .nav-cta {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding: 12px 22px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--bone);
  border: 1px solid var(--ink);
  text-decoration: none;
  text-shadow: none;
  /* Shadow-free Book pill on sub-page nav — matches the home-page nav
     treatment (v2 round 68). */
  box-shadow: none;
  transition:
    background var(--t) var(--ease),
    color      var(--t) var(--ease),
    border     var(--t) var(--ease),
    transform  var(--t) var(--ease);
}
.nav-sub .nav-cta:hover {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--bone);
  transform: translateY(-1px);
  box-shadow: none;
}
.nav-sub .nav-cta-arrow {
  display: inline-block;
  transition: transform var(--t) var(--ease);
}
.nav-sub .nav-cta:hover .nav-cta-arrow { transform: translateX(3px); }

/* Hide the BOOK pill when we're already on the booking page — no
   point in offering a CTA to navigate to where the user already is.
   `aria-current="page"` is set on the BOOK anchor in book/index.html
   and is the canonical accessibility hook for "this is the current
   page" — using it as the CSS selector means we don't need a special
   class or a per-page override. */
.nav-sub .nav-cta[aria-current="page"] { display: none; }

/* Mobile — keep brand + CTA, hide the inline link list (links live in footer). */
@media (max-width: 980px) {
  .nav-sub .brand-eras { font-size: 38px; }
  .nav-sub .brand-photo { font-size: 48px; }
  .nav-sub .brand-mark { width: 38px; height: 38px; --mark-y: 6px; }
  .nav-sub .brand { gap: 18px; }
}
@media (max-width: 760px) {
  .nav-sub { padding: 14px var(--gutter); gap: 12px; }
  .nav-sub .nav-links { display: none; }
  .nav-sub .brand-eras { font-size: 30px; }
  .nav-sub .brand-photo { font-size: 38px; }
  .nav-sub .brand-mark { width: 32px; height: 32px; --mark-y: 5px; }
  .nav-sub .brand { gap: 14px; }
  .nav-sub .nav-cta { padding: 10px 16px; font-size: 11.5px; letter-spacing: 0.16em; }
}

/* Breadcrumb — small text trail under the nav. */
.breadcrumb {
  max-width: var(--max);
  margin: clamp(40px, 6vh, 64px) auto 0;
  padding: 0 var(--gutter);
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--mute-soft);
}
.breadcrumb a {
  color: var(--mute);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--t) var(--ease), color var(--t) var(--ease);
}
.breadcrumb a:hover { color: var(--ink); border-bottom-color: var(--hair-strong); }
.breadcrumb span { color: var(--mute-soft); margin: 0 8px; }

/* The article wrapper. Comfortable reading width, editorial typography. */
.article {
  max-width: 760px;
  margin: 0 auto;
  padding: clamp(40px, 6vh, 80px) var(--gutter) clamp(80px, 12vh, 140px);
  font-family: var(--serif-2, var(--serif));
  font-size: clamp(16px, 1.1vw, 18px);
  line-height: 1.65;
  color: var(--ink);
}
.article > * + * { margin-top: 1.2em; }
.article-folio {
  margin: 0 0 18px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
}
.article-h1 {
  margin: 0 0 18px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(36px, 5.4vw, 64px);
  line-height: 1.02;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.article-h1 em { font-style: italic; }
.article-lede {
  margin: 0 0 24px;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(19px, 1.8vw, 24px);
  line-height: 1.4;
  color: var(--ink-soft);
  font-weight: 300;
}
.article-meta {
  margin: 0 0 36px;
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--mute-soft);
  border-bottom: 1px solid var(--hair);
  padding-bottom: 24px;
}
.article-meta time { color: var(--mute); }
.article h2 {
  margin: 2em 0 0.5em;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(26px, 3.2vw, 36px);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.article h2 em { font-style: italic; }
.article h3 {
  margin: 1.6em 0 0.4em;
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(20px, 2.2vw, 24px);
  line-height: 1.2;
  color: var(--ink);
}
.article h4 {
  margin: 1.4em 0 0.3em;
  font-family: var(--sans);
  font-weight: 500;
  font-size: 14px;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-transform: uppercase;
}
.article p { margin: 0; }
.article ul, .article ol {
  margin: 0;
  padding-left: 1.4em;
}
.article ul li, .article ol li { margin: 0.4em 0; }
.article a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hair-strong);
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.article a:hover { color: var(--accent); border-color: var(--accent); }
/* In-article CTA pill: the generic `.article a` rule above paints links with
   ink text + a hairline underline, which on the dark `.nav-cta` pill turns the
   label dark-on-dark (reads as a solid black box) and adds a stray underline.
   Re-assert the pill's own treatment with enough specificity to win. */
.article a.nav-cta,
.article a.nav-cta:hover { color: var(--bone); border-bottom: none; }
.article strong { font-weight: 500; color: var(--ink); }
.article em { font-style: italic; }
.article hr {
  border: 0;
  border-top: 1px solid var(--hair);
  margin: 2.4em 0;
}
.article blockquote {
  margin: 1.6em 0;
  padding: 0 0 0 1.4em;
  border-left: 2px solid var(--hair-strong);
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(17px, 1.4vw, 20px);
  color: var(--ink-soft);
}

/* Table of contents */
.article-toc {
  margin: 32px 0 56px;
  padding: 22px 26px;
  background: var(--cream);
  border: 1px solid var(--hair);
  border-radius: 4px;
}
.article-toc-label {
  margin: 0 0 12px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
}
.article-toc ol {
  margin: 0;
  padding-left: 1.2em;
  font-family: var(--sans);
  font-size: 14px;
  line-height: 1.8;
}
.article-toc ol li { margin: 0; }
.article-toc a { border-bottom: 0; color: var(--ink-soft); }
.article-toc a:hover { color: var(--accent); }

/* Callout — for important specs / quick reference */
.callout {
  margin: 1.6em 0;
  padding: 22px 26px;
  background: var(--cream);
  border-left: 3px solid var(--accent);
  border-radius: 0 4px 4px 0;
}
.callout-title {
  margin: 0 0 8px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
}
.callout p { margin: 0.4em 0; font-size: 15px; line-height: 1.55; }

/* Inline spec card — drop into any article */
.spec-card {
  margin: 1.6em 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0;
  border-top: 1px solid var(--hair);
  border-left: 1px solid var(--hair);
}
@media (max-width: 600px) {
  .spec-card { grid-template-columns: 1fr; }
}
.spec-card-row {
  padding: 16px 18px;
  border-right: 1px solid var(--hair);
  border-bottom: 1px solid var(--hair);
  background: var(--bone);
}
.spec-card-key {
  display: block;
  font-family: var(--sans);
  font-size: 10.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
  margin-bottom: 4px;
}
.spec-card-val {
  display: block;
  font-family: var(--serif-2, var(--serif));
  font-size: clamp(16px, 1.3vw, 20px);
  color: var(--ink);
}

/* Studio info grid (used on studio pages) */
.studio-info {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 24px;
  margin: 1.6em 0;
}
@media (max-width: 700px) {
  .studio-info { grid-template-columns: 1fr; }
}
.studio-info-card {
  padding: 22px 26px;
  background: var(--cream);
  border: 1px solid var(--hair);
}
.studio-info-card h4 {
  margin: 0 0 12px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
  font-weight: 500;
}
.studio-info-card p {
  margin: 0;
  font-family: var(--serif-2, var(--serif));
  font-size: 15px;
  line-height: 1.6;
}

/* Examples gallery grid */
.examples-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin: 2em 0;
}
@media (max-width: 720px) {
  .examples-grid { grid-template-columns: 1fr; }
}
.example-card {
  background: var(--cream);
  border: 1px solid var(--hair);
  overflow: hidden;
}
.example-card img {
  width: 100%;
  height: auto;
  display: block;
  aspect-ratio: 5 / 7;
  object-fit: cover;
  background: var(--hair);
}
.example-card-body {
  padding: 16px 18px 18px;
}
.example-card-label {
  display: inline-block;
  padding: 3px 10px;
  margin-bottom: 8px;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--sans);
  font-size: 10px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  border-radius: 999px;
}
.example-card-label.is-warn { background: #b86a3a; }
.example-card h4 {
  margin: 0 0 6px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 17px;
  line-height: 1.3;
}
.example-card p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--mute); }

/* Checker tool */
.checker {
  margin: 2em 0;
  padding: clamp(28px, 4vw, 44px);
  background: var(--cream);
  border: 1px solid var(--hair);
  border-radius: 4px;
}
.checker-drop {
  border: 2px dashed var(--hair-strong);
  border-radius: 4px;
  padding: clamp(28px, 5vw, 56px);
  text-align: center;
  cursor: pointer;
  transition: background var(--t) var(--ease), border-color var(--t) var(--ease);
}
.checker-drop:hover, .checker-drop.is-hover {
  background: var(--bone);
  border-color: var(--accent);
}
.checker-drop p {
  margin: 0;
  font-family: var(--sans);
  font-size: 14px;
  color: var(--mute);
}
.checker-drop strong { color: var(--ink); font-weight: 500; }
.checker-input { display: none; }
.checker-results {
  margin-top: 24px;
  display: none;
}
.checker-results.is-visible { display: block; }
.checker-summary {
  padding: 14px 18px;
  margin-bottom: 14px;
  font-family: var(--sans);
  font-size: 13px;
  letter-spacing: 0.04em;
  border-radius: 4px;
}
.checker-summary.is-pass { background: #d8eed8; color: #1a5a1a; }
.checker-summary.is-fail { background: #f4d8c8; color: #7a3818; }
.checker-rows {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--hair);
}
.checker-row {
  display: grid;
  grid-template-columns: 22px minmax(0, 1fr) auto;
  gap: 14px;
  align-items: center;
  padding: 12px 0;
  border-bottom: 1px solid var(--hair);
  font-family: var(--sans);
  font-size: 13.5px;
}
.checker-row .checker-icon {
  width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%;
  font-size: 11px; font-weight: 600;
  color: var(--bone);
}
.checker-row.is-pass .checker-icon { background: #2f7a2f; }
.checker-row.is-fail .checker-icon { background: #b86a3a; }
.checker-key { color: var(--mute); }
.checker-val { color: var(--ink); font-weight: 500; }

/* Hero (smaller than home hero) for sub-pages with a top image */
.sub-hero {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 7;
  overflow: hidden;
  background: var(--cream);
}
@media (max-width: 760px) { .sub-hero { aspect-ratio: 4 / 3; } }
.sub-hero img {
  width: 100%; height: 100%; display: block;
  object-fit: cover;
}

/* "Read more" related-content strip.
   IMPORTANT: position:relative + z-index:4 is required so the cards are
   clickable on the home page, where the position:fixed .bg-stage at z-index:0
   would otherwise sit on top of static content and intercept clicks. Matches
   the z-index used by .ed-section / .trust / .foot for the same reason. */
.related {
  position: relative;
  z-index: 4;
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(48px, 8vh, 96px) var(--gutter);
  border-top: 1px solid var(--hair);
  background: var(--bone);
}
.related h2 {
  margin: 0 0 24px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(22px, 2.4vw, 30px);
  letter-spacing: -0.01em;
}
.related-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}
@media (max-width: 880px) { .related-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .related-grid { grid-template-columns: 1fr; } }
.related-card {
  display: block;
  padding: 22px 24px 26px;
  background: var(--cream);
  border: 1px solid var(--hair);
  text-decoration: none;
  color: var(--ink);
  transition: background var(--t) var(--ease);
}
.related-card:hover { background: var(--bone); }
.related-folio {
  font-family: var(--sans);
  font-size: 10.5px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--mute);
  margin-bottom: 10px;
}
.related-title {
  font-family: var(--serif);
  font-size: 20px;
  line-height: 1.25;
  letter-spacing: -0.005em;
  margin-bottom: 8px;
}
.related-deck {
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.55;
  color: var(--mute);
  margin: 0;
}

/* 404 page */
.notfound {
  min-height: 80vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 80px 24px;
  text-align: center;
}
.notfound h1 {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(60px, 12vw, 140px);
  line-height: 1;
  letter-spacing: -0.02em;
  margin: 0 0 20px;
}
.notfound p { margin: 0 0 26px; color: var(--mute); font-size: 17px; max-width: 48ch; }
.notfound .btn-back {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 22px;
  background: var(--ink);
  color: var(--bone);
  border-radius: 999px;
  text-decoration: none;
  font-family: var(--sans);
  font-size: 14px;
}

/* ═══════════════════════════════════════════════════════════════════
   /book/ — dedicated booking page
   ═══════════════════════════════════════════════════════════════════
   The booking flow lives at `/book/` as its own page. The DOM is the
   same `#book-shell` 2-step machine used inside the home page section,
   so main.js wires the same handlers — these rules just trim the page
   chrome around the shell so the user lands directly on the booking
   shell, and make the shell + its anchor stable across step/location
   transitions.

   Key behaviors:
   • Compact page header (no "The Sessions" preamble). Short headline,
     short deck, then the shell.
   • Scroll anchor (`#book`) sits at the top of the shell and uses
     `scroll-margin-top` so the sticky nav-sub doesn't cover the
     content when the user lands on /book/ or jumps via `#book`.
   • The shell itself has `scroll-margin-top` too so the JS
     scrollIntoView() in step transitions lands the shell predictably
     under the nav, not behind it.

   The page uses `body.book-page` for narrow targeting; everything
   else inherits `.sub-page` styles (bone background, nav-sub, footer).
   ────────────────────────────────────────────────────────────────── */
.book-page-head {
  max-width: var(--max);
  margin: clamp(14px, 2vh, 24px) auto clamp(12px, 1.8vh, 22px);
  padding: 0 var(--gutter);
  text-align: center;
}
.book-page-head .ed-folio { margin: 0 0 8px; }
.book-page-title {
  /* Single-line headline — no deck, no subtitle. Sized smaller than
     the home page hero so the picker cards fit above the fold on a
     standard ~900 px viewport. */
  margin: 0 auto;
  font-size: clamp(28px, 3.8vw, 44px);
  line-height: 1.05;
}

/* The booking section on this page is tighter than the home-page
   .ed-section block — we don't want 12vh of padding above and below
   the shell on /book/, the shell IS the page. */
.ed-section-book {
  padding: 0 var(--gutter) clamp(60px, 9vh, 96px);
}
/* The shell sheds its outer padding on /book/. On desktop a thin
   horizontal padding keeps the picker grid from touching the
   cell-divider hairlines on the body edges; on mobile we strip the
   padding entirely so the picker grid runs edge-to-edge. The shell's
   own border is also dropped on this page — `.book-page .book-shell`
   IS the page focal point, it doesn't need framing like the in-page
   section on the home page does. */
.book-page .book-shell {
  padding: clamp(12px, 1.6vw, 20px) clamp(8px, 1.6vw, 18px);
  border: 0;
  background: transparent;
}
@media (max-width: 640px) {
  /* 10 px of horizontal padding around the booking shell on mobile so
     the schedule iframe, the bs-head row, the location toggle, and
     the picker grid all get a little breathing room from the viewport
     edges instead of sitting flush against them. */
  .book-page .book-shell {
    padding: 6px 10px;
  }
  /* Section gutter still collapses to zero — the shell provides the
     horizontal padding now. */
  .book-page .ed-section-book {
    padding-left: 0;
    padding-right: 0;
  }
  .book-page .picker-foot {
    padding: 0 var(--gutter);
  }
}
/* Breadcrumb runs tighter against the nav-sub on the booking page —
   the comfortable 6vh top margin used elsewhere wastes screen space
   that should be carrying the picker. */
.book-page .breadcrumb {
  margin-top: clamp(16px, 2.4vh, 28px);
}

/* The anchor target. `scroll-margin-top` is the canonical solution
   for sticky-nav offset on scroll-into-view (works for both native
   browser `:target` jumps and JS scrollIntoView with block:'start').
   The nav-sub on this page is ~ 96 px tall on desktop and ~ 68 px on
   mobile — leave a little extra headroom so the location toggle is
   visible immediately. */
.book-page #book {
  scroll-margin-top: clamp(72px, 11vh, 120px);
}
.book-page #book-shell {
  scroll-margin-top: clamp(72px, 11vh, 120px);
}

/* Reveal-on-scroll animations are managed by main.js for
   `.book-shell` — the shell would otherwise hold its opacity:0
   transform:translateY(8px) state until intersection. On /book/ the
   shell is the focal point and visible immediately, so we suppress
   the reveal hold here. */
.book-page .book-shell.reveal { opacity: 1; transform: none; }

/* /book/ compact location toggle.
   Rest state: prompt ("Pick a city to start…") + two city pills
   (NY / Miami). The studio sub-row alone is collapsed at rest — it
   only renders once a city is picked. The prompt always sits above
   the pills now (moved 2026-05-19) so the user reads the instruction
   before acting on it.

   Trigger: the `.is-active` class on a `.loc-city` pill is added by
   main.js's `selectCity()` the moment a city is picked. `:has()` is
   the canonical way to react to that state without JS bookkeeping. */
.book-page .loc-toggle {
  padding: clamp(4px, 0.6vh, 8px) clamp(8px, 1.4vw, 14px) clamp(14px, 2vh, 20px);
  margin-bottom: clamp(16px, 2vh, 24px);
  /* gap inherits the base 12px now that the prompt sits above the
     pills and stays visible at every state — the old `gap: 0` was
     tuned for a chooser that hid the prompt at rest. */
}
/* Studio-row hide rule moved to the global .loc-toggle block above;
   it now applies to home + /book/ + step-book chooser uniformly. */

/* Trim the gap between the picker step and the page header — the
   shell already has internal padding, no need for additional top
   margin pulled from .book-shell. */
.book-page .book-shell { margin-top: 0; }

/* Small trust strip below the shell. Single paragraph, centered,
   muted. Links carry the standard hairline underline. */
.book-page-foot {
  max-width: var(--max);
  margin: 0 auto;
  padding: clamp(24px, 4vh, 40px) var(--gutter) clamp(48px, 6vh, 72px);
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(15px, 1.2vw, 17px);
  color: var(--mute);
}
.book-page-foot a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--hair-strong);
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease);
  white-space: nowrap;
}
.book-page-foot a:hover { color: var(--accent); border-color: var(--accent); }

/* On very narrow screens, soften the page-head deck so the studio
   list reads as a clean two-line block. */
@media (max-width: 640px) {
  .book-page-head { margin-top: 22px; margin-bottom: 22px; }
  .book-page-foot { font-size: 14.5px; line-height: 1.5; }
}

/* ─── Swup page transitions ─────────────────────────────────────────
   Each page wraps its main content in `.swup-main.swup-transition-fade`.
   Swup adds `.is-changing` to the html element during a transition and
   `.is-leaving` / `.is-rendering` to mark the two halves: old content
   fading out, new content fading in. The duration here (260ms) is what
   the JS waits for via Swup's `animationSelector` config — keep this in
   sync if you bump the timing. Reduced-motion users skip the fade. */
.swup-transition-fade {
  transition: opacity 260ms ease;
  opacity: 1;
}
html.is-animating .swup-transition-fade {
  opacity: 0;
}
@media (prefers-reduced-motion: reduce) {
  .swup-transition-fade,
  html.is-animating .swup-transition-fade {
    transition: none;
    opacity: 1;
  }
}

/* ════════════════════════════════════════════════════════════════════
   ERAS FORMATTING — scroll-driven 3D panel act (home only)
   Inserted between the Besties and New York spreads. Sticky white stage
   over the fixed .bg-stage (z0); nav (z50) stays on top. Scoped .fmt-*
   classes only — nothing else on the page is affected. Logic: main.js §7.
   ════════════════════════════════════════════════════════════════════ */
.fmt-act { position: relative; height: 300vh; }              /* scroll length */
.fmt-stage {
  position: sticky; top: 0; height: 100vh; overflow: hidden;
  z-index: 5;                                                 /* above spreads(2)+bg(0), below nav(50) */
}
.fmt-white {
  position: fixed; inset: 0; z-index: 4; opacity: 0; will-change: opacity; pointer-events: none;
  background: radial-gradient(130% 100% at 50% 38%, #fff 0%, var(--cream) 55%, var(--bone-deep) 100%);
}
/* heading sits BELOW the animation; type comes from .ed-title (site-exact) */
.fmt-head {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 3; text-align: center;
  padding-bottom: 12vh; opacity: 0; will-change: opacity, transform; pointer-events: none;
}
.fmt-stripwrap {
  position: absolute; inset: 0; z-index: 2; display: flex; align-items: center;
  padding-bottom: 22vh; opacity: 0; will-change: opacity; pointer-events: none;
  /* perspective lives HERE (direct parent of .fmt-strip) so the turned prints
     foreshorten/recede — matching the standalone demo. On .fmt-stage it was
     flattened by this wrapper's default transform-style:flat. */
  perspective: 1050px; perspective-origin: 50% 55%;
}
.fmt-strip { display: flex; gap: 0; padding: 0 24vw; will-change: transform; transform-style: preserve-3d; }
.fmt-print {
  position: relative; flex: 0 0 auto; height: min(44vh, 330px); aspect-ratio: 2.5 / 3.5;
  background: #fff; border-radius: 7px; padding: 10px 10px 30px;
  transform-style: preserve-3d; backface-visibility: hidden; will-change: transform;
  box-shadow: 0 24px 50px -16px rgba(26,24,20,.34), 0 2px 8px rgba(26,24,20,.12);
}
.fmt-print .fmt-img { position: absolute; inset: 10px 10px 30px; border-radius: 3px; background-size: cover; background-position: center 26%; }
.fmt-print .fmt-lab {
  position: absolute; left: 0; right: 0; bottom: 8px; text-align: center;
  font-family: var(--sans); font-size: 10px; letter-spacing: .12em; color: #9a9087; text-transform: uppercase;
}
.fmt-print .fmt-chk {
  position: absolute; top: 16px; right: 16px; width: 20px; height: 20px; border-radius: 50%;
  background: #1d9e75; color: #fff; font-size: 12px; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 3px 10px rgba(29,158,117,.5);
}
/* directional sheen sells the turn */
.fmt-print::after {
  content: ""; position: absolute; inset: 0; border-radius: 7px; pointer-events: none;
  background: linear-gradient(100deg, rgba(0,0,0,.30) 0%, rgba(0,0,0,0) 42%, rgba(255,255,255,.14) 100%);
}
/* sub-pages never render .bg-stage; the act is home-only anyway, but hide defensively */
body.sub-page .fmt-act { display: none; }

/* ════════════════════════════════════════════════════════════════════
   3D ACT · OPEN 7 DAYS  (#hours-act)
   Shares .fmt-act's stage language (sticky stage, .ed-title heading) but
   its own motion + concept: a COMPACT top-bound flip calendar (day names
   only, Mon→Sun) above an ANALOG CLOCK whose hands sweep one full day
   (9am→9pm) while the SKY darkens from cream morning to deep-orange /
   near-black evening. Sky color, clock hands, time label and the
   readable-text color are all driven per-frame in main.js §7b.
   ════════════════════════════════════════════════════════════════════ */
.hours-act {
  position: relative; height: 360vh;                         /* scroll length — narrative completes by ~60%, then ~100vh of pinned tail for the TRUSTED BY wipe */
  /* CREAM backdrop (was --noir): the dusk sky (.hours-white) paints the dark
     evening on TOP, but only fully covers once the section pins. During the
     section's ENTRY from below (sky opacity still ramping in) the base showed
     through — with --noir that read as a dark band rolling up right after the
     Brickell map, then lightening. Cream base makes that entry roll over in
     the same cream the morning sky starts on. The dark END is unaffected: the
     inverted trust band (margin-top:-100vh) wipes over before the sky
     dissolves, so the near-black close still flows into the pricing band. */
  background: var(--bone);
}
.hours-stage {
  position: sticky; top: 0; height: 100vh; overflow: hidden;
  z-index: 5;                                                 /* above spreads(2)+bg(0), below nav(50) */
}
/* the "sky" — JS rewrites its background each frame (morning→evening) and
   drives opacity for the dissolve in/out. The radial here is just the
   first-paint fallback (9am cream). */
.hours-white {
  position: fixed; inset: 0; z-index: 4; opacity: 0; will-change: opacity, background; pointer-events: none;
  background: radial-gradient(130% 110% at 50% 32%, #fff 0%, var(--cream) 55%, var(--bone-deep) 100%);
}
.hours-head {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 3; text-align: center;
  padding-bottom: 11vh; opacity: 0; will-change: opacity, transform; pointer-events: none;
  color: var(--ink);                                          /* JS lerps toward cream as the sky darkens */
}
.hours-calwrap {
  position: absolute; inset: 0; z-index: 2; display: flex; align-items: center; justify-content: center;
  padding-bottom: 13vh; opacity: 0; will-change: opacity; pointer-events: none;
}
.hours-stack { display: flex; flex-direction: column; align-items: center; gap: clamp(20px, 4vh, 40px); }

/* the calendar pad — now ~1/3 the old height: a single short day card.
   Perspective lives HERE (the direct parent of the pages) and the pad is a
   FLAT stacking context — so the pages stack reliably by z-index while each
   one still flips in its own 3D (a shared preserve-3d scene hid the flip
   behind the last page). */
.hours-cal {
  position: relative; width: min(300px, 70vw); height: min(17vh, 132px);
  perspective: 1400px; perspective-origin: 50% 24%;
}
/* a flip page hinges at its top edge */
.cal-page {
  position: absolute; inset: 0; transform-style: preserve-3d; transform-origin: 50% 0%;
  will-change: transform;
}
.cal-face {
  position: absolute; inset: 0; backface-visibility: hidden; border-radius: 14px; overflow: hidden;
  background: #fffdf9; border: 1px solid rgba(26,24,20,.06);
  box-shadow: 0 22px 44px -20px rgba(26,24,20,.4), 0 2px 6px rgba(26,24,20,.12);
  display: flex; align-items: center; justify-content: center; text-align: center; padding: 14px 22px;
}
.cal-face.back { transform: rotateX(180deg); background: #F1EADE; }   /* paper reverse */
.cal-day {
  font-family: var(--serif); font-style: italic; font-weight: 400;
  font-size: clamp(30px, 6.4vh, 56px); line-height: 0.9; color: var(--ink); letter-spacing: -.01em;
}
/* weekend pages carry the brand accent so "weekends too" reads instantly */
.cal-page.is-weekend .cal-face.front { background: var(--accent); border-color: rgba(0,0,0,.12); }
.cal-page.is-weekend .cal-day { color: #fff; }
/* directional sheen sells the turn */
.cal-face.front::after {
  content: ""; position: absolute; inset: 0; border-radius: 14px; pointer-events: none;
  background: linear-gradient(180deg, rgba(255,255,255,.18) 0%, rgba(0,0,0,0) 32%, rgba(0,0,0,.16) 100%);
}
/* spiral binding stays put at the hinge while pages turn beneath it */
.cal-bind {
  position: absolute; top: -11px; left: 0; right: 0; height: 22px; z-index: 60;
  display: flex; gap: 15px; align-items: center; justify-content: center; pointer-events: none;
}
.cal-bind i {
  width: 9px; height: 9px; border-radius: 50%;
  background: #d7cebf; box-shadow: inset 0 -2px 3px rgba(0,0,0,.28), 0 1px 1px rgba(255,255,255,.6);
}

/* analog clock — inherits `color` (JS-driven) so every stroke stays legible
   as the sky darkens. Hour hand makes one revolution 9am→9pm. */
.hours-clock { display: flex; flex-direction: column; align-items: center; gap: 12px; color: var(--ink); }
.clock-svg { width: min(190px, 50vw); height: auto; display: block; overflow: visible; }
.clock-rim { fill: none; stroke: currentColor; stroke-opacity: .28; stroke-width: 2; }
.clock-ticks line { stroke: currentColor; stroke-opacity: .5; stroke-linecap: round; }
/* hands are rotated via the SVG `transform="rotate(deg 100 100)"` attribute
   set in JS — deliberately NO CSS transform/transform-origin/will-change here,
   which would override the rotation center and fling the hands off-center. */
.clock-hand { stroke: currentColor; stroke-linecap: round; }
.clock-hour { stroke-width: 5; }
.clock-min  { stroke-width: 3; stroke-opacity: .8; }
.clock-cap  { fill: currentColor; }
.clock-label {
  margin: 0; font-family: var(--sans); font-size: 13px; letter-spacing: .18em; text-transform: uppercase;
  color: currentColor; opacity: .82;
}
body.sub-page .hours-act { display: none; }

/* ════════════════════════════════════════════════════════════════════
   INVERTED BAND — Trust marquee + Pricing  (homepage)
   The closing "open 7 days" slide stays black (.hours-act bg = --noir),
   and the two sections that follow it — the "Trusted by students from…"
   marquee and the "Book your session" pricing block — invert to a black
   canvas with cream type. The band ENDS after the pricing table: the
   next section (.ed-section-quiet · Extras) keeps its bone background,
   so the page returns to cream from there on.
   The three tier cards already ship a dark pewter→onyx palette with
   cream text, so they're left untouched — only their surroundings flip.
   Scoped to `.trust` and `#pricing` so /book/ and sub-pages are unaffected.
   ════════════════════════════════════════════════════════════════════ */

/* Nav theme over this band is handled by the gradual `--ni`
   interpolation in the `.nav.is-light` rules (see the nav section
   above), driven per-frame by main.js. */

/* ── Section canvases ──────────────────────────────────────── */
.trust { background: var(--noir); }
#pricing { background: var(--noir); }

/* The trust strip's top/bottom hairlines need to lift to a light tint
   so they stay visible against the black canvas. */
.trust {
  border-top-color: var(--noir-line);
  border-bottom-color: var(--noir-line);
}

/* ── Trust marquee type ────────────────────────────────────── */
.trust .trust-label { color: rgba(250, 246, 239, 0.55); }
.trust .marquee-track { color: #EFE9DE; }

/* ── Wipe over the closing slide ────────────────────────────────
   Instead of the clock dissolving to black and THEN the marquee
   arriving (which left an empty black gap), the inverted band slides
   UP and OVER the still-lit, still-pinned clock — a curtain wipe.
   Mechanism: pull the band up by one viewport so it overlaps the last
   ~100vh of the pinned #hours-act, and lift it above the clock stage
   (z 5) and the fixed sky (z 4) so it covers them as it rises. The
   clock holds its final evening frame underneath (main.js pStory) and
   is wiped away rather than fading first. Scoped to the homepage band;
   /book/ and sub-pages hide #hours-act so they're unaffected. */
.trust { margin-top: -100vh; }
.trust,
#pricing { z-index: 10; }
.trust .marquee-track .dot { color: rgba(250, 246, 239, 0.42); }

/* ── Pricing section header ────────────────────────────────── */
#pricing .ed-folio { color: rgba(250, 246, 239, 0.60); }
#pricing .ed-title { color: var(--cream); }
#pricing .ed-deck { color: rgba(250, 246, 239, 0.74); }
#pricing .ed-deck strong { color: var(--cream); }

/* ── Location prompt ("Pick a city to start …") ─────────────── */
#pricing .loc-toggle { border-bottom-color: var(--noir-line); }
#pricing .loc-current { color: rgba(250, 246, 239, 0.72); }
#pricing .lc-current-label { color: rgba(250, 246, 239, 0.50); }
#pricing .lc-current-name { color: var(--cream); }

/* ── City + studio picker (NEW YORK / MIAMI) ────────────────── */
/* Resting pills: cream type, light hairline outlines. */
#pricing .city-pill { border-color: var(--noir-line-2); }
#pricing .city-pill.is-open { border-color: rgba(250, 246, 239, 0.85); }
#pricing .cp-city-btn { color: var(--cream); }
/* Open city → light fill with dark text (clean inversion of the
   light-mode ink fill / cream text). */
#pricing .cp-city-btn::before { background: var(--cream); }
#pricing .city-pill.is-open .cp-city-btn { color: var(--ink); }
#pricing .cp-divider { color: var(--noir-line-2); }
/* Studio sub-pills — recolour only the resting/hover states; the active
   state keeps its orange accent fill + cream text, which already reads
   correctly on black. :not(.is-active) guards the id-level specificity
   here from clobbering the active rule. */
#pricing .cp-studio:not(.is-active) { color: rgba(250, 246, 239, 0.64); }
#pricing .cp-studio:not(.is-active):hover { color: var(--cream); }
#pricing .cp-studio:not(.is-active) .ls-addr { color: rgba(250, 246, 239, 0.50); }

/* ── Tier-grid separators ──────────────────────────────────── */
/* The 1px column gaps are painted by the grid's own background showing
   through; lift it to a faint light line so the cards stay delineated
   on black, and add a hairline frame so the onyx (Besties) card's outer
   edge doesn't melt into the canvas. */
#pricing .picker-grid {
  background: var(--noir-line);
  box-shadow: 0 0 0 1px var(--noir-line);
}

/* ── Picker footnote ───────────────────────────────────────── */
#pricing .picker-foot { color: rgba(250, 246, 239, 0.70); }
#pricing .picker-foot a { color: var(--cream); border-bottom-color: rgba(250, 246, 239, 0.40); }

/* ── Booking step (revealed after a tier is chosen) ─────────── */
#pricing .bs-head { border-bottom-color: var(--noir-line); }
#pricing .bs-name { color: var(--cream); }
#pricing .bs-price { color: rgba(250, 246, 239, 0.62); }
#pricing .bs-meta { color: rgba(250, 246, 239, 0.70); }
#pricing .bs-studio-prompt,
#pricing .bs-studio-line { color: var(--cream); }
#pricing .bs-studio-foot { color: rgba(250, 246, 239, 0.62); }
#pricing .bs-back { color: var(--cream); border-color: var(--noir-line-2); }
#pricing .bs-back:hover { background: var(--cream); color: var(--ink); border-color: var(--cream); }

/* ════════════════════════════════════════════════════════════════════
   INVERTED CANVAS — /book/ page
   The dedicated booking page mirrors the home-page "Book your session"
   inverted band: a black (--noir) canvas with cream type. Same DOM,
   same picker / booking-step machine, so these rules reuse the exact
   same color tokens and overrides as the `#pricing` band above — just
   re-scoped to `.book-page`. The home-only scroll-wipe rules (.trust
   margin-up, z-index lift) are intentionally NOT mirrored; the book
   page has no scroll narrative to wipe over. The three tier cards keep
   their pewter→onyx palette (already cream-on-dark), so only their
   surroundings flip — identical to the home band.
   ════════════════════════════════════════════════════════════════════ */

/* ── Page canvas ───────────────────────────────────────────── */
/* Override the bone .sub-page background. The site footer (.foot) is
   already ink/cream, so the page flows seamlessly into it. */
body.book-page,
body.retouch-page { background: var(--noir); color: var(--cream); }
/* The booking section carries the base `.ed-section` class, which paints
   a bone background (z-index:4) over the body — flip it to noir here so
   the inverted type stays legible. */
.book-page .ed-section-book { background: var(--noir); }

/* ── Location prompt ("Pick a city to start …") ─────────────── */
.book-page .loc-toggle { border-bottom-color: var(--noir-line); }
.book-page .loc-current { color: rgba(250, 246, 239, 0.72); }
.book-page .lc-current-label { color: rgba(250, 246, 239, 0.50); }
.book-page .lc-current-name { color: var(--cream); }

/* ── City + studio picker (NEW YORK / MIAMI) ────────────────── */
.book-page .city-pill { border-color: var(--noir-line-2); }
.book-page .city-pill.is-open { border-color: rgba(250, 246, 239, 0.85); }
.book-page .cp-city-btn { color: var(--cream); }
.book-page .cp-city-btn::before { background: var(--cream); }
.book-page .city-pill.is-open .cp-city-btn { color: var(--ink); }
.book-page .cp-divider { color: var(--noir-line-2); }
.book-page .cp-studio:not(.is-active) { color: rgba(250, 246, 239, 0.64); }
.book-page .cp-studio:not(.is-active):hover { color: var(--cream); }
.book-page .cp-studio:not(.is-active) .ls-addr { color: rgba(250, 246, 239, 0.50); }

/* ── Tier-grid separators ──────────────────────────────────── */
.book-page .picker-grid {
  background: var(--noir-line);
  box-shadow: 0 0 0 1px var(--noir-line);
}

/* ── Picker footnote ───────────────────────────────────────── */
.book-page .picker-foot { color: rgba(250, 246, 239, 0.70); }
.book-page .picker-foot a { color: var(--cream); border-bottom-color: rgba(250, 246, 239, 0.40); }

/* ── Booking step (revealed after a tier is chosen) ─────────── */
.book-page .bs-head { border-bottom-color: var(--noir-line); }
.book-page .bs-name { color: var(--cream); }
.book-page .bs-price { color: rgba(250, 246, 239, 0.62); }
.book-page .bs-meta { color: rgba(250, 246, 239, 0.70); }
.book-page .bs-studio-prompt,
.book-page .bs-studio-line { color: var(--cream); }
.book-page .bs-studio-foot { color: rgba(250, 246, 239, 0.62); }
.book-page .bs-back { color: var(--cream); border-color: var(--noir-line-2); }
.book-page .bs-back:hover { background: var(--cream); color: var(--ink); border-color: var(--cream); }

/* ── Below-shell trust line (.book-page-foot) ──────────────── */
.book-page .book-page-foot p { color: rgba(250, 246, 239, 0.70); }
.book-page .book-page-foot a { color: var(--cream); border-bottom-color: rgba(250, 246, 239, 0.40); }
.book-page .book-page-foot a:hover { color: var(--accent); border-color: var(--accent); }

/* ── Sticky top nav (.nav-sub) ─────────────────────────────────
   The home page interpolates its nav from light→dark frosted via the
   per-frame `--ni` as the booking band rises. /book/ has no scroll
   narrative, so we just pin the nav to the dark end-state here, using
   the exact same target values the home `.nav.is-light` rules resolve
   to at `--ni:1`: dark frosted bar, cream wordmark, light-mute links.
   The Book pill is already hidden on /book/ via [aria-current]. */
.book-page .nav-sub,
.retouch-page .nav-sub {
  color: var(--cream);
  background: rgba(16, 12, 8, 0.58);
  border-bottom-color: var(--noir-line);
}
.book-page .nav-sub .brand,
.book-page .nav-sub .brand-eras,
.retouch-page .nav-sub .brand,
.retouch-page .nav-sub .brand-eras { color: var(--cream); }
.book-page .nav-sub .brand-photo,
.retouch-page .nav-sub .brand-photo { color: rgba(250, 246, 239, 0.74); }
.book-page .nav-sub .nav-links a,
.retouch-page .nav-sub .nav-links a { color: rgba(250, 246, 239, 0.74); }
.book-page .nav-sub .nav-links a:hover,
.retouch-page .nav-sub .nav-links a:hover { color: var(--cream); }

/* ════════════════════════════════════════════════════════════════════
   GALLERY · horizontal showreel + reviews carousel
   Lives between the Plus and Besties photo spreads. Opaque noir block so
   it cleanly covers the fixed `.bg-stage` as it scrolls past (z-index 4 >
   spreads' 2 > stage's 0). Apple-style: a scroll-snap track of portrait
   review cards with segmented progress dots, prev/next, and autoplay.
   ════════════════════════════════════════════════════════════════════ */
.gallery {
  position: relative;
  z-index: 4;
  /* Cream variant — matches the editorial sections' base. Still fully opaque
     so it cleanly covers the fixed photo `.bg-stage` as it scrolls past. */
  background: var(--bone);
  color: var(--ink);
  padding: clamp(72px, 11vh, 132px) 0 clamp(56px, 9vh, 104px);
  overflow: hidden;
  --gallery-interval: 3000ms;       /* autoplay dwell per card; JS reads this same var */
}

.gallery-head {
  max-width: var(--max);
  margin: 0 auto clamp(34px, 5vh, 56px);
  padding: 0 var(--gutter);
}
/* Eyebrow matches the slide kicker (.cover-kick / "RESIDENCY PHOTO"):
   same sans all-caps size, weight and tracking — only the color is kept
   dark, since this sits on the light bone gallery rather than on a photo. */
.gallery-folio {
  margin: 0 0 0.5em;
  font-family: var(--sans);
  font-size: clamp(13px, 1.4vw, 18px);
  font-weight: 600;
  letter-spacing: 0.24em;
  line-height: 1.1;
  text-transform: uppercase;
  color: var(--mute);
}
/* Headline matches the slide-2 statement (.cover / "A lasting impression"):
   same serif clamp and metrics, with the same ≤880px mobile bump below. */
.gallery-title {
  margin: 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(40px, 5.6vw, 78px);
  line-height: 0.98;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.gallery-title em { font-style: italic; }
@media (max-width: 880px) {
  /* Mirror the slide hero's mobile floor so the gallery headline scales
     with phone width exactly like "A lasting impression". */
  .gallery-title { font-size: clamp(48px, 13vw, 66px); }
}

/* ── Scroll-snap track ──────────────────────────────────────────────
   Native horizontal swipe / trackpad scroll on touch and desktop; the
   JS arrows/dots simply drive scrollLeft. Side padding lets the first
   and last cards rest dead-center under scroll-snap. */
.gallery-viewport { width: 100%; }
.gallery-track {
  list-style: none;
  margin: 0;
  display: flex;
  gap: clamp(16px, 2vw, 28px);
  padding: 8px max(var(--gutter), calc((100vw - min(58vw, 560px)) / 2)) 18px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.gallery-track::-webkit-scrollbar { display: none; }

.gcard {
  position: relative;
  flex: 0 0 auto;
  width: min(58vw, 560px);
  aspect-ratio: 4 / 5;
  max-height: 76vh;
  border-radius: 22px;
  overflow: hidden;
  scroll-snap-align: center;
  background: #14110d;
  box-shadow: 0 24px 60px -28px rgba(0, 0, 0, 0.7);
  /* Neighbours dim + recede; the centered card reads as the hero. JS adds
     `.is-active` to whichever card is snapped to center. */
  opacity: 0.5;
  transform: scale(0.94);
  transition: opacity var(--t) var(--ease), transform var(--t) var(--ease);
}
.gcard.is-active { opacity: 1; transform: scale(1); }

.gcard-img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Faces sit in the upper-center third — keep them clear of the lower
     scrim + quote. */
  object-position: 50% 26%;
}

/* Bottom-only scrim — strongest at the lower-left where the quote sits,
   fully transparent across the top two-thirds so faces stay untouched. */
.gcard-quote {
  position: absolute;
  inset: auto 0 0 0;
  margin: 0;
  padding: clamp(20px, 3vw, 34px);
  padding-top: clamp(56px, 12vh, 120px);
  display: flex;
  flex-direction: column;
  gap: 10px;
  color: var(--on-photo);
  background:
    linear-gradient(to top,
      rgba(10, 7, 4, 0.86) 0%,
      rgba(10, 7, 4, 0.66) 34%,
      rgba(10, 7, 4, 0.28) 66%,
      rgba(10, 7, 4, 0) 100%);
  /* Resting state: review sits just below, faded out. It slides up + fades
     in when its card becomes the centered (`.is-active`) card. Non-active
     cards keep the review hidden + non-interactive so peeking neighbours
     read as clean photos and their (hidden) links aren't clickable. */
  pointer-events: none;
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 560ms var(--ease) 120ms,
              transform 640ms var(--ease) 120ms;
}
/* `.is-revealed` is added by JS the first time the section scrolls into
   view, so the centered card's review slides in on arrival (not silently
   pre-shown). Once revealed, every change of active card re-animates. */
.gallery.is-revealed .gcard.is-active .gcard-quote {
  pointer-events: auto;
  opacity: 1;
  transform: translateY(0);
}
/* Stagger the inner lines so the review assembles rather than sliding as one
   slab — stars first, then quote, then attribution. */
.gcard-quote > * {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 520ms var(--ease), transform 560ms var(--ease);
}
.gallery.is-revealed .gcard.is-active .gcard-quote > * { opacity: 1; transform: translateY(0); }
.gallery.is-revealed .gcard.is-active .gcard-quote > .gcard-stars { transition-delay: 200ms; }
.gallery.is-revealed .gcard.is-active .gcard-quote > blockquote   { transition-delay: 290ms; }
.gallery.is-revealed .gcard.is-active .gcard-quote > figcaption   { transition-delay: 400ms; }

/* Source link (Google / Yelp) — opens the real review page in a new tab. */
.gcard-src {
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--on-photo-soft);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.gcard-src:hover { color: var(--on-photo); border-bottom-color: var(--on-photo-hair); }
.gcard-src:focus-visible { outline: 2px solid var(--on-photo); outline-offset: 3px; border-radius: 2px; }
.gcard-src-arrow { font-size: 0.92em; }
.gcard-stars {
  font-size: 14px;
  letter-spacing: 0.12em;
  color: #E8B04B;                    /* warm gold, distinct from brand terracotta */
  line-height: 1;
}
.gcard-quote blockquote {
  margin: 0;
  max-width: 44ch;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(17px, 1.55vw, 24px);
  line-height: 1.3;
  letter-spacing: -0.005em;
  color: var(--on-photo);
  text-wrap: pretty;
}
.gcard-quote figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 10px;
  margin-top: 2px;
}
.gcard-name {
  font-family: var(--sans);
  font-weight: 600;
  font-size: 13.5px;
  letter-spacing: 0.01em;
  color: var(--on-photo);
}
.gcard-meta {
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--on-photo-soft);
}

/* ── Apple-style control bar ────────────────────────────────────────── */
.gallery-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 1.6vw, 20px);
  margin-top: clamp(26px, 4vh, 44px);
  padding: 0 var(--gutter);
}
.gctl {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 1px solid var(--hair);
  border-radius: 999px;
  background: rgba(26, 24, 20, 0.04);
  color: var(--ink);
  cursor: pointer;
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  transition: background var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              transform var(--t-fast) var(--ease);
}
.gctl:hover { background: rgba(26, 24, 20, 0.10); border-color: var(--hair-strong); }
.gctl:active { transform: scale(0.92); }
.gctl:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.gctl svg { width: 22px; height: 22px; }
.gctl-play svg { fill: currentColor; }
/* Toggle which glyph the play/pause button shows. Default state = playing,
   so the PAUSE glyph is visible; `.is-paused` flips to the PLAY glyph. */
.gctl-play .ic-play { display: none; }
.gctl-play.is-paused .ic-pause { display: none; }
.gctl-play.is-paused .ic-play { display: block; }

.gallery-dots {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 4px;
}
.gdot {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  width: 9px;
  height: 9px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(26, 24, 20, 0.22);
  cursor: pointer;
  overflow: hidden;
  transition: width var(--t) var(--ease), background var(--t) var(--ease);
}
.gdot:hover { background: rgba(26, 24, 20, 0.4); }
.gdot:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.gdot.is-active { width: 34px; background: rgba(26, 24, 20, 0.15); }
.gdot-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: left center;
}
/* Active dot: fill is full by default (covers paused + reduced-motion).
   When the carousel is actively playing, JS adds `.is-playing` to the
   gallery, and the active dot's fill animates 0→100% across one interval. */
.gdot.is-active .gdot-fill { transform: scaleX(1); }
@keyframes gdot-progress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.gallery.is-playing .gdot.is-active .gdot-fill {
  animation: gdot-progress var(--gallery-interval) linear forwards;
}
/* Transient pause (hover / keyboard focus) freezes the fill where it is. */
.gallery.is-playing.is-suspended .gdot.is-active .gdot-fill { animation-play-state: paused; }

@media (max-width: 880px) {
  .gallery-track {
    padding: 8px max(var(--gutter), calc((100vw - 86vw) / 2)) 16px;
  }
  .gcard { width: 86vw; max-height: 70vh; }
  .gctl { width: 40px; height: 40px; }
  .gctl svg { width: 20px; height: 20px; }
}

/* Respect reduced-motion: no autoplay animation, no card scale/opacity
   transitions, instant scroll. (JS also skips autoplay; this is the
   CSS-side guarantee.) */
@media (prefers-reduced-motion: reduce) {
  .gallery-track { scroll-behavior: auto; }
  .gcard { transition: none; }
  .gallery.is-playing .gdot.is-active .gdot-fill { animation: none; transform: scaleX(1); }
  /* No slide-in: the active review just appears. */
  .gcard-quote, .gcard-quote > * { transition: none; }
  .gallery .gcard.is-active .gcard-quote,
  .gallery .gcard.is-active .gcard-quote > * { opacity: 1; transform: none; }
  .gallery .gcard.is-active .gcard-quote { pointer-events: auto; }
}

/* ════════════════════════════════════════════════════════════════════
   RETOUCHING PAGE  (/retouching/)
   A standalone add-on page on the SAME fully-black canvas as /book/
   (body.retouch-page → --noir, cream nav — see the inverted-canvas
   block above). Compact editorial header + a two-tier pricing table
   that reuses the booking picker's dark-card language (pewter /
   graphite). Also styles the "RETOUCHING" black-band button that
   drops in below the booking picker on the home page
   (#pricing .retouch-band).
   ════════════════════════════════════════════════════════════════════ */

/* The intro carries the base `.ed-section` class, which paints a bone
   background over the noir body — flip it back to noir and tighten the
   vertical rhythm so the page reads compact. */
.retouch-page .ed-section,
.retouch-intro {
  background: var(--noir);
  padding: clamp(40px, 6vh, 72px) var(--gutter) clamp(36px, 6vh, 64px);
}

/* Compact, centered header in cream. */
.retouch-intro .ed-section-head {
  text-align: center;
  margin-bottom: clamp(26px, 4vh, 44px);
}
.retouch-intro .ed-folio { color: rgba(250, 246, 239, 0.55); margin-bottom: 12px; }
.retouch-intro .ed-title { color: var(--cream); font-size: clamp(36px, 5vw, 64px); margin-bottom: 16px; }
.retouch-intro .ed-deck {
  margin-left: auto;
  margin-right: auto;
  max-width: 58ch;
  color: rgba(250, 246, 239, 0.74);
}

/* Two-column pricing grid. 1px gap on a noir-line backing + a hairline
   ring make the two dark cards read as a single table on the black
   canvas, exactly like the booking picker on /book/. */
.retouch-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1px;
  background: var(--noir-line);
  box-shadow: 0 0 0 1px var(--noir-line);
  max-width: 760px;
  margin: 0 auto;
}

/* Cards mirror the booking picker-card: left-aligned head + price +
   icon-glyph feature list, with the CTA pinned to the bottom so the
   two columns line up like a comparison table. The grid stretches both
   cards to equal height (default align-items: stretch), and the head /
   price rows are identical height across cards, so the feature rows
   align row-for-row across the two columns. */
.retouch-card {
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.6vw, 36px) clamp(20px, 2.2vw, 32px);
  text-align: left;
  background: #2A2B2E;            /* graphite — matches booking #tier-plus */
  color: var(--on-photo);
}
/* Two distinct dark tones (both lift off the noir page so each card
   stays visible on black): pewter for Basic, graphite for Advanced. */
#retouch-basic    { background: #46484C; }      /* pewter   */
#retouch-advanced { background: #24262A; }      /* graphite */

.retouch-card .pk-head {
  align-items: flex-start;
  gap: 4px;
  margin-bottom: clamp(10px, 1.6vh, 16px);
}
.retouch-card .pk-flag {
  margin: 0;
  color: var(--accent-soft);
  font-size: 10.5px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
}
/* Italic serif tagline, same role as the booking card's .pk-tag. */
.retouch-card .pk-tag {
  margin: 0;
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(18px, 1.8vw, 23px);
  line-height: 1.15;
  color: rgba(250, 246, 239, 0.72);
}
.retouch-card .pk-name {
  margin: 4px 0 0;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(36px, 4vw, 52px);
  line-height: 1;
  letter-spacing: -0.01em;
}
/* Inline price: "$25 per image" on a single baseline, like the booking
   card ($ glued to the number, "per image" as the uppercase suffix). */
.retouch-card .pk-price {
  align-items: baseline;
  justify-content: flex-start;
  margin: 0 0 clamp(14px, 2vh, 22px);
}
/* Keep "$" glued to the digits: the currency mark + number live in one
   inline-flex group that never wraps, so they always render as "$25". */
.retouch-card .pk-amt {
  display: inline-flex;
  align-items: baseline;
  white-space: nowrap;
}
.retouch-card .pk-cur { font-size: 16px; opacity: 0.7; margin-right: 3px; }
.retouch-card .pk-num { font-size: clamp(40px, 4vw, 56px); }
.retouch-card .pk-each {
  margin-left: 8px;
  align-self: baseline;
  color: rgba(251, 247, 238, 0.6);
  letter-spacing: 0.2em;
}

/* Left-aligned feature list with leading glyphs — identical layout to
   the booking picker (.pk-list / .pk-icon are styled globally; we just
   set the dark-card colors and let the list grow so the CTA drops to
   the bottom of the card). */
.retouch-card .pk-list {
  flex: 1 1 auto;
  gap: clamp(10px, 1.5vh, 13px);
  font-size: 13.5px;
}
.retouch-card .pk-list li { color: rgba(251, 247, 238, 0.82); }
.retouch-card .pk-icon { color: var(--accent-soft); }

/* CTA pill — cream outline on dark, fills on hover (same as the booking
   picker-featured CTA). `margin-top: auto` pins it to the card bottom so
   both columns' buttons share a baseline; centered horizontally. */
.retouch-card .picker-cta {
  align-self: center;
  margin: clamp(26px, 3.4vh, 40px) 0 0;
  color: var(--on-photo);
  border-color: rgba(251, 247, 238, 0.45);
}
.retouch-card .picker-cta:hover {
  background: var(--on-photo);
  color: var(--ink);
  border-color: var(--on-photo);
}

/* Footnote in cream on the noir canvas. */
.retouch-intro .picker-foot { color: rgba(250, 246, 239, 0.70); margin-top: clamp(18px, 3vh, 28px); }
.retouch-intro .picker-foot a { color: var(--cream); border-bottom-color: rgba(250, 246, 239, 0.40); }
.retouch-intro .picker-foot a:hover { color: var(--accent); border-color: var(--accent); }

@media (max-width: 560px) {
  /* Stack the two tiers on narrow phones — no room to keep six-item
     lists legible side by side. Seam becomes a 1px row gap. */
  .retouch-grid {
    grid-template-columns: 1fr;
    column-gap: 0;
    row-gap: 1px;
    max-width: 420px;
  }
}

/* ── Home-page "RETOUCHING" black band ─────────────────────────────
   Sits inside the booking section (#pricing), below the picker, as a
   full-width near-black band carrying a single ghost-outline button
   that routes to /retouching/. Breaks out of the section gutter the
   same way the picker grid does so it spans the section's content
   width. */
#pricing .retouch-band {
  margin: clamp(28px, 4vw, 48px) calc(-1 * var(--gutter)) 0;
  padding: clamp(40px, 6vw, 64px) var(--gutter);
  background: var(--noir);
  text-align: center;
}
.retouch-band-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 18px 44px;
  border: 1px solid rgba(251, 247, 238, 0.45);
  border-radius: 999px;
  background: transparent;
  color: var(--on-photo);
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  transition: background var(--t) var(--ease), color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.retouch-band-btn:hover {
  background: var(--on-photo);
  color: var(--ink);
  border-color: var(--on-photo);
}
.retouch-band-btn .arr { transition: transform var(--t) var(--ease); }
.retouch-band-btn:hover .arr { transform: translateX(4px); }

/* ════════════════════════════════════════════════════════════════════
   RETOUCHING ORDER PAGE  (/retouching/order/)
   The submission/checkout flow. Lives on the same noir canvas as
   /retouching/ and /book/ (body.retouch-page → --noir, cream nav).
   Two-step: (1) order details — tier toggle, photo upload, add-on /
   formatting chips, contact; (2) payment — live total + Stripe Payment
   Element. Two-column on desktop (form left, sticky summary right),
   single column under 900px. All form chrome is dark: low-cream fills,
   noir-line borders, cream text, accent focus ring.
   ════════════════════════════════════════════════════════════════════ */
.retouch-order .ed-section { background: var(--noir); padding-top: clamp(28px, 4vh, 52px); }
.retouch-order .ed-section-head { text-align: center; margin-bottom: clamp(22px, 3.4vh, 40px); }
.retouch-order .ed-folio { color: rgba(250, 246, 239, 0.55); margin-bottom: 10px; }
.retouch-order .ed-title { color: var(--cream); font-size: clamp(32px, 4.4vw, 56px); margin-bottom: 12px; }
.retouch-order .ed-deck { margin: 0 auto; max-width: 54ch; color: rgba(250, 246, 239, 0.72); }

/* ── Layout ─────────────────────────────────────────────────── */
.order-wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: clamp(20px, 3vw, 44px);
  max-width: 1040px;
  margin: 0 auto;
  align-items: start;
}
@media (max-width: 900px) {
  .order-wrap { grid-template-columns: 1fr; }
}

.order-form { display: flex; flex-direction: column; gap: clamp(24px, 3.2vh, 38px); }

/* ── Field block ────────────────────────────────────────────── */
.order-block { display: flex; flex-direction: column; gap: 12px; }
.order-block-head { display: flex; align-items: baseline; gap: 10px; justify-content: space-between; }
.order-label {
  font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase;
  color: rgba(250, 246, 239, 0.92);
}
.order-hint { font-family: var(--serif); font-style: italic; font-size: 14px; color: rgba(250, 246, 239, 0.55); }

/* ── Tier segmented toggle ──────────────────────────────────── */
.tier-seg {
  display: grid; grid-template-columns: 1fr 1fr; gap: 1px;
  background: var(--noir-line); box-shadow: 0 0 0 1px var(--noir-line);
  border-radius: 14px; overflow: hidden;
}
.tier-opt {
  appearance: none; -webkit-appearance: none; border: 0; cursor: pointer;
  background: #24262A; color: rgba(250, 246, 239, 0.78);
  padding: 16px 18px; text-align: left; transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.tier-opt:hover { background: #2E3034; }
.tier-opt[aria-pressed="true"] { background: #46484C; color: var(--cream); }
.tier-opt .to-name { display: block; font-family: var(--serif); font-size: 22px; line-height: 1; }
.tier-opt .to-price { display: block; margin-top: 6px; font-size: 12px; letter-spacing: 0.14em; color: var(--accent-soft); text-transform: uppercase; }

/* ── Dropzone ───────────────────────────────────────────────── */
.dropzone {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  padding: clamp(24px, 5vh, 44px) 20px; text-align: center; cursor: pointer;
  border: 1.5px dashed var(--noir-line-2); border-radius: 16px;
  background: rgba(250, 246, 239, 0.025);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.dropzone:hover, .dropzone.is-drag { border-color: var(--accent-soft); background: rgba(184, 106, 58, 0.08); }
.dropzone svg { width: 30px; height: 30px; color: var(--accent-soft); }
.dz-title { color: var(--cream); font-size: 15px; }
.dz-sub { color: rgba(250, 246, 239, 0.55); font-size: 13px; }
.dropzone input[type="file"] { display: none; }

/* ── File list ──────────────────────────────────────────────── */
.file-list { display: flex; flex-direction: column; gap: 8px; }
.file-row {
  display: grid; grid-template-columns: 44px 1fr auto; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: 12px;
  background: rgba(250, 246, 239, 0.04); border: 1px solid var(--noir-line);
}
.file-thumb { width: 44px; height: 44px; border-radius: 8px; object-fit: cover; background: #1c1d20; }
.file-meta { min-width: 0; }
.file-name { color: var(--cream); font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-size { color: rgba(250, 246, 239, 0.5); font-size: 12px; margin-top: 2px; }
.file-bar { height: 3px; border-radius: 2px; background: var(--noir-line); margin-top: 7px; overflow: hidden; }
.file-bar > i { display: block; height: 100%; width: 0; background: var(--accent); transition: width 180ms linear; }
.file-row.is-done .file-bar > i { background: #6Fae7e; }
.file-x {
  appearance: none; border: 0; background: transparent; cursor: pointer; color: rgba(250, 246, 239, 0.5);
  font-size: 18px; line-height: 1; padding: 4px 6px; border-radius: 6px;
}
.file-x:hover { color: var(--cream); background: rgba(250, 246, 239, 0.08); }

/* ── Option chips (add-ons + formatting) ────────────────────── */
.opt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px; }
.opt {
  display: flex; align-items: center; gap: 10px; cursor: pointer;
  padding: 12px 14px; border-radius: 12px;
  background: rgba(250, 246, 239, 0.04); border: 1px solid var(--noir-line);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.opt:hover { border-color: var(--noir-line-2); }
.opt input { position: absolute; opacity: 0; pointer-events: none; }
.opt .box {
  flex: none; width: 18px; height: 18px; border-radius: 5px;
  border: 1.5px solid var(--noir-line-2); display: grid; place-items: center;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.opt .box svg { width: 11px; height: 11px; color: var(--ink); opacity: 0; }
.opt input:checked ~ .box { background: var(--accent); border-color: var(--accent); }
.opt input:checked ~ .box svg { opacity: 1; }
.opt input:focus-visible ~ .box { box-shadow: 0 0 0 3px rgba(184, 106, 58, 0.4); }
.opt.is-on { border-color: var(--accent-soft); background: rgba(184, 106, 58, 0.07); }
.opt-txt { flex: 1; min-width: 0; }
.opt-name { color: var(--cream); font-size: 13.5px; }
.opt-price { color: var(--accent-soft); font-size: 12px; letter-spacing: 0.06em; margin-top: 1px; }

/* ── Text inputs ────────────────────────────────────────────── */
.fld-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) { .fld-grid { grid-template-columns: 1fr; } }
.fld { display: flex; flex-direction: column; gap: 6px; }
.fld label { font-size: 12px; color: rgba(250, 246, 239, 0.7); letter-spacing: 0.04em; }
.fld input, .fld textarea {
  font-family: var(--sans); font-size: 15px; color: var(--cream);
  background: rgba(250, 246, 239, 0.05); border: 1px solid var(--noir-line);
  border-radius: 10px; padding: 12px 14px; width: 100%;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.fld textarea { resize: vertical; min-height: 92px; }
.fld input::placeholder, .fld textarea::placeholder { color: rgba(250, 246, 239, 0.38); }
.fld input:focus, .fld textarea:focus { outline: none; border-color: var(--accent-soft); background: rgba(250, 246, 239, 0.07); }
.fld.invalid input, .fld.invalid textarea { border-color: #C8654B; }

/* ── Summary panel ──────────────────────────────────────────── */
.summary {
  position: sticky; top: 96px;
  background: #1A1B1E; border: 1px solid var(--noir-line); border-radius: 18px;
  padding: clamp(20px, 2.4vw, 28px);
  display: flex; flex-direction: column; gap: 14px;
}
@media (max-width: 900px) { .summary { position: static; } }
.sum-title { font-family: var(--serif); font-size: 22px; color: var(--cream); }
.sum-lines { display: flex; flex-direction: column; gap: 9px; }
.sum-line { display: flex; justify-content: space-between; gap: 12px; font-size: 13.5px; color: rgba(250, 246, 239, 0.78); }
.sum-line .sl-amt { color: var(--cream); white-space: nowrap; }
.sum-line.muted { color: rgba(250, 246, 239, 0.45); }
.sum-rule { height: 1px; background: var(--noir-line); margin: 4px 0; }
.sum-total { display: flex; justify-content: space-between; align-items: baseline; }
.sum-total .st-label { font-size: 12px; letter-spacing: 0.2em; text-transform: uppercase; color: rgba(250, 246, 239, 0.6); }
.sum-total .st-amt { font-family: var(--serif); font-size: 34px; color: var(--cream); }
.sum-note { font-size: 12px; color: rgba(250, 246, 239, 0.5); line-height: 1.5; }

/* ── Primary action button (full-width pill) ────────────────── */
.order-btn {
  appearance: none; cursor: pointer; width: 100%;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  padding: 17px 24px; border-radius: 999px; border: 1px solid var(--accent);
  background: var(--accent); color: #fff;
  font-family: var(--sans); font-size: 12px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  transition: background var(--t) var(--ease), opacity var(--t) var(--ease);
}
.order-btn:hover { background: var(--accent-soft); }
.order-btn[disabled] { opacity: 0.45; cursor: not-allowed; }
.order-btn.ghost { background: transparent; color: var(--cream); border-color: var(--noir-line-2); }
.order-btn.ghost:hover { background: rgba(250, 246, 239, 0.06); }
.order-btn .spin {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,0.4); border-top-color: #fff;
  animation: order-spin 0.7s linear infinite; display: none;
}
.order-btn.is-busy .spin { display: inline-block; }
@keyframes order-spin { to { transform: rotate(360deg); } }

/* ── Step transitions ───────────────────────────────────────── */
.step-pay { display: none; }
.retouch-order.at-payment .step-details { display: none; }
.retouch-order.at-payment .step-pay { display: block; }
#payment-element { margin: 6px 0 2px; min-height: 44px; }
.pay-back {
  background: none; border: 0; cursor: pointer; color: rgba(250, 246, 239, 0.6);
  font-size: 13px; padding: 6px 0; display: inline-flex; align-items: center; gap: 6px;
}
.pay-back:hover { color: var(--cream); }

/* Demo-mode card stub (shown when no live Stripe key is set) */
.pay-demo {
  border: 1px dashed var(--noir-line-2); border-radius: 12px; padding: 16px;
  color: rgba(250, 246, 239, 0.6); font-size: 13px; line-height: 1.5; background: rgba(250, 246, 239, 0.03);
}
.pay-demo b { color: var(--accent-soft); font-weight: 500; }

/* ── Error + success ────────────────────────────────────────── */
.order-error {
  display: none; padding: 12px 14px; border-radius: 10px; font-size: 13.5px;
  background: rgba(200, 101, 75, 0.12); border: 1px solid rgba(200, 101, 75, 0.5); color: #E9A892;
}
.order-error.show { display: block; }
.order-done { text-align: center; max-width: 540px; margin: 0 auto; display: flex; flex-direction: column; gap: 16px; align-items: center; }
.order-done[hidden] { display: none; }
.order-done .od-check { width: 56px; height: 56px; border-radius: 50%; display: grid; place-items: center; background: rgba(111, 174, 126, 0.16); color: #6Fae7e; }
.order-done .od-check svg { width: 28px; height: 28px; }
.order-done h2 { font-family: var(--serif); font-size: clamp(30px, 4vw, 44px); color: var(--cream); margin: 0; }
.order-done p { color: rgba(250, 246, 239, 0.72); margin: 0; }
