/* ==========================================================================
   The Barber Castle — homepage
   ========================================================================== */

/* ==========================================================================
   1. HERO — the lockup as the mechanic

   Adapted from _library/hero-animations/23. The brand's two words start pushed
   to the outer edges with the shop's photography rising in the gap between them,
   then converge on the crest. The words ARE the layout, which is why this is not
   a headline sitting on top of a photograph.

   The h1 is visually hidden and the letters are aria-hidden, so assistive tech
   reads one clean "Barber Castle — men's salon in Dubai and Helwan" rather than
   twelve separate letters.
   ========================================================================== */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  min-height: 100svh;
  padding-top: var(--header-h);
  overflow: hidden;
  isolation: isolate;
  background: var(--bc-canvas);
}

/* The stage is a fixed-height window; the words and panels move inside it. */
.hero__stage {
  position: relative;
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.55fr) minmax(0, 0.82fr);
  align-items: stretch;
  gap: clamp(0.75rem, 1.6vw, 1.5rem);
  width: 100%;
  min-height: 0;
  padding: clamp(1rem, 3vh, 2.5rem) 0;
}

/* --- The two words ------------------------------------------------------- */

.hero__word {
  display: flex;
  align-self: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: clamp(3.5rem, 9.5vw, 10rem);
  line-height: 0.84;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--bc-ink);
  will-change: transform;
}

/* Each letter rises from behind its own mask, so the word is revealed as
   geometry rather than cross-faded. */
.hero__letter {
  display: block;
  clip-path: inset(0 0 105% 0);
  will-change: clip-path, transform;
}

/* The spread is deliberately modest. A larger one reads as a stronger gesture
   but pushes a six-letter word past the viewport edge mid-animation, so the
   first thing a visitor sees is half a word. */
/*
 * The convergence, composed with the hover spread.
 *
 * `--hero-spread` is written by hero-parallax.js from the CSS-owned
 * `--hero-hover` intent. The word's resting position is the CSS transform here,
 * and the parallax adds its own offset on top, so the two never overwrite each
 * other. With the parallax absent — reduced motion, or a coarse pointer — the
 * spread still applies on hover, because it is a transform and not JS-owned.
 */
.hero__word {
  transform: translateX(var(--hero-spread, 0%));
}

.hero__word--a {
  --hero-rest: -11%;
}

.hero__word--b {
  --hero-rest: 11%;
}

/* The grid reverses under RTL, so word A is now the one on the right and the
   offsets have to swap with it: each word still starts pushed outboard and
   converges on the crest. hero-wordmark.js animates the same two values and
   flips its signs to match. */
[dir="rtl"] .hero__word--a {
  --hero-rest: 11%;
}

[dir="rtl"] .hero__word--b {
  --hero-rest: -11%;
}

.hero__word--a,
.hero__word--b {
  /* The opening timeline sets `translateX(±11%)` inline and then clears it. The
     resting value lives here so the hover spread has something to compose with
     after the intro finishes. */
  translate: var(--hero-rest, 0) 0;
}

/* --- The photography in the gap ------------------------------------------ */

.hero__gap {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  align-items: center;
  gap: clamp(0.4rem, 1vw, 0.85rem);
  height: 100%;
  min-height: 0;
}

.hero__panel {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-1);
  background: var(--bc-surface);
  /* Wiped open with a clip, staggered, exactly as the reference does. */
  clip-path: inset(100% 0 0 0);
  will-change: clip-path;
}

.hero__panel img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.14);
  will-change: transform;
}

/* Ragged heights, but all tall enough that the strip reads as photography
   rather than as three thumbnails. The centre panel is tallest because it
   carries the mark on the brass partition. */
/*
 * The resting state, expressed in CSS as well as in JS.
 *
 * `initHeroWordmark` sets `is-open` from its catch handler when the module fails
 * to load, and the safety timers in `main.js` set it if the scene never runs.
 * Without these rules that fallback was inert: the letters stayed clipped at
 * `inset(0 0 105% 0)` and the photography at `inset(100% 0 0 0)`, so the one
 * path meant to guarantee the headline is visible was the path that hid it.
 */
.hero.is-open .hero__letter {
  clip-path: inset(0 0 -20% 0);
}

.hero.is-open .hero__panel {
  clip-path: inset(0 0 0 0);
}

.hero.is-open .hero__crest {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.hero__panel--1 { height: 66%; }
.hero__panel--2 { height: 100%; }
.hero__panel--3 { height: 58%; }

/* --- The crest at the convergence point --------------------------------- */

/* The convergence point of the two words. The centre panel was chosen
   specifically because it shows brass slats and NOT a second copy of the mark,
   so this is the only mark on screen and it can sit exactly on the axis. */
.hero__crest {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 3;
  width: clamp(3.5rem, 7.5vw, 7rem);
  transform: translate(-50%, -50%) scale(0.86);
  color: var(--bc-brass);
  opacity: 0;
  filter: drop-shadow(0 0 26px rgba(200, 160, 104, 0.5));
  pointer-events: none;
  will-change: transform, opacity;
}

.hero__crest svg {
  display: block;
  width: 100%;
  height: auto;
}

/* The convergence point sits on top of a gold photograph, and a gold mark on
   a gold photograph is invisible. It therefore gets its own solid plate, cut as
   a beveled brass disc rather than a soft glow. */
.hero__crest::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: -1;
  width: 160%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background:
    radial-gradient(closest-side, var(--bc-canvas) 72%, rgba(13, 15, 14, 0.9) 100%);
  box-shadow:
    0 0 0 1px rgba(200, 160, 104, 0.45),
    0 18px 40px -18px rgba(0, 0, 0, 0.9);
}

/* --- Below the stage ----------------------------------------------------- */

.hero__foot {
  position: relative;
  z-index: 4;
  padding-bottom: clamp(1.5rem, 4vw, 3rem);
  background: linear-gradient(to top, var(--bc-canvas) 55%, rgba(13, 15, 14, 0));
}

.hero__foot-inner {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  grid-template-areas:
    "lede actions"
    "facts facts";
  align-items: end;
  gap: clamp(1rem, 2.5vw, 2.5rem) clamp(1.5rem, 4vw, 4rem);
}

.hero__lede { grid-area: lede; }
.hero__actions { grid-area: actions; justify-content: flex-end; align-items: center; }
.hero__facts { grid-area: facts; max-width: none; }

.hero__lede {
  max-width: 46ch;
  font-size: var(--fs-body-lg);
  color: var(--bc-ink-2);
  text-wrap: pretty;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}

/* Keep the actions from being squeezed when the lede runs long. */
@media (max-width: 1000px) {
  .hero__foot-inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "lede"
      "actions"
      "facts";
  }

  .hero__actions {
    justify-content: flex-start;
  }
}

.hero__facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-4) var(--s-5);
  margin: 0;
  padding-top: var(--s-4);
  border-top: 1px solid var(--bc-line);
  max-width: 62ch;
}

.hero__facts dt {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
  margin-bottom: 0.3rem;
}

.hero__facts dd {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
}

/* --- Tablet: two words beside a narrower strip -------------------------- */

@media (max-width: 1000px) {
  .hero__stage {
    grid-template-columns: 1fr minmax(0, 1fr) 1fr;
  }

  .hero__word {
    font-size: clamp(3rem, 8vw, 6rem);
  }

  .hero__panel--3 {
    display: none;
  }

  .hero__gap {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* --- Phone: the words stack, the photography becomes a band ------------- */

@media (max-width: 767px) {
  .hero {
    min-height: auto;
  }

  .hero__stage {
    display: flex;
    flex-direction: column;
    gap: var(--s-4);
    padding: var(--s-5) 0 var(--s-4);
  }

  .hero__word {
    font-size: clamp(2.25rem, 12.5vw, 4.25rem);
    transform: none;
  }

  /* BARBER sets flush left, CASTLE flush right, and both are allowed to shrink
     rather than overflow: a clipped wordmark is worse than a smaller one. */
  .hero__word--a { align-self: flex-start; }
  .hero__word--b { align-self: flex-end; }

  .hero__gap {
    width: 100%;
    height: clamp(11rem, 34vw, 15rem);
  }

  .hero__gap {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .hero__panel--1,
  .hero__panel--2,
  .hero__panel--3 {
    display: block;
    height: 100%;
  }

  /* The mark is too small to read on a phone between two words, so it is
     dropped from the convergence point rather than left as a smudge. */
  .hero__crest {
    display: none;
  }

  .hero__facts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 420px) {
  .hero__facts {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Reduced motion: the words and panels are simply present. The stage keeps its
   final layout, so nothing is displaced and nothing needs a fallback image. */
@media (prefers-reduced-motion: reduce) {
  .hero__word {
    transform: none !important;
  }

  .hero__letter {
    clip-path: none !important;
    transform: none !important;
  }

  .hero__panel {
    clip-path: none !important;
  }

  .hero__panel img {
    transform: none !important;
  }

  .hero__crest {
    opacity: 1;
    transform: translate(-50%, -50%);
  }
}

/* --- 2. Branch grid ------------------------------------------------------ */


.tile--placeholder .tile__placeholder-art {
  position: absolute;
  inset: 0;
  z-index: -2;
  display: grid;
  place-items: center;
  background:
    repeating-linear-gradient(90deg, rgba(200, 160, 104, 0.16) 0 2px, transparent 2px 26px),
    linear-gradient(160deg, var(--bc-green) 0%, #0a1c19 100%);
}

.tile--placeholder .tile__placeholder-art .crest {
  width: clamp(5rem, 12vw, 9rem);
  aspect-ratio: 1;
  background: var(--bc-brass);
  opacity: 0.9;
  -webkit-mask: url("/assets/img/crest-mask.svg") center/contain no-repeat;
  mask: url("/assets/img/crest-mask.svg") center/contain no-repeat;
}

@media (max-width: 860px) {
  .branch-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- 3. Proof ------------------------------------------------------------ */


/* --- 4. Story ------------------------------------------------------------ */




/* --- 5. Services grid ---------------------------------------------------- */











@media (max-width: 960px) {
  .service-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 600px) {
  .service-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- 6. Craft ring (3D Animation 3 adaptation) --------------------------- */

.craft {
  position: relative;
  height: 280vh;
  background: #060807;
}

.craft__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  background: radial-gradient(70% 55% at 58% 52%, rgba(18, 86, 76, 0.4) 0%, rgba(6, 8, 7, 0) 72%);
}

/* The glow is centred on the ring, and the ring sits clear of the caption
   column. Under RTL the caption column is on the right, so the ring moves left
   and the glow moves with it. */
[dir="rtl"] .craft__stage {
  background: radial-gradient(70% 55% at 42% 52%, rgba(18, 86, 76, 0.4) 0%, rgba(6, 8, 7, 0) 72%);
}

.craft__canvas {
  position: absolute;
  inset: 0;
  z-index: 1;
}

.craft__canvas canvas {
  width: 100%;
  height: 100%;
}

/* Keeps the caption column legible over the brightest part of the ring. */
.craft__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    rgba(6, 8, 7, 0.92) 0%,
    rgba(6, 8, 7, 0.72) 26%,
    rgba(6, 8, 7, 0.18) 52%,
    rgba(6, 8, 7, 0) 70%
  );
}

/* The scrim exists to keep the caption column legible over the brightest part
   of the ring, so it has to darken the side the captions are now on. A gradient
   angle is physical, not logical: 100deg runs left-to-right and has to become
   260deg, which is the same gradient mirrored, not a different one. */
[dir="rtl"] .craft__stage::after {
  background: linear-gradient(
    260deg,
    rgba(6, 8, 7, 0.92) 0%,
    rgba(6, 8, 7, 0.72) 26%,
    rgba(6, 8, 7, 0.18) 52%,
    rgba(6, 8, 7, 0) 70%
  );
}

.craft__fallback {
  position: absolute;
  inset: 0;
  display: none;
}

.craft__fallback img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.5;
}

.craft__beats {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: block;
  pointer-events: none;
}

.craft__beat {
  position: absolute;
  inset-inline-start: var(--gutter);
  bottom: clamp(3rem, 12vh, 8rem);
  max-width: min(36ch, 80vw);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* The scene sets `opacity` and `is-active` inline on each frame. */
.craft__beat.is-active {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
}

.craft__beat .btn {
  margin-top: 0.5rem;
}

/* Static, no-WebGL presentation: normal stacked beats over the photo */
.craft--static {
  height: auto;
}

.craft--static .craft__stage {
  position: relative;
  height: auto;
  min-height: 0;
  display: block;
  padding-block: var(--section-y);
}

.craft--static .craft__stage::after {
  display: none;
}

.craft--static .craft__fallback {
  display: block;
  position: relative;
  inset: auto;
  height: clamp(18rem, 50vw, 30rem);
  margin-bottom: clamp(2rem, 4vw, 3rem);
  border-radius: var(--r-1);
  overflow: hidden;
}

.craft--static .craft__fallback img {
  opacity: 0.72;
}

.craft--static .craft__beats {
  position: static;
  inset: auto;
}

.craft--static .craft__beat {
  position: static;
  max-width: 46ch;
  opacity: 1;
  visibility: visible;
  margin-bottom: 2.5rem;
  pointer-events: auto;
}

@media (max-width: 900px) {
  .craft {
    height: 220vh;
  }
  .craft__beat {
    max-width: min(26ch, 82vw);
    bottom: 2.5rem;
  }
}

/* --- 7. Packages --------------------------------------------------------- */

.package-row {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--bc-line);
}

.package-row__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: 0.5rem clamp(1rem, 3vw, 3rem);
  padding: 1.25rem 0;
  border-bottom: 1px solid var(--bc-line);
}

.package-row__name {
  font-family: var(--font-display);
  font-size: var(--fs-display-s);
  text-transform: uppercase;
  line-height: 1;
}

.package-row__was {
  font-size: var(--fs-small);
  color: var(--bc-ink-3);
  text-decoration: line-through;
}

.package-row__price {
  font-size: var(--fs-body-lg);
  font-weight: 600;
  color: var(--bc-brass);
  white-space: nowrap;
}

/* --- 8. Memberships ------------------------------------------------------ */


.plan {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  padding: clamp(1.5rem, 2.4vw, 2rem);
  border: 1px solid rgba(244, 242, 236, 0.22);
  border-radius: var(--r-1);
  background: rgba(9, 24, 21, 0.35);
}

.plan--feature {
  background: rgba(9, 24, 21, 0.62);
  border-color: var(--bc-brass);
}

.plan__flag {
  margin: 0;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
}







@media (max-width: 860px) {
  .plan-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- 9. Standards -------------------------------------------------------- */

.standards {
  list-style: none;
  margin: 2rem 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.standards h3 {
  font-family: var(--font-body);
  font-size: var(--fs-h4);
  font-weight: 600;
  margin-bottom: 0.35rem;
}

.standards p {
  color: var(--bc-ink-2);
  font-size: var(--fs-small);
  margin: 0;
  max-width: 46ch;
}

.standards li {
  padding-top: 1.25rem;
  border-top: 1px solid var(--bc-line);
}

/* --- 10. Selected work --------------------------------------------------- */

.work-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  grid-auto-rows: clamp(140px, 20vw, 240px);
  gap: clamp(0.5rem, 1.2vw, 1rem);
}

.work {
  position: relative;
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-1);
}

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

.work:hover img {
  transform: scale(1.05);
}

.work figcaption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 2.5rem 1rem 0.85rem;
  font-size: var(--fs-meta);
  letter-spacing: 0.04em;
  color: var(--bc-ink);
  background: linear-gradient(to top, rgba(8, 10, 9, 0.85), transparent);
}

.work--tall {
  grid-row: span 2;
}

.work--wide {
  grid-column: span 2;
}

@media (max-width: 900px) {
  .work-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-auto-rows: clamp(130px, 30vw, 200px);
  }
  .work--wide {
    grid-column: span 2;
  }
}

/* --- 11. Team ------------------------------------------------------------ */






@media (max-width: 700px) {
  .team-strip {
    grid-template-columns: minmax(0, 1fr);
    max-width: 420px;
  }
}

/* --- 12. Products -------------------------------------------------------- */

.products {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: center;
}


@media (max-width: 760px) {
  .products {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- 14. Posts ----------------------------------------------------------- */







@media (max-width: 800px) {
  .post-grid {
    grid-template-columns: minmax(0, 1fr);
    max-width: 460px;
  }
}

/* --- 15. Reviews --------------------------------------------------------- */

.reviews {
  display: grid;
  grid-template-columns: minmax(0, 0.7fr) minmax(0, 1.3fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: center;
  padding: clamp(1.75rem, 3.5vw, 3rem);
  border: 1px solid var(--bc-line);
  border-radius: var(--r-1);
  background: var(--bc-surface);
}






@media (max-width: 860px) {
  .reviews {
    grid-template-columns: minmax(0, 1fr);
  }
  .reviews__score {
    border-right: 0;
    border-bottom: 1px solid var(--bc-line);
    padding-right: 0;
    padding-bottom: 1.5rem;
  }
}

/* --- 16. Visit ----------------------------------------------------------- */

.visit {
  display: grid;
  grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
  gap: clamp(1.5rem, 3vw, 3rem);
  align-items: center;
}


.visit__facts {
  margin: 1.75rem 0 2rem;
  display: flex;
  flex-direction: column;
}

.visit__facts > div {
  display: grid;
  grid-template-columns: 8rem minmax(0, 1fr);
  gap: 1rem;
  padding: 0.9rem 0;
  border-top: 1px solid var(--bc-line);
}

.visit__facts dt {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
  padding-top: 0.2rem;
}

.visit__facts dd {
  margin: 0;
  font-size: var(--fs-small);
}

.visit__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
}

@media (max-width: 860px) {
  .visit {
    grid-template-columns: minmax(0, 1fr);
  }
  .visit__media {
    order: 2;
  }
  .visit__facts > div {
    grid-template-columns: 6rem minmax(0, 1fr);
  }
}

/* --- 17. Closing --------------------------------------------------------- */

.closing {
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(18, 86, 76, 0.5) 0%, rgba(13, 15, 14, 0) 70%),
    var(--bc-canvas);
  border-top: 1px solid var(--bc-line);
  text-align: center;
}

.closing__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.25rem;
}

.closing__inner .eyebrow {
  margin-bottom: 0;
}

.closing__inner .lede {
  max-width: 52ch;
}

.closing__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-3);
  justify-content: center;
  margin-top: 0.75rem;
}
@media (max-width: 560px) {
  .hero__actions .btn,
  .closing__actions .btn,
  .visit__actions .btn {
    width: 100%;
  }
}

/* --- Hero photo variable (fed from the real <img> so the mark can never drift
       from the photograph underneath it) -------------------------------------- */

.hero {
  --hero-photo: none;
}

/* ==========================================================================
   BRAND FILM
   Three of the shop's own reels, presented at their native 9:16 so nothing is
   cropped and nothing is letterboxed. Posters load first and the video bytes
   are only fetched when a visitor presses play, which keeps the ~7 MB of film
   off the critical path entirely.
   ========================================================================== */

.film-strip__row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--s-5);
  margin: var(--s-7) 0 0;
  padding: 0;
  list-style: none;
}

.film-card {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
}

.film-card__frame {
  position: relative;
  margin: 0;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  border-radius: var(--r-2);
  background: var(--bc-surface);
  /* Brass hairline, brightest along the top edge where the light falls. */
  border: 1px solid var(--bc-line-brass);
  box-shadow: 0 18px 40px -28px rgba(0, 0, 0, 0.9);
  transition: border-color var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out);
}

.film-card:hover .film-card__frame {
  border-color: rgba(200, 160, 104, 0.7);
  transform: translateY(-3px);
}

.film-card__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* Slight grade so the three reels read as one set rather than three clips. */
  filter: saturate(1.02) contrast(1.03);
  transition: opacity var(--dur) var(--ease-out);
}

/* The still is a real <img loading="lazy"> rather than a poster attribute, so
   the browser defers it until the card nears the viewport. It sits above the
   video and hands over on playback. Without scripting the frame still reads as
   a photograph with a working play control. */
.film-card__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity var(--dur) var(--ease-out);
}

.film-card.is-playing .film-card__poster {
  opacity: 0;
}

/* A gentle push-in while playing, so a still poster does not look broken. */
.film-card.is-playing .film-card__video {
  animation: film-drift 18s linear infinite alternate;
}

@keyframes film-drift {
  from { transform: scale(1); }
  to { transform: scale(1.045); }
}

/* Bottom scrim keeps the duration legible over bright footage. */
.film-card__frame::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 42%;
  background: linear-gradient(to top, rgba(6, 8, 7, 0.78), rgba(6, 8, 7, 0));
  pointer-events: none;
}

.film-card__play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  color: var(--bc-brass-soft);
  transition: color var(--dur-fast) var(--ease-out);
}

.film-card__play::before {
  content: "";
  position: absolute;
  width: clamp(3.5rem, 8vw, 4.5rem);
  height: clamp(3.5rem, 8vw, 4.5rem);
  border-radius: var(--r-pill);
  background: rgba(6, 8, 7, 0.42);
  border: 1px solid rgba(200, 160, 104, 0.55);
  backdrop-filter: blur(3px);
  transition: background var(--dur) var(--ease-out),
              border-color var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-out);
}

.film-card__play:hover::before,
.film-card__play:focus-visible::before {
  background: rgba(200, 160, 104, 0.9);
  border-color: var(--bc-brass-bright);
  transform: scale(1.06);
}

.film-card__play:focus-visible {
  outline: none;
}

.film-card__play:focus-visible::before {
  box-shadow: 0 0 0 3px var(--bc-canvas), 0 0 0 5px var(--bc-focus);
}

.film-card__icon {
  grid-area: 1 / 1;
  position: relative;
  width: clamp(1.5rem, 3.4vw, 1.875rem);
  height: clamp(1.5rem, 3.4vw, 1.875rem);
  transition: opacity var(--dur-fast) var(--ease-out),
              transform var(--dur) var(--ease-out);
}

.film-card__play:hover .film-card__icon {
  color: var(--bc-ink-on-brass);
}

/* Play shows until playing; pause takes over. */
.film-card__icon--pause {
  opacity: 0;
  transform: scale(0.7);
}

.film-card.is-playing .film-card__icon--play {
  opacity: 0;
  transform: scale(0.7);
}

.film-card.is-playing .film-card__icon--pause {
  opacity: 1;
  transform: scale(1);
}

/* Once playing the button only needs a small target for stopping. */
.film-card.is-playing .film-card__play {
  align-items: end;
  justify-items: end;
  pointer-events: none;
}

.film-card.is-playing .film-card__play::before {
  width: 2.75rem;
  height: 2.75rem;
  inset: auto var(--s-3) var(--s-3) auto;
  position: absolute;
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out);
  pointer-events: auto;
}

.film-card.is-playing .film-card__play:hover::before,
.film-card.is-playing .film-card__play:focus-visible::before {
  opacity: 1;
}

.film-card__time {
  position: absolute;
  inset-inline-start: var(--s-3);
  bottom: var(--s-3);
  z-index: 2;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  color: var(--bc-ink-2);
  font-variant-numeric: tabular-nums;
}

.film-card.is-playing .film-card__time {
  display: none;
}

.film-card__title {
  font-size: var(--fs-h4);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  line-height: 1.15;
}

.film-card__text {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  max-width: 34ch;
}

/* Tablet: two up. */
@media (max-width: 900px) {
  .film-strip__row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Phone: one row of snap points, so the reels read as a shelf of cards
   instead of a stack that has to be scrolled past one at a time. */
@media (max-width: 640px) {
  .film-strip__row {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: min(72vw, 20rem);
    gap: var(--s-4);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Bleed to the gutter so the next card peeks and invites the swipe. */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
    padding-bottom: var(--s-3);
    scrollbar-width: none;
  }

  .film-strip__row::-webkit-scrollbar {
    display: none;
  }

  .film-card {
    scroll-snap-align: center;
  }
}

/* Motion is opt-in; the drift is decoration, the play control is not. */
@media (prefers-reduced-motion: reduce) {
  .film-card.is-playing .film-card__video {
    animation: none;
  }

  .film-card__frame,
  .film-card__play::before {
    transition: none;
  }
}

/* ==========================================================================
   BRAND MARK — engraved draw-on
   The filled mark is the resting state, so a visitor without scripting, or with
   reduced motion requested, sees the real artwork. When scripting is available
   the outline of the same path is drawn once as it scrolls into view, then
   handed back to the fill. One path, two renders, no duplicated geometry.
   ========================================================================== */

.brand-mark {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
  margin-bottom: var(--s-5);
  color: var(--bc-brass);
}

.brand-mark svg {
  display: block;
  width: clamp(4rem, 8vw, 5.5rem);
  height: auto;
  overflow: visible;
}

/* Filled mark: the resting state, always present. */
.brand-mark [data-brand-mark-fill] {
  fill: var(--bc-brass-edge);
  opacity: 0.92;
}

/* Outline: hidden until the draw runs. */
.brand-mark [data-brand-mark-outline] {
  fill: none;
  stroke: currentColor;
  opacity: 0;
  stroke-linejoin: round;
  stroke-linecap: round;
}

.brand-mark.is-drawing [data-brand-mark-outline] {
  opacity: 1;
  stroke: var(--bc-brass-bright);
}

.brand-mark.is-drawn [data-brand-mark-outline] {
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out);
}

.brand-mark__cap {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  color: var(--bc-ink-3);
  max-width: 26ch;
  line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
  .brand-mark [data-brand-mark-outline] {
    display: none;
  }
}

/* ==========================================================================
   2. THE MENU RAIL
   A pinned horizontal rail of category panels, adapted from
   _library/scroll-animation/66 (sticky stack) with the reference's zero-radius
   grid discipline rather than a card row.

   The rail is a <section> per category, not a card: no border, no shadow, no
   radius. A hairline rule under each row is the only separation, which is what
   lets 52 prices read as one ledger.
   ========================================================================== */

.hm {
  position: relative;
  background: var(--bc-canvas-2);
  border-top: 1px solid var(--bc-line);
  border-bottom: 1px solid var(--bc-line);
}

.hm__head {
  /* Only the top padding is its own. The heading-to-lede gap and the space
     before the chip index come from .section-head, so #menu has the same 9.6px
     and 50.4px as every other section that uses the component. It previously
     overrode both, which is what made the ledger read as a different page. */
  padding: var(--section-y) 0 0;
}

/* Heading block and fact rail. Collapses to one column below 900px, where the
   rail reads as a short list under the lede rather than as a second column. */
.hm__head-grid {
  display: grid;
  gap: var(--s-5);
  align-items: end;
}

@media (min-width: 900px) {
  .hm__head-grid {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s-7);
  }
}

.hm__head-text {
  min-width: 0;
}

.hm__lede {
  max-width: 52ch;
  /* Tightened from the component default. The heading, lede and rail then sit as
     one block instead of drifting apart down the page. */
  margin-bottom: 0;
}

/* The fact rail borrows the visual language of `.page-hero__meta` on the inner
   pages — a hairline, micro labels in brass, small values — so the two read as
   the same system. It is namespaced because that component is owned elsewhere. */
.hm__facts {
  display: grid;
  gap: var(--s-3) var(--s-6);
  margin: 0;
  padding: var(--s-3) 0 0;
  border-top: 1px solid var(--bc-line);
}

@media (min-width: 900px) {
  .hm__facts {
    grid-template-columns: repeat(3, minmax(0, auto));
    align-content: end;
    min-width: 0;
  }
}

.hm__facts dt {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
  margin-bottom: 0.25rem;
}

.hm__facts dd {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  white-space: nowrap;
}

/* The durations caveat. Microcopy, sitting directly above the sticky chip index
   so it is the last thing read before the rail starts moving.

   `margin-block` and not the `margin` shorthand, deliberately: the element also
   carries `.shell`, whose `margin-inline: auto` centres it inside the gutter
   padding. A `margin` shorthand would reset margin-inline to 0 and pin the note
   to the left edge, while `max-width` in ch then squeezed it into a 300px
   column and wrapped one sentence over three lines.

   No `max-width` here either — the note is one sentence and reads as a single
   line under the lede when it spans the shell, which is also what keeps its left
   edge on the same rail as the heading above it. */
.hm__caveat {
  margin-block: var(--s-4) 0;
  font-size: var(--fs-meta);
  line-height: 1.55;
  color: var(--bc-ink-3);
}

.hm__body {
  position: relative;
}

/* --- The rail ------------------------------------------------------------ */

.hm__rail {
  display: flex;
  align-items: stretch;
  gap: clamp(1.5rem, 3vw, 3rem);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  padding: 0 var(--gutter) var(--s-6);
  /* Without this the snap engine re-aligns the first panel flush to the
     scrollport on load, which silently cancels the gutter above. The snapport
     inset has to carry the same gutter the content padding does. */
  scroll-padding-inline: var(--gutter);
  /* The rail is its own scroller; it must not be the page's. */
  scrollbar-width: thin;
  scrollbar-color: var(--bc-brass-deep) transparent;
}

.hm__rail::-webkit-scrollbar {
  height: 6px;
}

.hm__rail::-webkit-scrollbar-track {
  background: rgba(244, 242, 236, 0.06);
}

.hm__rail::-webkit-scrollbar-thumb {
  background: var(--bc-brass-deep);
}

.hm__panel {
  flex: 0 0 auto;
  width: clamp(20rem, 34vw, 30rem);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  padding-top: var(--s-4);
  border-top: 2px solid var(--bc-brass);
}

.hm__panel-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  padding-bottom: var(--s-4);
}

.hm__panel-name {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  line-height: 1;
  text-transform: uppercase;
  letter-spacing: 0.005em;
  color: var(--bc-ink);
}

.hm__panel-count {
  flex: none;
  font-size: var(--fs-meta);
  letter-spacing: var(--ls-meta);
  color: var(--bc-brass);
}

.hm__list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--bc-line);
}

/* A ledger row: name, duration, price. The price column is what makes 52 rows
   scannable, so it is right-aligned and tabular. */
.hm__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  gap: 0.5rem var(--s-3);
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--bc-line);
  transition: color var(--dur-fast) var(--ease-out);
}

.hm__row:hover {
  color: var(--bc-brass-soft);
}

.hm__row-name {
  font-size: var(--fs-small);
  color: var(--bc-ink);
  min-width: 0;
}

.hm__row-dur {
  font-size: var(--fs-micro);
  color: var(--bc-ink-3);
  white-space: nowrap;
}

.hm__row-price {
  font-size: var(--fs-small);
  color: var(--bc-brass);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.hm__row:hover .hm__row-name {
  color: var(--bc-ink);
}

.hm__panel-link {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  align-self: flex-start;
}

.hm__panel--end {
  border-top-color: var(--bc-line-2);
  width: clamp(18rem, 28vw, 24rem);
}

.hm__end-eyebrow {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
  margin-bottom: var(--s-2);
}

.hm__end-title {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.2vw, 2rem);
  line-height: 1.02;
  text-transform: uppercase;
  margin-bottom: var(--s-3);
}

.hm__end-text {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  margin-bottom: var(--s-4);
  text-wrap: pretty;
}

.hm__end-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
  margin-bottom: var(--s-4);
}

.hm__end-note {
  margin-top: auto;
  padding-top: var(--s-3);
  border-top: 1px solid var(--bc-line);
}

/* --- Category index ------------------------------------------------------
   Four of the seven panels start off screen. The chips make that visible and
   make the far ones reachable in one click, which a 1px hairline could not. */

.hm__index {
  position: sticky;
  top: var(--header-h);
  z-index: 6;
  background: linear-gradient(
    to bottom,
    var(--bc-canvas-2) 72%,
    rgba(17, 21, 19, 0)
  );
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--bc-line);
}

.hm__index-inner {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding-block: var(--s-3);
}

.hm__chips {
  display: flex;
  flex: 1 1 auto;
  gap: 0.35rem;
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-x: auto;
  scrollbar-width: none;
  /* The chip row scrolls on a phone; the rail keeps its own padding so the two
     gutters stay aligned. */
  scroll-padding-inline: var(--gutter);
}

.hm__chips::-webkit-scrollbar {
  display: none;
}

.hm__chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  flex: none;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--bc-line);
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--bc-ink-2);
  font: inherit;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out);
}

.hm__chip:hover {
  color: var(--bc-ink);
  border-color: var(--bc-line-2);
}

.hm__chip:focus-visible {
  outline: 2px solid var(--bc-focus);
  outline-offset: 2px;
}

.hm__chip-count {
  color: var(--bc-ink-3);
  font-variant-numeric: tabular-nums;
}

.hm__chip[aria-current="true"] {
  background: var(--bc-brass-plate);
  border-color: var(--bc-brass);
  color: var(--bc-ink-on-brass);
}

.hm__chip[aria-current="true"] .hm__chip-count {
  color: rgba(26, 20, 9, 0.66);
}

.hm__position {
  flex: none;
  margin: 0;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  color: var(--bc-brass);
  font-variant-numeric: tabular-nums;
}

/* --- Progress track ------------------------------------------------------
   A tick per panel, so the position within the seven is readable at a glance
   rather than inferred from a hairline. */

.hm__progress {
  margin: 0 var(--gutter);
  padding-bottom: var(--s-4);
}

.hm__progress[hidden] {
  display: none;
}

.hm__progress-track {
  position: relative;
  height: 3px;
  background: rgba(244, 242, 236, 0.1);
}

.hm__progress-bar {
  position: absolute;
  /* Anchored to the inline start so the bar grows the way the rail scrolls.
     Written as `inset` with a physical 0 on the right, it filled from the left
     in both directions while the rail itself ran right to left. */
  inset-block: 0;
  inset-inline: 0 auto;
  width: 0;
  background: var(--bc-brass-edge);
  transition: width 120ms linear;
}

.hm__progress-ticks {
  position: absolute;
  inset: 0;
}

.hm__progress-ticks span {
  position: absolute;
  top: -3px;
  width: 1px;
  height: 9px;
  background: rgba(13, 15, 14, 0.55);
}

@media (max-width: 767px) {
  .hm__head {
    padding: var(--s-7) 0 var(--s-5);
  }

  /* Phone: the rail becomes a snap shelf of one panel plus a peek, so the
     gesture is discoverable without a scrollbar. */
  .hm__rail {
    gap: var(--s-4);
    scroll-snap-type: x mandatory;
  }

  .hm__panel {
    width: min(84vw, 22rem);
  }
}

/* ==========================================================================
   4. PRICES — packages and plans as one ledger
   The old version spent a full section on a five-row table and another on a
   three-card grid. Two columns, one rhythm, one type scale.
   ========================================================================== */

.prices__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  margin-top: var(--s-6);
}

.prices__col-head {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
  padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--bc-line-2);
  margin-bottom: var(--s-2);
}

.prices__packages .package-row {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 0;
}

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

.price-plans {
  list-style: none;
  margin: 0;
  padding: 0;
}

.price-plans__item {
  padding: var(--s-4) 0;
  border-bottom: 1px solid var(--bc-line);
}

.price-plans__head {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  margin-bottom: 0.35rem;
}

.price-plans__name {
  font-family: var(--font-display);
  font-size: clamp(1.125rem, 1.6vw, 1.5rem);
  text-transform: uppercase;
  line-height: 1;
}

.price-plans__price {
  margin-inline-start: auto;
  font-size: var(--fs-h4);
  color: var(--bc-brass);
  white-space: nowrap;
}

.price-plans__price span {
  font-size: var(--fs-micro);
  color: var(--bc-ink-3);
  letter-spacing: var(--ls-label);
  text-transform: uppercase;
  margin-inline-start: 0.2em;
}

.price-plans__flag {
  order: -1;
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-canvas);
  background: var(--bc-brass-plate);
  padding: 0.15rem 0.4rem;
  border-radius: var(--r-1);
}

.price-plans__terms {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  max-width: 52ch;
}

.price-plans__item--feature {
  border-inline-start: 2px solid var(--bc-brass);
  padding-inline-start: var(--s-4);
}

.prices__plans-link {
  margin-top: var(--s-4);
  align-self: flex-start;
}

@media (max-width: 900px) {
  .prices__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-7);
  }
}

/* ==========================================================================
   5. PROOF BAND
   Rating, standards, roster and finished work in one band. The roster is a
   hairline-divided list, not three cards.
   ========================================================================== */

.proof-band {
  background: var(--bc-surface);
  border-top: 1px solid var(--bc-line);
}

.proof-band__top {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.6fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: start;
  padding-bottom: var(--s-7);
  border-bottom: 1px solid var(--bc-line);
}

.proof-band__score {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-2);
  /* A brass edge, not a card: the only thing separating this from the copy. */
  border-inline-start: 2px solid var(--bc-brass);
  padding-inline-start: var(--s-4);
}

.proof-band__value {
  font-family: var(--font-display);
  font-size: clamp(4rem, 9vw, 7.5rem);
  line-height: 0.86;
  color: var(--bc-brass);
}

.proof-band__stars {
  font-size: var(--fs-body);
  color: var(--bc-brass-soft);
  letter-spacing: 0.1em;
}

.proof-band__count {
  font-size: var(--fs-small);
  color: var(--bc-ink);
}

.proof-band__score .btn {
  margin-top: var(--s-3);
}

.proof-band__body .lede {
  max-width: 58ch;
  margin-bottom: var(--s-5);
}

.proof-band__facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, auto));
  justify-content: start;
  gap: var(--s-6);
  margin: 0;
  padding-top: var(--s-4);
  border-top: 1px solid var(--bc-line);
}

.proof-band__facts dt {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-ink-3);
  margin-bottom: 0.2rem;
}

.proof-band__facts dd {
  margin: 0;
  font-size: var(--fs-h4);
  color: var(--bc-brass);
}

/* --- Roster -------------------------------------------------------------- */

.proof-band__team {
  padding: var(--s-7) 0;
  border-bottom: 1px solid var(--bc-line);
}

.proof-band__sub {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-brass);
  margin-bottom: var(--s-4);
}

.roster {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-4);
  list-style: none;
  margin: 0;
  padding: 0;
}

.roster__item {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

.roster__item img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--r-1);
  margin-bottom: var(--s-2);
  background: var(--bc-canvas);
  filter: grayscale(0.35) contrast(1.04);
  transition: filter var(--dur) var(--ease-out);
}

.roster__item:hover img {
  filter: grayscale(0) contrast(1.04);
}

.roster__name {
  font-size: var(--fs-h4);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
}

.roster__item--link {
  justify-content: center;
  align-items: flex-start;
}

/* --- Finished work ------------------------------------------------------- */

.proof-band__work {
  padding-top: var(--s-7);
}

.proof-band__work .work-grid {
  list-style: none;
  margin: 0;
  padding: 0;
}

@media (max-width: 900px) {
  .proof-band__top {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-5);
  }

  .roster {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s-4) var(--s-3);
  }
}

@media (max-width: 480px) {
  .proof-band__facts {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-4);
  }
}

/* ==========================================================================
   3 (cont). THE CRAFT — story, standards and products, merged
   ========================================================================== */

.craft-story {
  background: var(--bc-canvas);
}

.craft-story__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  align-items: start;
}

/* Explicit placement rather than a span. The left column is intro + pending
   products, the right is the photograph + the numbered standards, so both
   columns fill instead of leaving a dead gap under the intro. */
.craft-story__intro {
  grid-column: 1;
  grid-row: 1;
}

.craft-story__media {
  grid-column: 2;
  grid-row: 1;
}

.craft-story__standards {
  grid-column: 2;
  grid-row: 2;
}

.craft-story__products {
  grid-column: 1;
  grid-row: 2;
  align-self: end;
}

.craft-story__body {
  margin-top: var(--s-4);
  color: var(--bc-ink-2);
  text-wrap: pretty;
}

.craft-story__media {
  margin: 0;
}

.craft-story__media img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--r-1);
  background: var(--bc-surface);
}

.craft-story__media figcaption {
  margin-top: var(--s-2);
}

.craft-story__standards,
.craft-story__products {
  padding-top: var(--s-5);
  border-top: 1px solid var(--bc-line);
}

.craft-story__standards .standards {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: standard;
}

.craft-story__standards .standards li {
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--bc-line);
  counter-increment: standard;
}

.craft-story__standards .standards h4 {
  font-size: var(--fs-h4);
  text-transform: uppercase;
  letter-spacing: var(--ls-label);
  margin-bottom: 0.25rem;
  color: var(--bc-ink);
}

.craft-story__standards .standards h4::before {
  content: counter(standard, decimal-leading-zero) ". ";
  color: var(--bc-brass);
}

.craft-story__standards .standards p {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
}

.craft-story__products-text {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  max-width: 46ch;
  text-wrap: pretty;
}

@media (max-width: 900px) {
  .craft-story__grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .craft-story__intro,
  .craft-story__media,
  .craft-story__standards,
  .craft-story__products {
    grid-column: auto;
    grid-row: auto;
  }
}

/* ==========================================================================
   6. VISIT BAND — both branches, home service and the journal
   ========================================================================== */

.visit-band__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  /* Stretch, so the pending branch is a deliberate full-height placeholder
     beside the real one rather than a short card floating in dead space. */
  align-items: stretch;
  margin-top: var(--s-6);
}

.branch {
  display: flex;
  flex-direction: column;
  gap: var(--s-4);
}

.branch__media {
  margin: 0;
  overflow: hidden;
  border-radius: var(--r-1);
}

.branch__media img {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  background: var(--bc-surface);
}

.branch__name {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 2.4vw, 2.25rem);
  line-height: 1;
  text-transform: uppercase;
  margin-bottom: var(--s-3);
}

.branch__facts {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3) var(--s-4);
  margin: 0 0 var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--bc-line);
}

.branch__facts dt {
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-meta);
  text-transform: uppercase;
  color: var(--bc-ink-3);
  margin-bottom: 0.15rem;
}

.branch__facts dd {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--bc-ink);
}

/* The pending branch keeps the same shape, with an honest brass state line
   instead of invented details. */
.branch--pending {
  display: flex;
  flex-direction: column;
  padding: var(--s-5);
  border: 1px dashed var(--bc-line-2);
  border-radius: var(--r-1);
  background: rgba(255, 255, 255, 0.015);
}

/* Push the single call to action to the bottom of the placeholder, which lines
   it up with the Dubai column's actions. */
.branch--pending .branch__actions {
  margin-top: auto;
  padding-top: var(--s-4);
}

.branch__text {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  text-wrap: pretty;
}

.branch__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}

/* --- Home service + journal -------------------------------------------- */

.visit-band__extras {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  margin-top: var(--s-7);
  padding-top: var(--s-6);
  border-top: 1px solid var(--bc-line);
}

.visit-band__home p {
  font-size: var(--fs-small);
  color: var(--bc-ink-2);
  max-width: 44ch;
  text-wrap: pretty;
}

.journal-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--bc-line);
}

.journal-list li {
  border-bottom: 1px solid var(--bc-line);
}

.journal-list a {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  color: var(--bc-ink);
  transition: color var(--dur-fast) var(--ease-out);
}

.journal-list a:hover {
  color: var(--bc-brass-soft);
}

.journal-list__tag {
  flex: none;
  color: var(--bc-brass);
}

.journal-list__title {
  font-size: var(--fs-body);
}

@media (max-width: 900px) {
  .visit-band__grid,
  .visit-band__extras {
    grid-template-columns: minmax(0, 1fr);
  }

  .branch__facts {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- The canvas thresholds ------------------------------------------------ */

/*
 * Where the page changes ground, a lit brass seam sits between the two canvases
 * instead of an abrupt edge.
 *
 * The gradient runs from the outgoing canvas through a brass glow into the
 * incoming one, so the switch happens across a band of gradient rather than at a
 * line. It is static geometry: there is no scroll state, so there is nothing to
 * latch and nothing to reverse.
 */
.canvas-threshold {
  position: relative;
  /* Short. The band used to be clamp(5rem, 13vw, 11rem), which at 1440px is a
     176px-tall gradient — large enough that the middle of it reads as a smear
     across the page rather than as a change of ground. A crossing is an event,
     not a field, so the band is now a little over half that and the brass is
     held inside it. */
  height: clamp(3rem, 6.5vw, 5.5rem);
  /* Overlaps the padding of the sections either side, but only just. At -0.5 the
     overlap was large enough that the band read as floating over the content
     instead of being the join between the two sections. */
  margin-block: calc(var(--section-y) * -0.34);
  pointer-events: none;
  z-index: 2;
}

/*
 * Dark -> light.
 *
 * Both grounds are named explicitly. `var(--bc-canvas)` cannot be used for the
 * light end: the band is a sibling of the light sections, not a descendant, so
 * it inherits the dark root value and the gradient would have run dark to dark.
 *
 * THE BRASS WINDOW. Every brass stop sits between 54% and 68% of the band. The
 * previous ramp carried brass from 40% to 60% of a band twice this height, and
 * an interpolation that wide desaturates: the middle of it turned into a warm
 * grey haze, which is the one thing a seam must not be. Narrowing the window
 * keeps the metal saturated for the two or three percent of the band it
 * actually occupies, so it reads as a lit edge rather than a fog.
 */
.canvas-threshold--to-light {
  --from: #0d0f0e;
  --to: #e8ece9;
  background: linear-gradient(
    to bottom,
    var(--from) 0%,
    var(--from) 40%,
    color-mix(in oklab, var(--from) 82%, var(--to)) 48%,
    color-mix(in oklab, var(--from) 26%, var(--bc-brass-deep)) 53%,
    color-mix(in oklab, var(--to) 54%, var(--bc-brass-soft)) 58%,
    color-mix(in oklab, var(--to) 92%, var(--bc-brass)) 63%,
    var(--to) 70%,
    var(--to) 100%
  );
}

/* Light -> dark: the same ramp, running the other way. */
.canvas-threshold--to-dark {
  --from: #e8ece9;
  --to: #0d0f0e;
  background: linear-gradient(
    to bottom,
    var(--from) 0%,
    var(--from) 40%,
    color-mix(in oklab, var(--from) 92%, var(--bc-brass)) 48%,
    color-mix(in oklab, var(--from) 54%, var(--bc-brass-soft)) 53%,
    color-mix(in oklab, var(--to) 26%, var(--bc-brass-deep)) 58%,
    color-mix(in oklab, var(--to) 82%, var(--to)) 63%,
    var(--to) 70%,
    var(--to) 100%
  );
}

/*
 * The leading edge of the threshold, as a real lit rule rather than just a
 * colour in the gradient.
 *
 * `--sweep` runs 0 -> 1 -> 0 as the band crosses the viewport, so the rule
 * brightens in the middle of the crossing and fades out either side. It is
 * written by `canvas-wipe.js` from the band's own rect on every frame, which
 * makes it exactly reversible: scrolling back up re-runs the same numbers
 * rather than reversing a tween.
 */
.canvas-threshold::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  /* The lit-metal ramp rather than one flat brass colour, so the seam has a
     bright centre and dark ends instead of reading as a drawn bar. */
  background: var(--bc-brass-edge);
  /* A floor under the sweep. The rule used to be `opacity: var(--sweep, 0)`,
     which means it is invisible whenever the band is simply sitting in the
     viewport — and that is most of the time. The band was then nothing but a
     soft gradient wash with no edge anywhere, which is what made the crossing
     read as a smudge rather than a join. Holding it at 0.34 and letting the
     sweep carry it to full means there is always a visible seam, and the
     crossing still brightens it. */
  opacity: calc(0.34 + var(--sweep, 0) * 0.66);
  transform: scaleX(var(--sweep-scale, 1));
  /* Fades out before the viewport edge. Without this the rule stops dead at
     x=0 and x=100%, which is what made it look like a border between two
     sections rather than light falling across a seam. */
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0%,
    #000 13%,
    #000 87%,
    transparent 100%
  );
  mask-image: linear-gradient(
    90deg,
    transparent 0%,
    #000 13%,
    #000 87%,
    transparent 100%
  );
  /* Tighter than the 18px it had. A wide glow re-creates the haze the shorter
     band was meant to remove. */
  box-shadow: 0 0 9px color-mix(in oklab, var(--bc-brass-bright) 50%, transparent);
}

/* Without scripting the band is still there; it just never lights up. */
.canvas-threshold {
  --sweep: 0;
}

/* --- The light canvas, scoped --------------------------------------------- */

/*
 * The brass-tinted plates and rules that were tuned for a dark ground need
 * explicit light values. The tokens above already handle ink, lines and
 * surfaces; these are the few places that reach past the token layer.
 */
[data-canvas="light"] .branch {
  border-color: var(--bc-line);
  background: var(--bc-surface);
}

[data-canvas="light"] .btn--primary {
  color: var(--bc-ink-on-brass);
  background: var(--bc-brass-plate);
}

[data-canvas="light"] .btn--ghost {
  border-color: var(--bc-line-2);
  color: var(--bc-ink);
}

[data-canvas="light"] .link-arrow {
  color: var(--bc-ink);
}

[data-canvas="light"] .eyebrow {
  color: var(--bc-brass-dark);
}

/*
 * A branch that is announced but not yet open.
 *
 * Distinguished from `.branch--pending` (a shop that trades with unconfirmed
 * details) by carrying a brass edge: these are an intention, not a gap in the
 * facts. No address, phone or date is shown for either, because none is known.
 */
.branch--soon {
  border-color: var(--bc-line-brass);
  background:
    linear-gradient(
      168deg,
      color-mix(in oklab, var(--bc-surface) 84%, var(--bc-brass-soft)) 0%,
      var(--bc-surface) 58%
    );
  position: relative;
  overflow: hidden;
}

.branch--soon::before {
  content: "";
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: 3px;
  background: var(--bc-brass-edge);
}

.branch--soon .branch__name {
  color: var(--bc-ink);
}

/* A small, quiet status mark instead of an invented date. */
.branch--soon .branch__text {
  color: var(--bc-ink-2);
}

/*
 * `body` paints `--bc-canvas`, and that resolves against the ROOT value, so it
 * is the dark ground everywhere regardless of which canvas a section is on.
 * Without this the light sections were painting near-black ink onto the dark
 * body — pale text on a dark ground, which is why prices, proof and visit read
 * as dark panels with washed-out type.
 *
 * A section therefore has to paint its own ground.
 */
[data-canvas="light"] {
  background: var(--bc-canvas);
  color: var(--bc-ink);
}

/*
 * Roster monogram.
 *
 * The team is shown as initials on a brass plate rather than as portraits: a
 * photograph exists for only two of the five barbers, and the one unassigned
 * portrait in the media folder cannot be attributed to anyone with confidence.
 * Same 4:5 plate as the old photographs, so the band keeps its rhythm.
 */
.roster__initial {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 4 / 5;
  margin-bottom: var(--s-2);
  border: 1px solid var(--bc-line-brass);
  border-radius: var(--r-1);
  background:
    radial-gradient(
      120% 90% at 50% 8%,
      color-mix(in oklab, var(--bc-surface) 82%, var(--bc-brass-soft)) 0%,
      var(--bc-surface) 62%
    );
  color: var(--bc-brass);
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 4.4vw, 3.75rem);
  line-height: 1;
  letter-spacing: 0.02em;
  transition: border-color var(--dur) var(--ease-out),
              color var(--dur) var(--ease-out);
}

.roster__item:hover .roster__initial {
  border-color: var(--bc-brass);
  color: var(--bc-brass-bright);
}

[data-canvas="light"] .roster__initial {
  color: var(--bc-brass);
  border-color: var(--bc-line-brass);
}

[data-canvas="light"] .roster__item:hover .roster__initial {
  color: var(--bc-brass-deep);
  border-color: var(--bc-brass);
}

/* Five barbers plus the link, so the band must not hold a fixed four columns. */
.roster {
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}

/* ==========================================================================
   Fixes from the cross-section coherence audit
   ========================================================================== */

/*
 * M2 — the work captions were unreadable on the light canvas.
 *
 * `.work figcaption` takes `color: var(--bc-ink)`, which is near-black ink, and
 * paints it over a hard-coded dark scrim that sits on photography. Measured at
 * 1.02:1 on a dark frame and 1.44:1 on a white one. The scrim must stay dark
 * because it sits on photographs, so the ink has to be the light one.
 */
[data-canvas="light"] .work figcaption {
  color: #f4f2ec;
}

/*
 * M3 / M4 — the light canvas had no working hover on either button.
 *
 * Both light rules lost a specificity tie to a later rule in this file, and the
 * dark hover ramps do not transfer anyway: the dark ghost hover washes with
 * white, which composites to nothing on pale paper, and the dark primary hover
 * uses the *lighter* end of the brass ramp, which is the wrong direction here
 * because on the light canvas the light end of the ramp is the dark end.
 */
[data-canvas="light"] .btn--primary:hover {
  background: linear-gradient(168deg, #6b4d16 0%, #7d5c1f 38%, #856228 100%);
  color: var(--bc-ink-on-brass);
}

[data-canvas="light"] .btn--ghost:hover {
  border-color: var(--bc-ink);
  background: rgba(10, 14, 13, 0.06);
}

/*
 * S3 — the arrow link lost its accent role on the light canvas.
 *
 * On dark it is brass. Here it was overridden to body ink, so the same component
 * read as an accent in six places and as plain copy in three, with the emphasis
 * order of a section changing across the page. The light brass is 5.14:1, so the
 * accent survives the canvas change and the component stays one component.
 */
[data-canvas="light"] .link-arrow {
  color: var(--bc-brass);
}

[data-canvas="light"] .link-arrow:hover {
  color: var(--bc-brass-deep);
}

/*
 * S7 — three non-text control boundaries sat below the 3:1 needed to identify a
 * control. The chips and the ghost button carry their own label, so theirs is
 * arguable; the "coming soon" brass edge is the ONLY thing separating an
 * announced branch from one that trades with unconfirmed details, so it has to be
 * visible as a boundary.
 */
[data-canvas="light"] .hm__chip {
  border-color: color-mix(in oklab, var(--bc-ink) 34%, transparent);
}

[data-canvas="light"] .branch--soon {
  border-color: var(--bc-brass);
}

/*
 * M6 — the mobile craft block reserved 220vh of nothing.
 *
 * `.craft--static { height: auto }` and `@media (max-width: 900px) { .craft
 * { height: 220vh } }` have equal specificity and the media query comes later, so
 * it won: the static fallback was 1856.8px tall with 1100.4px of content, leaving
 * 756px of empty dark ground. `!important` here is the honest fix, because the
 * intent of the static fallback is precisely to stop reserving scroll height, and
 * reordering two distant rules would be fragile.
 */
.craft--static {
  height: auto !important;
}

/*
 * M7 — nineteen interactive elements were under 44px tall on touch. `.link-arrow`
 * measured 23.8px, which fails even the 24px floor of WCAG 2.2 SC 2.5.8.
 * Scoped to coarse pointers so the desktop mouse experience is untouched. The
 * negative margin on the prose link keeps the text rhythm while the hit box grows.
 */
/*
 * Keyed on the width breakpoint as well as the pointer type, and deliberately so.
 * The codebase's mobile convention is 767px, and a narrow window without a
 * coarse pointer is exactly the case a pointer-only query misses while a person
 * is using a trackpad on a small window. A phone always satisfies both.
 */
@media (max-width: 767px), (pointer: coarse) {
  .link-arrow,
  .hm__chip,
  .branch__actions .btn,
  .proof-band__score .btn {
    min-height: 44px;
  }

  .link-arrow {
    display: inline-flex;
    align-items: center;
  }

  .hm__chip {
    padding-block: 0.6rem;
  }

  /*
   * Standalone text links get a grown hit box. WCAG 2.2 SC 2.5.8 exempts a link
   * inside a sentence, and forcing those to 44px would break the line rhythm of
   * the prose they sit in, so only the ones that act as controls are grown.
   */
  .hero__facts a,
  .branch__facts a,
  .journal-list a,
  .proof-band__body > .section-head__text a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* ==========================================================================
   The hero, on hover
   ========================================================================== */

/*
 * Registered as a typed custom property so it can be transitioned. An
 * unregistered custom property is just a string and will not animate, which is
 * why this is declared rather than written inline in the rule below.
 */
@property --hero-hover {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/*
 * The lockup breathes open when you point at it.
 *
 * The page opens by converging BARBER and CASTLE onto the mark. On hover this
 * eases them back apart by a small amount, so the hero's resting state quietly
 * undoes its own opening gesture — the mark is the point, the words are what
 * gathered around it. It is the one memorable move on the page and it costs one
 * transform per layer.
 *
 * `--hero-hover` is the intent; the numbers are applied by hero-parallax.js,
 * which already owns `transform` on these elements. Two writers on one property
 * is exactly the bug that silently killed the parallax once already, so CSS
 * declares the intent and never the transform.
 */
.hero__stage {
  --hero-hover: 0;
  transition: --hero-hover 640ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .hero__stage:hover {
    --hero-hover: 1;
  }
}

/* Keyboard parity: focusing anything inside the stage opens it too. */
.hero__stage:focus-within {
  --hero-hover: 1;
}

/*
 * The three photographs get a brass hairline as they come forward. The panels
 * are not transform-written (only their images are), so this is pure CSS and
 * cannot fight the parallax.
 */
.hero__panel {
  transition: box-shadow 420ms var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .hero__stage:hover .hero__panel {
    box-shadow:
      inset 0 0 0 1px color-mix(in oklab, var(--bc-brass) 55%, transparent),
      0 18px 44px -18px rgba(0, 0, 0, 0.75);
  }
}

/* ==========================================================================
   Two staff, one client

   Four 16:9 studio frames between #craft and #film. They sit on the dark canvas
   as lit panels: a bright image on a near-black ground reads as a lightbox,
   which is the same idea as the lit shopfront in the film poster below. The
   frames carry a brass hairline for the same reason the menu rows do — it is the
   one edge the site uses to say "this is finished".
   ========================================================================== */

.pair__grid {
  display: grid;
  gap: var(--s-4);
  margin: 0;
  padding: 0 var(--gutter);
  list-style: none;
  /* An explicit two columns, not auto-fit.
     auto-fit with minmax(min(100%, 20rem), 1fr) resolves to THREE columns at
     1440px, which put three frames in the first row and orphaned the fourth with
     a hole beside it. Four frames do not divide into three, and an orphan row
     reads as a mistake rather than a rhythm.

     Two columns is stable at every width, gives each frame real presence at
     647px across on a desktop, and — usefully — gives the wide establishing
     shot the room it needs, which a 315px strip of four does not. One column
     below 640px. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 640px) {
  .pair__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

.pair__figure {
  margin: 0;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--bc-line-brass);
  border-radius: var(--r-1);
  background: var(--bc-surface);
}

.pair__figure img {
  width: 100%;
  height: 100%;
  /* The panels are exactly 16:9 and the grid columns are not, so the frame is
     pinned to the image's own ratio and the image fills it. Without this a
     narrower column would letterbox and a wider one would crop silently. */
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transform-origin: center;
  /* A slow settle on load. Scale is transformed rather than width, so it does
     not re-run layout; reduced motion drops it below. */
  transition: transform var(--dur-slow) var(--ease-out);
}

/* Nothing dramatic: a hair of scale, the way a photograph on a lightbox catches
   the eye when you look at it. 1.03 rather than a real zoom, so a row of four
   does not feel like a carousel. */
.pair__item:hover .pair__figure img,
.pair__figure:focus-within img {
  transform: scale(1.03);
}

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

  .pair__item:hover .pair__figure img,
  .pair__figure:focus-within img {
    transform: none;
  }
}
