@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,900&family=Inter:wght@300;400;500;600&display=swap');

/* ==========================================================================
   Champs Musical Coffee Shop Productions
   Palette is the client's: coffee brown, banana yellow, cream white.
   One ground, one ink, one accent, two neutrals. Nothing else.
   ========================================================================== */

:root {
  /* colour */
  --ground:        #241610;   /* espresso — the ground */
  --ground-lift:   #312016;   /* one step up, for surfaces on the ground */
  --ink:           #F7F0E1;   /* cream — the ink */
  --accent:        #F2C14E;   /* banana — CTA / active only, never decoration */
  --accent-press:  #D9A930;
  --coffee:        #6B4A31;   /* neutral 1 — surfaces, dividers, text on cream */
  --coffee-deep:   #3E2617;   /* the same brown, pressed down: headings on cream */
  --cream-ground:  #FFFDD0;   /* the logo clip's own white, used as the video hero's ground */
  --taupe:         #A79683;   /* neutral 2 — small text on the GROUND only.
                                 Does not pass AA on cream. Never used there. */

  /* type */
  --display: 'Fraunces', 'Iowan Old Style', Georgia, serif;
  --text:    'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* scale contrast is the whole design: enormous display, small everything else */
  --fs-mega:   clamp(3.25rem, 11vw, 9.5rem);
  --fs-h1:     clamp(2.6rem, 7.5vw, 6rem);
  --fs-h2:     clamp(2rem, 4.6vw, 3.6rem);
  --fs-h3:     clamp(1.25rem, 1.9vw, 1.6rem);
  --fs-body:   1rem;
  --fs-small:  0.8125rem;

  /* space */
  --gutter:  clamp(1.25rem, 5vw, 6rem);
  --section: clamp(5.5rem, 13vh, 11rem);
  --measure: 62ch;

  --radius: 2px;
  --ease:   cubic-bezier(0.22, 0.61, 0.36, 1);
  --slow:   900ms;
}

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

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--text);
  font-size: var(--fs-body);
  font-weight: 300;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Site-wide wallpaper: beans on wood, scrolling with the page behind every
   page, under a coffee brown tint heavy enough that cream ink keeps its
   contrast. Absolute (not fixed) so it travels with the content, sized to the
   full width and tiled down the document. */
body { position: relative; }
body::before {
  content: '';
  position: absolute; inset: 0; z-index: -10;
  background:
    linear-gradient(color-mix(in srgb, var(--coffee) 35%, transparent), color-mix(in srgb, var(--coffee) 35%, transparent)),
    linear-gradient(color-mix(in srgb, var(--ground) 78%, transparent), color-mix(in srgb, var(--ground) 78%, transparent)),
    url('../img/champs-beans-wood-wallpaper.webp') top center / 100% auto repeat-y var(--ground);
}

img { max-width: 100%; height: auto; display: block; }

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 900;
  line-height: 0.95;
  letter-spacing: -0.025em;
  margin: 0;
  text-wrap: balance;
}
/* Scale contrast is the design. Set it explicitly — never inherit the
   browser's 2em/1.5em defaults, which flatten the whole relationship. */
h1 { font-size: var(--fs-h1); margin-bottom: 1.75rem; }
h2 { font-size: var(--fs-h2); margin-bottom: 1.5rem; }
h3 { font-size: var(--fs-h3); }

p { margin: 0 0 1.15rem; max-width: var(--measure); }

/* -------------------------------------------------------------------------
   Skip link + focus. Visible focus everywhere, no exceptions.
   ------------------------------------------------------------------------- */
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--accent); color: var(--ground);
  padding: 0.75rem 1.25rem; font-weight: 600;
}
.skip:focus { left: 0; }

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 3px;
}

/* -------------------------------------------------------------------------
   Header / nav. Current page or in-view section is highlighted.
   ------------------------------------------------------------------------- */
.site-head {
  position: fixed; inset: 0 0 auto 0; z-index: 100;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  gap: 1rem;
  padding: 0.7rem var(--gutter);
  background: color-mix(in srgb, var(--ground) 82%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
}

.wordmark {
  font-family: var(--display);
  font-weight: 900;
  font-size: 1.75rem;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
  line-height: 1.1;
}
.wordmark span { color: var(--accent); }

.nav {
  grid-column: 2;
  display: flex; align-items: center; justify-content: center;
  flex-wrap: nowrap;
  gap: clamp(0.9rem, 2.2vw, 2rem);
}

.nav > a {
  position: relative;
  color: var(--ink);
  text-decoration: none;
  font-size: 1.0625rem;
  font-weight: 400;
  padding: 0.35rem 0;
}
/* underline wipes in from the left */
.nav > a::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  height: 1px; width: 100%;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 420ms var(--ease);
}
.nav > a:hover::after,
.nav > a:focus-visible::after { transform: scaleX(1); }
.nav > a[aria-current="page"] { color: var(--accent); }
.nav > a[aria-current="page"]::after { transform: scaleX(1); }

/* Page dropdown: replaces the inline links where they no longer fit.
   Native <details>, so it opens and closes with JS off. */
.nav-drop { display: none; position: relative; }
.nav-drop summary {
  display: flex; align-items: center; gap: 0.5rem;
  min-height: 44px;
  padding: 0 0.8rem;
  border: 1px solid color-mix(in srgb, var(--ink) 28%, transparent);
  border-radius: var(--radius);
  font-size: 0.95rem; font-weight: 400; white-space: nowrap;
  list-style: none; cursor: pointer;
}
.nav-drop summary::-webkit-details-marker { display: none; }
.nav-drop summary::after {
  content: ''; width: 0.42em; height: 0.42em;
  border-right: 1.5px solid var(--accent); border-bottom: 1.5px solid var(--accent);
  transform: translateY(-25%) rotate(45deg);
  transition: transform 240ms var(--ease);
}
.nav-drop[open] summary::after { transform: translateY(25%) rotate(-135deg); }
.nav-drop__list {
  position: absolute; right: 0; top: calc(100% + 0.5rem);
  min-width: 12rem;
  display: flex; flex-direction: column;
  padding: 0.35rem 0;
  background: var(--ground-lift);
  border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  border-radius: var(--radius);
  box-shadow: 0 18px 36px -14px rgb(0 0 0 / 0.7);
}
.nav-drop__list a {
  padding: 0.8rem 1.1rem;
  color: var(--ink);
  text-decoration: none;
  font-weight: 400;
}
.nav-drop__list a:hover,
.nav-drop__list a:focus-visible { background: color-mix(in srgb, var(--ink) 7%, transparent); }
.nav-drop__list a[aria-current="page"] { color: var(--accent); }

@media (max-width: 900px) {
  .site-head { padding: 0.55rem 1.25rem; grid-template-columns: 1fr auto; }
  /* Shrinks only below ~385px, so the longest label still fits at 320. */
  .wordmark { font-size: clamp(1.05rem, 5.2vw, 1.25rem); white-space: nowrap; flex-shrink: 0; }
  .nav > a { display: none; }
  .nav-drop { display: block; }
}

/* -------------------------------------------------------------------------
   Buttons. Magnetic drift is applied by JS via --mx/--my.
   ------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: 0.6rem;
  font-family: var(--text); font-size: 0.9375rem; font-weight: 600;
  padding: 0.95rem 1.6rem;
  border-radius: var(--radius);
  text-decoration: none;
  border: 1px solid transparent;
  transform: translate(var(--mx, 0px), var(--my, 0px));
  transition: transform 500ms var(--ease), background-color 260ms var(--ease), color 260ms var(--ease);
}
.btn-primary { background: var(--accent); color: var(--ground); }
.btn-primary:hover { background: var(--accent-press); }
.btn-ghost { border-color: color-mix(in srgb, var(--ink) 42%, transparent); color: var(--ink); }
.btn-ghost:hover { border-color: var(--ink); }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 2rem; }

/* text link: underline wipe from left */
.link {
  color: var(--ink);
  text-decoration: none;
  position: relative;
  padding-bottom: 2px;
}
.link::after {
  content: ''; position: absolute; left: 0; bottom: 0;
  width: 100%; height: 1px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 420ms var(--ease);
}
.link:hover::after, .link:focus-visible::after { transform: scaleX(1); }

/* -------------------------------------------------------------------------
   Section shell. Every section is its own composition with a hard boundary.
   ------------------------------------------------------------------------- */
.band {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: var(--section) var(--gutter);
  min-height: 84vh;
  display: flex; align-items: center;
}
.band--tall { min-height: 100svh; }
.band, .band--flat { background: color-mix(in srgb, var(--ground) 55%, transparent); }
.band--lift { background: color-mix(in srgb, var(--ground-lift) 70%, transparent); }

/* Section divider: a yellow hairline that draws itself across the seam as the
   section engages. Same scaleX pattern as .link::after. */
.band + .band::before {
  content: ''; position: absolute; z-index: 3;
  top: 0; left: 0; right: 0; height: 2px;
  background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 1100ms var(--ease) 120ms;
}
.band + .band.is-active::before { transform: scaleX(1); }

/* full-bleed background image, parallax-shifted by JS via --py */
.band__bg {
  position: absolute; inset: -12% 0;
  z-index: -2;
  background-size: cover;
  background-position: center;
  transform: translate3d(0, var(--py, 0px), 0);
  will-change: transform;
}
/* the scrim. Legibility over atmosphere: if a heading is hard to read,
   the scrim is wrong, not the heading. */
.band__scrim {
  position: absolute; inset: 0; z-index: -1;
  background:
    linear-gradient(90deg, color-mix(in srgb, var(--ground) 94%, transparent) 0%, color-mix(in srgb, var(--ground) 74%, transparent) 38%, color-mix(in srgb, var(--ground) 18%, transparent) 72%, transparent 100%),
    linear-gradient(180deg, color-mix(in srgb, var(--ground) 42%, transparent) 0%, transparent 26%, transparent 74%, color-mix(in srgb, var(--ground) 42%, transparent) 100%);
}
@media (max-width: 780px) {
  .band__scrim {
    background: linear-gradient(180deg, color-mix(in srgb, var(--ground) 82%, transparent) 0%, color-mix(in srgb, var(--ground) 58%, transparent) 55%, color-mix(in srgb, var(--ground) 88%, transparent) 100%);
  }
}

/* Photo-backed bands. The photo sits under a flat tint: cream on the page
   heroes (which then invert to brown ink, same as the video hero), coffee
   brown everywhere else so cream ink keeps its contrast. */
.band__scrim--coffee { background: color-mix(in srgb, var(--coffee) 78%, transparent); }
.band__scrim--cream  { background: color-mix(in srgb, var(--cream-ground) 84%, transparent); }
.band__scrim--coffee-light { background: color-mix(in srgb, var(--coffee) 50%, transparent); }
.band__scrim--cream-60 { background: color-mix(in srgb, var(--cream-ground) 60%, transparent); }
.band--cream { color: var(--coffee); }
.band--cream h1, .band--cream h2 { color: var(--coffee-deep); }
.band--cream :focus-visible { outline-color: var(--coffee-deep); }
.band--cream .btn-primary { background: var(--coffee-deep); color: var(--cream-ground); }
.band--cream .btn-primary:hover { background: var(--coffee); }
.band--cream .btn-ghost { border-color: color-mix(in srgb, var(--coffee) 45%, transparent); color: var(--coffee-deep); }
.band--cream .btn-ghost:hover { border-color: var(--coffee-deep); }

.band__inner { position: relative; z-index: 2; width: 100%; max-width: 1240px; margin: 0 auto; }
.band__col { max-width: 52ch; }

/* foreground cut-out: arrives from its nearest edge, pins, then fades + blurs */
.cutout {
  position: absolute; z-index: 1;
  pointer-events: none; user-select: none;
  width: clamp(230px, 34vw, 520px);
  opacity: 0;
  filter: blur(14px);
  transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease), filter var(--slow) var(--ease);
}
/* rises out of the section's bottom edge and sits OVER the cards (z-index 3
   beats .band__inner's 2). pointer-events: none keeps the cards clickable
   through it. */
.cutout--bottom {
  /* The source frame cuts the cup off at both sides, so those edges are
     masked out rather than hidden by an ever-deeper bleed. z-index 1 puts it
     under .band__inner (2): the cards overlap the cup, not the other way. */
  /* the cup's apex sits at 39% of the frame, not 50%, so the image is anchored
     left and run wide to put that apex on the section's centre line */
  left: 0; bottom: -44vw;
  z-index: 1;
  width: 128vw;
  filter: none;
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%);
  transform: translateY(34%) scale(0.97);
}
.cutout--right  { right: clamp(-3rem, -1vw, 1rem); bottom: -8%; transform: translateX(22%) scale(0.97); }
.cutout--left   { left:  clamp(-4rem, -2vw, 0rem); bottom: -12%; transform: translateX(-22%) scale(0.97); }
.band.is-active .cutout { opacity: 1; filter: blur(0); transform: translateX(0) scale(1); }

/* Reviews: the barista cut-out runs big on the left and the copy is pulled to
   the centre so the two overlap only at the edge. The band keeps its own
   height; whatever the image can't fit is clipped by the band's overflow. */
#reviews .cutout--left { width: auto; height: 104%; left: 0; bottom: 0; z-index: 3; }
#reviews::before { z-index: 4; } /* divider hairline stays on top of the cut-out */
#reviews .band__col { margin-left: clamp(0px, 14vw, 220px); max-width: 64ch; }
#reviews h2 { font-size: var(--fs-h1); }
#reviews p { font-size: clamp(1.15rem, 1.6vw, 1.5rem); max-width: none; }
#reviews .rating { font-size: 1.25rem; }
#reviews .rating b { font-size: 2.5rem; }
#reviews .btn { font-size: 1.15rem; padding: 1rem 1.6rem; }
/* no parallax bleed here: the photo sits at its natural cover size */
#reviews .band__bg { inset: 0; transform: none; }

@media (max-width: 780px) {
  .cutout { width: clamp(180px, 52vw, 300px); opacity: 0.42; }
  .band.is-active .cutout { opacity: 0.42; }
  /* the bottom cut-out's bleed is tied to its width, so it keeps its own */
  .cutout--bottom { width: 128vw; }
}

/* -------------------------------------------------------------------------
   Line-mask reveal: whole lines rise from behind a mask. Never per-word.
   ------------------------------------------------------------------------- */
/* The mask must clear descenders, or "properly" loses its y. */
.lines > * {
  display: block;
  overflow: hidden;
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}
.lines .ln {
  display: block;
  transform: translateY(105%);
  transition: transform 1000ms var(--ease);
}
.is-active .lines .ln { transform: translateY(0); }
.is-active .lines .ln:nth-child(2) { transition-delay: 90ms; }
.is-active .lines .ln:nth-child(3) { transition-delay: 180ms; }

.rise {
  opacity: 0; transform: translateY(22px);
  transition: opacity 800ms var(--ease), transform 800ms var(--ease);
}
.is-active .rise { opacity: 1; transform: none; }
.is-active .rise-1 { transition-delay: 220ms; }
.is-active .rise-2 { transition-delay: 320ms; }
.is-active .rise-3 { transition-delay: 420ms; }
.is-active .rise-4 { transition-delay: 520ms; }

/* -------------------------------------------------------------------------
   Hero
   ------------------------------------------------------------------------- */
/* Longer headline than --fs-mega was drawn for: sized so the widest line
   fits one screen line at every width. */
.hero h1 { font-size: clamp(2.1rem, 6.4vw, 5rem); }
.hero .band__col { max-width: none; }

/* Title sequence: a curtain wipe clears, then the lines rise.
   Plays once per browser session (sessionStorage), never on return scroll. */
.curtain {
  position: absolute; inset: 0; z-index: 6;
  background: var(--ground);
  transform-origin: top;
  pointer-events: none;
}
.seq .curtain { animation: curtain 1150ms var(--ease) 120ms forwards; }
.no-seq .curtain { display: none; }
@keyframes curtain { to { transform: scaleY(0); } }


/* -------------------------------------------------------------------------
   Cards. The frame never moves; the image scales inside it.
   ------------------------------------------------------------------------- */
.grid {
  display: grid; gap: clamp(1rem, 2.4vw, 2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
  margin-top: clamp(2.5rem, 6vw, 4.5rem);
}

/* The whole card is the link, so the box is the hit area instead of lifting
   on hover. The .link inside is a span that still carries the underline. */
.card {
  display: block;
  position: relative;
  color: inherit; text-decoration: none;
  background: color-mix(in srgb, var(--ground-lift) 88%, transparent);
  border: 1px solid color-mix(in srgb, var(--ink) 13%, transparent);
  border-radius: var(--radius);
  padding: clamp(1.5rem, 2.6vw, 2.25rem);
  cursor: pointer;
  transition: background-color 380ms var(--ease), border-color 380ms var(--ease);
}
.card:hover, .card:focus-visible {
  background: var(--ground-lift);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
}
.card:hover .link::after, .card:focus-visible .link::after { transform: scaleX(1); }
.card h3 { font-size: var(--fs-h3); margin-bottom: 0.75rem; }
.card p { font-size: 0.9375rem; color: var(--taupe); margin-bottom: 0; }
.card .link { display: inline-block; margin-top: 1.15rem; font-size: var(--fs-small); font-weight: 500; }

/* image tile: fixed frame, image scales inside */
.tile {
  position: relative; overflow: hidden;
  border-radius: var(--radius);
  aspect-ratio: 4 / 5;
  background: var(--ground-lift);
  transition: transform 520ms var(--ease);
}
.tile img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 900ms var(--ease);
}
.tile:hover { transform: translateY(-6px); }
.tile:hover img { transform: scale(1.07); }
.tile figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 2.5rem 1.1rem 1rem;
  font-size: var(--fs-small);
  color: var(--ink);
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--ground) 92%, transparent));
}

/* -------------------------------------------------------------------------
   Facts / contact rows
   ------------------------------------------------------------------------- */
.facts { list-style: none; padding: 0; margin: 2.25rem 0 0; display: grid; gap: 1.4rem; }
.facts dt, .facts .k {
  font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--taupe); margin-bottom: 0.3rem;
}
.facts dd, .facts .v { margin: 0; font-size: 1.0625rem; color: var(--ink); }
.facts a { color: var(--ink); }

.map-frame {
  border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 16 / 11;
  background: var(--ground-lift);
}
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

.split {
  display: grid; gap: clamp(2rem, 5vw, 4.5rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr));
  align-items: start;
}

.rating {
  display: inline-flex; align-items: baseline; gap: 0.5rem;
  font-size: var(--fs-small); color: #fff;
  margin-top: 1.75rem;
}
.rating b { font-family: var(--display); font-size: 1.5rem; color: var(--accent); font-weight: 900; }

/* -------------------------------------------------------------------------
   Footer. Every link goes somewhere real.
   ------------------------------------------------------------------------- */
.site-foot {
  background: color-mix(in srgb, var(--ground) 68%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  padding: clamp(3.5rem, 7vw, 5.5rem) var(--gutter) 2rem;
}
.foot-grid {
  max-width: 1240px; margin: 0 auto;
  display: grid; gap: 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
}
.foot-grid h2 {
  font-family: var(--text); font-weight: 600;
  font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.14em;
  color: var(--taupe); margin-bottom: 1rem;
}
.foot-grid ul { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.6rem; }
.foot-grid a { color: var(--ink); text-decoration: none; font-size: 0.9375rem; }
.foot-grid a:hover { color: var(--accent); }
.foot-grid address { font-style: normal; font-size: 0.9375rem; color: var(--taupe); line-height: 1.7; }
.foot-base {
  max-width: 1240px; margin: 3rem auto 0;
  padding-top: 1.5rem;
  border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  display: flex; flex-wrap: wrap; gap: 0.75rem 1.5rem; justify-content: space-between;
  font-size: var(--fs-small); color: var(--taupe);
}

/* -------------------------------------------------------------------------
   Video hero (home page only, .hero--video).

   The clip is the Champs logo animating a pour: a white disc on a black
   square, with a generator watermark parked in the dead corner. Two moves
   make it belong to the page rather than sit on it -- the square is masked
   to the disc (killing the surround and the watermark), and a cream layer is
   multiplied over it so the clip's white field becomes the section's own
   ground while the brown artwork only warms. Both are lifted from the
   approved Claude Design comp.

   Because that ground is cream, this one section inverts the site palette:
   coffee brown ink instead of cream ink. #3E2617 on #FFFDD0 is 9.0:1 and
   #6B4A31 on #FFFDD0 is 7.6:1, so both clear AA comfortably.
   ------------------------------------------------------------------------- */
.hero--video {
  /* The disc's diameter and how far right of centre it finally sits.
     The logo fills its circle almost edge to edge -- the wordmark sits at
     98% of the radius -- so the disc can never be allowed to clip: every
     term here is a real limit, not padding. 85svh keeps the portafilter
     clear of the fixed header, and the shift is deliberately a smaller
     fraction of the viewport than the disc, so on narrower screens the
     disc slides back toward centre instead of off the right edge. */
  --disc: min(58vw, 800px, 85svh);
  --disc-shift: min(18vw, 320px);
  /* The band starts at the top of the page and the header floats over it, so
     centring the disc on the band tucks the portafilter under the nav. Drop
     it by about half the header's height to centre it in the space actually
     visible below the nav instead. 44px clears the header at every viewport
     height from 700px up, while staying inside the band's bottom edge (the
     window is roughly 26px-58px at 700px tall, widening as it gets taller). */
  --disc-drop: 44px;

  background: var(--cream-ground);
  color: var(--coffee);
}
.hero--video h1 { color: var(--coffee-deep); }
.hero--video :focus-visible { outline-color: var(--coffee-deep); }

/* the accent yellow is drawn for the dark ground and goes muddy on cream, so
   the CTA pair is restated in brown here */
.hero--video .btn-primary { background: var(--coffee-deep); color: var(--cream-ground); }
.hero--video .btn-primary:hover { background: var(--coffee); }
.hero--video .btn-ghost { border-color: color-mix(in srgb, var(--coffee) 45%, transparent); color: var(--coffee-deep); }
.hero--video .btn-ghost:hover { border-color: var(--coffee-deep); }

/* keep the copy clear of the disc's final resting place */
.hero--video .band__col { max-width: 50%; }

/* The move: the disc is centred on the content box, then glides right. Both
   states are one transform on one element, so it is a single composited
   property and the start pose is a true centre rather than an approximation
   of one. */
.hero--video .hero__media {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 0;
  width: var(--disc);
  aspect-ratio: 1;
  pointer-events: none;
  transform: translate(-50%, calc(-50% + var(--disc-drop))) scale(0.96);
  opacity: 0;
  transition:
    transform 1500ms var(--ease) 200ms,
    opacity 900ms var(--ease) 200ms;
}
.hero--video.is-ready .hero__media {
  transform: translate(calc(-50% + var(--disc-shift)), calc(-50% + var(--disc-drop)));
  opacity: 1;
}

/* A composited <video> is not reliably clipped by overflow + border-radius,
   so the circle is cut with a mask, which does clip it. The 0.5% feather
   kills the stair-stepping a hard mask edge leaves on a big circle. */
.hero__disc {
  position: absolute; inset: 0;
  border-radius: 50%;
  overflow: hidden;
  /* closest-side matters: a bare `circle` sizes itself to the farthest CORNER,
     which would put the stops on a 382px radius in a 540px box and cut a
     circle far smaller than the element. */
  -webkit-mask-image: radial-gradient(circle closest-side, #000 99.3%, transparent 100%);
  mask-image: radial-gradient(circle closest-side, #000 99.3%, transparent 100%);
}
/* The source is a white logo disc inside a black ring. Measured against the
   frames: the wordmark reaches 93.4% of the half-width and the ring starts at
   99.4%, so any scale between 100.6% and 107.1% both hides the ring and keeps
   the logo whole. 104% sits in the middle of that window, roughly 9px of
   margin either way at the desktop size. The comp's 106% is inside the window
   too, but leaves almost nothing on the wordmark side. */
.hero__video, .hero__tint {
  position: absolute;
  left: -2%; top: -2%;
  width: 104%; height: 104%;
  object-fit: cover;
  filter: brightness(1.035) saturate(1.04);
}
/* the clip's white becomes the section's cream; its browns just warm. Sits
   over both clips, so the handover underneath it is invisible. */
.hero__tint {
  background: var(--cream-ground);
  mix-blend-mode: multiply;
  pointer-events: none;
  filter: none;
}

/* The handover. Both clips are stacked and decoded; only which one is opaque
   changes, and it changes in one frame with no transition -- a fade would
   cross-expose two different frames of the same artwork and read as a ghost. */
.hero__video--tail { opacity: 0; }
.hero--video.is-tail .hero__video--pour { opacity: 0; }
.hero--video.is-tail .hero__video--tail { opacity: 1; }

/* The copy needs no gate of its own: .lines .ln and .rise are already hidden
   until their band turns .is-active, so site.js simply withholds .is-active
   from this one hero until the clip is ready. */

@media (max-width: 860px) {
  /* no room to sit beside the copy: the disc stacks above it and drops the
     sideways move, keeping only the fade-up */
  .hero--video { --disc-shift: 0px; }
  .hero--video .band__inner { display: flex; flex-direction: column; }
  .hero--video .band__col { max-width: none; }
  .hero--video .hero__media {
    position: relative;
    left: auto; top: auto;
    width: min(88vw, 420px);
    margin: 0 auto clamp(1.5rem, 5vw, 2.5rem);
    transform: translateY(20px) scale(0.96);
  }
  .hero--video.is-ready .hero__media { transform: none; }
}

/* -------------------------------------------------------------------------
   Reduced motion: every element sits in its final position immediately and
   the complete reading experience survives. Nothing is hidden behind an
   animation that never plays.
   ------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .cutout { opacity: 1 !important; filter: none !important; transform: none !important; }
  .lines .ln { transform: none !important; }
  .rise { opacity: 1 !important; transform: none !important; }
  .band__bg { transform: none !important; }
  .curtain { display: none !important; }
}
