/* ============================================================
   JUDY LIM — MAISON
   Foundation stylesheet — Cartier-borrowed editorial discipline.
   Sans-first hierarchy. Photography outranks text. Restraint.
   ============================================================ */

/* ---- 0. Image protection + brand text selection ----------- */
/* Light-touch right-click + drag protection on all images.
   Devtools can bypass, but this stops the casual save-to-desktop. */
img {
  -webkit-user-drag: none;
  -khtml-user-drag: none;
  -moz-user-drag: none;
  -o-user-drag: none;
  user-drag: none;
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

/* Pink brand selection — porcelain wash, ink text. The same pink
   that lives in the CTA hover + the Studio Kit halo. One token,
   three jobs. */
::selection {
  background: var(--porcelain);
  color: var(--ink);
}

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

/* ---- 1. Design Tokens ------------------------------------- */
:root {
  /* Palette — pure, restrained, refined */
  --canvas:        #FCFBF9;   /* warm-tinted near-white — main background */
  --canvas-pure:   #FFFFFF;   /* pure white for elevated panels */
  --ink:           #0A0908;   /* near-black for type & CTAs */
  --ink-soft:      #6B6660;   /* secondary text */
  --hairline:      #E8E2D9;   /* thin divider lines */
  --hairline-soft: #F0EBE2;
  --blush:         #F6E6DE;   /* Inquire section background — nude warm blush */
  --blush-soft:    #FCF3EF;
  --porcelain:     #FFF7FA;   /* CTA hover state — cool porcelain pink (Maison accent) */
  --gold-whisper:  #B8967A;   /* extreme accent — used 3x site-wide max */

  /* Type scale — sans-first */
  --t-eyebrow:    0.6875rem;  /* 11px */
  --t-label:      0.75rem;    /* 12px */
  --t-body-sm:    0.875rem;   /* 14px */
  --t-body:       1.0625rem;  /* 17px — serif runs slightly smaller than sans at same px */
  --t-body-lg:    1.25rem;    /* 20px — for editorial blocks */
  --t-h3:         clamp(1rem, 1.4vw, 1.25rem);
  --t-h2:         clamp(1.125rem, 1.6vw, 1.5rem);
  --t-h1-serif:   clamp(2.5rem, 7vw, 6rem);     /* serif statement (TORONTO/L.A.) */
  --t-display:    clamp(1.25rem, 2vw, 1.75rem); /* uppercase section h2 */

  /* Spacing — balanced Cartier rhythm (rebalanced for symmetry + breathing) */
  /* Nav geometry. The bar is padding-block (--s-3 ×2 = 48px) plus the
     wordmark (21–30px) → roughly 70–78px unscrolled, 54–62px scrolled.
     --nav-clear is that height plus editorial breath, and is what any
     section sitting directly beneath a SOLID nav should use for its
     top padding. Pages whose first section is full-bleed under a
     TRANSPARENT nav must not use it — the image is meant to run under
     the bar. Five hardcoded variants of this value had drifted apart
     across the stylesheet; they now derive from one place. */
  --nav-h:     clamp(70px, 6vw, 78px);
  --nav-clear: clamp(120px, 12vw, 152px);

  --s-1: 0.5rem;   /*  8 */
  --s-2: 1rem;     /* 16 */
  --s-3: 1.5rem;   /* 24 */
  --s-4: 2rem;     /* 32 */
  --s-5: 3rem;     /* 48 */
  --s-6: 4rem;     /* 64 */
  --s-7: 5.5rem;   /* 88 */
  --s-8: 7.5rem;   /* 120 */
  --s-9: 10rem;    /* 160 */

  /* Layout */
  --container:        1240px;
  --container-wide:   1480px;
  --container-narrow: 720px;
  --pad:              clamp(1.25rem, 5vw, 4rem);

  /* Motion — speed-tightened (was 220/480/760) */
  --ease-out:   cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --d-fast: 160ms;
  --d-base: 320ms;
  --d-slow: 520ms;
}

/* ---- 2. Reset & base -------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* Reset semantic elements that browsers default with margins */
figure, blockquote { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* iOS Safari momentum scrolling — without this, scrolling feels
     stiff on iPhones. */
  -webkit-overflow-scrolling: touch;
}

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: 'Crimson Pro', Georgia, 'Times New Roman', serif;
  font-size: var(--t-body);
  font-weight: 300;
  line-height: 1.5;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

/* While the intro loader is active, lock html scrolling so the page
   beneath cannot accidentally be scrolled by a touch event that fires
   through the overlay. JS also sets body { overflow:hidden } — this
   is belt-and-suspenders for iOS Safari which sometimes ignores body
   overflow when the touch starts inside the body's first flex child. */
html.intro-locked {
  overflow: hidden;
  height: 100%;
}

/* ===========================================================
   INTRO LOADER — homepage threshold ritual (C.7 colophon)
   Cream overlay, words reveal in sequence, fake 0–100 counter,
   then slides up to reveal hero. Fires once per session,
   homepage only. JS gate in js/intro-loader.js.
   =========================================================== */
.intro-loader {
  display: none;                /* JS turns to flex when .is-active */
  position: fixed;
  inset: 0;
  z-index: 9999;                /* above nav (z:100) and float-book (z:50) */
  background: var(--canvas);
  overflow: hidden;
  /* Exit transition: opacity fades + slides up off-screen */
  transition:
    transform 600ms var(--ease-out),
    opacity 600ms var(--ease-out);
}

.intro-loader.is-active {
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 1;
  transform: translateY(0);
}

.intro-loader.is-exiting {
  opacity: 0;
  transform: translateY(-100%);
}

/* Inner card — flex centered, with porcelain halo behind the words.
   Same signature wash as the Studio Kit halo to build the Maison
   color moment across the site (intro → kit → ...). */
.intro-loader__inner {
  position: relative;
  text-align: center;
  padding: var(--pad);
}

.intro-loader__inner::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(360px, 60vw, 640px);
  height: clamp(360px, 60vw, 640px);
  transform: translate(-50%, -50%);
  background: radial-gradient(
    ellipse at center,
    rgba(255, 220, 232, 0.45) 0%,
    rgba(255, 235, 242, 0.22) 35%,
    rgba(255, 247, 250, 0.10) 55%,
    transparent 75%
  );
  filter: blur(40px);
  z-index: 0;
  pointer-events: none;
}

/* Words — Crimson Pro Light 300, lockstep with locked typography
   spec ([[judy-lim-typography]]). Letter-spacing tracks the body
   register, not the heading register, to read as "considered prose"
   rather than "stamped headline." */
.intro-loader__words {
  position: relative;             /* above ::before halo */
  z-index: 1;
  margin: 0;
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  font-size: clamp(1.25rem, 3.5vw, 2rem);
  line-height: 1.55;
  letter-spacing: 0.01em;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  gap: 0.25em;
}

/* Emphasis word (judy lim) — Regular 400, NOT italic per locked spec.
   The slight weight shift reads as nameplate-style importance without
   the gestural slant of italic. */
.intro-loader__words strong {
  font-weight: 400;
}

/* Each line starts hidden; animation reveals them in sequence */
.intro-loader__line {
  display: block;
  opacity: 0;
  transform: translateY(8px);
}

/* Stagger:
   Line 1 at 200ms  (after overlay paints, the eye lands)
   Line 2 at 480ms  (~280ms after line 1 — close enough to feel
                     continuous, far enough to read as deliberate)
   Line 3 at 760ms  (same interval — three beats of the same rhythm)
   Each line takes 500ms to fade+rise to rest. Last line is fully
   rendered at ~1260ms, leaving ~1240ms of "hold" before exit at
   2500ms. That hold is the breath. */
.intro-loader.is-active .intro-loader__line--1 {
  animation: introLineIn 500ms 200ms var(--ease-out) forwards;
}
.intro-loader.is-active .intro-loader__line--2 {
  animation: introLineIn 500ms 480ms var(--ease-out) forwards;
}
.intro-loader.is-active .intro-loader__line--3 {
  animation: introLineIn 500ms 760ms var(--ease-out) forwards;
}

@keyframes introLineIn {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Counter — bottom-right corner, Alan Sans 0.2em, intentionally
   muted (40% opacity) so it reads as a rhythm device, not a
   demand for attention. */
.intro-loader__counter {
  position: absolute;
  bottom: clamp(20px, 4vw, 40px);
  right: clamp(20px, 4vw, 40px);
  margin: 0;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  opacity: 0.4;
  font-variant-numeric: tabular-nums;   /* digits don't wiggle as they tick */
  z-index: 1;
}

/* Reduced motion — belt-and-suspenders alongside the JS gate.
   If somehow the loader is shown to a reduced-motion user, kill
   the animation entirely and show all three lines instantly. */
@media (prefers-reduced-motion: reduce) {
  .intro-loader__line {
    opacity: 1;
    transform: none;
    animation: none !important;
  }
  .intro-loader {
    transition: none;
  }
}

img, picture, video, svg {
  max-width: 100%;
  height: auto;
  display: block;
}

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

a {
  color: inherit;
  text-decoration: none;
  transition: color var(--d-fast) var(--ease-out),
              opacity var(--d-fast) var(--ease-out);
}

a:hover { opacity: 0.6; }

/* ---- Global image safety net — prevents horizontal overflow on
   mobile and matches aspect-ratio when responsive containers shrink.
   This is the single biggest defense against "site explodes on a
   phone with one bad image" — every <img> now caps at parent width. */
img,
svg,
video {
  max-width: 100%;
  height: auto;
}

/* Body-level overflow guard — last-resort safety against any
   stray element that escapes its container on small viewports.
   Vertical scroll continues to work; only horizontal is blocked. */
body {
  overflow-x: hidden;
}

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

/* ---- 3. Typography primitives ----------------------------- */
.serif {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  letter-spacing: -0.01em;
}

.serif--italic {
  font-style: italic;
}

.eyebrow {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  display: inline-block;
  margin: 0;
}

.eyebrow--muted { color: var(--ink-soft); }
.eyebrow--inverse { color: var(--canvas); }

/* Section header — centered, uppercase letter-spaced sans */
.section-h {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  text-align: center;
  margin: 0 0 var(--s-3);
  line-height: 1.4;
}

/* Statement serif (used for TORONTO / to / L.A.) */
.statement {
  font-family: 'Crimson Pro', Georgia, serif;
  font-size: var(--t-h1-serif);
  font-weight: 300;
  letter-spacing: 0.02em;
  line-height: 1;
  text-align: center;
  text-transform: uppercase;
  margin: 0;
}

.statement em {
  font-style: normal;
  font-weight: 400;
  font-size: 0.45em;
  letter-spacing: 0.01em;
  display: block;
  margin: 0.4em auto;
  text-transform: lowercase;
  color: var(--ink-soft);
}

p { margin: 0 0 var(--s-2); }
p:last-child { margin-bottom: 0; }

.body-prose {
  max-width: 56ch;
  margin-inline: auto;
  text-align: center;
  color: var(--ink-soft);
  font-weight: 300;
  font-size: var(--t-body);
  line-height: 1.5;
  letter-spacing: 0.01em;
}

.body-prose--left {
  text-align: left;
  margin-inline: 0;
  color: var(--ink);
}

/* Subline — +1 weight contrast (Regular 400). Use ONLY directly under an Alan Sans header. */
.subline {
  font-family: 'Crimson Pro', Georgia, serif;
  font-style: normal;
  font-weight: 400;
  font-size: var(--t-body-lg);
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-align: center;
  max-width: 58ch;
  margin: 0 auto;
  /* Modern typography — auto-prevents orphans (single word on
     final line) and balances line lengths. Chrome 117+, Safari 17.4+.
     Older browsers ignore it gracefully. Belt-and-suspenders alongside
     explicit &nbsp; on known offenders. */
  text-wrap: pretty;
}

/* Editorial prose — regular serif at editorial scale, centered.
   Use for narrative blocks like A Note from Judy. */
.editorial-prose {
  font-family: 'Crimson Pro', Georgia, serif;
  font-style: normal;
  font-weight: 300;
  font-size: clamp(1.125rem, 1.5vw, 1.375rem); /* 18 → 22px */
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-align: center;
  max-width: 58ch;
  margin: 0 auto;
}

.editorial-prose p { margin-bottom: var(--s-3); }
.editorial-prose p:last-child { margin-bottom: 0; }

/* ---- 4. Layout primitives --------------------------------- */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--pad);
}

.container--wide {
  max-width: var(--container-wide);
}

.container--narrow {
  max-width: var(--container-narrow);
  text-align: center;
}

/* Wide, not edge-to-edge. Drops the 1240px text measure so imagery can
   breathe, but keeps a real inset so tiles never touch the viewport
   edge — and a ceiling so columns don't sprawl into portrait-sized
   monoliths on ultrawide displays.

   The earlier max-width:none version let each column reach ~600px on a
   1900px screen, which at the card's 4:5 ratio made a single tile
   taller than the viewport. */
.container--full {
  max-width: 1720px;
  padding-inline: clamp(var(--s-4), 5vw, var(--s-7));
}

section {
  padding-block: clamp(var(--s-5), 7vw, var(--s-7));
}

section.section-flush { padding-block: 0; }
section.section-tight { padding-block: clamp(var(--s-4), 5vw, var(--s-5)); }

/* ===========================================================
   TIKTOK SHOWCASE — canvas (off-white) section with centered
   9:16 video, prev/next nav arrows, dot pagination below.
   Inverted from the original black treatment so the section
   reads as part of the editorial canvas rather than breaking
   the cream continuity with a hard band.
   =========================================================== */
/* The section begins on canvas and dissolves to ink as it enters
   the viewport — a tonal shift that marks the change of register
   (still editorial → moving image) without a hard band appearing
   mid-scroll. The .is-dark class is toggled by an
   IntersectionObserver in js/tiktok-carousel.js.

   Every child that carries an explicit ink/canvas value gets a
   matching dark-state rule below, so the whole section inverts as
   one object rather than in pieces. */
.tiktok-showcase {
  background: var(--canvas);
  color: var(--ink);
  padding-block: clamp(var(--s-5), 7vw, var(--s-7));
  transition: background-color 900ms var(--ease-out),
              color 900ms var(--ease-out);
}

.tiktok-showcase.is-dark {
  background: var(--ink);
  color: var(--canvas);
}

.tiktok-showcase__head {
  text-align: center;
  margin-bottom: clamp(var(--s-4), 5vw, var(--s-6));
}

.tiktok-showcase__head .eyebrow,
.tiktok-showcase__heading {
  color: var(--ink);
  transition: color 900ms var(--ease-out);
}

.tiktok-showcase__head .eyebrow {
  opacity: 0.62;
}

.tiktok-showcase.is-dark .tiktok-showcase__head .eyebrow,
.tiktok-showcase.is-dark .tiktok-showcase__heading {
  color: var(--canvas);
}

.tiktok-showcase.is-dark .tiktok-showcase__head .eyebrow {
  opacity: 0.7;   /* cream on ink needs slightly more presence */
}

/* Footer link inverts with the section */
.tiktok-showcase__foot .link-arrow {
  transition: color 900ms var(--ease-out),
              border-color 900ms var(--ease-out);
}

.tiktok-showcase.is-dark .tiktok-showcase__foot .link-arrow {
  color: var(--canvas);
  border-bottom-color: rgba(252, 251, 249, 0.4);
}

/* Reduced motion — the colour still changes (it is information,
   not decoration) but it changes instantly rather than dissolving. */
@media (prefers-reduced-motion: reduce) {
  .tiktok-showcase,
  .tiktok-showcase__head .eyebrow,
  .tiktok-showcase__heading,
  .tiktok-showcase__foot .link-arrow,
  .tiktok-card,
  .tiktok-card__scrim {
    transition: none;
  }
}

/* ---- Three-up facade grid -------------------------------
   Cards render as lightweight posters; the TikTok iframe is only
   created on tap (see js/tiktok-carousel.js). Desktop = 3 columns.
   Mobile = horizontal scroll-snap strip so three 9:16 cards don't
   produce a 2400px-tall stack. */
.tiktok-grid {
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 1080px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(var(--s-2), 2.5vw, var(--s-4));
}

.tiktok-card {
  position: relative;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  background: var(--porcelain);
  border: 0.5px solid rgba(10, 9, 8, 0.08);
  transition: background-color 900ms var(--ease-out),
              border-color 900ms var(--ease-out);
}

/* On ink, the porcelain card would glare. Drop to a near-black
   tint with a cream hairline so the card reads as an aperture
   cut into the black rather than a tile sitting on top of it. */
.tiktok-showcase.is-dark .tiktok-card {
  background: rgba(252, 251, 249, 0.05);
  border-color: rgba(252, 251, 249, 0.14);
}

/* When a card is playing, the TikTok embed brings its own chrome
   (header + controls) so the frame needs its native taller ratio. */
.tiktok-card.is-playing {
  aspect-ratio: 325 / 730;
  background: rgba(10, 9, 8, 0.04);
}

.tiktok-card__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

/* ---- Facade (the un-played state) ----------------------- */
.tiktok-card__facade {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: block;
  overflow: hidden;
}

.tiktok-card__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  transition: transform 700ms var(--ease-out),
              opacity 500ms var(--ease-out);
  opacity: 1;
}

/* Thumbnails fetched from TikTok's oEmbed arrive after first paint.
   They fade in over the porcelain card rather than popping, so the
   card never flickers between two finished states. */
.tiktok-card__poster.is-loading {
  opacity: 0;
}

.tiktok-card__facade:hover .tiktok-card__poster,
.tiktok-card__facade:focus-visible .tiktok-card__poster {
  transform: scale(1.03);
}

/* Scrim — sits above the poster so the play glyph and handle stay
   legible on any frame. Also provides the porcelain wash on cards
   that have no poster yet (typographic fallback state). */
.tiktok-card__scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      rgba(10, 9, 8, 0.10) 0%,
      rgba(10, 9, 8, 0.02) 40%,
      rgba(10, 9, 8, 0.34) 100%);
  transition: background 900ms var(--ease-out);
}

/* On ink the surrounding field is already dark, so the scrim can
   ease off — less veil over Judy's work, same legibility for the
   play glyph and handle. */
.tiktok-showcase.is-dark .tiktok-card__scrim {
  background:
    linear-gradient(to bottom,
      rgba(10, 9, 8, 0.04) 0%,
      rgba(10, 9, 8, 0.00) 40%,
      rgba(10, 9, 8, 0.30) 100%);
}

.tiktok-card__facade:hover .tiktok-card__scrim,
.tiktok-card__facade:focus-visible .tiktok-card__scrim {
  background:
    linear-gradient(to bottom,
      rgba(10, 9, 8, 0.16) 0%,
      rgba(10, 9, 8, 0.06) 40%,
      rgba(10, 9, 8, 0.40) 100%);
}

/* Play affordance — hairline circle, cream stroke. Restrained;
   this is a Maison, not a media player. */
.tiktok-card__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(44px, 4.5vw, 58px);
  height: clamp(44px, 4.5vw, 58px);
  border-radius: 50%;
  border: 1px solid rgba(252, 251, 249, 0.75);
  background: rgba(10, 9, 8, 0.18);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--canvas);
  transition: background var(--d-base) var(--ease-out),
              border-color var(--d-base) var(--ease-out),
              transform var(--d-base) var(--ease-out);
}

.tiktok-card__play svg {
  width: 38%;
  height: 38%;
  margin-left: 8%;   /* optical centring — a triangle's mass sits left */
}

.tiktok-card__facade:hover .tiktok-card__play,
.tiktok-card__facade:focus-visible .tiktok-card__play {
  background: var(--canvas);
  border-color: var(--canvas);
  color: var(--ink);
  transform: translate(-50%, -50%) scale(1.06);
}

/* Handle — bottom-left, Alan Sans 0.2em on the scrim */
.tiktok-card__meta {
  position: absolute;
  left: clamp(12px, 2vw, 18px);
  bottom: clamp(12px, 2vw, 18px);
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--canvas);
  opacity: 0.9;
}

/* Keyboard focus — visible ring on the card, not just the glyph.
   The ring inverts with the section so contrast holds either way. */
.tiktok-card__facade:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

.tiktok-showcase.is-dark .tiktok-card__facade:focus-visible {
  outline-color: var(--canvas);
}

/* Footer link below the grid */
.tiktok-showcase__foot {
  text-align: center;
  margin: clamp(var(--s-4), 4vw, var(--s-5)) 0 0;
}

/* ---- Mobile: horizontal scroll-snap strip ---------------
   Three stacked 9:16 cards would be ~2400px tall on a phone.
   A snap strip keeps the section to one screen and shows a peek
   of the next card, which signals "there is more" without dots. */
@media (max-width: 699.98px) {
  .tiktok-grid {
    display: flex;
    gap: var(--s-2);
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* Bleed to the screen edges so the peek reads intentionally */
    margin-inline: calc(var(--pad) * -1);
    padding-inline: var(--pad);
    padding-bottom: 4px;
  }
  .tiktok-grid::-webkit-scrollbar { display: none; }

  .tiktok-card {
    flex: 0 0 72%;
    max-width: 300px;
    scroll-snap-align: center;
  }
}

/* Reduced motion — no poster zoom, no play-button scale */
@media (prefers-reduced-motion: reduce) {
  .tiktok-card__poster,
  .tiktok-card__play {
    transition: none;
  }
  .tiktok-card__facade:hover .tiktok-card__poster,
  .tiktok-card__facade:focus-visible .tiktok-card__poster {
    transform: none;
  }
  .tiktok-card__facade:hover .tiktok-card__play,
  .tiktok-card__facade:focus-visible .tiktok-card__play {
    transform: translate(-50%, -50%);
  }
}

/* ============================================================
   RHYTHMIC SPACING — phrase-based pacing
   ------------------------------------------------------------
   Phrase 1: Hero (flush) + Philosophy        → cling top, breathe bottom
   Phrase 2: Editorial img (flush) + Note     → cling top, breathe bottom
   Phrase 3: Signature 4-strip + Cities       → tight inside, breathe bottom
   Phrase 4: Studio Kit (standalone)          → breathe both sides
   Phrase 5: Duo (flush) + Inquire            → cling top to duo
   ============================================================ */

/* Page-header sections — equal top spacing across all sub-pages.
   The fixed nav is 64px tall; adding ~56px breathing means the
   eyebrow always sits with the same visible space below the nav,
   regardless of viewport size. */
/* Sections that sit directly beneath the solid nav and must clear it.
   #artisans-head is deliberately NOT in this list: on the Artists page
   the full-bleed hero is now the first element, so that heading sits
   mid-page. It kept the nav clearance after the hero was introduced,
   which read as an oversized gap beneath the image. */
#pricing-head,
#contact-head {
  padding-top: var(--nav-clear);
}

/* Phrase 1 — Philosophy: slight cling at top, generous breath at bottom */
#philosophy {
  padding-block-start: clamp(var(--s-5), 6vw, var(--s-6));
  padding-block-end:   clamp(var(--s-6), 8vw, var(--s-7));
}

/* Phrase 2 — Note: balanced breathing, generous on both sides */
#note {
  padding-block-start: clamp(var(--s-5), 6vw, var(--s-6));
  padding-block-end:   clamp(var(--s-5), 6vw, var(--s-6));
}

/* Phrase 3 — 4-strip: slightly tighter inside the phrase */
.section-tight {
  padding-block-start: clamp(var(--s-4), 5vw, var(--s-5));
  padding-block-end:   clamp(var(--s-4), 5vw, var(--s-5));
}

#cities {
  padding-block-start: clamp(var(--s-5), 6vw, var(--s-6));
  padding-block-end:   clamp(var(--s-6), 8vw, var(--s-7));
}

/* Phrase 4 — Studio Kit stands alone, breathes most generously */
#kit {
  padding-block: clamp(var(--s-7), 9vw, var(--s-8));
}

/* Phrase 5 — Inquire balanced canvas */
.inquire {
  padding-block-start: clamp(var(--s-6), 7vw, var(--s-7));
  padding-block-end:   clamp(var(--s-6), 7vw, var(--s-7));
}

.hairline {
  border: 0;
  border-top: 1px solid var(--hairline);
  margin: 0;
}

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

/* ---- 5. Navigation — Maison Triptych ---------------------
   Flat 5-item flexbox with space-evenly distribution so all
   four items + the logo have equal spacing across the nav.
   Default state: solid cream + ink text (every page that isn't
   a hero page). Pages with <body class="has-transparent-nav">
   start transparent + white text + inverted logo, transitioning
   to solid on scroll. Book lives in the floating pill, not here.
   ----------------------------------------------------------- */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  padding: var(--s-3) var(--pad);

  /* Default — solid cream, ink text */
  background: rgba(252, 251, 249, 0.94);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--hairline);
  color: var(--ink);

  transition: background var(--d-base) var(--ease-out),
              color var(--d-base) var(--ease-out),
              border-color var(--d-base) var(--ease-out),
              padding var(--d-base) var(--ease-out);
}

/* Drawer-open state — nav turns white with ink-color logo/icons
   regardless of which page or scroll position. Overrides the
   has-transparent-nav inversion so the menu chrome reads as a
   self-contained card. */
.nav.is-menu-open {
  background: var(--canvas) !important;
  color: var(--ink) !important;
}

body.has-transparent-nav .nav.is-menu-open .nav__brand-logo {
  /* Cancel the white-invert filter; the logo reads black on the
     now-white nav bar. */
  filter: none !important;
}

/* Transparent + white text on hero pages (when at the top) */
body.has-transparent-nav .nav:not(.is-scrolled) {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom-color: transparent;
  color: var(--canvas);
}

/* Logo inversion — black SVG → white when over hero */
body.has-transparent-nav .nav:not(.is-scrolled) .nav__brand-logo {
  filter: brightness(0) invert(1);
}

.nav__brand-logo {
  transition: filter var(--d-base) var(--ease-out);
}

/* Tighten padding once scrolled */
.nav.is-scrolled {
  padding-block: var(--s-2);
}

.nav__brand {
  display: inline-flex;
  align-items: center;
  line-height: 0;
}

.nav__brand-logo {
  display: block;
  width: auto;
  /* Reduced 25% from the previous doubled size — the wordmark was
     reading too loud at full-doubled scale and crowding the nav row.
     This sits between the original (14–20px) and the doubled
     (28–40px) values for a more considered register. */
  height: clamp(21px, 2vw, 30px);
  pointer-events: none;
}

/* Individual nav links — direct flex siblings of the nav */
.nav__item {
  position: relative;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: inherit;
  text-decoration: none;
  padding-block: var(--s-1);
  white-space: nowrap;
  transition: opacity var(--d-fast) var(--ease-out);
}

/* Animated underline — grows left-to-right on hover. More
   noticeable than the previous opacity-only change, while still
   reading as luxury-quiet (Cartier / Loewe register). */
.nav__item::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 2px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--d-base) var(--ease-out);
}

.nav__item:hover {
  opacity: 1;
}

.nav__item:hover::after {
  transform: scaleX(1);
}

/* Current page — persistent dim + active underline reads as "you
   are here" without competing with the hover state. */
.nav__item[aria-current="page"] {
  opacity: 0.72;
}

.nav__item[aria-current="page"]::after {
  transform: scaleX(1);
  opacity: 0.5;
}

/* Staggered page-load reveal — same cubic-bezier as everything else.
   The nav items + logo enter sequentially from the top, 120ms apart,
   starting 200ms after page load (so the page paints first). */
.nav__item,
.nav__brand {
  opacity: 0;
  transform: translateY(-8px);
  animation: navReveal 700ms cubic-bezier(0.22, 1, 0.36, 1) forwards;
}

.nav__item:nth-of-type(1) { animation-delay: 200ms; }   /* Portfolio */
.nav__item:nth-of-type(2) { animation-delay: 320ms; }   /* Artists */
.nav__brand                { animation-delay: 440ms; }   /* Logo */
.nav__item:nth-of-type(3) { animation-delay: 560ms; }   /* Pricing */
.nav__item:nth-of-type(4) { animation-delay: 680ms; }   /* Contact */

@keyframes navReveal {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .nav__item,
  .nav__brand {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

/* Mobile toggle — hidden on desktop */
.nav__toggle {
  display: none;
  background: none;
  border: 0;
  padding: var(--s-1);
  cursor: pointer;
  color: inherit;
}

.nav__toggle span {
  display: block;
  width: 22px;
  height: 1px;
  background: currentColor;
  margin: 6px 0;
  transition: transform var(--d-base) var(--ease-out),
              opacity var(--d-base) var(--ease-out);
}

/* Mobile drawer — OVERLAY CARD register: cream card with equal 16-24px
   margin on all sides, positioned below the nav. Hero photo shows
   through the margins around the card, same architectural treatment
   as the artist-detail Book modal floating over the artist's full-bleed
   photo. The list inside reads as a directory of doors; the card
   itself reads as a published artifact. */
.nav__drawer {
  /* Hidden on desktop entirely (no layout cost). Mobile media query
     flips display to flex so the drawer becomes "available" — still
     invisible via opacity:0 + transform offset until .is-open lands. */
  display: none;
  position: fixed;
  /* CONTENT-SIZED CARD, CENTERED ON BOTH AXES via inset:0 + margin:auto.
     This is the canonical centering trick for fixed elements with
     intrinsic/capped dimensions: setting all four insets to 0 makes
     the containing rectangle the full viewport, and margin:auto then
     distributes leftover space equally on all sides. Card is sized
     to its content (~440-480px tall) but capped by max-width 480px
     and max-height so it never crowds the viewport.

     The hero photo (or future video) shows through ABOVE and BELOW
     the card equally — matches the artist-detail Book modal
     register: cream card floating over editorial backdrop. */
  inset: 0;
  margin: auto;
  width: calc(100% - clamp(32px, 8vw, 48px));   /* same total side margin as before */
  max-width: 480px;
  height: fit-content;
  max-height: calc(100dvh - clamp(32px, 8vw, 48px));
  background: var(--canvas);
  border: 0.5px solid var(--hairline);
  overflow-y: auto;
  flex-direction: column;
  /* Hidden-but-mounted state. .is-open below promotes to visible.
     PREVIOUSLY a second `transition: max-height` declaration was
     overriding this one (CSS doesn't merge transition properties —
     the later one wins outright). That, combined with max-height:0
     and no .is-open override, kept the drawer permanently invisible
     even when the toggle fired. Bug fixed by removing both. */
  transform: translateY(-12px);
  opacity: 0;
  pointer-events: none;
  transition:
    transform var(--d-base) var(--ease-out),
    opacity var(--d-base) var(--ease-out);
  z-index: 90;
}

.nav__drawer.is-open {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

/* Drawer menu list — content-sized (no longer flexes-grow to fill).
   Combined with `bottom: auto` on the drawer itself, this keeps the
   card compact so the hero/video shows through below it. */
.nav__drawer ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0;
  flex: 0 0 auto;
}

/* Bottom block — Book CTA + social icons, anchored at the bottom
   of the drawer card with their own padded breath. */
.nav__drawer-foot {
  display: flex;
  flex-direction: column;
  gap: clamp(20px, 5vw, 28px);
  padding: clamp(20px, 5vw, 28px) var(--pad);
  border-top: 0.5px solid var(--hairline);
}

.nav__drawer-book {
  align-self: stretch;
  text-align: center;
  border-radius: 0;
}

.nav__drawer-socials {
  display: flex;
  align-items: center;
  gap: clamp(16px, 4vw, 22px);
  justify-content: flex-start;
}

.nav__drawer-socials a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  color: var(--ink);
  text-decoration: none;
  transition: opacity var(--d-fast) var(--ease-out);
}

.nav__drawer-socials a:hover { opacity: 0.65; }

.nav__drawer-socials svg {
  width: 20px;
  height: 20px;
  display: block;
}

/* Each menu row — left-aligned label + right-aligned chevron. Tall row
   height (clamp 56–72px) so the touch target is generous and the
   list reads as a directory, not a compact dropdown.
   IMPORTANT: scope to `ul a` so this does NOT leak onto the Book CTA
   (uses .btn styling) or the social icon links (own width/height +
   SVG content). Previously the unscoped `.nav__drawer a` rule was
   making the Book button text invisible (ink-on-ink) and rendering
   a chevron `::after` on every social icon. */
.nav__drawer ul a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.82rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  text-decoration: none;
  padding: clamp(20px, 5vw, 28px) var(--pad);
  border-top: 0.5px solid var(--hairline);
  transition: background var(--d-fast) var(--ease-out);
}

.nav__drawer ul a:hover,
.nav__drawer ul a:active {
  background: rgba(10, 9, 8, 0.03);
}

/* Chevron — pseudo-element so HTML stays clean. SVG-style arrow
   rendered as a CSS triangle/border for crisp 1px stroke at any DPI. */
.nav__drawer ul a::after {
  content: '';
  width: 8px;
  height: 8px;
  border-right: 1px solid var(--ink-soft);
  border-top: 1px solid var(--ink-soft);
  transform: rotate(45deg);
  flex-shrink: 0;
  margin-left: var(--s-2);
}

.nav__drawer ul li:last-child a {
  border-bottom: 0.5px solid var(--hairline);
}

@media (max-width: 768px) {
  /* On mobile, swap the flat flex for a 3-column grid:
     [toggle] [logo] [empty spacer] — preserves logo centering */
  .nav {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    justify-content: initial;
  }

  .nav__toggle {
    display: flex;
    flex-direction: column;
    justify-content: center;
    grid-column: 1;
    justify-self: start;
  }

  .nav__brand {
    grid-column: 2;
    justify-self: center;
  }

  /* Desktop nav items hide on mobile — drawer takes over */
  .nav__item {
    display: none;
  }

  .nav__drawer {
    /* Mobile: drawer becomes available in the layout, but stays
       opacity:0 + offset until .is-open lands. Flex preserves the
       vertical menu list + bottom anchor for the Book CTA. */
    display: flex;
  }
}

/* ===========================================================
   FLOATING BOOK PILL — bottom-right of every page
   The concierge bell. Appears after the bride scrolls past
   the fold (200px). Hides while the modal is open.
   =========================================================== */

.float-book {
  position: fixed;
  bottom: var(--s-3);
  right: var(--s-3);
  z-index: 50;

  display: inline-flex;
  align-items: center;
  padding: 14px 26px;
  background: var(--ink);
  color: var(--canvas);
  border: 1px solid var(--ink);

  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  text-decoration: none;
  cursor: pointer;
  border-radius: 0;
  white-space: nowrap;

  box-shadow: 0 2px 14px rgba(10, 9, 8, 0.10);

  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;

  transition: opacity var(--d-base) var(--ease-out),
              transform var(--d-base) var(--ease-out),
              background var(--d-fast) var(--ease-out),
              color var(--d-fast) var(--ease-out);
}

.float-book.is-visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}

.float-book:hover {
  background: var(--porcelain);
  color: var(--ink);
}

/* When the pill overlaps the dark footer, reverse the colors so it
   stays visible. JS toggles .is-on-dark via main.js scroll listener. */
.float-book.is-on-dark {
  background: var(--canvas);
  color: var(--ink);
  border-color: var(--canvas);
  box-shadow: 0 2px 14px rgba(0, 0, 0, 0.30);
}

.float-book.is-on-dark:hover {
  background: var(--porcelain);
  color: var(--ink);
}

body.modal-open .float-book {
  opacity: 0;
  transform: translateY(10px);
  pointer-events: none;
}

/* ===========================================================
   INQUIRY STAGE — Contact page multi-step card
   Pink-drift backdrop + glass card with sharp corners + step
   indicator + paneled fieldsets. Inspired by reference
   screenshot but with Maison restraint (no rounded corners,
   no loud type, subtle animated gradient).
   =========================================================== */

/* Inquiry stage starts at the top of the page and extends behind
   the fixed nav. The top padding clears the nav so the card itself
   sits below it; the background fills from top:0 of the section
   (i.e., the very top of the viewport), so the pink wash is
   continuous from the nav baseline down. */
.inquiry-stage {
  position: relative;
  padding: var(--nav-clear) 0 clamp(var(--s-5), 6vw, var(--s-7));
  overflow: hidden;
  /* FULL VIEWPORT HEIGHT — matches the About page (.viewer--portfolio)
     so the editorial bride photo extends to the bottom of the viewport
     instead of being cropped at the form's natural height. Same
     dvh/svh/vh layered fallback used on the home hero + Studio Kit. */
  min-height: 100vh;
  min-height: 100svh;
  min-height: 100dvh;
  /* Center the inquiry card vertically inside the now-tall section */
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Editorial photo background — contac-duo.jpeg sits behind the
   inquiry-card glass. The card's backdrop-filter (blur 12px) softens
   the image to ambient warmth so the form copy stays legible. The
   slight inset overflow + ease-out drift animation give the still
   image a gentle living quality. */
.inquiry-stage__bg {
  position: absolute;
  inset: -8%;
  z-index: 0;
  pointer-events: none;
  background-image: url('../assets/images/web/contac-duo.webp');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  /* Subtle drift to keep the still photo alive */
  animation: contactDrift 28s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes contactDrift {
  0%   { transform: scale(1) translate(0, 0); }
  100% { transform: scale(1.04) translate(-1%, -0.8%); }
}

/* Legacy pink drift — retained for any other surface that may still
   reference it (artist Book modal backdrop, etc.) */
@keyframes pinkDrift {
  0%   { transform: scale(1) translate(0, 0); }
  100% { transform: scale(1.06) translate(1.5%, -1.2%); }
}

@media (prefers-reduced-motion: reduce) {
  .inquiry-stage__bg {
    animation: none;
  }
}

/* The card itself — sharp corners (no border-radius), subtle glass */
.inquiry-card {
  position: relative;
  z-index: 1;
  background: rgba(255, 255, 255, 0.74);
  backdrop-filter: blur(12px) saturate(110%);
  -webkit-backdrop-filter: blur(12px) saturate(110%);
  border: 0.5px solid rgba(10, 9, 8, 0.06);
  /* Sharp corners — Maison signature */
  border-radius: 0;
  padding: clamp(var(--s-4), 4vw, var(--s-6));
  max-width: 900px;
  margin: 0 auto;
}

/* Step indicator — circles + labels + chevron separators */
.inquiry-steps {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  margin-bottom: clamp(var(--s-4), 4vw, var(--s-5));
  padding-bottom: clamp(var(--s-3), 3vw, var(--s-4));
  border-bottom: 0.5px solid var(--hairline);
  flex-wrap: wrap;
}

.inquiry-step {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  background: transparent;
  border: 0;
  padding: var(--s-1) var(--s-2);
  cursor: pointer;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  opacity: 0.45;
  transition:
    opacity var(--d-base) var(--ease-out),
    color var(--d-base) var(--ease-out);
}

.inquiry-step:hover {
  opacity: 0.8;
}

.inquiry-step.is-active {
  opacity: 1;
  color: var(--ink);
  cursor: default;
}

.inquiry-step.is-completed {
  opacity: 0.7;
}

.inquiry-step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 0.5px solid currentColor;
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0;
  transition:
    background var(--d-base) var(--ease-out),
    color var(--d-base) var(--ease-out),
    border-color var(--d-base) var(--ease-out);
}

.inquiry-step.is-active .inquiry-step__num {
  background: var(--ink);
  color: var(--canvas);
  border-color: var(--ink);
}

.inquiry-step.is-completed .inquiry-step__num {
  background: rgba(10, 9, 8, 0.12);
  color: var(--ink);
  border-color: transparent;
}

.inquiry-step__sep {
  color: var(--ink-soft);
  font-size: 0.875rem;
  opacity: 0.4;
  user-select: none;
}

/* Each step's fieldset = a panel. Only the active one is shown. */
.inquiry-panel {
  border: 0;
  padding: 0;
  margin: 0;
  display: none;
}

.inquiry-panel.is-active {
  display: block;
  animation: panelIn 360ms cubic-bezier(0.22, 1, 0.36, 1);
}

@keyframes panelIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Panel title — Alan Sans uppercase (matches the CONTACT h1 voice).
   The serif fall-back from before broke the type system; this version
   stays inside the brand's sans-led hierarchy. */
.inquiry-panel__title {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-size: clamp(1.125rem, 1.6vw, 1.5rem);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 var(--s-2);
  padding: 0;
  display: block;
}

.inquiry-panel__sub {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: var(--t-body);
  line-height: 1.55;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  margin: 0 0 clamp(var(--s-4), 4vw, var(--s-5));
}

/* Generous, consistent spacing between every form row + group
   inside a panel. Fixes the "label touching the field above"
   problem by making row-to-row and group-to-group gaps explicit. */
.inquiry-panel > .form__row,
.inquiry-panel > .form__group,
.inquiry-panel > .form__agree {
  margin-top: clamp(28px, 2.4vw, 36px);
}

.inquiry-panel > .form__row:first-of-type,
.inquiry-panel > .form__group:first-of-type {
  margin-top: 0;            /* sub already provides space above the first row */
}

/* Form row layout — was missing entirely, which is why groups stacked
   without breathing room. Single column at narrow widths, two-column
   grid at wider widths, with explicit gaps in both cases. */
.form__row {
  display: flex;
  flex-direction: column;
  gap: clamp(28px, 2.4vw, 36px);
}

@media (min-width: 700px) {
  .form__row--2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(20px, 1.8vw, 32px);
  }
}

/* Form labels — Alan Sans uppercase. Sit *below* the red Required
   tag, *above* the input. Generous bottom gap to the input. */
.inquiry-card .form__group label {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;     /* ~11px */
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  margin-bottom: 10px;
  display: block;
}

/* Required indicator — small red asterisk inline with the label.
   Compact, unobtrusive, and the asterisk legend at the bottom of
   the form explains the convention. */
.inquiry-card .form__req-mark {
  color: #8B1A1A;
  font-weight: 500;
  margin-left: 2px;
  font-size: 1em;
  vertical-align: baseline;
}

/* Legacy req tags inside .inquiry-card are hidden — we use
   form__req-mark (the asterisk) instead. */
.inquiry-card .form__req,
.inquiry-card .form__req-tag {
  display: none;
}

/* Disclaimer at the bottom of the form explaining the asterisk */
.inquiry-disclaimer {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;       /* 11px */
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
  text-transform: none;
  margin: clamp(20px, 2vw, 28px) 0 0;
  text-align: left;
}

.inquiry-disclaimer .form__req-mark {
  color: #8B1A1A;
}

/* Field styling — sharp corners, slightly lighter borders, refined
   placeholder. Overrides the global .form input rules in the
   inquiry-card scope. */
.inquiry-card input,
.inquiry-card textarea,
.inquiry-card select {
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(10, 9, 8, 0.18);
  border-radius: 0;
  padding: 0.75rem 0.875rem;
  color: var(--ink);
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.9375rem;
  letter-spacing: 0.01em;
  width: 100%;
  transition: border-color var(--d-fast) var(--ease-out),
              background var(--d-fast) var(--ease-out);
}

.inquiry-card input:focus,
.inquiry-card textarea:focus,
.inquiry-card select:focus {
  outline: none;
  border-color: var(--ink);
  background: rgba(255, 255, 255, 0.85);
}

/* Custom chevron on selects — replaces the browser default so the
   glyph sits with the right padding inside the field. Uses an SVG
   data URI so no external request. */
.inquiry-card select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8' fill='none' stroke='%230A0908' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='1 1.5 6 6.5 11 1.5'/></svg>");
  background-position: right 14px center;
  background-size: 10px 7px;
  background-repeat: no-repeat;
  padding-right: 2.25rem;
  cursor: pointer;
}

/* Placeholder — Crimson Pro 300, slightly smaller than typed text,
   slightly muted grey but readable. */
.inquiry-card input::placeholder,
.inquiry-card textarea::placeholder {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.875rem;
  font-style: normal;
  color: rgba(10, 9, 8, 0.42);
  letter-spacing: 0.01em;
}

/* ---- Multi-select service group (revision 4.3) ------------
   A checkbox list rather than a multi-select <select>. Native
   multi-selects require ctrl/cmd-click, which almost nobody
   discovers and which is unusable on touch. Checkboxes make the
   "choose all that apply" instruction self-evident. */
.form__group-label {
  display: block;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  margin-bottom: 4px;
}

.form__checkbox-group {
  display: grid;
  gap: 2px;
  margin-top: 10px;
  border: 1px solid rgba(10, 9, 8, 0.18);
  padding: 4px;
  background: rgba(255, 255, 255, 0.6);
}

/* Invalid state mirrors the focus treatment used elsewhere, so the
   group reads as part of the same form language. */
.form__checkbox-group.is-invalid {
  border-color: #8B1A1A;
}

.form__checkbox {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 10px;
  cursor: pointer;
  transition: background var(--d-fast) var(--ease-out);
}

.form__checkbox:hover {
  background: rgba(10, 9, 8, 0.03);
}

.form__checkbox input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  border: 1px solid var(--ink);
  background: transparent;
  border-radius: 0;
  cursor: pointer;
  position: relative;
  margin: 0;
  flex-shrink: 0;
  transition: background var(--d-fast) var(--ease-out);
}

.form__checkbox input[type="checkbox"]:checked {
  background: var(--ink);
}

.form__checkbox input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  left: 4.5px;
  top: 0.5px;
  width: 5px;
  height: 10px;
  border: solid var(--canvas);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

.form__checkbox input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.form__checkbox span {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  font-size: 0.9375rem;
  letter-spacing: 0.01em;
  color: var(--ink);
}

/* A disabled stylist select means no region has been chosen yet. Muted
   rather than hidden, so the field's existence is still legible. */
.inquiry-card select:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Custom checkbox — sharp corners, ink fill, refined checkmark */
.inquiry-card .form__agree {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  cursor: pointer;
  margin-top: clamp(28px, 2.4vw, 36px);
}

.inquiry-card .form__agree input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  border: 1px solid var(--ink);
  background: rgba(255, 255, 255, 0.6);
  border-radius: 0;          /* sharp */
  cursor: pointer;
  position: relative;
  margin: 0;
  flex-shrink: 0;
  padding: 0;
  transition: background var(--d-fast) var(--ease-out);
}

.inquiry-card .form__agree input[type="checkbox"]:checked {
  background: var(--ink);
}

.inquiry-card .form__agree input[type="checkbox"]:checked::after {
  content: '';
  position: absolute;
  left: 5px;
  top: 1px;
  width: 5px;
  height: 10px;
  border: solid var(--canvas);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}

/* Disclaimer text — Alan Sans, sentence case (not uppercase),
   slightly muted. Replaces the Crimson serif disclaimer voice. */
.inquiry-card .form__agree > span {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.8125rem;     /* 13px */
  font-weight: 400;
  letter-spacing: 0.02em;
  line-height: 1.5;
  color: var(--ink-soft);
  text-transform: none;
  padding-top: 1px;          /* optical alignment with checkbox */
}

/* Hint text below a field — refined to match the new spacing */
.inquiry-card .form__hint {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.75rem;
  color: var(--ink-soft);
  margin: 8px 0 0;
  letter-spacing: 0.01em;
}

/* Navigation row */
.inquiry-nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--s-3);
  margin-top: clamp(var(--s-4), 4vw, var(--s-5));
  padding-top: clamp(var(--s-3), 3vw, var(--s-4));
  border-top: 0.5px solid var(--hairline);
}

.inquiry-nav__back {
  background: transparent;
  border: 0;
  cursor: pointer;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  padding: var(--s-2) 0;
  opacity: 0.7;
  transition: opacity var(--d-fast) var(--ease-out);
}

.inquiry-nav__back:hover {
  opacity: 1;
}

.inquiry-nav__back[hidden] {
  visibility: hidden;
  display: inline-flex;   /* preserve layout space */
  opacity: 0;
  pointer-events: none;
}

.inquiry-nav__next,
.inquiry-nav__submit {
  margin-left: auto;
}

/* Next Step is now a text-link (.link-arrow class) — reset any
   inherited button-only padding so it sits naturally beside the
   Back link */
.inquiry-nav .link-arrow.inquiry-nav__next {
  padding: var(--s-1) 0;
}

/* Submit button: when the user reaches step 3, the next becomes submit */
.inquiry-nav__submit[hidden],
.inquiry-nav__next[hidden] {
  display: none;
}

/* Mobile — stack step indicator labels under numbers if too tight */
@media (max-width: 640px) {
  .inquiry-step__label {
    display: none;
  }
  .inquiry-step.is-active .inquiry-step__label {
    display: inline;
  }
  .inquiry-card {
    padding: var(--s-3);
  }
}

/* ===========================================================
   INQUIRY FORM — mobile tightening
   Default form spacing (rows ~28-36px, label margin 10px, input
   pad 12px) was sized for desktop comfort. On mobile that meant
   ~96px per field × 5 fields = 480px of pure form scroll per
   step. This block tightens vertical rhythm by ~35-40% so a step
   fits in roughly one mobile viewport without excess scroll,
   without sacrificing touch-target height (inputs stay ≥44px
   tall via padding + 15px font-size).
   =========================================================== */
@media (max-width: 600px) {
  /* Panel title + sub — pull closer together */
  .inquiry-panel__title {
    margin-bottom: 6px;
  }
  .inquiry-panel__sub {
    margin-bottom: var(--s-3);
    font-size: 0.9375rem;
  }

  /* Row-to-row spacing — half the desktop gap */
  .inquiry-panel > .form__row,
  .inquiry-panel > .form__group,
  .inquiry-panel > .form__agree {
    margin-top: 16px;
  }
  .form__row {
    gap: 16px;
  }

  /* Labels closer to their fields */
  .inquiry-card .form__group label {
    margin-bottom: 6px;
    font-size: 0.625rem;     /* 10px on mobile */
  }

  /* Inputs — slightly tighter vertical pad. 0.6875rem + 15px
     line-height keeps the tap target at ~44px (Apple HIG). */
  .inquiry-card input,
  .inquiry-card textarea,
  .inquiry-card select {
    padding: 0.6875rem 0.75rem;
    font-size: 0.9375rem;     /* keeps iOS from zooming the input on focus */
  }

  /* Step indicator — pull above the form closer */
  .inquiry-steps {
    margin-bottom: var(--s-3);
  }

  /* Nav row (Back / Next / Submit) — tighter top spacing */
  .inquiry-nav {
    margin-top: var(--s-3);
    padding-top: var(--s-2);
  }

  /* Agreement checkbox — pull closer to the field above */
  .inquiry-card .form__agree {
    margin-top: 16px;
  }
}


.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-1);
  padding: 0.9375rem 1.75rem;
  background: var(--ink);
  color: var(--canvas);
  border: 1px solid var(--ink);
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  cursor: pointer;
  transition: background var(--d-fast) var(--ease-out),
              color var(--d-fast) var(--ease-out);
  border-radius: 0;
  white-space: nowrap;
  text-decoration: none;
}

.btn:hover {
  background: var(--porcelain);
  color: var(--ink);
  opacity: 1;
}

.btn--outline {
  background: transparent;
  color: var(--ink);
}

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

.btn--inverse {
  background: var(--canvas);
  color: var(--ink);
  border-color: var(--canvas);
}

.btn--inverse:hover {
  background: transparent;
  color: var(--canvas);
}

.btn--sm { padding: 0.625rem 1.25rem; font-size: 0.625rem; }
.btn--full { width: 100%; }

/* Editorial text link — same type system as the eyebrow + nav.
   Replaces filled .btn for editorial CTAs (Philosophy, View All
   Artisans, View My Products, Inquire). Underline matches the
   word's width exactly via native text-decoration. Reserves filled
   .btn for commerce moments only (Book + Submit). */
.link-arrow {
  display: inline-block;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 7px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
  transition: opacity var(--d-fast) var(--ease-out),
              text-decoration-color var(--d-fast) var(--ease-out);
}

.link-arrow:hover,
.link-arrow:focus-visible {
  opacity: 0.6;
}

/* Tiny text link with hairline underline */
.linklet {
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  border-bottom: 1px solid currentColor;
  padding-bottom: 4px;
  display: inline-block;
}

/* ---- 7. Hero — full-bleed photography, no text overlay ---- */
.hero {
  position: relative;
  /* Override the global section padding-block so the image goes
     truly full-bleed (top edge flush with nav, bottom edge flush
     with the next section). */
  padding-block: 0;
  /* FULL VIEWPORT FILL across all devices:
     - vh: fallback for old browsers (includes URL bar area on iOS,
           so technically extends BEYOND visible viewport — but
           the overflow:hidden + position:relative contain it).
     - svh (Small Viewport Height): the visible viewport when iOS
           Safari's URL bar IS showing. Conservative, never overflows.
     - dvh (Dynamic Viewport Height): adjusts in real-time as the
           URL bar shows/hides. Best UX on modern iOS/Android.
     Browser picks the last one it understands; older browsers
     fall back to vh. Hero now fills the screen exactly across
     iPhone SE (375×667) → iPhone Pro Max (430×932) → tablet → desktop. */
  min-height: 100vh;
  min-height: 100svh;
  min-height: 100dvh;
  overflow: hidden;
  background: var(--canvas);
}

.hero__media {
  position: absolute;
  inset: 0;
}

.hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
}

/* ---- Full-bleed Vimeo background film --------------------
   An iframe has a fixed 16:9 aspect and cannot be object-fit,
   so we oversize it and let the section's overflow:hidden crop
   it. The four size rules together guarantee the frame always
   covers the viewport whichever way the aspect mismatch runs:
     wide viewport  → width:100vw wins, height overflows
     tall viewport  → min-width:177.78vh wins, width overflows
   177.78vh is 100vh × (16/9) — the width needed for a 16:9 frame
   to fill a full-height box. */
.hero--video {
  background: var(--ink);   /* letterbox guard while the player boots */
}

.hero__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
  z-index: 0;
}

.hero__video {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 1;
  /* Cross-fade in once the player has started, so the poster→film
     handoff is a dissolve rather than a cut. */
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
  pointer-events: none;   /* hero film is decorative, not clickable */
}

.hero__video.is-ready {
  opacity: 1;
}

.hero__video-frame {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 100vw;
  height: 56.25vw;        /* 100 ÷ 16 × 9 */
  min-height: 100vh;
  min-width: 177.78vh;    /* 100 × 16 ÷ 9 */
  border: 0;
  display: block;
}

/* Pause control — bottom-right, hairline circle. Hidden until the
   player is live (JS unsets [hidden]). Satisfies WCAG 2.2.2. */
.hero__motion {
  position: absolute;
  right: clamp(16px, 3vw, 32px);
  bottom: clamp(16px, 3vw, 32px);
  z-index: 3;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid rgba(252, 251, 249, 0.5);
  background: rgba(10, 9, 8, 0.22);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--d-fast) var(--ease-out),
              border-color var(--d-fast) var(--ease-out),
              opacity var(--d-fast) var(--ease-out);
  opacity: 0.55;
}

.hero__motion:hover,
.hero__motion:focus-visible {
  opacity: 1;
  background: rgba(10, 9, 8, 0.4);
  border-color: var(--canvas);
}

.hero__motion:focus-visible {
  outline: 2px solid var(--canvas);
  outline-offset: 3px;
}

/* Glyph — two bars (pause). Becomes a triangle when paused. */
.hero__motion-glyph {
  display: block;
  width: 10px;
  height: 11px;
  border-left: 2px solid var(--canvas);
  border-right: 2px solid var(--canvas);
  box-sizing: border-box;
}

.hero__motion.is-paused .hero__motion-glyph {
  width: 0;
  height: 0;
  border-left: 9px solid var(--canvas);
  border-right: 0;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
  margin-left: 3px;   /* optical centring for the triangle */
}

/* ---- Inquiry stage, flat variant -------------------------
   Used on the Pricing page, where the form closes the page rather
   than being the page. Drops the Contact page's full-viewport
   height and its photographic backdrop — the pricing lanes above
   are already carrying the visual weight, and a second full-bleed
   image would compete with them.

   The card keeps its own styling, so the form looks and behaves
   identically to /contact/ without a second stylesheet path. */
.inquiry-stage--flat {
  min-height: 0;
  padding-block: clamp(var(--s-6), 8vw, var(--s-7));
  display: block;
  background: var(--canvas);
  border-top: 0.5px solid var(--hairline);
}

.inquiry-stage--flat .inquiry-card {
  margin-inline: auto;
  max-width: 780px;
}

/* ===========================================================
   ARTIST DETAIL — closing sequence

   The page used to run from the work carousel straight into the dark
   footer. Two sections now close it:

     1. .artist-close  — porcelain wash, the named ask
     2. .artist-more   — cream, lateral navigation to other artists

   The tonal run is cream (viewer) → porcelain (ask) → cream (roster)
   → ink (footer). The porcelain step is what stops the cream page
   from butting directly against the black band, and it is the third
   appearance of the accent after the Studio Kit and the intro loader
   — repetition is what makes a colour read as a signature rather
   than a one-off.
   =========================================================== */
.artist-close {
  /* The wash resolves INTO the canvas rather than stopping at
     porcelain. Previously the gradient ended on --porcelain (#FFF7FA)
     while the next section began on --canvas (#FCFBF9) — close enough
     to look like a mistake, far enough apart to draw a visible seam
     straight across the page.

     Ending on --canvas means the colour has already arrived at the
     page background before the section boundary, so the two sections
     read as one continuous field. */
  background:
    radial-gradient(ellipse 120% 90% at 50% 0%,
      rgba(255, 218, 231, 0.42) 0%,
      rgba(255, 236, 243, 0.34) 38%,
      rgba(255, 247, 250, 0.55) 68%,
      var(--canvas) 100%);
  text-align: center;
  /* Tightened so the ask and the roster strip below share one screen.
     The bottom is deliberately shallow — the two sections are one
     visual unit now, and a generous gap between them would reinstate
     the boundary the gradient just removed. */
  padding-top: clamp(var(--s-5), 6vw, var(--s-6));
  padding-bottom: clamp(var(--s-3), 3vw, var(--s-4));
  border-top: 0.5px solid var(--hairline);
}

.artist-close .section-h {
  margin-bottom: var(--s-2);
}

.artist-close__sub {
  margin-bottom: clamp(var(--s-3), 4vw, var(--s-4));
  color: var(--ink-soft);
}

.artist-close__cta {
  min-width: 220px;
}

/* ---- Lateral roster -------------------------------------
   No background of its own: the page canvas shows through, which is
   the colour the gradient above has already resolved to. Nothing to
   line up, so nothing can misalign. */
.artist-more {
  background: transparent;
  padding-top: clamp(var(--s-4), 4vw, var(--s-5));
  padding-bottom: clamp(var(--s-5), 6vw, var(--s-6));
}

.artist-more__head {
  display: block;
  text-align: center;
  margin-bottom: clamp(var(--s-3), 3vw, var(--s-4));
  opacity: 0.55;
}

/* Six across on desktop. These are orientation cues, not the roster
   proper — small enough to read as a footer index, large enough to
   recognise a face. */
.artist-more__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: clamp(var(--s-2), 2vw, var(--s-3));
}

.artist-more__link {
  display: block;
  text-decoration: none;
  color: inherit;
}

.artist-more__media {
  display: block;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background: var(--porcelain);
  margin-bottom: 12px;
}

.artist-more__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 20%;
  display: block;
  transition: transform 700ms var(--ease-out);
}

.artist-more__link:hover .artist-more__media img,
.artist-more__link:focus-visible .artist-more__media img {
  transform: scale(1.04);
}

/* Override the global a:hover opacity fade — dimming a portrait
   against cream reads as a wash rather than an affordance. */
.artist-more__link:hover,
.artist-more__link:focus-visible {
  opacity: 1;
}

.artist-more__link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 4px;
}

.artist-more__name {
  display: block;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ink);
}

.artist-more__city {
  display: block;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.5625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--ink-soft);
  margin-top: 4px;
}

.artist-more__foot {
  text-align: center;
  margin: clamp(var(--s-3), 3vw, var(--s-4)) 0 0;
}

/* Cap the tile height so the closing sequence — ask plus roster strip —
   lands inside one screen on a laptop. Six columns across a 1240px
   measure would otherwise give ~180px tiles at 3:4, and the two
   sections together would run past the fold. */
.artist-more__media {
  max-height: clamp(180px, 26vh, 260px);
}

/* Three across on tablet, then a snap strip on phones — six portraits
   stacked two-wide would run longer than the profile above them. */
@media (max-width: 899.98px) {
  .artist-more__list {
    grid-template-columns: repeat(3, 1fr);
  }
}

@media (max-width: 599.98px) {
  .artist-more__list {
    display: flex;
    gap: var(--s-2);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    margin-inline: calc(var(--pad) * -1);
    padding-inline: var(--pad);
  }
  .artist-more__list::-webkit-scrollbar { display: none; }
  .artist-more__item {
    flex: 0 0 42%;
    max-width: 170px;
    scroll-snap-align: start;
  }
}

@media (prefers-reduced-motion: reduce) {
  .artist-more__media img {
    transition: none;
  }
  .artist-more__link:hover .artist-more__media img,
  .artist-more__link:focus-visible .artist-more__media img {
    transform: none;
  }
}

/* ===========================================================
   DIRECT CONTACT — Contact page, beneath the inquiry form

   The typographic counterweight to the form above it. The form is
   Alan Sans labels and boxed fields — structured, institutional.
   This is Crimson Pro Light at display scale — open, human, closer
   to a letterhead than an interface.

   That contrast is the whole point: it says the studio can also
   simply be written to.
   =========================================================== */
.contact-direct {
  background: var(--canvas);
  text-align: center;
  padding-block: clamp(var(--s-6), 9vw, var(--s-7));
}

.contact-direct__eyebrow {
  display: block;
  margin-bottom: clamp(var(--s-4), 5vw, var(--s-5));
  opacity: 0.55;
}

.contact-direct__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(var(--s-4), 5vw, var(--s-5));
}

.contact-direct__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

/* Small sans label — keeps the serif value from floating unanchored,
   and gives screen readers a clear pairing. */
.contact-direct__label {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
}

/* The value itself — serif, light, large. Tracking is pulled slightly
   negative because Crimson Pro opens up at display sizes and the
   default 0.01em body tracking reads loose above ~2rem. */
.contact-direct__value {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  font-size: clamp(1.625rem, 5.5vw, 3rem);
  line-height: 1.15;
  letter-spacing: -0.005em;
  color: var(--ink);
  text-decoration: none;
  /* Hairline that draws in on hover rather than sitting there —
     the link reads as text first, affordance second. */
  border-bottom: 1px solid transparent;
  padding-bottom: 0.08em;
  transition: border-color var(--d-base) var(--ease-out),
              opacity var(--d-base) var(--ease-out);
  /* Long addresses must not force a horizontal scrollbar on 320px */
  overflow-wrap: anywhere;
}

.contact-direct__value:hover,
.contact-direct__value:focus-visible {
  border-bottom-color: var(--ink);
  opacity: 1;   /* override the global a:hover fade — this is display type */
}

.contact-direct__value:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 6px;
  border-bottom-color: transparent;
}

.contact-direct__note {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  font-size: var(--t-body);
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  margin: clamp(var(--s-4), 5vw, var(--s-5)) 0 0;
}

/* ===========================================================
   WORK BAND — crossfade image sequence
   Base: full-bleed width, contained height (reusable as an
   editorial strip anywhere on the site).
   =========================================================== */
.work-band {
  position: relative;
  width: 100%;
  height: clamp(320px, 52vh, 560px);
  overflow: hidden;
  background: var(--ink);
  margin-block: clamp(var(--s-3), 4vw, var(--s-5));
}

/* --hero variant: Artists page masthead. Full viewport, flush
   under the transparent nav, no surrounding margin — the same
   treatment the homepage hero used before it became a film.
   Viewport-height stack: vh fallback → svh (URL bar showing) →
   dvh (tracks the bar in real time). */
.work-band--hero {
  height: auto;
  min-height: 100vh;
  min-height: 100svh;
  min-height: 100dvh;
  margin-block: 0;
}

/* The pause control inherits .hero__motion; only its footprint
   differs on the band. */
.work-band__motion {
  right: clamp(12px, 2.5vw, 24px);
  bottom: clamp(12px, 2.5vw, 24px);
}

.work-band--hero .work-band__motion {
  right: clamp(16px, 3vw, 32px);
  bottom: clamp(16px, 3vw, 32px);
}

/* The section that follows the hero shouldn't carry its usual
   top padding into a flush-bleed edge — it reads as a gap. */
.work-band--hero + .section-tight {
  padding-top: clamp(var(--s-5), 7vw, var(--s-7));
}

@media (max-width: 699.98px) {
  .work-band {
    height: clamp(260px, 42vh, 400px);
  }
  /* --hero keeps its full-viewport height on mobile; the base
     height above must not override it. */
  .work-band--hero {
    height: auto;
  }
}

/* Hero auto-cycle — crossfade slideshow. Now lives on the Artists
   page as the work band; the slide mechanics are shared. */
.hero__cycle {
  position: absolute;
  inset: 0;
}

.hero__slide {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  transition: opacity 1200ms cubic-bezier(0.4, 0, 0.2, 1);
  will-change: opacity;
  pointer-events: none;
}

.hero__slide.is-active {
  opacity: 1;
}

.hero__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
}

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

/* Tiny watermark caption — barely-there orientation cue */
.hero__caption {
  position: absolute;
  inset: auto 0 var(--s-4) 0;
  text-align: center;
  z-index: 2;
  color: var(--canvas);
  text-shadow: 0 1px 8px rgba(0,0,0,0.25);
}

.hero__caption .eyebrow {
  color: var(--canvas);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  opacity: 0.92;
}

.hero__caption-dot {
  display: inline-block;
  margin: 0 0.75em;
  opacity: 0.6;
}

/* Placeholder hero (until image lands) — soft gradient */
.hero__media--placeholder {
  background: linear-gradient(135deg, #E8DDD2 0%, #D4C5B5 50%, #B8967A 100%);
}

.hero__media--placeholder::before {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(circle at 25% 30%, rgba(255,255,255,0.18), transparent 50%),
    radial-gradient(circle at 75% 70%, rgba(255,255,255,0.12), transparent 55%);
}

/* ---- 8. Section: editorial centered text ------------------ */
.editorial-block {
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s-3);
}

.editorial-block .eyebrow,
.editorial-block .section-h { color: var(--ink); }

.editorial-block .section-h { margin-bottom: 0; }
.editorial-block .body-prose,
.editorial-block .subline { margin-bottom: var(--s-1); }

/* ---- 9. Full-bleed editorial image ------------------------ */
.fullbleed-img {
  width: 100%;
  height: clamp(380px, 52vh, 640px);
  overflow: hidden;
  background: var(--hairline-soft);
}

.fullbleed-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* B&W double-portrait — transparent PNG on cream that floats into
   the top of the Note from Judy section. The image carries its own
   composition; the cream canvas reads through the transparency. The
   negative margin-bottom pulls #note up so the heading appears just
   below the image, creating an editorial layered moment. */
.editorial-bw {
  position: relative;
  z-index: 2;
  padding-block: 0;
  margin-top: clamp(var(--s-2), 3vw, var(--s-4));
  margin-bottom: clamp(-120px, -7vw, -60px);  /* pull Note section up under image */
  pointer-events: none;  /* never trap clicks meant for content below */
}

/* Mobile — remove the negative-margin overlap. The editorial-bw image
   and the #note heading need to stack cleanly on small viewports
   where the layered desktop effect crashes them into each other. */
@media (max-width: 768px) {
  .editorial-bw {
    margin-bottom: var(--s-3);
  }
}

.editorial-bw__inner {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding-inline: var(--pad);
  display: flex;
  justify-content: center;
}

.editorial-bw__img {
  display: block;
  width: 100%;
  height: auto;
  max-height: clamp(420px, 64vh, 760px);
  object-fit: contain;
}

/* Ensure the Note from Judy section paints below the floating image */
#note {
  position: relative;
  z-index: 1;
  padding-top: clamp(var(--s-5), 6vw, var(--s-7));
}

/* ---- 10. 4-strip signature gallery ------------------------ */
.strip {
  display: grid;
  gap: 2px;
  grid-template-columns: repeat(2, 1fr);
}

@media (min-width: 760px) {
  .strip { grid-template-columns: repeat(4, 1fr); }
}

.strip__item {
  aspect-ratio: 3/4;
  overflow: hidden;
  background: var(--hairline-soft);
}

.strip__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1400ms var(--ease-out);
}

.strip__item:hover img { transform: scale(1.03); }

/* ============================================================
   ARTISAN QUARTET — home page 4-panel filmstrip
   Vertical panels with hairline dividers, editorial 01–04
   numbering, Alan Sans names + Crimson Pro cities.
   Random 2 Toronto + 2 LA shuffle per load (via JS).
   ============================================================ */

.artisan-quartet {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  margin-top: var(--s-5);
  margin-bottom: var(--s-4);
  border-top: 0.5px solid var(--hairline);
  border-bottom: 0.5px solid var(--hairline);
}

/* Artisans section — clean cream ground (products.jpg backdrop
   retired after clarification that the user wanted the change
   on Studio Kit, not Artisans). */

.quartet-panel {
  display: flex;
  flex-direction: column;
  border-left: 0.5px solid var(--hairline);
  background: var(--canvas);
  opacity: 0;
  transform: translateY(12px);
  /* 340ms matches FADE_MS in js/artisan-quartet.js — keep them in step
     or the swap will fire before the panel has finished clearing. */
  transition:
    opacity 340ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 620ms cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--qd, 0ms);
}

/* Mid-rotation state. JS adds .is-swapping to one panel at a time on
   a 140ms offset, swaps its contents at the midpoint, then removes
   the class — producing a wave across the row rather than the whole
   quartet blinking out and back in together.

   transition-delay is zeroed here: the --qd stagger belongs to the
   initial reveal. During rotation the offset comes from JS timing,
   and leaving both in place would compound into a visible lag. */
.quartet-panel.is-swapping {
  opacity: 0;
  transform: translateY(8px);
  transition-delay: 0ms;
}

.quartet-panel:first-child {
  border-left: 0;
}

.artisan-quartet.is-visible .quartet-panel {
  opacity: 1;
  transform: translateY(0);
}

/* .is-swapping must beat the .is-visible rule above — same specificity
   would let source order decide, and this needs to win regardless. */
.artisan-quartet.is-visible .quartet-panel.is-swapping {
  opacity: 0;
  transform: translateY(8px);
}

/* ---- Pause control ------------------------------------------
   Sits under the quartet, right-aligned, deliberately quiet. It is
   a compliance affordance (WCAG 2.2.2) rather than a feature, so it
   holds at 45% until hovered or focused. Hidden until JS confirms
   rotation is actually running — no dead control on file:// or for
   reduced-motion visitors. */
.quartet-controls {
  display: flex;
  justify-content: flex-end;
  padding-inline: var(--pad);
  margin-top: calc(var(--s-2) * -1);
  margin-bottom: var(--s-3);
}

.quartet-motion {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: transparent;
  border: 0;
  padding: 8px 2px;
  cursor: pointer;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  opacity: 0.45;
  transition: opacity var(--d-fast) var(--ease-out);
}

.quartet-motion:hover,
.quartet-motion:focus-visible {
  opacity: 1;
}

.quartet-motion:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
}

/* Glyph: two bars (pause) → triangle (play) */
.quartet-motion__glyph {
  display: block;
  width: 8px;
  height: 9px;
  border-left: 2px solid currentColor;
  border-right: 2px solid currentColor;
  box-sizing: border-box;
}

.quartet-motion.is-paused .quartet-motion__glyph {
  width: 0;
  height: 0;
  border-left: 7px solid currentColor;
  border-right: 0;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
}

.quartet-motion.is-paused .quartet-motion__text::after {
  content: 'd';   /* "Pause" → "Paused" */
}

/* Reduced motion — the panels hold their first batch. JS also hides
   the control, so this is belt-and-braces. */
@media (prefers-reduced-motion: reduce) {
  .quartet-panel {
    transition: none;
  }
  .quartet-controls {
    display: none;
  }
}

.quartet-panel__media {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 5;
  text-decoration: none;
  background: var(--blush);
}

.quartet-panel__media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Compositional lock — anchor every face at the upper-third
     so the row reads as a coherent eye-line band. */
  object-position: center 22%;
  /* House wash — Maison-tier LUT applied to every portrait so
     varying source photography lands in the same tonal register.
     ~10% desaturation + a hair more contrast + slight lift. */
  filter: contrast(0.97) saturate(0.90) brightness(1.02);
  transition:
    filter 700ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 1200ms cubic-bezier(0.22, 1, 0.36, 1);
}

.quartet-panel__media:hover img,
.quartet-panel__media:focus-visible img {
  /* Reward engagement — color and contrast snap back on hover */
  filter: none;
  transform: scale(1.03);
}

.quartet-panel__foot {
  /* Vertical stack: name → city → view profile, all left-aligned.
     View Profile is no longer pinned to the right side; it sits on
     its own line below the location for a cleaner editorial column. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: var(--s-3) clamp(var(--s-2), 1.6vw, var(--s-3));
  gap: var(--s-2);
}

.quartet-panel__info {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.quartet-panel__name {
  /* MATCHES THE HEADER (.section-h) typography register — same
     Alan Sans family, same 500 weight, same 0.2em tracking, same
     uppercase case. Sized smaller than --t-display since it sits
     inside a panel rather than as a section heading. */
  font-family: 'Alan Sans', sans-serif;
  font-size: clamp(14px, 1.2vw, 18px);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0;
  line-height: 1.2;
}

.quartet-panel__city {
  /* Match Artists-page register — Alan Sans uppercase eyebrow
     instead of serif lowercase. Reads as a coherent system across
     the home quartet and the /artists/ grid. */
  font-family: 'Alan Sans', sans-serif;
  font-weight: 500;
  font-size: clamp(9px, 0.7vw, 11px);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.3;
}

/* View Profile link — now sits on its own line below the location
   (vertical stack via .quartet-panel__foot flex-direction: column). */
.quartet-panel__view {
  font-size: clamp(10px, 0.78vw, 12px);
  letter-spacing: 0.2em;
  white-space: nowrap;
  align-self: flex-start;
  flex-shrink: 0;
}

/* Legacy number style — retained in case any panel still references
   it during a transition period. Will be removed once all panels
   have been migrated to View Profile. */
.quartet-panel__num {
  font-family: 'Alan Sans', sans-serif;
  font-size: clamp(11px, 0.85vw, 13px);
  font-weight: 400;
  letter-spacing: 0.15em;
  color: var(--ink-soft);
  margin: 0;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 859.98px) {
  .artisan-quartet {
    grid-template-columns: 1fr 1fr;
  }
  .quartet-panel:nth-child(3) {
    border-left: 0;
    border-top: 0.5px solid var(--hairline);
  }
  .quartet-panel:nth-child(4) {
    border-top: 0.5px solid var(--hairline);
  }
}

/* ============================================================
   FILMSTRIP — 8-panel signature work showcase
   Same visual language as artisan-quartet: vertical panels,
   hairline dividers, staggered fade-in reveal. Image-only
   (no caption foot — these are editorial photographs, not
   artist cards).
   ============================================================ */

.filmstrip {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 0;
  border-top: 0.5px solid var(--hairline);
  border-bottom: 0.5px solid var(--hairline);
  margin-block: clamp(var(--s-3), 4vw, var(--s-5));
}

.filmstrip-panel {
  margin: 0;
  border-left: 0.5px solid var(--hairline);
  overflow: hidden;
  aspect-ratio: 2 / 5;
  background: var(--blush);
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity 700ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 700ms cubic-bezier(0.22, 1, 0.36, 1);
  transition-delay: var(--qd, 0ms);
}

.filmstrip-panel:first-child {
  border-left: 0;
}

.filmstrip.is-visible .filmstrip-panel {
  opacity: 1;
  transform: translateY(0);
}

.filmstrip-panel img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1200ms cubic-bezier(0.22, 1, 0.36, 1);
}

.filmstrip-panel:hover img {
  transform: scale(1.03);
}

/* Tutorial variant — Hermès-style watercolor illustrations.
   Uses the original tall-narrow 2/5 panel height (inherited from
   .filmstrip-panel). Object-fit cover anchors the illustrations
   to the upper-third so the numbered step at top stays visible
   and the sides crop gracefully (the illustrations are centrally
   composed, so side-crop preserves the subject). Cream ground
   instead of blush so the line art reads cleanly. */
.filmstrip--tutorial {
  margin-block: clamp(var(--s-2), 3vw, var(--s-4));
}

.filmstrip--tutorial .filmstrip-panel {
  background: var(--canvas);
}

.filmstrip--tutorial .filmstrip-panel img {
  object-fit: cover;
  object-position: center 30%;  /* anchor to upper-third — keeps the numbered "1, 2..." visible */
}

.filmstrip--tutorial .filmstrip-panel:hover img {
  transform: none;  /* technical illustrations don't dance on hover */
}

@media (max-width: 1099.98px) {
  .filmstrip {
    grid-template-columns: repeat(4, 1fr);
  }
  .filmstrip-panel {
    aspect-ratio: 3 / 5;
  }
  .filmstrip-panel:nth-child(5) {
    border-left: 0;
  }
  .filmstrip-panel:nth-child(n+5) {
    border-top: 0.5px solid var(--hairline);
  }
}

@media (max-width: 600px) {
  .filmstrip {
    grid-template-columns: repeat(2, 1fr);
  }
  .filmstrip-panel:nth-child(odd) {
    border-left: 0;
  }
  .filmstrip-panel:nth-child(even) {
    border-left: 0.5px solid var(--hairline);
  }
  .filmstrip-panel:nth-child(n+3) {
    border-top: 0.5px solid var(--hairline);
  }
}

/* ============================================================
   ROSTER SHOWCASE — auto-fading viewer on home page
   Portfolio-style 50/50, cycles through artists with staggered fade
   ============================================================ */

.roster-showcase {
  margin-block: clamp(var(--s-4), 6vw, var(--s-6));
  background: var(--canvas);
}

.roster-showcase__body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: clamp(420px, 52vh, 560px);
}

.roster-showcase__left {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  padding: clamp(var(--s-4), 4vw, var(--s-6));
  gap: var(--s-1);
}

.roster-showcase__portrait {
  width: clamp(112px, 13vw, 168px);
  height: clamp(112px, 13vw, 168px);
  overflow: hidden;
  background: var(--hairline-soft);
  margin-bottom: var(--s-3);
}

.roster-showcase__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 600ms var(--ease-out);
}

.roster-showcase__name {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  margin: 0 0 0.25rem;
  transition: opacity 600ms var(--ease-out);
}

.roster-showcase__city {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0;
  transition: opacity 600ms var(--ease-out);
}

.roster-showcase__right {
  overflow: hidden;
  background: var(--hairline-soft);
  min-height: 320px;
}

.roster-showcase__right img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 600ms var(--ease-out);
}

/* Staggered fade-out — portrait first, text next, work image last */
.roster-showcase.is-fading .roster-showcase__portrait img { opacity: 0; transition-delay: 0ms; }
.roster-showcase.is-fading .roster-showcase__name          { opacity: 0; transition-delay: 80ms; }
.roster-showcase.is-fading .roster-showcase__city          { opacity: 0; transition-delay: 80ms; }
.roster-showcase.is-fading .roster-showcase__right img     { opacity: 0; transition-delay: 160ms; }

@media (max-width: 880px) {
  .roster-showcase__body { grid-template-columns: 1fr; }
  .roster-showcase__right { order: -1; aspect-ratio: 4/3; min-height: auto; }
}

/* ---- 11. TORONTO / to / L.A. black banner statement ------- */
.statement-banner {
  background: var(--ink);
  color: var(--canvas);
  padding: clamp(var(--s-4), 7vw, var(--s-6)) var(--pad);
  margin-block: clamp(var(--s-3), 4vw, var(--s-5));
  text-align: center;
}

.statement-banner .statement {
  color: var(--canvas);
}

.statement-banner .statement em {
  color: rgba(252, 251, 249, 0.6);
  font-family: 'Crimson Pro', Georgia, serif;
  font-style: normal;
}

/* ---- 12. Artist roster grid ------------------------------- */
.roster {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 700px) {
  .roster { grid-template-columns: repeat(2, 1fr); gap: var(--s-3); }
}

@media (min-width: 1000px) {
  .roster { grid-template-columns: repeat(4, 1fr); }
}

.artist {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
}

.artist__media {
  aspect-ratio: 4/5;
  overflow: hidden;
  background: var(--hairline-soft);
  margin-bottom: var(--s-2);
}

.artist__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1200ms var(--ease-out);
}

.artist:hover .artist__media img { transform: scale(1.03); }

.artist__name {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-body-lg);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0 0 0.25rem;
}

.artist__meta {
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 var(--s-2);
}

.artist__cta {
  margin-top: auto;
}

/* ---- 13. Studio Kit — single product image w/ centered CTA overlay
   Image is height-constrained so the whole composition fits the
   viewport without scroll. CTA sits dead-center, overlaying the
   image. A soft porcelain radial gradient sits behind the image —
   a Maison halo, not a hard block. */
/* FULL-BLEED Studio Kit — image spans 100vw using the negative-margin
   escape pattern (left: 50% + margin-left: -50vw). The image renders at
   natural aspect ratio so a wide cinematic flat-lay stays cinematic.
   The pink halo was scaffolding for a constrained image; the full-bleed
   composition IS the visual now, so the gradient is retired. */
.kit-hero {
  position: relative;
  /* CONTENT-FIT GRID: section sizes to its content (copy + capped
     image) rather than locking to 100dvh. The previous viewport-fit
     created dead space below the image on any viewport taller than
     ~700px because the image is capped at max-height: 520px and
     align-content: center distributed the leftover as air.
     Padding clamps now do all the breathing-room work. */
  display: grid;
  grid-template-rows: auto auto;
  grid-template-areas:
    "copy"
    "media";
  justify-items: center;
  gap: clamp(var(--s-3), 3vw, var(--s-4));
  width: 100%;
  max-width: none;
  margin: 0;
  padding: clamp(var(--s-4), 5vw, var(--s-6)) var(--pad);
  overflow: hidden;
  background: var(--canvas);
}

/* Porcelain halo — a soft, diffused radial of --porcelain (#FFF7FA)
   sitting BEHIND the product image. Sized to the image cell so the
   wash doesn't bleed into the section padding. Heavy blur diffuses
   the edges into the canvas, reading as natural studio light spill
   rather than a hard color block. Pointer-events disabled so the
   CTA stays clickable through it.

   Behavioral psychology note: warm-pink in luxury bridal triggers
   the "boudoir / intimate ceremony" association without using a
   wedding-photography cliché. Subtle is the whole point — Brunello
   Cucinelli, not Bridal Barbie. */
.kit-hero::before {
  content: '';
  grid-area: media;
  justify-self: center;
  align-self: center;
  /* Sized slightly larger than the image so the glow extends past
     the product edges, creating a halo not a backdrop. */
  width: clamp(360px, 70vw, 760px);
  height: clamp(360px, 70vw, 760px);
  background: radial-gradient(
    ellipse at center,
    rgba(255, 220, 232, 0.55) 0%,
    rgba(255, 235, 242, 0.30) 30%,
    rgba(255, 247, 250, 0.12) 55%,
    transparent 75%
  );
  filter: blur(28px);
  z-index: 0;
  pointer-events: none;
}

/* Image sits in the MEDIA grid cell, CTA shares the same cell
   and overlays centered. max-height caps so the whole composition
   fits inside a single viewport. */
.kit-hero__img {
  display: block;
  width: auto;
  max-width: 100%;
  height: auto;
  max-height: clamp(280px, 50vh, 520px);
  object-fit: contain;
  object-position: center;
  position: relative;
  z-index: 1;
  margin: 0;
  padding: 0;
  grid-area: media;
}

/* Image hygiene — block-level, no inline-image baseline gap. The
   nuclear "padding-bottom: 0" + footer pull-up has been retired:
   the Studio Kit now wants SYMMETRIC breathing room above and below
   the image, so the section's own padding clamp does the work and
   the footer sits at its natural position. */
#kit .kit-hero__img {
  display: block;
  vertical-align: top;
  font-size: 0;
  line-height: 0;
}

/* Overlay wrapper — display:contents so its children become direct
   grid items of .kit-hero. Editorial-block lands in 'copy' area at
   top; CTA lands in 'media' area on top of the image (same cell)
   with higher z-index. */
.kit-hero__overlay {
  display: contents;
}

.kit-hero__overlay .editorial-block {
  grid-area: copy;
  width: 100%;
  max-width: 720px;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(var(--s-2), 2vw, var(--s-3));
}

.kit-hero__overlay .kit-hero__cta {
  /* CTA OVERLAYS the image — same grid cell as the image, but
     z-index puts it on top. align/justify keep it centered. */
  grid-area: media;
  z-index: 2;
  align-self: center;
  justify-self: center;
}

/* Re-enable clicks on the CTA itself */
.kit-hero__overlay .kit-hero__cta,
.kit-hero__overlay .editorial-block {
  pointer-events: auto;
}

/* Editorial block — centered text, no bottom margin (the gap is
   handled by the overlay's flex gap). */
.kit-hero__overlay .editorial-block {
  margin: 0;
  gap: var(--s-3);
}

/* Studio Kit CTA — text-link sitting directly below the copy. The
   subtle canvas backing carried from before keeps the link readable
   against any busy product backdrop. */
.kit-hero__cta {
  display: inline-block;
  padding: 14px 26px;
  background: rgba(252, 251, 249, 0.86);
  backdrop-filter: blur(12px) saturate(115%);
  -webkit-backdrop-filter: blur(12px) saturate(115%);
  border: 0.5px solid rgba(10, 9, 8, 0.06);
  white-space: nowrap;
}

/* Mobile — overlay STAYS positioned over the image (image now fills
   100dvh so there's plenty of vertical room for copy + CTA over
   the product backdrop). The stacked-below treatment retired. */
@media (max-width: 768px) {
  .kit-hero__overlay {
    /* Keep absolute, but tighten the top anchor + padding for phones */
    top: clamp(var(--s-4), 8vw, var(--s-5));
    gap: var(--s-3);
  }
  .kit-hero__cta {
    padding: 12px 20px;
    font-size: 0.7rem;
  }
}

.kit__item {
  width: 100%;
  aspect-ratio: 1/1.6;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--s-1);
}

.kit__item img {
  max-width: 90%;
  max-height: 90%;
  object-fit: contain;
  mix-blend-mode: multiply;
}

/* ---- 14. Close-up duo (Inquire lead-in) ------------------- */
.duo {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0;
}

.duo__item {
  aspect-ratio: 4/5;
  overflow: hidden;
  background: var(--hairline-soft);
}

@media (max-width: 699.98px) {
  .duo { grid-template-columns: 1fr; }
  .duo__item { aspect-ratio: 4/3; }
}

.duo__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 1400ms var(--ease-out);
}

.duo__item:hover img { transform: scale(1.03); }

/* ---- 15. Inquire — soft blush canvas, two columns -------- */
.inquire {
  background: linear-gradient(180deg, var(--blush) 0%, var(--blush-soft) 100%);
  padding-block: clamp(var(--s-5), 7vw, var(--s-6));
}

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

@media (min-width: 900px) {
  .inquire__grid {
    grid-template-columns: 1fr 1.2fr;
    gap: clamp(var(--s-4), 5vw, var(--s-6));
    align-items: start;
  }
}

.inquire__intro h2 {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0 0 var(--s-2);
  text-align: left;
}

.inquire__intro .body-prose {
  text-align: left;
  margin-inline: 0;
  color: var(--ink);
  max-width: 38ch;
}

.inquire__note {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid rgba(10, 9, 8, 0.12);
}

.inquire__note h3 {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-weight: 500;
  margin: 0 0 var(--s-1);
}

.inquire__note p {
  font-size: var(--t-body-sm);
  color: var(--ink-soft);
  margin: 0;
  line-height: 1.55;
}

/* ---- 16. Form — bordered boxes on blush ------------------ */
.form {
  display: grid;
  gap: var(--s-3);
}

/* Compact modifier — tighter rows for modal / inline contexts */
.form--compact { gap: var(--s-2); }
.form--compact .form__group { gap: 4px; }
.form--compact input,
.form--compact textarea,
.form--compact select { padding: 0.5rem 0.75rem; }
.form--compact textarea { min-height: 56px; }

.form__hint {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--ink-soft);
  margin: 0;
  letter-spacing: 0.01em;
}

.form__req {
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--whisper-dk, #9E6D4F);
  margin-left: 0.375rem;
}

/* Native select styled to match input fields */
.form select {
  appearance: none;
  -webkit-appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%),
                    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
  background-position: calc(100% - 16px) 50%, calc(100% - 11px) 50%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 2rem;
  cursor: pointer;
}

.form select:focus {
  background-image: linear-gradient(45deg, transparent 50%, var(--ink) 50%),
                    linear-gradient(135deg, var(--ink) 50%, transparent 50%);
}

/* Modal-specific compaction so the inquiry fits in viewport */
.modal .form { gap: var(--s-2); }
.modal .form input,
.modal .form textarea,
.modal .form select { padding: 0.5rem 0.75rem; }
.modal .form textarea { min-height: 56px; }
.modal .form__check-list {
  flex-direction: row;
  flex-wrap: wrap;
  gap: 8px;
}
.modal .form__check { gap: 6px; padding: 4px 0; }

.form h2 {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0 0 var(--s-2);
}

.form__group {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}

.form label {
  font-size: var(--t-body-sm);
  color: var(--ink);
  letter-spacing: 0.01em;
  text-transform: none;
  font-weight: 400;
}

.form input,
.form textarea,
.form select {
  background: var(--canvas-pure);
  border: 1px solid rgba(10, 9, 8, 0.18);
  padding: 0.75rem 0.875rem;
  color: var(--ink);
  font-size: var(--t-body);
  border-radius: 0;
  font-family: inherit;
  transition: border-color var(--d-fast) var(--ease-out);
  width: 100%;
}

.form input::placeholder,
.form textarea::placeholder { color: rgba(10, 9, 8, 0.35); }

.form input:focus,
.form textarea:focus,
.form select:focus {
  outline: none;
  border-color: var(--ink);
}

.form textarea { resize: vertical; min-height: 84px; }

/* Checkbox-style milestone list */
.form__check-list {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  padding: 0;
  margin: 0;
  list-style: none;
}

.form__check {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  cursor: pointer;
}

.form__check input {
  appearance: none;
  width: 14px;
  height: 14px;
  border: 1px solid rgba(10, 9, 8, 0.4);
  background: var(--canvas-pure);
  margin: 0;
  cursor: pointer;
  position: relative;
  flex-shrink: 0;
}

.form__check input:checked {
  background: var(--ink);
  border-color: var(--ink);
}

.form__check input:checked::after {
  content: '';
  position: absolute;
  inset: 2px 3px;
  border-right: 1.5px solid var(--canvas);
  border-bottom: 1.5px solid var(--canvas);
  transform: rotate(45deg) translate(-1px, -1px);
}

.form__check span {
  font-size: var(--t-body-sm);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-weight: 500;
}

.form__check input:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.form__agree {
  display: flex;
  align-items: flex-start;
  gap: var(--s-2);
  font-size: var(--t-body-sm);
  color: var(--ink-soft);
  cursor: pointer;
}

.form__agree input {
  margin-top: 3px;
}

.form__success {
  padding: var(--s-4);
  border: 1px solid var(--ink);
  background: var(--canvas-pure);
  display: none;
}

.form__success.is-visible { display: block; }

/* ---- 17. Footer — Dark Maison colophon -------------------
   White text on ink. Newsletter form + three link columns,
   oversized city masthead, italic 'by wrkby.com' signature.
   Header type 1pt smaller, body 1.5pt smaller. text-wrap:
   pretty so paragraph copy never breaks with a widow. */

.footer {
  background: var(--ink);
  color: var(--canvas);
  /* Tightened top + bottom — was clamp(--s-6, 8vw, --s-8) on top */
  padding: clamp(var(--s-4), 5vw, var(--s-5)) 0 var(--s-3);
}

.footer__inner {
  max-width: 1400px;
  margin: 0 auto;
  padding-inline: var(--pad);
}

/* ===========================================================
   FOOTER INSTAGRAM STRIP — Monastery-style horizontal feed
   Sits at the top of the footer. Handle label (Alan Sans
   uppercase, tracked) sits above a dense row of square thumbs
   that scrolls horizontally on every viewport. White text /
   thin white hairlines over the ink ground. Thumbnails are
   placeholders until real Instagram pulls are wired.
   =========================================================== */
.footer__ig {
  padding-block: clamp(var(--s-4), 4vw, var(--s-6))
                 clamp(var(--s-4), 4vw, var(--s-5));
}

.footer__ig-label {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  /* Pink accent — the Maison porcelain token rendered on the
     ink-black footer ground reads as a quiet rose. Ties the social
     handle to the same pink register used on the Service Menu bar
     and CTA hover states. */
  color: var(--porcelain);
  opacity: 1;
  margin: 0 0 clamp(var(--s-2), 1.4vw, var(--s-3));
  text-decoration: none;
  display: inline-block;
  transition: color var(--d-fast) var(--ease-out);
}

.footer__ig-label:hover {
  color: #FFE6EE;  /* slightly deeper pink on hover */
}

/* Strip viewport — FULL BLEED, edge-to-edge, no fade.
   Escapes any parent footer container via 100vw + negative-margin
   so the strip touches the viewport edges on both sides. The track
   inside is what moves; the strip itself stays static. */
.footer__ig-strip {
  position: relative;
  overflow: hidden;
  /* Break out of .footer__inner constraint to span full viewport */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
}

/* Track — holds the tiles and animates left in a continuous loop.
   Tiles are cloned by JS so the -50% translate produces a seamless
   wrap (the second half is identical to the first). */
.footer__ig-track {
  display: flex;
  /* Roughly doubled from clamp(12–18px). Wider intervals are what make
     each frame read as a chosen image rather than a contact sheet —
     the gap is doing as much work here as the tile size. */
  gap: clamp(20px, 2vw, 36px);
  width: max-content;
  /* Duration scales with track length, not independently. Tile plus gap
     went from ~238px to ~311px at desktop (≈1.31×), so the loop runs
     1.31× longer to hold the same perceived velocity (~40px/sec). Left
     at 110s the marquee would visibly speed up. */
  animation: ig-marquee 145s linear infinite;
  will-change: transform;
}

/* Pause on hover — luxury convention; lets the bride dwell on a tile */
.footer__ig-strip:hover .footer__ig-track,
.footer__ig-track:focus-within {
  animation-play-state: paused;
}

@keyframes ig-marquee {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Reduced-motion users: stop the marquee, let them scroll manually. */
@media (prefers-reduced-motion: reduce) {
  .footer__ig-track { animation: none; }
  .footer__ig-strip { overflow-x: auto; }
}

.footer__ig-tile {
  /* +25% over clamp(160–220px). Fewer frames on screen at once, each
     one larger — the row reads as a curated selection rather than a
     feed sample. On a 1920px viewport this drops the visible count
     from roughly eight tiles to five. */
  flex: 0 0 clamp(200px, 21vw, 275px);
  aspect-ratio: 1 / 1;
  /* Faint white tile until the real thumbnail loads / replaces it.
     The 0.06 fill + 0.08 hairline give the placeholder a quiet
     architectural presence rather than an empty box. */
  background: rgba(255, 255, 255, 0.06);
  border: 0.5px solid rgba(255, 255, 255, 0.08);
  overflow: hidden;
  display: block;
  position: relative;
  transition: opacity var(--d-base) var(--ease-out);
}

.footer__ig-tile:hover {
  opacity: 0.78;
}

.footer__ig-tile img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

/* Thin rule below the IG strip — same hairline weight as other
   footer dividers. Separates the editorial feed from the link cols. */
.footer__ig + .footer__cols {
  padding-top: clamp(var(--s-4), 4vw, var(--s-5));
  border-top: 0.5px solid rgba(255, 255, 255, 0.10);
}

/* Three equal columns since the newsletter was removed. The old
   1.4fr first column existed to give the signup form room; with three
   link lists of similar length, equal columns read as composed and a
   wider first column would look like a mistake. */
.footer__cols {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(var(--s-3), 3vw, var(--s-5));
  align-items: start;
}

.footer__h {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.75rem;              /* 12px — bumped up from 10px */
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--canvas);
  margin: 0 0 var(--s-2);
}

.footer__lede {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 1rem;                 /* 16px — bumped up from 14px */
  line-height: 1.35;               /* tighter — was 1.55 */
  letter-spacing: 0.01em;
  color: rgba(252, 251, 249, 0.72);
  margin: 0 0 var(--s-3);
  max-width: 32ch;
  text-wrap: pretty;
}

.footer__list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;                        /* tightened from 6px */
}

.footer__list a {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 1rem;                 /* 16px — bumped up */
  line-height: 1.2;                /* tightened from 1.35 */
  letter-spacing: 0.01em;
  color: rgba(252, 251, 249, 0.72);
  text-decoration: none;
  transition: color var(--d-fast) var(--ease-out);
}

.footer__list a:hover {
  color: var(--canvas);
}

/* Newsletter form — single input + arrow submit, no chrome */
.footer__form {
  margin-top: var(--s-2);
}

.footer__form-row {
  display: flex;
  align-items: center;
  border-bottom: 0.5px solid rgba(252, 251, 249, 0.4);
  padding-bottom: 6px;
  transition: border-color var(--d-fast) var(--ease-out);
}

.footer__form-row:focus-within {
  border-bottom-color: var(--canvas);
}

.footer__input {
  flex: 1;
  background: transparent;
  border: 0;
  color: var(--canvas);
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.875rem;             /* 14px */
  letter-spacing: 0.01em;
  padding: 4px 0;
  outline: none;
}

.footer__input::placeholder {
  color: rgba(252, 251, 249, 0.42);
}

/* White square with black chevron — replaces the previous ↗ arrow.
   Sharp corners, sits flush at the end of the input. The square
   anchors to the bottom of the row and offsets the row's
   padding-bottom (6px) so its lower edge sits EXACTLY on the
   hairline rule below the input — reads as a button resting on
   the line, not floating above it. */
.footer__submit {
  background: var(--canvas);
  color: var(--ink);
  border: 0;
  width: 30px;
  height: 30px;
  cursor: pointer;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  flex-shrink: 0;
  margin-left: 8px;
  align-self: flex-end;
  margin-bottom: -6px;
  transition: background var(--d-fast) var(--ease-out);
}

.footer__submit svg {
  width: 12px;
  height: 12px;
  display: block;
}

.footer__submit:hover,
.footer__submit:focus-visible {
  background: var(--porcelain);
}

/* Hairline rules in dark mode */
.footer__rule {
  border: 0;
  border-top: 0.5px solid rgba(252, 251, 249, 0.16);
  /* Tightened from clamp(--s-5, 6vw, --s-7) — was creating large
     vertical gaps between the cols, brandmark, masthead, and bottom row. */
  margin: clamp(var(--s-3), 3vw, var(--s-4)) 0;
}

/* Centered Judy Lim wordmark between the columns and the cities line */
.footer__brandmark {
  text-align: center;
  /* Tightened — was clamp(--s-3, 4vw, --s-5) */
  margin-bottom: clamp(var(--s-2), 2vw, var(--s-3));
}

.footer__brandmark-img {
  display: inline-block;
  width: auto;
  /* Slightly larger — the brandmark needs more presence in the
     dark footer composition without going display-loud. */
  height: clamp(28px, 3vw, 42px);
  opacity: 0.92;
}

/* Cities line — same scale as the section headers, centered.
   Pulled back from the oversized masthead so the footer stays
   in the Maison register (premium-quiet, not display-loud). */
.footer__masthead {
  text-align: center;
  padding: 0;
}

.footer__cities {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-size: 0.75rem;           /* 12px — matches .footer__h */
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--canvas);
  margin: 0;
  line-height: 1.2;             /* tightened from 1.6 */
}

/* Bottom row — 3-column grid: copyright LEFT, social icons CENTER,
   wrkby.com RIGHT. Copyright + credit both in Alan Sans (no italic
   Crimson) so the bottom band reads as one typographic system. */
.footer__bottom {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-3);
}

.footer__copy {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-style: normal;
  font-size: 0.625rem;          /* 10px — 2pt smaller than 12px */
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: rgba(252, 251, 249, 0.45);
  margin: 0;
  justify-self: start;
}

.footer__credit {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-style: normal;
  font-size: 0.625rem;          /* 10px — 2pt smaller */
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: rgba(252, 251, 249, 0.45);
  text-decoration: none;
  transition: color var(--d-fast) var(--ease-out);
  justify-self: end;
}

.footer__credit:hover {
  color: var(--canvas);
}

.footer__social {
  display: flex;
  gap: clamp(var(--s-2), 2vw, var(--s-3));
  align-items: center;
  justify-self: center;
}

.footer__social a {
  color: var(--canvas);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0.85;
  transition: opacity var(--d-fast) var(--ease-out);
}

.footer__social a:hover {
  opacity: 1;
}

.footer__social svg {
  width: 22px;
  height: 22px;
  display: block;
}

/* Linktree is taller than wide (27:33 aspect); render at slightly
   smaller height to match the optical weight of IG + TT. */
.footer__social a[aria-label^="Linktree"] svg {
  width: 16px;
  height: 20px;
}

/* Responsive */
@media (max-width: 859.98px) {
  .footer__cols {
    grid-template-columns: 1fr 1fr;
    gap: var(--s-4);
  }
}

@media (max-width: 500px) {
  .footer__cols {
    grid-template-columns: 1fr;
  }
  .footer__bottom {
    grid-template-columns: 1fr;
    gap: var(--s-2);
    text-align: center;
  }
  .footer__copy,
  .footer__credit {
    justify-self: center;
  }
}

/* ---- 18. Motion design ------------------------------------ */

/* (a) Scroll reveal — simple fade-up for individual elements.
   Slowed slightly + lifted starting offset so the motion reads as
   considered rather than reactive. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 760ms var(--ease-out),
              transform 760ms var(--ease-out);
  transition-delay: 120ms;
  will-change: opacity, transform;
}

.reveal.is-visible { opacity: 1; transform: none; }

/* (b) Staggered editorial-block — eyebrow → heading → subline → CTA cascade.
   Bigger increments (180ms vs the old 100ms) and a base delay before the
   first element so the cascade has a beat before it begins. Slower per-
   element transition (780ms) so each line lands rather than flickers. */
.editorial-block > * {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity 780ms var(--ease-out),
              transform 780ms var(--ease-out);
  will-change: opacity, transform;
}

.editorial-block.is-visible > *:nth-child(1) { transition-delay:  140ms; }
.editorial-block.is-visible > *:nth-child(2) { transition-delay:  320ms; }
.editorial-block.is-visible > *:nth-child(3) { transition-delay:  500ms; }
.editorial-block.is-visible > *:nth-child(4) { transition-delay:  680ms; }
.editorial-block.is-visible > *:nth-child(5) { transition-delay:  860ms; }

.editorial-block.is-visible > * {
  opacity: 1;
  transform: translateY(0);
}

/* (c) Clip-path mask reveal — Cartier's signature unveiling */
.clip-reveal {
  clip-path: inset(0 0 100% 0);
  -webkit-clip-path: inset(0 0 100% 0);
  transition: clip-path 850ms cubic-bezier(0.65, 0, 0.35, 1),
              -webkit-clip-path 850ms cubic-bezier(0.65, 0, 0.35, 1);
  will-change: clip-path;
}

.clip-reveal.is-revealed {
  clip-path: inset(0 0 0 0);
  -webkit-clip-path: inset(0 0 0 0);
}

/* (d) Parallax — image scaled slightly to prevent edge gaps when translated */
[data-parallax] {
  transform: translateY(0) scale(1.08);
  transform-origin: center center;
  will-change: transform;
}

.hero__media [data-parallax],
.fullbleed-img [data-parallax] {
  /* Already absolute-positioned in their containers — just need the scale */
}

/* (e) Body holds while Lenis takes over — prevents native scroll flash */
html.lenis-loaded { overflow: hidden; }
html.lenis-loaded body { overflow: visible; }

/* ============================================================
   PRICING PAGE — two artist lanes (Judy / Studio), addons, notes
   ============================================================ */

/* Two-lane grid: each lane is one artist menu. Featured lane (Judy)
   gets the porcelain halo + ink border treatment. */
.pricing-lanes {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: 1fr;
  margin-top: var(--s-5);
}

@media (min-width: 860px) {
  .pricing-lanes {
    grid-template-columns: 1fr 1fr;
    gap: clamp(var(--s-3), 3vw, var(--s-5));
    align-items: stretch;
  }
}

.pricing-lane {
  background: var(--canvas-pure);
  border: 0.5px solid var(--hairline);
  padding: clamp(var(--s-4), 4vw, var(--s-6));
  display: flex;
  flex-direction: column;
  position: relative;
}

.pricing-lane--featured {
  border: 1px solid var(--ink);
  background: var(--canvas-pure);
  /* Pink halo BEHIND the card via blurred box-shadow rings — the
     card itself stays solid cream, the pink only blooms outside. */
  box-shadow:
    0 0 40px 8px rgba(255, 247, 250, 0.95),
    0 0 80px 24px rgba(255, 247, 250, 0.55),
    0 0 140px 50px rgba(255, 247, 250, 0.28);
}

.pricing-lane__head {
  margin-bottom: clamp(var(--s-3), 3vw, var(--s-4));
}

.pricing-lane__eyebrow {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 var(--s-2);
}

.pricing-lane__name {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-size: clamp(1.25rem, 2vw, 1.625rem);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 var(--s-2);
  line-height: 1.2;
}

.pricing-lane__desc {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  margin: 0;
}

/* Commission menu — vertical list of commission types within a lane */
.pricing-commissions {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--s-3);
  border-top: 0.5px solid var(--hairline);
}

.pricing-commission {
  padding: clamp(var(--s-3), 2.5vw, var(--s-4)) 0;
  border-bottom: 0.5px solid var(--hairline);
}

.pricing-commission__label {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.6875rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 8px;
  line-height: 1;
}

.pricing-commission__price {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-size: clamp(1.125rem, 1.8vw, 1.4rem);
  letter-spacing: 0.03em;
  line-height: 1.2;
  color: var(--ink);
  margin: 0 0 6px;
}

.pricing-commission__meta {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 400;
  font-size: 0.9375rem;        /* 15px — was 13px, +2pt */
  color: var(--ink);            /* black, was ink-soft */
  letter-spacing: 0.03em;
  margin: 0;
  line-height: 1.4;
}

/* Service menu — collapsed by default behind <details>/<summary> */
.pricing-menu {
  margin: 0 0 var(--s-3);
}

/* PORCELAIN HIGHLIGHT BAR — same pink-wash treatment used for the
   CTA hover state and Studio Kit halo. Applied to both pricing-lane
   service menus so the disclosure surfaces read as "open this for
   more" without competing with the rest of the card chrome. */
.pricing-menu > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-2);
  /* Bigger horizontal + vertical padding inside the porcelain bar */
  padding: var(--s-3) var(--s-3);
  background: var(--porcelain);
  border: 0.5px solid rgba(10, 9, 8, 0.06);
  transition: background var(--d-fast) var(--ease-out);
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  border-bottom: 0.5px solid var(--hairline);
  transition: opacity var(--d-fast) var(--ease-out);
}

.pricing-menu > summary::-webkit-details-marker {
  display: none;
}

.pricing-menu > summary:hover {
  background: #FFEEF4;  /* slightly deeper pink on hover */
}

/* Hairline rule restored under summary when CLOSED so it reads as a
   section divider; removed when OPEN since the body content
   takes over visually. */
.pricing-menu:not([open]) > summary {
  border-bottom: 0.5px solid rgba(10, 9, 8, 0.08);
}

.pricing-menu__chev {
  display: inline-flex;
  font-size: 1rem;
  line-height: 1;
  transition: transform var(--d-base) var(--ease-out);
  color: var(--ink-soft);
}

.pricing-menu[open] .pricing-menu__chev {
  transform: rotate(90deg);
}

.pricing-menu__body {
  padding: var(--s-3) 0 0;
}

.pricing-menu__heading {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.5625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--ink-soft);
  margin: var(--s-2) 0 8px;
}

.pricing-menu__heading:first-child {
  margin-top: 0;
}

.pricing-menu__list {
  margin: 0 0 var(--s-3);
  padding: 0;
}

.pricing-menu__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-2);
  padding: 6px 0;
  border-bottom: 0.5px solid var(--hairline-soft);
}

.pricing-menu__row dt {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.875rem;
  color: var(--ink-soft);
  letter-spacing: 0.01em;
  margin: 0;
}

.pricing-menu__row dd {
  font-family: 'Crimson Pro', serif;
  font-weight: 400;
  font-size: 0.875rem;
  color: var(--ink);
  margin: 0;
  text-align: right;
  white-space: nowrap;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}

.pricing-lane__footnote {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 400;
  font-style: normal;
  font-size: 0.75rem;           /* same size as before */
  color: var(--ink-soft);
  line-height: 1.45;
  margin: 0 0 var(--s-3);
  letter-spacing: 0.04em;
}

.pricing-lane__cta {
  margin-top: auto;
  align-self: flex-start;
  padding-top: var(--s-2);
}

.tier {
  background: var(--canvas-pure);
  border: 0.5px solid var(--hairline);
  padding: clamp(var(--s-4), 3vw, var(--s-5));
  display: flex;
  flex-direction: column;
  position: relative;
  transition: border-color var(--d-base) var(--ease-out);
}

.tier--featured {
  border: 1px solid var(--ink);
  /* Porcelain halo — same family as the Studio Kit gradient.
     Brightest at the top-center near the "MOST COMMISSIONED" badge,
     fading down through the card toward the canvas base. */
  background:
    radial-gradient(
      ellipse 110% 60% at 50% 18%,
      var(--porcelain) 0%,
      rgba(255, 247, 250, 0.5) 38%,
      transparent 78%
    ),
    var(--canvas-pure);
}

.tier--featured::before {
  content: 'Most Commissioned';
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  background: var(--ink);
  color: var(--canvas);
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.28em;
  padding: 0.5rem 0.875rem;
  white-space: nowrap;
}

.tier__eyebrow {
  font-family: 'Alan Sans', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 var(--s-2);
}

.tier__name {
  font-family: 'Alan Sans', sans-serif;
  font-size: 1.125rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  line-height: 1.3;
  margin: 0 0 var(--s-3);
  color: var(--ink);
}

.tier__price {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 400;
  font-style: normal;
  font-size: clamp(1.75rem, 3vw, 2.5rem);
  line-height: 1;
  margin: 0 0 var(--s-2);
  color: var(--ink);
}

.tier__currency {
  font-family: 'Alan Sans', sans-serif;
  font-size: var(--t-eyebrow);
  font-style: normal;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin-left: 0.5rem;
  vertical-align: middle;
}

.tier__desc {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: 0 0 var(--s-3);
}

.tier__divider {
  border: 0;
  border-top: 0.5px solid var(--hairline);
  margin: var(--s-2) 0 var(--s-3);
}

.tier__best {
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 var(--s-1);
}

.tier__best-value {
  font-family: 'Crimson Pro', serif;
  font-weight: 400;
  font-size: 0.9375rem;
  font-style: normal;
  color: var(--ink);
  margin: 0 0 var(--s-3);
  line-height: 1.4;
}

.tier__included-label {
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 var(--s-2);
}

.tier__included {
  list-style: none;
  padding: 0;
  margin: 0 0 var(--s-4);
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}

.tier__included li {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.9375rem;
  line-height: 1.45;
  letter-spacing: 0.01em;
  color: var(--ink);
  padding-left: 1rem;
  position: relative;
}

.tier__included li::before {
  content: '—';
  position: absolute;
  left: 0;
  color: var(--ink-soft);
}

.tier__cta {
  margin-top: auto;
  /* As a text-link (was filled button) — no full-width stretch.
     Aligned to the left, sitting on its own line below the bullets. */
  align-self: flex-start;
  padding-top: var(--s-2);
}

/* Dual-artist pricing split — side-by-side columns showing
   With Judy / With Studio rates. Single-column variant for
   destination (Judy only). Hairlines top + bottom for the band. */
.tier__pricing-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  margin: 0 0 var(--s-3);
  border-top: 0.5px solid var(--hairline);
  border-bottom: 0.5px solid var(--hairline);
}

.tier__pricing-split--single {
  grid-template-columns: 1fr;
}

.tier__pricing-label {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 0.5625rem;       /* 9px */
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 4px;
  line-height: 1;
}

.tier__pricing-value {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 400;
  font-size: clamp(1.25rem, 2vw, 1.625rem);
  line-height: 1.15;
  color: var(--ink);
  margin: 0;
}

.tier__pricing-meta {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.75rem;
  color: var(--ink-soft);
  margin: 6px 0 0;
  letter-spacing: 0.01em;
}

/* Service menu — inline table on the Bridal tier showing all
   service compositions paired across both artist tiers */
.tier__service-menu {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--s-3);
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.875rem;
  color: var(--ink);
}

.tier__service-menu th,
.tier__service-menu td {
  padding: 6px 0;
  text-align: left;
  font-weight: 300;
  letter-spacing: 0.01em;
  vertical-align: baseline;
}

.tier__service-menu th {
  color: var(--ink);
  width: 56%;
  font-size: 0.875rem;
}

.tier__service-menu td {
  width: 22%;
  text-align: right;
  color: var(--ink);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
}

.tier__service-menu tr + tr th,
.tier__service-menu tr + tr td {
  border-top: 0.5px solid var(--hairline-soft);
}

.tier__service-menu-caption {
  caption-side: bottom;
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.5625rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-align: left;
  margin-top: 8px;
}

/* Footnote at the bottom of each tier — small, muted */
.tier__footnote {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-style: italic;
  font-size: 0.75rem;
  color: var(--ink-soft);
  line-height: 1.4;
  margin: 0 0 var(--s-3);
  letter-spacing: 0.01em;
}

/* Thank You page — hero cycle as background, centered overlay card.
   Card uses the same chrome as the contact inquiry-card (cream-on-glass,
   sharp corners, hairline border). The hero takes the full viewport
   height (minus a comfortable nav clearance) so the card sits in the
   center of a moving editorial backdrop. */
.thank-you-hero {
  min-height: 100vh;
  min-height: 100svh;
  position: relative;
  background: var(--canvas);
}

.thank-you-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--nav-clear) clamp(var(--s-3), 4vw, var(--s-5)) clamp(var(--s-5), 6vw, var(--s-7));
}

.thank-you-card {
  max-width: 640px;
  width: 100%;
  text-align: center;
  /* See-through cream — lets the hero portrait read through, with
     heavy backdrop blur to soften the image behind the text so the
     ink-on-cream prose stays readable across all 8 cycling slides. */
  background: rgba(252, 251, 249, 0.62);
  backdrop-filter: blur(24px) saturate(120%);
  -webkit-backdrop-filter: blur(24px) saturate(120%);
  border: 0.5px solid rgba(10, 9, 8, 0.10);
  border-radius: 0;
  padding: clamp(var(--s-5), 5vw, var(--s-7)) clamp(var(--s-4), 5vw, var(--s-6));
}

.thank-you-card .eyebrow {
  margin-bottom: var(--s-3);
}

.thank-you-card .inquiry-panel__title {
  margin: 0 0 var(--s-3);
  font-size: clamp(1.5rem, 3vw, 2rem);
}

.thank-you-card .inquiry-panel__sub {
  max-width: 48ch;
  margin-inline: auto;
  text-align: center;
  margin-bottom: clamp(var(--s-4), 4vw, var(--s-5));
}

.thank-you-card__nav {
  display: flex;
  justify-content: center;
}

/* Editorial couple — sized so it shares a single viewport with the
   CTA section beneath it. Top padding matches the Fine Print
   section so the rhythm above the image is consistent across the
   page's editorial breaks. */
.pricing-editorial {
  padding-top: clamp(var(--s-4), 5vw, var(--s-5));
  padding-bottom: clamp(var(--s-3), 3vw, var(--s-4));
}

.pricing-editorial__img {
  max-width: 640px;
  margin: 0 auto;
  padding-inline: var(--pad);
}

.pricing-editorial__img img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 45vh;
  object-fit: contain;
}

/* Mobile — collapse the stacked padding between Fine Print and the
   editorial couple image. The previous section already carries its
   own bottom rhythm; doubling it on small screens leaves a dead zone
   of white between the disclaimer and the photo. */
@media (max-width: 719.98px) {
  .pricing-editorial {
    padding-top: var(--s-2);
    padding-bottom: var(--s-2);
  }
  .pricing-editorial__img img {
    max-height: 42vh;
  }
}

/* Add-Ons & Fees appendix — INVERTED dark block. Ink-black ground
   with canvas-white text. The H1 heading uses .section-h scale.
   Acts as an editorial counterpoint to the cream pricing lanes. */
.pricing-addons {
  margin-top: clamp(var(--s-5), 5vw, var(--s-7));
  padding: clamp(var(--s-5), 5vw, var(--s-7));
  background: var(--ink);
  color: var(--canvas);
}

.pricing-addons__heading {
  color: var(--canvas);
  margin: 0 0 clamp(var(--s-4), 4vw, var(--s-5));
  text-align: left;
}

.pricing-addons__list {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  margin: 0;
}

@media (min-width: 720px) {
  .pricing-addons__list {
    grid-template-columns: 1fr 1fr;
    gap: 0 clamp(var(--s-4), 4vw, var(--s-6));
  }
}

.pricing-addons__row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-2);
  padding: 12px 0;
  border-bottom: 0.5px solid rgba(252, 251, 249, 0.18);
}

.pricing-addons__row dt {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 400;
  font-size: 0.875rem;
  color: rgba(252, 251, 249, 0.65);
  letter-spacing: 0.03em;
  line-height: 1.4;
}

.pricing-addons__row dd {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-weight: 500;
  font-size: 0.875rem;
  color: var(--canvas);
  margin: 0;
  text-align: right;
  white-space: nowrap;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
}

/* Mobile — service menu becomes more compact */
@media (max-width: 640px) {
  .tier__service-menu th,
  .tier__service-menu td {
    font-size: 0.8125rem;
  }
  .tier__service-menu th {
    width: 48%;
  }
  .tier__service-menu td {
    width: 26%;
  }
}

/* Notes section under the tiers */
.pricing-notes {
  margin-top: var(--s-7);
  display: grid;
  gap: var(--s-3);
  grid-template-columns: 1fr;
  max-width: 880px;
  margin-inline: auto;
}

/* Stacked single-column layout at every viewport — was 2-column at
   ≥720px. The user wanted these 4 fine-print items to read as a
   single vertical list, not a side-by-side grid. */

.note-item {
  border-top: 0.5px solid var(--hairline);
  padding-top: var(--s-2);
}

.note-item__title {
  font-family: 'Alan Sans', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  margin: 0 0 var(--s-1);
}

.note-item__body {
  font-family: 'Crimson Pro', serif;
  font-weight: 300;
  font-size: 0.9375rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  margin: 0;
}

/* (f) Accessibility — turn off all motion when reduced-motion is requested */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .reveal,
  .editorial-block > * { opacity: 1; transform: none; }
  .clip-reveal {
    clip-path: inset(0 0 0 0);
    -webkit-clip-path: inset(0 0 0 0);
  }
  [data-parallax] { transform: scale(1); }
  html { scroll-behavior: auto; }
}

/* (g) Mobile — disable parallax (causes jank on touch scroll) */
@media (max-width: 820px) {
  [data-parallax] { transform: scale(1.04) !important; }
}

/* ---- 19. Mobile nav — toggle morphs into X when drawer open ----
   Spans are flex-column with 6px top/bottom margins (non-collapsing
   in flex), so centers sit at y≈6.5, 19.5, 32.5 — a 13px step.
   translate the outer two by exactly that to form a true X.
   The drawer is now cream (Aesop register), so the X stays ink-color
   for contrast against the light ground. */
@media (max-width: 768px) {
  .nav__toggle.is-open {
    color: var(--ink);
  }
  .nav__toggle.is-open span:nth-child(1) {
    transform: translateY(13px) rotate(45deg);
  }
  .nav__toggle.is-open span:nth-child(2) { opacity: 0; }
  .nav__toggle.is-open span:nth-child(3) {
    transform: translateY(-13px) rotate(-45deg);
  }
}

/* ============================================================
   ARTISTS PAGE — filter chips, carousel cards, modal
   ============================================================ */

/* Tighter head→filter rhythm to match the rest of the site.
   Was ~150–200px of dead space between the roster description
   and the All/Toronto/L.A. chips; now ~50–80px. */
#artisans-head {
  padding-bottom: clamp(var(--s-2), 2vw, var(--s-3));
}

#artisans-grid-section {
  padding-top: clamp(var(--s-3), 3vw, var(--s-4));
}

/* ---- A. Filter chips -------------------------------------- */
.filter {
  display: flex;
  justify-content: center;
  gap: var(--s-1);
  flex-wrap: wrap;
  margin-bottom: var(--s-6);
}

.filter__chip {
  padding: 0.625rem 1.5rem;
  border: 1px solid var(--hairline);
  background: transparent;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  cursor: pointer;
  transition: background var(--d-fast) var(--ease-out),
              border-color var(--d-fast) var(--ease-out),
              color var(--d-fast) var(--ease-out);
  border-radius: 0;
}

.filter__chip:hover {
  border-color: var(--ink);
  color: var(--ink);
}

.filter__chip.is-active {
  background: var(--ink);
  color: var(--canvas);
  border-color: var(--ink);
}

/* ---- B. Artists grid + card ------------------------------- */
.artisans-grid {
  display: grid;
  /* Split row vs column gaps —
     ROW (between artist rows): 24px = var(--s-3) — tighter card-to-card
                                stacking on mobile, deliberate breathing
                                between rows on desktop multi-column.
     COLUMN (between artist columns): 48px = var(--s-5) on mobile
                                (irrelevant since mobile is 1-col),
                                drops to 32px at desktop breakpoints. */
  row-gap: var(--s-3);
  column-gap: var(--s-5);
  grid-template-columns: 1fr;
  transition: opacity var(--d-base) var(--ease-out);
}

@media (min-width: 720px) {
  .artisans-grid {
    grid-template-columns: repeat(2, 1fr);
    column-gap: var(--s-4);
  }
}

/* Three columns arrive earlier now that the grid is edge-to-edge —
   at 1100px inside a full-width container each column was already
   wider than it had been at 1240px inside the capped one, so the old
   breakpoint was leaving a two-column layout on screens with room
   for three. */
@media (min-width: 960px) {
  .artisans-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

.artisans-grid.is-filtering { opacity: 0.4; }

/* Sentinel for the endless roster — zero-height marker below the grid.
   Crossing it appends the next cycle (js/artists-page.js). */
.artisans-sentinel {
  height: 1px;
  width: 100%;
  pointer-events: none;
}

/* Cards reveal individually as they scroll in, using the same
   .reveal → .is-visible transition as the rest of the site. The stagger
   comes from their natural vertical offset rather than a delay chain:
   in a three-column grid a row enters together, which reads as
   composed. A per-card delay would make each row shimmer. */
.artisans-grid .artisan.reveal {
  transition-delay: 0ms;
}

.artisan {
  display: flex;
  flex-direction: column;
}

.artisan[data-hidden="true"] { display: none; }

/* ---- C. Carousel ----------------------------------------- */
.carousel {
  position: relative;
  aspect-ratio: 4/5;
  overflow: hidden;
  background: var(--hairline-soft);
  margin-bottom: var(--s-3);
  user-select: none;
}

.carousel__track {
  display: flex;
  height: 100%;
  transition: transform 700ms var(--ease-out);
  cursor: grab;
  touch-action: pan-y;
}

.carousel__track.is-dragging {
  transition: none;
  cursor: grabbing;
}

.carousel__slide {
  flex: 0 0 100%;
  height: 100%;
  position: relative;
}

.carousel__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

.carousel__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  /* 44×44px on touch devices (Apple HIG minimum), 32×32 on desktop
     where hover precision is fine. */
  width: clamp(32px, 6vw, 44px);
  height: clamp(32px, 6vw, 44px);
  border-radius: 50%;
  background: rgba(252, 251, 249, 0.88);
  border: 0;
  color: var(--ink);
  font-family: 'Alan Sans', sans-serif;
  font-size: 1.125rem;
  cursor: pointer;
  opacity: 0;
  transition: opacity var(--d-fast) var(--ease-out),
              background var(--d-fast) var(--ease-out);
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  line-height: 1;
}

.carousel:hover .carousel__nav,
.carousel:focus-within .carousel__nav { opacity: 1; }

/* Touch devices have no hover — show nav buttons by default so
   mobile users can actually use the carousel. The hover-fade
   behavior is preserved for pointer (desktop) users. */
@media (hover: none), (pointer: coarse) {
  .carousel__nav { opacity: 0.9; }
}

.carousel__nav:hover { background: var(--canvas); }
.carousel__nav--prev { left: 12px; }
.carousel__nav--next { right: 12px; }

.carousel__dots {
  position: absolute;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 6px;
  z-index: 2;
}

.carousel__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: rgba(252, 251, 249, 0.55);
  cursor: pointer;
  transition: background var(--d-fast) var(--ease-out), transform var(--d-fast) var(--ease-out);
  border: 0;
  padding: 0;
}

.carousel__dot:hover { background: rgba(252, 251, 249, 0.85); }
.carousel__dot.is-active {
  background: var(--canvas);
  transform: scale(1.3);
}

/* ---- D. Artist card meta + actions -----------------------
   Grid-based layout so View Profile pulls to the right baseline-
   aligned with the name, and Book drops to its own row beneath
   the location line. .artisan__actions becomes display:contents so
   its children (view + book) participate directly in the article
   grid — no HTML restructure needed.

   Visual order:
     [carousel — full-bleed]
     NAME ........................ VIEW PROFILE
     TORONTO · LOS ANGELES
     [BOOK]
*/
.artisan {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "media media"
    "name  book"
    "loc   loc"
    "view  .";
  column-gap: var(--s-2);
  /* INSIDE the card: 12px between name → loc → view rows — tight
     intimacy register for the artist's metadata block. Card-to-card
     spacing (24px) lives on .artisans-grid below. */
  row-gap: 12px;
}

.artisan__media-link {
  grid-area: media;
  display: block;
  text-decoration: none;
  color: inherit;
  /* TIGHT — name sits close to the image as a caption.
     The image's own composition (subject + natural negative space
     at the bottom of the photographic frame) already creates visual
     breathing; adding margin here doubles that and reads as a gap. */
  margin-bottom: 4px;
}

/* Kill the global a:hover { opacity: 0.6 } fade on the artisan image
   wrapper. On a photographic image, dropping to 60% opacity reads as
   a "white wash" against the cream canvas — distracting and cheap.
   The carousel itself already provides hover affordance (nav arrows
   fade in), so the image stays at full integrity. */
.artisan__media-link:hover,
.artisan__media-link:focus {
  opacity: 1;
}

.artisan__name {
  grid-area: name;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: 1.0625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0;
  color: var(--ink);
  align-self: baseline;
}

.artisan__name a {
  color: inherit;
  text-decoration: none;
}
.artisan__name a:hover { opacity: 0.7; }

/* Discipline — italic, sitting with the city on the same line.
   The client asked for it italicised, which also gives the metadata
   line two registers: the craft in italic, the place upright. Reads
   as a caption rather than a second label. */
.artisan__discipline {
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0.12em;
  color: var(--ink);
  opacity: 0.85;
}

.artisan__loc {
  grid-area: loc;
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  /* Margins zeroed — row-gap on .artisan grid handles spacing now. */
  margin: 0;
}

/* The wrapping <div class="artisan__actions"> is now transparent in
   the grid — its children participate directly in the parent layout. */
.artisan__actions {
  display: contents;
}

/* View Profile — text-link with underline, sits bottom-left
   (its own row, beneath the location line). */
.artisan__view {
  grid-area: view;
  justify-self: start;
  white-space: nowrap;
}

/* Book — filled button, top-right baseline-aligned with the name. */
.artisan__book {
  grid-area: book;
  align-self: baseline;
  justify-self: end;
  border-radius: 0;
}

/* ============================================================
   ARTIST DETAIL VIEWER (/artists/detail.html)
   50/50 split: small circle portrait + bio left, big carousel right
   ============================================================ */

.viewer {
  /* PERMANENT NAV CLEARANCE — locked floor of 200px so the portrait
     can NEVER kiss the nav baseline, regardless of viewport width
     or nav-height drift from future logo / padding changes. The
     200px floor was chosen as: worst-case nav height (~120px) +
     guaranteed ~80px of editorial breath. The clamp only scales
     up on wider viewports for proportional rhythm. */
  padding-top: clamp(200px, 18vw, 240px);
  padding-bottom: clamp(48px, 6vw, 80px);
}

/* ---- Top control bar ---- */
.viewer__bar {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--s-3);
  padding: var(--s-2) var(--pad);
  border-bottom: 0.5px solid var(--hairline);
  background: var(--canvas);
}

/* Filter dropdown */
.viewer__filter-wrap { position: relative; }

.viewer__filter {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 0;
  padding: 8px 0;
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  cursor: pointer;
}

.viewer__filter-arrow {
  font-size: 9px;
  opacity: 0.55;
  margin-left: 2px;
  transition: transform var(--d-fast) var(--ease-out);
}

.viewer__filter-wrap.is-open .viewer__filter-arrow { transform: rotate(180deg); }

.viewer__filter-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  background: var(--canvas);
  border: 0.5px solid var(--hairline);
  list-style: none;
  margin: 0;
  padding: 4px 0;
  min-width: 140px;
  visibility: hidden;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity var(--d-fast) var(--ease-out), transform var(--d-fast) var(--ease-out), visibility var(--d-fast);
  z-index: 50;
  box-shadow: 0 4px 12px rgba(10,9,8,0.06);
}

.viewer__filter-wrap.is-open .viewer__filter-menu {
  visibility: visible;
  opacity: 1;
  transform: translateY(0);
}

.viewer__filter-menu button {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  padding: 10px 16px;
  font-family: 'Alan Sans', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink);
  cursor: pointer;
}

.viewer__filter-menu button:hover { background: var(--bone-soft, #FAF7F2); }
.viewer__filter-menu button.is-active { color: var(--whisper, #B8967A); }

/* Thumbnail strip */
.viewer__thumbs {
  display: flex;
  gap: 8px;
  justify-content: center;
  align-items: center;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 4px 0;
}
.viewer__thumbs::-webkit-scrollbar { display: none; }

.viewer__thumb {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: 1.5px solid transparent;
  background: var(--hairline-soft);
  padding: 0;
  overflow: hidden;
  cursor: pointer;
  transition: border-color var(--d-fast) var(--ease-out), transform var(--d-fast) var(--ease-out);
}

.viewer__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 50%;
}

.viewer__thumb:hover { transform: scale(1.06); }
.viewer__thumb.is-active { border-color: var(--ink); }

/* Book button — already uses .btn */
.viewer__book {
  white-space: nowrap;
}

/* ---- 50/50 body ---- */
.viewer__body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: calc(100vh - 64px - 64px);
  min-height: calc(100svh - 128px);
}

@media (max-width: 880px) {
  .viewer__body { grid-template-columns: 1fr; min-height: auto; }
}

/* Left: profile */
.viewer__left {
  padding: clamp(var(--s-5), 5vw, var(--s-7)) clamp(var(--s-4), 4vw, var(--s-6));
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 6px;
  max-width: 560px;
  margin-left: auto;
}

@media (max-width: 880px) {
  .viewer__left { margin: 0; max-width: none; text-align: center; align-items: center; }
}

.viewer__portrait {
  width: clamp(96px, 14vw, 144px);
  height: clamp(96px, 14vw, 144px);
  border-radius: 50%;
  overflow: hidden;
  background: var(--hairline-soft);
  margin-bottom: var(--s-3);
}

.viewer__portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Anchor the crop to the upper third so the face stays in frame —
     portrait-orientation source photos lose the face under default
     center-center crop in a square container. */
  object-position: center 20%;
  border-radius: 50%;
}

.viewer__name {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: clamp(1rem, 1.4vw, 1.25rem);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0 0 4px;
  color: var(--ink);
  line-height: 1.3;
}

.viewer__city {
  font-family: 'Alan Sans', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  margin: 0 0 var(--s-3);
}

.viewer__bio {
  font-family: 'Crimson Pro', Georgia, serif;
  font-weight: 300;
  font-size: var(--t-body);
  line-height: 1.55;
  letter-spacing: 0.01em;
  color: var(--ink);
  margin: 0 0 var(--s-3);
  max-width: 42ch;
}

.viewer__specs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.viewer__spec {
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
  padding: 5px 10px;
  border: 0.5px solid var(--hairline);
}

/* Right: portfolio carousel */
.viewer__right {
  background: var(--hairline-soft);
  position: relative;
  overflow: hidden;
}

.viewer__carousel {
  position: absolute;
  inset: 0;
}

.viewer__carousel-track {
  display: flex;
  height: 100%;
  transition: transform 800ms var(--ease-out);
}

.viewer__slide {
  flex: 0 0 100%;
  height: 100%;
  margin: 0;
}

.viewer__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.viewer__slide--empty {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: 'Alan Sans', sans-serif;
  font-size: var(--t-eyebrow);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-soft);
}

.viewer__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(252, 251, 249, 0.86);
  border: 0;
  color: var(--ink);
  font-family: 'Alan Sans', sans-serif;
  font-size: 1.25rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  opacity: 0;
  transition: opacity var(--d-fast) var(--ease-out), background var(--d-fast) var(--ease-out);
  padding: 0;
  line-height: 1;
}

.viewer__right:hover .viewer__nav,
.viewer__right:focus-within .viewer__nav { opacity: 1; }

.viewer__nav:hover { background: var(--canvas); }

.viewer__nav--prev { left: 16px; }
.viewer__nav--next { right: 16px; }

.viewer__pager {
  position: absolute;
  bottom: 16px;
  left: 16px;
  right: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  z-index: 2;
}

.viewer__pager-label {
  font-family: 'Alan Sans', sans-serif;
  font-size: 0.625rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: rgba(252, 251, 249, 0.85);
  text-shadow: 0 1px 4px rgba(0,0,0,0.25);
}

.viewer__pager-dots {
  display: flex;
  gap: 5px;
}

.viewer__pager-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(252, 251, 249, 0.5);
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: background var(--d-fast) var(--ease-out), transform var(--d-fast) var(--ease-out);
}

.viewer__pager-dot:hover { background: rgba(252, 251, 249, 0.85); }
.viewer__pager-dot.is-active {
  background: var(--canvas);
  transform: scale(1.3);
}

/* Mobile: top bar collapses, thumbnails scroll horizontally */
@media (max-width: 600px) {
  .viewer__bar {
    grid-template-columns: auto auto;
    grid-template-areas:
      "filter book"
      "thumbs thumbs";
    gap: var(--s-2);
  }
  .viewer__filter-wrap { grid-area: filter; }
  .viewer__book        { grid-area: book; }
  .viewer__thumbs      { grid-area: thumbs; justify-content: flex-start; }
}

/* ============================================================
   PORTFOLIO PAGE — full-bleed founder viewer (no top bar)
   ============================================================ */
.viewer--portfolio {
  /* Top padding matches the nav height EXACTLY — the right column
     (carousel) has no internal padding and would otherwise show
     this padding as a visible white gap above the image. With this
     calibrated to the nav height, the right image sits flush with
     the nav baseline. The left column compensates with its own
     internal padding (clamp(--s-5, 5vw, --s-6) inside .viewer__left)
     so the editorial breath above the portrait still reads. */
  padding-top: clamp(64px, 5.5vw, 80px);
  padding-bottom: 0;
}

.viewer--portfolio .viewer__body {
  min-height: calc(100vh - 64px);   /* full viewport minus fixed nav */
  min-height: calc(100svh - 64px);
}

/* Left column: center horizontally AND vertically, so the portrait/
   bio block aligns with the carousel's vertical midline on desktop.
   On mobile (≤880px) the body collapses to single column and
   centering creates a dead zone — see the @media override below
   that switches to flex-start for mobile. */
.viewer--portfolio .viewer__left {
  text-align: center;
  align-items: center;
  justify-content: center;
  margin-left: auto;
  margin-right: auto;
  max-width: 520px;
  padding: clamp(var(--s-5), 5vw, var(--s-6));
}

/* Square portrait modifier */
.viewer__portrait--square,
.viewer__portrait--square img {
  border-radius: 0 !important;
}

.viewer--portfolio .viewer__portrait {
  /* LOCKED 4:5 PORTRAIT FRAME — every uploaded image will render in
     this aspect, no matter its source dimensions. Combined with
     object-fit:cover + object-position:center top below, the focal
     point is always anchored at top-center of the source — face
     stays in frame for any portrait or headshot upload. */
  width: clamp(180px, 22vw, 260px);
  aspect-ratio: 4 / 5;
  height: auto;
  margin-bottom: var(--s-4);
  /* Subtle background fill behind the image — invisible if cover
     fills cleanly, fallback if a contain-fit image is uploaded. */
  background: var(--hairline-soft);
  overflow: hidden;
}

.viewer--portfolio .viewer__portrait img {
  /* TOP-CENTER FOCAL POINT — face always anchored to the upper
     region of the 4:5 frame, regardless of source aspect ratio. */
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}

/* Tighten city + heading spacing */
.viewer--portfolio .viewer__name {
  margin-top: var(--s-2);
}

.viewer--portfolio .viewer__bio {
  margin-inline: auto;
  max-width: 38ch;
}

@media (max-width: 880px) {
  .viewer--portfolio .viewer__body {
    grid-template-columns: 1fr;
    min-height: auto;
  }
  .viewer--portfolio .viewer__right {
    min-height: 60vh;
  }
  /* Mobile-specific top spacing — desktop is calibrated for the
     right-column image to sit flush against the nav. On mobile the
     right column drops BELOW the left, so the section's top padding
     no longer needs to be nav-tight. We bump it to ~112–132px so
     the visible space ABOVE the portrait equals the visible space
     BELOW the bio paragraph (which the left column's own
     padding-bottom of ~48px + grid gap produces). */
  .viewer--portfolio {
    padding-top: clamp(112px, 22vw, 132px);
  }
  /* Mobile — pack the left column content to the TOP. Centering
     here would create a vertical dead zone above the portrait since
     the column collapses to single-column layout and has nothing
     to align against. */
  .viewer--portfolio .viewer__left {
    justify-content: flex-start;
  }
}

/* ---- E. Modal — Tom Ford-style overlay ------------------- */
.modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  visibility: hidden;
  opacity: 0;
  transition: opacity var(--d-base) var(--ease-out),
              visibility var(--d-base) var(--ease-out);
}

.modal.is-open {
  visibility: visible;
  opacity: 1;
}

/* Full-bleed artist image behind the modal (set via JS when an
   artist's Book button is clicked). Falls back to the pink drift
   when no image is set. background-position is "center top" so the
   artist's face/head is always anchored to the top of the viewport
   — never cropped behind the modal card. */
.modal__bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center top;
  background-repeat: no-repeat;
  filter: grayscale(35%) brightness(0.85);
  opacity: 0;
  transition: opacity var(--d-slow) var(--ease-out);
}

.modal.has-artist-bg .modal__bg {
  opacity: 1;
}

/* Pink drift backdrop — retired. The booking modal now uses a
   plain ink-tinted overlay regardless of whether an artist image
   is set, so the user gets a calm dark ground without the pink
   bloom that was visible behind the inquiry card. */
.modal__drift {
  display: none;
}

.modal.has-artist-bg .modal__drift {
  display: none;
}

.modal__overlay {
  position: absolute;
  inset: 0;
  background: rgba(10, 9, 8, 0.35);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  cursor: pointer;
}

.modal:not(.has-artist-bg) .modal__overlay {
  /* Pink drift is retired — keep the dark overlay consistent so
     the booking modal sits on calm ink regardless of artist context. */
  background: rgba(10, 9, 8, 0.55);
}

.modal__dialog {
  position: relative;
  z-index: 1;
  background: rgba(252, 251, 249, 0.92);
  backdrop-filter: blur(14px) saturate(110%);
  -webkit-backdrop-filter: blur(14px) saturate(110%);
  border: 0.5px solid rgba(10, 9, 8, 0.08);
  max-width: 720px;
  width: calc(100% - 2rem);
  max-height: calc(100vh - 2rem);
  max-height: calc(100svh - 2rem);
  overflow-y: auto;
  padding: clamp(var(--s-4), 5vw, var(--s-6));
  margin: 1rem auto;
  margin-top: clamp(1rem, 4vh, 4rem);
  /* Sharp corners — Maison signature */
  border-radius: 0;
  box-shadow: 0 30px 80px -20px rgba(10, 9, 8, 0.4);
  transform: translateY(20px);
  transition: transform var(--d-base) var(--ease-out);
}

.modal.is-open .modal__dialog {
  transform: translateY(0);
}

.modal__close {
  position: absolute;
  top: var(--s-3);
  right: var(--s-3);
  width: 36px;
  height: 36px;
  background: transparent;
  border: 0;
  cursor: pointer;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  z-index: 2;
}

.modal__close::before,
.modal__close::after {
  content: '';
  position: absolute;
  width: 18px;
  height: 1px;
  background: currentColor;
}

.modal__close::before { transform: rotate(45deg); }
.modal__close::after  { transform: rotate(-45deg); }

.modal__close:hover { opacity: 0.6; }

.modal__head {
  margin-bottom: var(--s-4);
  padding-right: var(--s-5);
}

.modal__head h2 {
  font-family: 'Alan Sans', 'Inter', sans-serif;
  font-size: var(--t-display);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  margin: 0 0 var(--s-2);
}

.modal__sub {
  font-family: 'Crimson Pro', Georgia, serif;
  font-style: normal;
  font-weight: 400;
  font-size: var(--t-body-lg);
  letter-spacing: 0.01em;
  color: var(--ink-soft);
  margin: 0;
}

.modal__head h2 + .modal__sub {
  margin-top: 6px;
}

/* Modal dialog inherits inquiry-card styling for form inputs +
   labels + asterisks + checkboxes by adding the .inquiry-card
   class to the dialog element. These rules below ensure the
   multi-step step indicator + nav row work inside the modal. */
.modal__dialog .inquiry-steps {
  margin-top: var(--s-3);
  margin-bottom: var(--s-4);
  padding-bottom: var(--s-3);
}

.modal__dialog .inquiry-nav {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
}

.modal__dialog .inquiry-disclaimer {
  margin-top: var(--s-3);
}

.modal__sub strong {
  color: var(--ink);
  font-style: normal;
  font-family: 'Alan Sans', sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  font-size: var(--t-eyebrow);
  letter-spacing: 0.2em;
  display: inline-block;
  padding: 0.25rem 0.625rem;
  border: 1px solid var(--ink);
  margin-left: 0.5rem;
  vertical-align: middle;
}

/* Body locks scroll when modal is open */
body.modal-open { overflow: hidden; }

/* ---- 20. Utilities ---------------------------------------- */
.u-center { text-align: center; }
.u-flex-center { display: flex; justify-content: center; align-items: center; }
.u-max-prose { max-width: 60ch; }
.u-mt-3 { margin-top: var(--s-3); }
.u-mt-4 { margin-top: var(--s-4); }
.u-mt-5 { margin-top: var(--s-5); }
.u-hidden { display: none !important; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: var(--s-2);
  background: var(--ink);
  color: var(--canvas);
  z-index: 1000;
}

.skip-link:focus { left: var(--s-2); top: var(--s-2); }
