/* maison.css — Concept E layout: "Maison".
   A crisp white-cube gallery crossed with a luxury maison .com. Pure white, vast
   air, the work floating in soft light, two full-bleed cinemagraph "video" bands,
   and a signature VITRINE: a pinned "focus pull" that draws each piece from full
   view into a macro of its surface as the collector scrolls.

   Visual values reference tokens-e.css only (HARD RULE). Structural CSS (display,
   grid, position, object-fit, overflow) is allowed here. base.css supplies the
   reset + shared primitives (.eyebrow, .link, .btn, .reveal, .unveil, a11y, grain). */

h1, h2, h3, .hero__name { font-optical-sizing: auto; }

/* The maison wall is plain bright white. The base grain supplies a faint tooth. */
body.maison { background: var(--ink); }

/* Shared small label, used across sections */
.kicker {
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 500;
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--stone-dim);
}

/* ---------------------------------------------------------------- CINEMAGRAPH
   Honest stand-in for Chase's forthcoming video: his real photograph with a
   slow, weighted drift + one slow pass of gallery light. Swap the <img> for a
   <video> in the same .cine box and nothing else changes. */
.cine { position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.cine img,
.cine video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  filter: var(--art-grade);
  transform-origin: 58% 42%;
  animation: cine-drift var(--dur-film) var(--ease) infinite alternate;
  will-change: transform;
}
.cine__sweep {
  position: absolute;
  inset: -25% -45%;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(74deg,
    transparent 42%, var(--light) 50%, transparent 58%);
  mix-blend-mode: soft-light;
  opacity: 0.8;
  animation: cine-sweep calc(var(--dur-film) * 1.6) var(--ease) infinite;
}
@keyframes cine-drift {
  from { transform: scale(1.02) translate3d(0, 0, 0); }
  to   { transform: scale(1.11) translate3d(-1.6%, -1.2%, 0); }
}
@keyframes cine-sweep {
  0%   { transform: translateX(-60%); opacity: 0; }
  35%  { opacity: 0.8; }
  65%  { opacity: 0.8; }
  100% { transform: translateX(60%); opacity: 0; }
}

/* ---------------------------------------------------------------- NAV */
.nav {
  --nav-fg: var(--on-film);      /* light over the hero film, in either skin */
  position: fixed;
  inset: 0 0 auto 0;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-5);
  padding: var(--s-5) var(--gutter);
  z-index: var(--z-nav);
  transition: background var(--dur) var(--ease),
              border-color var(--dur) var(--ease),
              color var(--dur) var(--ease);
  border-bottom: 1px solid transparent;
}
.nav.is-stuck {
  --nav-fg: var(--stone);        /* near-black on the white wall */
  background: color-mix(in srgb, var(--ink) 86%, transparent);
  -webkit-backdrop-filter: blur(16px) saturate(1.05);
  backdrop-filter: blur(16px) saturate(1.05);
  border-bottom-color: var(--hairline-2);
}
.nav__brand {
  grid-column: 2;
  justify-self: center;
  display: block;
  line-height: 1;
  color: var(--nav-fg);
}
/* The signature is a monochrome silhouette masked in the nav's own foreground
   token, so it turns near-black on the white bar, off-white on the Noir bar,
   and stays light over the hero film — the same swap the text color made,
   with no per-skin branching. aspect-ratio matches the source PNG exactly so
   the masked box never letterboxes. */
.nav__brandmark {
  display: block;
  height: clamp(2.6rem, 3.6vw, 3.3rem);
  aspect-ratio: 3125 / 1562;
  background-color: currentColor;
  -webkit-mask: url(../assets/chase-parker-logo-white.png) center / contain no-repeat;
  mask: url(../assets/chase-parker-logo-white.png) center / contain no-repeat;
  transition: background-color var(--dur) var(--ease);
}
.nav__links { display: flex; align-items: center; gap: clamp(var(--s-5), 3vw, var(--s-7)); }
.nav__links--left { grid-column: 1; justify-self: start; }
.nav__links--right { grid-column: 3; justify-self: end; }
.nav__links a {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--nav-fg);
  opacity: 0.78;
  transition: opacity var(--dur-fast) var(--ease);
}
.nav__links a:hover { opacity: 1; }
.nav__appt {
  opacity: 1 !important;
  border: 1px solid color-mix(in srgb, var(--nav-fg) 38%, transparent);
  padding: var(--s-3) var(--s-5);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
.nav__appt:hover {
  background: var(--accent);
  color: var(--ink);
  border-color: var(--accent);
}
/* Work dropdown. The parent stays a link to the Work overview; the caret beside
   it is a separate disclosure button, because one control cannot both navigate
   and expand. Hover opens it for a mouse, the caret opens it for a keyboard. */
.nav__group { position: relative; display: flex; align-items: center; gap: var(--s-2); }
.nav__grouplink { /* inherits .nav__links a */ }
.nav__caret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* 44px tap target per WCAG 2.2 AA, pulled back with negative margin so the
     caret still sits tight against the word. */
  min-width: 44px;
  min-height: 44px;
  margin: calc(var(--s-3) * -1) calc(var(--s-4) * -1) calc(var(--s-3) * -1) calc(var(--s-3) * -1);
  padding: 0;
  background: none;
  border: 0;
  color: var(--nav-fg);
  opacity: 0.78;
  cursor: pointer;
  transition: opacity var(--dur-fast) var(--ease);
}
.nav__caret:hover { opacity: 1; }
.nav__caret-mark {
  display: block;
  width: 6px;
  height: 6px;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur-fast) var(--ease);
}
.nav__group.is-open .nav__caret-mark { transform: translateY(1px) rotate(-135deg); }
/* Marks the section the visitor is inside, from the collapsed bar. */
.nav__group[data-in-section] .nav__grouplink { opacity: 1; }

.nav__sub {
  position: absolute;
  top: calc(100% + var(--s-4));
  left: calc(var(--s-4) * -1);
  min-width: 15rem;
  display: grid;
  list-style: none;
  margin: 0;
  padding: var(--s-3) 0;
  background: var(--ink);
  border: 1px solid var(--hairline-2);
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.nav__sub[hidden] { display: none; }
.nav__group.is-open .nav__sub { opacity: 1; transform: none; }
/* Scoped through .nav__sub so it outweighs `.nav__links a` on specificity alone,
   without !important. The panel is always the solid wall, never the hero film, so
   it keeps its own ink rather than inheriting the masthead's --nav-fg. */
.nav__sub .nav__sublink {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--s-5);
  color: var(--stone-mute);
  opacity: 1;
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.nav__sub .nav__sublink:hover { color: var(--stone); background: var(--hairline-2); }
.nav__sub .nav__sublink[aria-current="page"] { color: var(--stone); }

/* The legal masthead: no CTA, no dropdown, just the way back out. */
.nav--legal .nav__back {
  gap: var(--s-2);
  font-size: var(--text-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--nav-fg);
  opacity: 0.78;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: opacity var(--dur-fast) var(--ease);
}
.nav--legal .nav__back:hover { opacity: 1; }

.nav__toggle {
  display: none;
  grid-column: 3;
  justify-self: end;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  color: var(--nav-fg);
  padding: var(--s-3);
  min-width: 44px;
  min-height: 44px;
  cursor: pointer;
}
/* Hamburger: middle bar is the span, top/bottom are its pseudo-elements. */
.nav__toggle-bars,
.nav__toggle-bars::before,
.nav__toggle-bars::after {
  content: "";
  display: block;
  width: 22px;
  height: 1.5px;
  background: currentColor;
}
.nav__toggle-bars { position: relative; }
.nav__toggle-bars::before { position: absolute; left: 0; top: -6px; }
.nav__toggle-bars::after { position: absolute; left: 0; top: 6px; }
.nav__toggle-bars,
.nav__toggle-bars::before,
.nav__toggle-bars::after {
  transition: transform var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
}
/* Open state: the bars fold into a close (×). */
.nav__toggle.is-open .nav__toggle-bars { background: transparent; }
.nav__toggle.is-open .nav__toggle-bars::before { transform: translateY(6px) rotate(45deg); }
.nav__toggle.is-open .nav__toggle-bars::after { transform: translateY(-6px) rotate(-45deg); }

/* Mobile menu overlay: full-surface panel of the nav links (built in maison.js).
   Sits just under the fixed masthead so the wordmark and the × stay tappable;
   nav-locked flips the masthead ink so both read over the panel in either skin. */
.navmenu {
  position: fixed;
  inset: 0;
  z-index: calc(var(--z-nav) - 1);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.navmenu.is-open { opacity: 1; }
.navmenu[hidden] { display: none; }
.navmenu__scrim { position: absolute; inset: 0; background: var(--ink); }
.navmenu__sheet {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-5);
  padding: var(--s-9) var(--gutter);
  /* Empty areas fall through to the scrim so a tap off the links closes. */
  pointer-events: none;
}
.navmenu__link,
.navmenu__appt { pointer-events: auto; }
.navmenu__link {
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  line-height: 1;
  color: var(--stone);
  /* Padding lifts the hit area to the 44px minimum without changing the look. */
  padding-block: var(--s-2);
  transition: opacity var(--dur-fast) var(--ease);
}
.navmenu__link:hover { opacity: 0.62; }
.navmenu__link[aria-current="page"] { opacity: 0.62; }
/* Work's categories, set smaller directly under it. No disclosure to tap on a
   phone — the whole point of the panel is that everything is already open. */
.navmenu__sub {
  display: grid;
  justify-items: center;
  gap: var(--s-1);
  margin-top: calc(var(--s-3) * -1);
  list-style: none;
}
.navmenu__sublink {
  pointer-events: auto;
  font-size: var(--text-sm);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--stone-mute);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: color var(--dur-fast) var(--ease);
}
.navmenu__sublink:hover,
.navmenu__sublink[aria-current="page"] { color: var(--stone); }
.navmenu__appt { margin-top: var(--s-5); }
body.nav-locked { overflow: hidden; }
body.nav-locked .nav { --nav-fg: var(--stone); }

@media (prefers-reduced-motion: reduce) {
  .navmenu,
  .nav__toggle-bars,
  .nav__toggle-bars::before,
  .nav__toggle-bars::after { transition: none; }
}

/* ---------------------------------------------------------------- HERO */
.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  isolation: isolate;
  overflow: hidden;
}
.hero__media { position: absolute; inset: 0; z-index: 0; }
/* directional scrim: dark at the top (for the nav) and bottom (for the name),
   so the white type is bedded into the image, never floating on a raw photo */
.hero__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to bottom, var(--vignette) 0%, transparent 26%),
    linear-gradient(to top, var(--vignette) 0%, transparent 46%);
}
.hero__frame {
  position: relative;
  z-index: 2;
  align-self: stretch;
  display: grid;
  /* single row: the name block beds at the hero's base (eyebrow removed) */
  grid-template-rows: 1fr;
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding: calc(var(--s-10) + var(--s-7)) var(--gutter) var(--s-9);
}
.hero__base { align-self: end; display: grid; gap: var(--s-3); }
.hero__name {
  font-family: var(--font-display);
  font-size: var(--text-display);
  font-weight: 400;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--on-film);
  /* Forces the break after CHASE at every width. Type size is untouched — this
     only governs where the line ends. Without it the name sets on one line
     below ~1300px and two lines above, so the hero had two different
     compositions depending on the visitor's window, and the one-line version
     ran the whole word PARKER through the middle of the sculpture. */
  max-width: 9ch;
}
.hero__role {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--on-film);
  opacity: 0.78;
}
.hero__scroll {
  position: absolute;
  z-index: 2;
  right: var(--gutter);
  bottom: var(--s-9);
  display: grid;
  justify-items: center;
  gap: var(--s-3);
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--on-film);
  opacity: 0.82;
  min-height: 44px;
}
.hero__scroll i {
  display: block;
  width: 1px;
  height: var(--s-7);
  background: color-mix(in srgb, var(--on-film) 60%, transparent);
  position: relative;
  overflow: hidden;
}
.hero__scroll i::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--on-film);
  animation: drip var(--dur-cue) var(--ease-out) infinite;
}
@keyframes drip {
  0%   { transform: translateY(-100%); }
  55%  { transform: translateY(100%); }
  100% { transform: translateY(100%); }
}
@media (max-width: 767px) {
  /* The scroll cue is a desktop affordance. Scrolling needs no prompt on touch,
     and on the phone crop the cue collides with the sculpture, so it is dropped
     rather than repositioned. */
  .hero__scroll { display: none; }
}

/* ---------------------------------------------------------------- INTENT */
.intent {
  padding-block: var(--space);
}
.intent__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  gap: var(--s-6);
  justify-items: start;
}
.intent__line {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--text-2xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-tight);
  color: var(--stone-dim);   /* resting shade; still clears AA (see maison.js) */
  max-width: 24ch;
  text-wrap: balance;
  --p: 0;      /* scroll progress 0..1, written by maison.js */
  --sweep: 3;  /* softness: how many words are mid-transition at once */
}
/* Each word fills dim -> ink as the sweep front (driven by --p) passes it.
   --wp is this word's local 0..1 progress; the front spans --sweep words so the
   highlight travels as a soft band rather than snapping one word at a time. */
.intent__word {
  --wp: clamp(0, calc((var(--p) * (var(--n) + var(--sweep)) - var(--i)) / var(--sweep)), 1);
  color: color-mix(in srgb, var(--stone) calc(var(--wp) * 100%), var(--stone-dim));
}

/* ---------------------------------------------------------------- VITRINE
   the signature moment: a pinned "focus pull". Each piece arrives full, then
   scrolling draws the eye into a macro of its surface, then it dissolves to the
   next. The DEFAULT styles below are the fallback (a clean vertical catalogue);
   maison.js adds .is-live to switch on the pinned, scroll-driven behaviour. */
.vitrine { padding-block: var(--space); }
.vitrine__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.vitrine__hud {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s-6);
  flex-wrap: wrap;
  margin-bottom: var(--s-9);
}
.vitrine__heading { display: grid; gap: var(--s-4); }
.vitrine__heading h2 { font-size: var(--text-2xl); font-weight: 400; }
.vitrine__count {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  color: var(--stone-dim);
  font-variant-numeric: tabular-nums;
}
.vitrine__ticks, .vitrine__hint { display: none; }

/* the works (fallback: a stacked vertical catalogue) */
.lots { display: grid; gap: var(--space); }
.lot { display: grid; gap: var(--s-7); justify-items: center; text-align: center; }
.lot__focus { display: grid; place-items: center; width: 100%; }
.lot__focus img {
  width: auto;
  height: auto;
  max-height: 70vh;
  max-width: min(100%, 640px);
  box-shadow: var(--shadow-mount);
}
.lot__placard {
  display: grid;
  gap: var(--s-3);
  justify-items: center;
  text-align: center;
  max-width: 46ch;
  margin-inline: auto;
}
.lot__no {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--stone-dim);
}
.lot__title { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 400; }
.lot__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--s-2) var(--s-5);
  font-size: var(--text-sm);
  color: var(--stone-mute);
}
.lot__meta span { white-space: nowrap; }
.lot__meta span + span { position: relative; padding-left: var(--s-5); }
.lot__meta span + span::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1px;
  height: 0.9em;
  transform: translateY(-50%);
  background: var(--hairline);
}

/* ---- LIVE: pinned focus-pull (added by maison.js when motion is allowed) ---- */
/* Heights use vh (not svh) on purpose: the scroll engine maps progress against
   window.innerHeight, so the pinned travel must be measured in the same unit or
   the last pieces never fully resolve on mobile. */
.vitrine.is-live {
  height: calc((var(--vlen, 5) + 1) * 100vh);
  padding-block: 0;
}
.vitrine.is-live .vitrine__inner {
  position: sticky;
  top: 0;
  height: 100vh;
  max-width: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
}
/* a persistent header + a bottom white fade so the placard reads over a macro */
.vitrine.is-live .vitrine__hud {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 4;
  margin: 0;
  padding: calc(var(--s-10) + var(--s-4)) var(--gutter) 0;
  align-items: flex-start;
}
.vitrine.is-live .vitrine__heading h2 { font-size: var(--text-lg); }
/* a white fade across the top beds the HUD over any macro that climbs into it */
.vitrine.is-live .vitrine__inner::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 18vh;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(to bottom, var(--ink) 10%, transparent);
}
.vitrine.is-live .lots { position: absolute; inset: 0; }
.vitrine.is-live .lot {
  position: absolute;
  inset: 0;
  display: block;
  isolation: isolate;       /* keep art / fade / placard ordering local to the lot */
  opacity: 0;
  will-change: opacity;
  transition: none;
}
/* a light white wash under the placard so its text always reads, kept subtle
   (per Chase) now that the piece itself feathers into the page on every edge */
.vitrine.is-live .lot::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 32vh;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(to top, var(--ink) 4%, transparent 100%);
}
.vitrine.is-live .lot__focus {
  position: absolute;
  inset: 0;
  place-items: center;
  padding-bottom: var(--s-9);   /* lift the piece clear of the placard band */
  z-index: 1;
}
.vitrine.is-live .lot__focus img {
  max-height: 72vh;
  max-width: min(86%, 760px);
  transform-origin: var(--fx, 50%) var(--fy, 40%);
  will-change: transform;
  transition: none;
  /* soft feather on ALL four edges so the floated piece melts into the white
     room evenly (Chase: "all around, less effect"). Two crossed gradients
     intersected; feather width is a single token. The ramp is eased — it
     climbs to full opacity quickly so only the outermost sliver is soft,
     keeping the fade off the body of the piece while the melt stays smooth. */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, color-mix(in srgb, var(--stone) 70%, transparent) calc(var(--vitrine-feather) * .4), var(--stone) var(--vitrine-feather), var(--stone) calc(100% - var(--vitrine-feather)), color-mix(in srgb, var(--stone) 70%, transparent) calc(100% - var(--vitrine-feather) * .4), transparent 100%),
    linear-gradient(to bottom, transparent 0, color-mix(in srgb, var(--stone) 70%, transparent) calc(var(--vitrine-feather) * .4), var(--stone) var(--vitrine-feather), var(--stone) calc(100% - var(--vitrine-feather)), color-mix(in srgb, var(--stone) 70%, transparent) calc(100% - var(--vitrine-feather) * .4), transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent 0, color-mix(in srgb, var(--stone) 70%, transparent) calc(var(--vitrine-feather) * .4), var(--stone) var(--vitrine-feather), var(--stone) calc(100% - var(--vitrine-feather)), color-mix(in srgb, var(--stone) 70%, transparent) calc(100% - var(--vitrine-feather) * .4), transparent 100%),
    linear-gradient(to bottom, transparent 0, color-mix(in srgb, var(--stone) 70%, transparent) calc(var(--vitrine-feather) * .4), var(--stone) var(--vitrine-feather), var(--stone) calc(100% - var(--vitrine-feather)), color-mix(in srgb, var(--stone) 70%, transparent) calc(100% - var(--vitrine-feather) * .4), transparent 100%);
  mask-composite: intersect;
}
.vitrine.is-live .lot__placard {
  position: absolute;
  inset: auto 0 var(--s-7) 0;
  z-index: 3;
  gap: var(--s-2);
  max-width: none;
}
/* a slim index ticker on the right, and the scroll hint */
.vitrine.is-live .vitrine__ticks {
  display: grid;
  gap: var(--s-3);
  position: absolute;
  right: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  z-index: 4;
  list-style: none;
  padding: 0;
}
.vitrine.is-live .vitrine__ticks li {
  width: var(--s-5);
  height: 1px;
  background: var(--hairline);
  transition: width var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.vitrine.is-live .vitrine__ticks li.is-on { width: var(--s-7); background: var(--accent); }
.vitrine.is-live .vitrine__hint {
  display: block;
  position: absolute;
  inset: auto 0 var(--s-5) 0;
  z-index: 4;
  text-align: center;
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--stone-dim);
  transition: opacity var(--dur) var(--ease);
}

/* ---------------------------------------------------------------- SURFACE */
.surface {
  position: relative;
  min-height: 88vh;
  display: grid;
  align-items: end;
  overflow: hidden;
  isolation: isolate;
}
/* directional scrim so the line is bedded into the image. THREE stacked passes
   of the vignette so the bottom band reads over bright, busy imagery without
   introducing a non-token colour. The middle pass was added 7/27 with the
   landscape surface plate: that crop puts silver butterfly highlights directly
   behind the line, and two passes measured 2.85:1 against the 3:1 this text
   needs. Three measures 4.4:1 at 390/1440/2560px. Do not drop back to two
   without re-measuring the pixels behind the text. */
.surface__veil {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to top,
      var(--vignette) 0%,
      color-mix(in srgb, var(--vignette) 40%, transparent) 34%,
      transparent 62%),
    linear-gradient(to top,
      var(--vignette) 0%,
      transparent 46%),
    linear-gradient(to top,
      var(--vignette) 0%,
      transparent 26%);
}
.surface__line {
  position: relative;
  z-index: 2;
  max-width: var(--maxw);
  margin-inline: auto;
  width: 100%;
  padding: var(--space) var(--gutter) var(--s-9);
  display: grid;
  gap: var(--s-4);
}
.surface__line .kicker { color: var(--on-film); opacity: 0.92; }
.surface__line p {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-tight);
  color: var(--on-film);
  max-width: 46ch;
}

/* ---------------------------------------------------------------- ARTIST */
.artist {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: var(--space) var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1fr);
  gap: clamp(var(--s-7), 7vw, var(--s-10));
  align-items: center;
}
.artist__portrait { position: relative; }
.artist__portrait img { width: 100%; height: auto; box-shadow: var(--shadow-mount-soft); }
.artist__body { display: grid; gap: var(--s-5); max-width: var(--maxw-text); }
.artist__body p { color: var(--stone); }
.artist__body p + p { color: var(--stone-mute); }
.artist__sign { margin-top: var(--s-4); display: grid; gap: var(--s-1); }
.artist__name { font-family: var(--font-display); font-size: var(--text-xl); }
.artist__role {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--stone-dim);
}

/* ---------------------------------------------------------------- PROVENANCE */
.prov-section { background: var(--ink-2); }
.prov {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: var(--space) var(--gutter);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--s-7), 6vw, var(--s-10));
  align-items: start;
}
/* With the Placements column removed, Recognition stands alone in a single
   centered column instead of the left half of the two-column grid. */
.prov--solo { grid-template-columns: 1fr; max-width: 760px; }
.prov__col { display: grid; gap: var(--s-6); align-content: start; }
.prov__col h2 { font-size: var(--text-xl); font-weight: 400; }
.prov__intro { color: var(--stone-mute); max-width: 40ch; }
.prov__rec { display: grid; gap: 0; padding: 0; list-style: none; }
.prov__rec li {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-5);
  align-items: baseline;
  padding: var(--s-4) 0;
  border-top: 1px solid var(--hairline-2);
  font-size: var(--text-base);
}
.prov__rec li:last-child { border-bottom: 1px solid var(--hairline-2); }
/* recognition rows that carry an article: quiet link, arrow surfaces on hover */
.prov__go {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: var(--s-5);
  align-items: baseline;
  width: 100%;
  color: inherit;
  text-decoration: none;
}
.prov__rec li:has(.prov__go) { grid-template-columns: 1fr; }
.prov__arrow {
  opacity: 0;
  transform: translateX(-4px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.prov__go:hover .prov__arrow,
.prov__go:focus-visible .prov__arrow { opacity: 1; transform: none; }
.prov__go:hover span:nth-child(2) { text-decoration: underline; text-underline-offset: 4px; }
.prov__rec .idx {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wide);
  color: var(--stone-dim);
  font-variant-numeric: tabular-nums;
}
.prov__places { display: grid; gap: 0; padding: 0; list-style: none; }
.prov__places li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-5);
  padding: var(--s-4) 0;
  border-top: 1px solid var(--hairline-2);
}
.prov__places li:last-child { border-bottom: 1px solid var(--hairline-2); }
/* placement rows that carry a detail page: same quiet-link pattern as recognition */
.prov__places li:has(.prov__place-go) { padding: 0; }
.prov__place-go {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--s-5);
  width: 100%;
  padding: var(--s-4) 0;
  color: inherit;
  text-decoration: none;
}
.prov__place-go .prov__where { margin-right: auto; }
.prov__place-go:hover .prov__arrow,
.prov__place-go:focus-visible .prov__arrow { opacity: 1; transform: none; }
.prov__place-go:hover .prov__where { text-decoration: underline; text-underline-offset: 4px; }
.prov__where { font-size: var(--text-lg); }
.prov__city {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--stone-dim);
}

/* ---------------------------------------------------------------- REQUEST */
.request {
  min-height: 88vh;
  display: grid;
  place-items: center;
  text-align: center;
  padding: var(--space) var(--gutter);
}
.request__inner { display: grid; justify-items: center; gap: var(--s-5); max-width: 50ch; }
.request__title { font-size: var(--text-3xl); font-weight: 400; }
.request__sub { font-size: var(--text-lg); color: var(--stone-mute); line-height: var(--leading-body); }
.request__actions {
  margin-top: var(--s-4);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-5);
  align-items: center;
  justify-content: center;
}
/* The phone link is a tap target too; lift it to the 44px minimum. */
.request__actions .link {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
}

/* ---------------------------------------------------------------- FOOTER */
.footer {
  border-top: 1px solid var(--hairline-2);
  padding: var(--s-9) var(--gutter) var(--s-7);
}
.footer__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--s-7);
}
.footer__brand { display: grid; gap: var(--s-2); align-content: start; }
.footer__name { line-height: 1; color: var(--stone); }
/* Same masked signature as the nav, painted in the footer's ink so it reads
   dark on the white skin and light on the Noir skin. */
.footer__brandmark {
  display: block;
  height: clamp(9rem, 12vw, 11.7rem);
  aspect-ratio: 3125 / 1562;
  background-color: currentColor;
  -webkit-mask: url(../assets/chase-parker-logo-white.png) center left / contain no-repeat;
  mask: url(../assets/chase-parker-logo-white.png) center left / contain no-repeat;
}
.footer__tag {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--stone-dim);
}
.footer__contact { display: grid; gap: 0; justify-items: end; text-align: right; }
.footer__label {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--stone-dim);
  margin-bottom: var(--s-1);
}
.footer__contact a {
  font-size: var(--text-sm);
  color: var(--stone-mute);
  /* Chris asked the three contact lines to sit closer, so the tap target is
     tightened from the 44px house guideline to 32px. That still clears the
     WCAG 2.2 AA target-size minimum (24px, SC 2.5.8) for these stacked links. */
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  transition: color var(--dur-fast) var(--ease);
}
.footer__contact a:hover { color: var(--stone); }
/* Instagram row: the glyph and the handle read as one control. */
.footer__ig { gap: var(--s-2); }
.footer__ig-icon { flex: none; }
.footer__bottom {
  max-width: var(--maxw);
  margin: var(--s-8) auto 0;
  display: flex;
  justify-content: space-between;
  /* The legal links carry a 44px tap target, so without this the copyright and
     the district sit off their baseline. */
  align-items: center;
  gap: var(--s-5);
  flex-wrap: wrap;
  font-size: var(--text-xs);
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--stone-dim);
}
/* Marketing footer bottom row: the legal links sit centered on the PAGE, with
   the copyright hard left and the district hard right. A 1fr/auto/1fr grid
   centers the middle cell regardless of the unequal side widths (plain
   space-between only looks centered when the sides happen to match). */
.footer:not(.footer--legal) .footer__bottom {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}
.footer:not(.footer--legal) .footer__bottom > :first-child { justify-self: start; }
.footer:not(.footer--legal) .footer__bottom > :last-child { justify-self: end; }
.footer:not(.footer--legal) .footer__legal { justify-self: center; justify-content: center; }
/* The legal set. In the marketing footer it sits on the bottom rule beside the
   copyright; on a legal page it stacks as its own column. */
.footer__legal { display: flex; flex-wrap: wrap; gap: var(--s-5); align-items: center; }
.footer__legal a {
  color: var(--stone-dim);
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  transition: color var(--dur-fast) var(--ease);
}
.footer__legal a:hover { color: var(--stone); }
.footer__legal a[aria-current="page"] { color: var(--stone); }
.footer__legal--stack {
  display: grid;
  gap: var(--s-2);
  justify-items: end;
  text-align: right;
}
.footer__legal--stack a { font-size: var(--text-sm); color: var(--stone-mute); }
.footer--legal .footer__bottom { justify-content: space-between; }

/* ---------------------------------------------------------------- COMMISSION
   The centerpiece. The collaboration is the heart of working with Chase, so it
   gets the most air: a large lead statement set in the display roman, then three
   numbered beats with big ghost numerals (depth, per the doctrine), then the
   "shouldn't / won't" close. Its own layout, shared with no other section. */
.commission {
  padding-block: var(--space);
  border-top: 1px solid var(--hairline-2);
}
.commission__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
  display: grid;
  gap: clamp(var(--s-8), 7vw, var(--s-10));
}
.commission__lead { display: grid; gap: var(--s-6); }
.commission__lead h2 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-tight);
  color: var(--stone);
  max-width: 28ch;       /* on the heading, so ch tracks the display size */
  text-wrap: balance;
}
.commission__steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(var(--s-6), 4vw, var(--s-8));
}
.commission__step {
  display: grid;
  gap: var(--s-4);
  align-content: start;
  padding-top: var(--s-6);
  border-top: 1px solid var(--hairline);
  position: relative;
  isolation: isolate;
}
.commission__no {
  font-family: var(--font-display);
  font-size: var(--text-3xl);
  line-height: 1;
  color: var(--ghost-strong);    /* far-plane ghost numeral */
  font-variant-numeric: lining-nums;
}
.commission__step h3 {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  color: var(--stone);
}
.commission__step p { color: var(--stone-mute); max-width: 34ch; }
.commission__close {
  font-family: var(--font-display);
  font-size: var(--text-xl);
  line-height: var(--leading-snug);
  letter-spacing: var(--track-tight);
  color: var(--stone);
  max-width: 40ch;
  justify-self: start;
  text-wrap: balance;
}

/* ---------------------------------------------------------------- EVENTS
   "See the work in person." A quiet schedule: a date block on the left, the
   venue and a kind tag on the right, separated by hairlines. */
.events { background: var(--ink-2); }
/* scripts/maison.js hides the whole band when the live calendar comes back
   empty. Stated explicitly, as .capture and .navmenu do, so a later `display:`
   on .events cannot silently out-specify the UA's [hidden] rule. */
.events[hidden] { display: none; }
.events__inner {
  max-width: var(--maxw);
  margin-inline: auto;
  padding: var(--space) var(--gutter);
  display: grid;
  gap: clamp(var(--s-7), 5vw, var(--s-9));
}
.events__head { display: grid; gap: var(--s-5); max-width: 40ch; }
/* h2 on the home band; h1 on the dedicated events page (single h1 per page) */
.events__head h1,
.events__head h2 { font-family: var(--font-display); font-weight: 400; font-size: var(--text-2xl); }
.events__intro { color: var(--stone-mute); }
/* "See more events" sits with the list, not a full section-gap below it */
.events__more { margin-top: calc(-1 * clamp(var(--s-5), 3vw, var(--s-7))); }
.events__list { display: grid; gap: 0; padding: 0; list-style: none; }
.event {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: clamp(var(--s-5), 4vw, var(--s-8));
  padding: var(--s-6) 0;
  border-top: 1px solid var(--hairline);
}
.events__list .event:last-child { border-bottom: 1px solid var(--hairline); }
.event__date {
  display: grid;
  justify-items: center;
  gap: var(--s-1);
  min-width: 4ch;
}
.event__month {
  font-size: var(--text-xs);
  letter-spacing: var(--track-wider);
  text-transform: uppercase;
  color: var(--stone-dim);
}
.event__day { font-family: var(--font-display); font-size: var(--text-xl); line-height: 1; color: var(--stone); }
.event__year { font-size: var(--text-xs); letter-spacing: var(--track-wide); color: var(--stone-dim); }
.event__main { display: grid; gap: var(--s-2); }
.event__title { font-family: var(--font-display); font-size: var(--text-lg); color: var(--stone); }
.event__where {
  font-size: var(--text-sm);
  color: var(--stone-mute);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
}
/* The start time, when the calendar event has one. Sits with the venue and the
   city rather than in the date column, which is 4ch wide and would break
   "6:30 PM" across two lines. Never wraps mid-time. */
.event__time { letter-spacing: var(--track-wide); white-space: nowrap; }
.event__where span + span { position: relative; padding-left: var(--s-4); }
.event__where span + span::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 1px;
  height: 0.9em;
  transform: translateY(-50%);
  background: var(--hairline);
}

/* --- Events skeleton ---
   Stands in for the rows between first paint and the calendar feed answering
   (scripts/events-feed.js). Each bar is sized to the text it replaces so the
   real dates land in the rows the skeleton already reserved. Deliberately
   quiet: this is a held breath, not a loading animation competing with the
   photography above it. */
.skel {
  display: block;
  border-radius: 1px;
  background: var(--hairline);
  animation: skel-pulse 1.6s ease-in-out infinite;
}
.skel--month,
.skel--year { width: 3.5ch; height: var(--text-xs); }
.skel--day { width: 2.5ch; height: var(--text-xl); }
.skel--title { width: min(14ch, 60%); height: calc(var(--text-lg) * 1.2); }
.skel--where { width: min(22ch, 80%); height: calc(var(--text-sm) * 1.2); }

@keyframes skel-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

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

/* ---------------------------------------------------------------- CAPTURE MODAL
   Demo only (not wired). A refined invitation, not a marketing interrupt. */
.capture {
  position: fixed;
  inset: 0;
  z-index: var(--z-nav);
  display: grid;
  place-items: center;
  padding: var(--gutter);
}
.capture[hidden] { display: none; }
.capture__scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.capture.is-open .capture__scrim { opacity: 1; }
.capture__card {
  position: relative;
  z-index: 1;
  width: min(34rem, 100%);
  background: var(--ink);
  border: 1px solid var(--hairline-2);
  box-shadow: var(--shadow-mount);
  padding: clamp(var(--s-7), 5vw, var(--s-9));
  opacity: 0;
  transform: translateY(var(--s-5));
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.capture.is-open .capture__card { opacity: 1; transform: none; }
.capture__close {
  position: absolute;
  top: var(--s-4);
  right: var(--s-4);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  color: var(--stone-dim);
  font-size: var(--text-xl);
  line-height: 1;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease);
}
.capture__close:hover { color: var(--stone); }
.capture__body { display: grid; gap: var(--s-5); }
.capture__body h2 { font-family: var(--font-display); font-weight: 400; font-size: var(--text-xl); color: var(--stone); }
.capture__body p { color: var(--stone-mute); }
.capture__form { display: grid; gap: var(--s-4); margin-top: var(--s-2); }
/* the submit is a real <button>, so it must declare its own fill (otherwise it
   inherits the UA default). Primary action = solid accent in either skin. */
.capture__form .btn {
  justify-content: center;
  background: var(--accent);
  color: var(--ink);
  border-color: var(--accent);
}
.capture__field {
  width: 100%;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--stone);
  background: var(--ink);
  border: 1px solid var(--hairline);
  padding: var(--s-4);
  min-height: 44px;
}
.capture__field::placeholder { color: var(--stone-dim); }
.capture__field:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.capture__optin {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s-3);
  align-items: start;
  font-size: var(--text-sm);
  color: var(--stone-mute);
  cursor: pointer;
}
.capture__optin input { margin-top: 0.2em; width: 18px; height: 18px; accent-color: var(--accent); }
.capture__thanks { color: var(--stone); font-family: var(--font-display); font-size: var(--text-lg); }
.capture__thanks-body { color: var(--stone-mute); line-height: var(--leading-body); }

/* Request-modal validation error. The message text is the indicator (color is
   never the sole cue); a left rule and weight set it apart from the fields. */
.capture__error {
  color: var(--stone);
  font-size: var(--text-sm);
  line-height: var(--leading-snug);
  padding-left: var(--s-3);
  border-left: 2px solid var(--accent);
}
.capture__error[hidden] { display: none; }

/* ---------------------------------------------------------------- RESPONSIVE */
@media (max-width: 900px) {
  .nav { grid-template-columns: 1fr auto 1fr; }
  .nav__links { display: none; }
  .nav__brand { grid-column: 2; }
  .nav__toggle { display: inline-flex; }
  /* The legal masthead has no hamburger, so its links must survive here or the
     only way back to the site disappears on a phone. */
  .nav--legal .nav__links { display: flex; }
  .artist,
  .prov { grid-template-columns: 1fr; gap: var(--s-8); }
  .artist__portrait { max-width: 460px; }
  .footer__contact { justify-items: start; text-align: left; }
  .footer__legal--stack { justify-items: start; text-align: left; }
  .commission__steps { grid-template-columns: 1fr; gap: var(--s-7); }
  /* The 1fr/auto/1fr bottom row can't wrap, so on narrow screens it stacks
     into one centered column. */
  .footer:not(.footer--legal) .footer__bottom {
    grid-template-columns: 1fr;
    justify-items: center;
    text-align: center;
    gap: var(--s-4);
  }
  .footer:not(.footer--legal) .footer__bottom > :first-child,
  .footer:not(.footer--legal) .footer__bottom > :last-child { justify-self: center; }
}

@media (max-width: 560px) {
  .event {
    grid-template-columns: auto 1fr;
    gap: var(--s-5);
    row-gap: var(--s-4);
  }
}

@media (max-width: 560px) {
  .lot__focus img { max-height: 60vh; }
  .vitrine.is-live .lot__focus img { max-height: 64vh; max-width: 92%; }
  .vitrine.is-live .vitrine__ticks { display: none; }
  .lot__meta { gap: var(--s-2) var(--s-4); }
  .footer__inner { gap: var(--s-6); }
  .vitrine__hud { align-items: flex-start; }
  /* Full-width CTA so it meets the section edges instead of drifting centered,
     and the long label wraps centered rather than orphaning the arrow. */
  .request__actions .btn { width: 100%; justify-content: center; }
}

/* ---------------------------------------------------------------- MOTION OFF */
@media (prefers-reduced-motion: reduce) {
  .cine img,
  .cine video { animation: none; transform: scale(1.02); }
  .cine__sweep { display: none; }
  .hero__scroll i::after { animation: none; }
  /* the vitrine is never set .is-live under reduced-motion (see maison.js), so
     it stays the plain vertical catalogue defined above. */
}

/* Honeypot. The UA's [hidden] rule is weak: any `display` on a form input would
   silently out-specify it and put the trap on screen. Same defensive pattern as
   .capture[hidden] and .events[hidden] above. */
.capture__trap[hidden] { display: none; }
