/* spearsandarrows.tv — static site styles.
   Carried over from the Webflow site: black ground, #CC494F accent, white
   type. Display face is GT Flexa Condensed, body is Montserrat — both the
   site's own self-hosted files. */

@font-face {
  font-family: "GT Flexa Condensed";
  src: url("/fonts/GT-Flexa-Condensed-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "GT Flexa Condensed";
  src: url("/fonts/GT-Flexa-Condensed-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "GT Flexa Condensed";
  src: url("/fonts/GT-Flexa-Condensed-Bold.woff2") format("woff2");
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "GT Flexa Condensed";
  src: url("/fonts/GT-Flexa-Condensed-Medium-Italic.woff2") format("woff2");
  font-weight: 500; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "Avenir LT Std";
  src: url("/fonts/AvenirLTStd-Book.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: swap;
}

:root {
  --black: #000;
  --white: #fff;
  --accent: #cc494f;
  --grey: #9a9a9a;
  --line: #2a2a2a;

  --display: "GT Flexa Condensed", "Arial Narrow", Helvetica, Arial, sans-serif;
  --body: "Montserrat", "Avenir LT Std", -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;

  --inset: clamp(16px, 2.8vw, 40px);
  --maxw: 1200px;
}

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

body {
  margin: 0;
  background: var(--black);
  color: var(--white);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img, video { display: block; max-width: 100%; }
a { color: inherit; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 60;
  background: var(--white); color: var(--black); padding: 10px 16px; text-decoration: none;
}
.skip:focus { left: 0; }

:where(a, button):focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ------------------------------------------------------------- top bar */

/* Insets measured off the live page: the mark sits 40px in, the burger 30px
   from the right with its top bar at y=30. They are not the same number on
   the original either. */
.topbar {
  position: absolute; inset: 0 0 auto 0; z-index: 40;
  /* raised above the doors only while the menu is open; see .menu-open */
  display: flex; align-items: flex-start; justify-content: space-between;
  padding: 0 30px 0 40px;
  pointer-events: none;
}
.topbar > * { pointer-events: auto; }
.menu-open .topbar { z-index: 96; }

/* The animated monogram, 73x90 on the live page. */
.logo img { width: 73px; height: auto; }

.burger {
  appearance: none; border: 0; background: transparent;
  padding: 30px 0 10px; cursor: pointer; line-height: 0;
}
.burger__bars { display: block; width: 40px; }
.burger__bars i {
  display: block; height: 2px; background: var(--white);
  transition: transform 0.2s ease, opacity 0.2s ease;
}
.burger__bars i + i { margin-top: 6px; }
.burger[aria-expanded="true"] .burger__bars i:nth-child(1) { transform: translateY(4px) rotate(45deg); }
.burger[aria-expanded="true"] .burger__bars i:nth-child(2) { transform: translateY(-4px) rotate(-45deg); }

/* ---------------------------------------------------------------- menu */

/* The menu is what is written on the shut doors: opening it closes them, and
   the content lands as they do. So it sits above the panels, and it brings no
   backdrop of its own — the doors are the backdrop. */
.menu {
  position: fixed; inset: 0; z-index: 34;
  background: transparent;
  display: grid; align-content: center;
  padding: 40px 0;
  visibility: hidden; opacity: 0;
  overflow-y: auto;
}

/* The transitions only exist once the page has settled. The script's first
   act is to drop .no-js, which moves the menu from the inline fallback
   (visible, opaque) to hidden — and with a transition already attached, that
   is a transition, so the menu sat on screen for the length of it on every
   single load. .js-ready goes on two frames later, after that change has
   been painted, so the menu is simply hidden and only animates thereafter.

   It arrives as the doors land rather than on a travel of its own: the
   panels take 420ms to meet, so the words come up over the last of that and
   there is one movement on screen instead of two. */
.js-ready .menu {
  transition: opacity 200ms ease 260ms, visibility 0s linear 460ms;
}
/* Above the doors only once it is open. Until the script runs, the markup is
   in its no-JS state with the nav shown inline, and that must stay under the
   doors rather than flashing over them while the page loads. */
.menu.is-open { z-index: 95; visibility: visible; opacity: 1; }
.js-ready .menu.is-open { transition: opacity 200ms ease 260ms, visibility 0s; }

/* Two columns with the ampersand ruling the page between them, which is what
   the artwork is for. Live at 1440: pages left at x=144, ampersand 302 wide
   starting at 569, directors centred in the right column at x=994. A 144px
   gutter with a 302px middle column reproduces all three. */
.menu__cols {
  display: grid;
  grid-template-columns: 1fr clamp(120px, 21vw, 302px) 1fr;
  align-items: center;
  padding: 0 clamp(20px, 10vw, 144px);
  width: 100%;
}
/* The same box as the doors' seam, to the pixel. The menu's ampersand and
   the doors' ampersand are the same mark in the same place, so closing the
   doors over an open menu leaves it sitting still while everything else
   moves. When they differed the mark jumped 63px up and grew 126px taller
   at the moment of the click, which read as a glitch rather than a
   transition.

   It is taken out of flow for that; the grid's middle column keeps its own
   width, so the two lists stay just as far apart. */
/* The middle column is empty on purpose: it is the gutter the doors' seam
   runs down. There is only one ampersand on screen now, and it belongs to
   the doors. */

.menu ul { list-style: none; margin: 0; padding: 0; }

/* Placed explicitly: the ampersand is out of flow, so auto-placement would
   otherwise drop the directors into the middle column where it sits. */
.menu__pages { grid-column: 1; justify-self: start; text-align: left; }
.menu__pages a {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; text-decoration: none; display: block;
  font-size: clamp(1.7rem, 3.5vw, 3.15rem);      /* live: 50.4px */
  line-height: 63px; color: var(--white);
}
.menu__pages a.menu__home { font-size: clamp(1.9rem, 4vw, 3.6rem); }  /* live: 57.6px */
.menu__pages a:hover,
.menu__pages a[aria-current="page"] { color: var(--accent); }

/* One size for the names, and the heading is a flat 1.3x it. Both read off
   this one property so the ratio holds at every width and the two cannot
   drift apart the way they did when each carried its own clamp. */
.menu__dirs {
  grid-column: 3; justify-self: center; text-align: center;
  --dirname: clamp(1.25rem, 2.6vw, 1.64rem);
}
/* Caps, at the client's request: it read as a touch small and wanted
   capitals. Set in the display face at 700 and a step larger than the names
   under it, which is how every other piece of nav text on this page is set,
   so it reads as the head of the column. Not the thin, letter-spaced small
   caps live uses: the red and the extra size do that work, and no tracking
   is added. */
.menu__label {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; color: var(--accent);
  margin: 0 0 14px; font-size: calc(var(--dirname) * 1.3);
  line-height: 1.35;
}
.menu__directors a {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; text-decoration: none; display: block;
  /* 26.2px, 30% over live's 20.16px. The old clamp leaned on 1.4vw, so
     live's size only ever arrived at exactly 1440 and anything near 1000px
     got the floor instead; the vw term is steep enough now that the ceiling
     is reached by about 1010px and holds from there up. */
  font-size: var(--dirname);
  line-height: 38px; color: var(--white); white-space: nowrap;
}
.menu__directors a:hover,
.menu__directors a[aria-current="page"] { color: var(--accent); }

/* No JS means the overlay can never open, so show the nav inline instead. */
.no-js .menu {
  position: static; visibility: visible; opacity: 1; transform: none;
  background: #0a0a0a; padding: 90px 20px 24px;
}
.no-js .menu__cols { grid-template-columns: 1fr; justify-items: center; padding: 0; }
/* Same trap as the phone menu: one track in the template, but the lists are
   still pinned to columns 1 and 3, so they need moving as well as centring. */
.no-js .menu__pages,
.no-js .menu__dirs { grid-column: 1; justify-self: center; }
.no-js .menu__pages { text-align: center; }
.no-js .menu__pages a { font-size: 1.4rem; line-height: 2; }
.no-js .menu__directors a { font-size: 1.05rem; display: inline-block; padding: 4px 10px; line-height: 1.6; }
.no-js .menu__directors li { display: inline-block; }
.no-js .burger { display: none; }

/* --------------------------------------------------------------- doors */
/* Two panels that draw shut over the page on the way out and slide open on
   the way in. They meet flush in the middle on a hard-landing curve, so the
   close reads as a click rather than a fade.

   The opening half is CSS alone and runs on load, which is the important
   part: a visitor whose JavaScript never arrives still gets the doors opened
   and is never stranded behind a black screen. Only the closing half needs a
   script, and without one a link just navigates the way it always did. */

.doors {
  position: fixed; inset: 0; z-index: 90;
  pointer-events: none; overflow: hidden;

  /* How long the shut doors hold before parting — the beat the monogram
     plays in. One number; everything below reads it.

     The gif's motion is 2100ms: 17 frames at 100ms plus one at 400ms, read
     out of the file's own graphic control blocks rather than sampled off the
     screen. It used to be unknowable from the file, because 17 of those 19
     frames carried a delay of zero, which the format leaves to the browser —
     every engine substitutes its own minimum, so the duration was a browser
     policy, not a property of the artwork. The delays are explicit now, so
     2100ms is what every browser plays.

     2300 leaves 200ms on the finished monogram before it fades. That is a
     real margin now rather than a lucky one: the gate below keeps this hold
     paused until the gif is decoded, so both clocks start on one frame. */
  --doors-hold: 2300ms;
}

/* Second and later pages of a visit: the intro has been seen, so the doors
   are just a transition. An inline script in the head sets this class, and
   if it never runs the full intro simply plays again — which is the old
   behaviour, not a broken one. */
.doors-seen .doors { --doors-hold: 240ms; }
.doors-seen .doors__mark { display: none; }

/* The monogram waits for its own first frame.

   A gif's clock starts when it paints, and nothing can drive one from script
   — no play, no seek, no restart — so the only moment available to choose is
   that first paint. Cached, it painted during parse and was already part
   drawn; cold, it painted late and got cut off. The hold above counts from
   the moment this stylesheet applies either way, so the two were never
   holding each other, and 200ms of margin was the whole safety net.

   So the mark is not rendered until site.js says the bytes are decoded: a gif
   that is not being rendered is not animating, so there is no clock yet to be
   out of step with. site.js adds .doors-ready, which paints frame one, and
   rewinds the hold to its start in the same breath so both begin together.

   Nothing here is load bearing and nothing is ever paused. The panels open
   from this stylesheet on their own schedule exactly as before; the class
   only decides whether the monogram is on screen while they wait. If site.js
   never runs, or the image never decodes, the doors still open on time — the
   arrival simply goes without its intro. The one visitor who loses something
   is the one with scripting off, who used to see the monogram and now sees
   the hold without it. */
.doors__mark { display: none; }
.doors-ready .doors__mark { display: block; }

/* The monogram animating centre screen before the reveal, as on the live
   site. It sits after the panels in the markup, so it paints over them
   without needing a z-index of its own, and it is gone by the time they
   finish parting. */
/* Live sizes it off the viewport height, not a fixed pixel width: 50vh tall
   with its top at 20vh, which is 366x450 at 1440x900 and keeps the mark the
   same share of the screen at every size. It sits a little above centre. */
.doors__mark {
  position: absolute; top: 20svh; left: 50%;
  height: 50svh; width: auto;
  transform: translateX(-50%);
  animation: doors-mark 420ms ease var(--doors-hold) both;
}
@keyframes doors-mark { from { opacity: 1; } to { opacity: 0; } }

/* 50.05% so the halves overlap by a hair. At a flat 50% a fractional viewport
   width leaves a one-pixel stripe of the page showing down the join. */
.doors i {
  position: absolute; top: 0; bottom: 0; width: 50.05%;
  display: block; background: var(--black);
  animation-duration: 620ms;
  animation-delay: var(--doors-hold);
  animation-timing-function: cubic-bezier(0.62, 0, 0.24, 1);
  animation-fill-mode: both;
}

.doors i:nth-child(1) { left: 0; animation-name: doors-open-l; }
.doors i:nth-child(2) { right: 0; animation-name: doors-open-r; }

/* The join itself: the site's ampersand, a hairline the full height of the
   screen with the flourish at the middle. It comes after the panels in the
   markup so it lays over them rather than being split between the two.
   Full height, and its own ratio gives 306px wide at 900 tall, which is the
   302x900 the live site uses.

   It shows on an arrival where the monogram is skipped, and draws in as the
   doors shut. On a first arrival the monogram has the middle of the screen
   and the two would sit on top of each other, so the seam sits that one out. */
.doors__seam {
  position: absolute; top: 0; left: 50%;
  transform: translateX(-50%);
  height: 100svh; width: auto;
  opacity: 0;
}
.doors-seen .doors__seam {
  animation: doors-mark 420ms ease var(--doors-hold) both;
}
.doors.is-shutting .doors__seam {
  animation: doors-seam-in 300ms ease 60ms both;
}
@keyframes doors-seam-in { from { opacity: 0; } to { opacity: 1; } }

@keyframes doors-open-l { from { transform: translateX(0); }     to { transform: translateX(-100%); } }
@keyframes doors-open-r { from { transform: translateX(0); }     to { transform: translateX(100%); } }
@keyframes doors-shut-l { from { transform: translateX(-100%); } to { transform: translateX(0); } }
@keyframes doors-shut-r { from { transform: translateX(100%); }  to { transform: translateX(0); } }

/* The home page arrives behind the monogram, not behind the doors. The
   panels stay shut and the whole cover fades once the mark has played, so
   the reel is revealed rather than parted. Sliding doors are the transition
   between pages, and the home page is not somewhere you transition to from
   inside the site often enough to want them here.

   Scoped to :not(.is-shutting) so leaving the home page, and opening the
   menu on it, still draw the panels across as everywhere else. */
.is-home .doors:not(.is-shutting) i {
  animation: none; transform: translateX(0);
}
.is-home .doors:not(.is-shutting) {
  animation: doors-clear 520ms ease var(--doors-hold) both;
}
@keyframes doors-clear { from { opacity: 1; } to { opacity: 0; } }

/* Shutting is quicker than opening and starts at once: the next page is
   already being fetched behind the panels, so the wait buys something. The
   timings above are longhands on purpose — the `animation` shorthand here
   would reset animation-name back to none. */
.doors.is-shutting i {
  animation-duration: 420ms;
  animation-delay: 0s;
  animation-timing-function: cubic-bezier(0.5, 0, 0.26, 1);
}
.doors.is-shutting i:nth-child(1) { animation-name: doors-shut-l; }
.doors.is-shutting i:nth-child(2) { animation-name: doors-shut-r; }

/* ---------------------------------------------------------------- home */
/* The home page is the showreel, full bleed under the chrome. Geometry
   measured off the live Webflow page at 1440 and 390: the video fills the
   viewport, a centred wordmark bar sits directly under it, then the footer. */

.home { min-height: 0; padding: 0; display: block; }

/* These selectors are deliberately more specific than the .hero rules further
   down the file. A plain `.home__reel iframe` ties with `.hero iframe` on
   specificity and loses on order, which silently reinstates its `inset: 0`
   and throws the centring transform off by half the viewport. */
.home .home__reel {
  aspect-ratio: auto;
  height: 100svh;
  min-height: 420px;
  width: 100%;
  background: #000 center / cover no-repeat;
}
/* The iframe is 16:9 but the box is the viewport. Scaling from the centre on
   whichever axis is short covers it without letterboxing — the same result the
   Webflow page gets from a 1000%-wide absolutely positioned wrap. */
.home .home__reel .hero__frame { overflow: hidden; }
.home .home__reel .hero__frame iframe {
  position: absolute;
  top: 50%; left: 50%; right: auto; bottom: auto;
  width: 100vw; height: 56.25vw;
  min-width: 177.78svh; min-height: 100svh;
  transform: translate(-50%, -50%);
  border: 0; pointer-events: none;
}

/* No JS, no iframe: the directors stay reachable from the page itself. */
.home__directors {
  list-style: none; margin: 0; padding: 40px var(--inset);
  display: flex; flex-wrap: wrap; gap: 8px 28px; justify-content: center;
}
.home__directors a {
  font-family: var(--display); font-size: 1.1rem; text-transform: uppercase;
  text-decoration: none; letter-spacing: .01em;
}
.home__directors a:hover { color: var(--accent); }

/* ------------------------------------------------------- wordmark bar */
/* 1440: bar 34px tall, lockup 200x34 centred. 390: 17px tall, 100x17. */
.wordbar {
  display: flex; justify-content: center; align-items: center;
  background: var(--black); padding: 0;
}
.wordbar img {
  width: clamp(100px, 13.9vw, 200px); height: auto;
}

/* -------------------------------------------------------------- footer */
/* 1440: 158px tall, stamp 100x100 at the left inset, social right-aligned,
   copyright and small lockup beneath them. 390: the same, stacked tighter. */
.sitefoot {
  background: var(--black);
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 24px;
  padding: 30px 53px 28px;
}
.sitefoot__stamp { width: 100px; height: 100px; flex: none; }

.sitefoot__end {
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
  padding-top: 20px;
}
.sitefoot__social {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; gap: 0;
}
.sitefoot__social a {
  display: grid; place-items: center; width: 35px; height: 35px;
}
.sitefoot__social img {
  width: 30px; height: 30px; object-fit: contain;
  opacity: .92; transition: opacity .18s ease;
}
.sitefoot__social a:hover img { opacity: 1; }

.sitefoot__copy {
  margin: 0; display: flex; align-items: center; gap: 10px;
  font-size: 1rem; line-height: 28px; color: var(--white);
}
.sitefoot__copy img { width: 120px; height: 20px; }

@media (max-width: 760px) {
  .sitefoot { padding: 18px 14px 22px; gap: 12px; }
  /* The stamp stays 100px on the live page at every width. */
  .sitefoot__end { padding-top: 10px; gap: 6px; }
  /* Three 30px boxes per row, so the icons wrap 3 + 2 as they do on the live
     page instead of running as one line. */
  .sitefoot__social {
    flex-wrap: wrap; justify-content: flex-end; gap: 0; max-width: 90px;
  }
  .sitefoot__social a { width: 30px; height: 31px; }
  .sitefoot__social img { width: 21px; height: 21px; }
  .sitefoot__copy {
    font-size: .8rem; line-height: 22px; gap: 6px;
    flex-wrap: wrap; justify-content: flex-end;
  }
  .sitefoot__copy img { width: 100px; height: 16px; }
  /* The live page stops the reel short of the fold so the wordmark bar peeks
     in, which is what tells you there is anything below it. */
  .home .home__reel { height: 93svh; }
  .home .home__reel .hero__frame iframe { min-height: 93svh; }
}

/* ------------------------------------------------------------ director */
/* Measured off the live page at 1440x900. The hero is the whole fold: the
   reel full bleed, the name sitting on it bottom-left, the bio folded away
   behind a chevron. Live numbers — hero 1440x900, name at 144,812 at
   60.8px/60.8px, bio 900x300 at 144,563 in Montserrat 16/28, portrait
   200x200 at 1044,563. 144 is 10% of 1440, so the inset is written as one. */

.director { padding-bottom: 80px; }

/* 10vw lands on the live 144px at 1440 and still leaves a sane gutter on a
   phone. The card is bottom-anchored so a long bio grows upward from a fixed
   footing instead of shifting the name by a different amount per director. */
.director__card { --dinset: clamp(20px, 10vw, 144px); }

.hero { position: relative; aspect-ratio: 16 / 9; background: #080808; overflow: hidden; }
.hero__frame { position: absolute; inset: 0; }
.hero iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* The home reel plays; it does not open on a still of itself. A poster that
   holds for a beat and then cuts to the same film starting over reads as a
   stutter no matter which frame it is, so the ground stays black until the
   video is actually there and then the iframe fades up.

   The poster is still carried on the element as --poster, for the two cases
   where no video is ever coming: scripting off, so nothing mounts the
   iframe, and reduced motion, which site.js honours by not mounting it. In
   both the still is the page rather than a fallback for one. */
.no-js .hero--home {
  background-image: var(--poster);
  background-size: cover; background-position: center;
}
@media (prefers-reduced-motion: reduce) {
  .hero--home { background-image: var(--poster); background-size: cover; background-position: center; }
}

/* The director hero is the fold, not a 16:9 band. svh so a phone's collapsing
   address bar cannot leave a strip of the next section showing. */
.hero--director {
  aspect-ratio: auto; height: 100svh; min-height: 460px;
  background-size: cover; background-position: center;
}

/* The iframe has to be cover-scaled now that the box is no longer 16:9.
   Stretching it instead letterboxes the video inside it and Vimeo's own white
   background shows through as a band — 45px of it at 1440x900.
   These selectors are deliberately more specific than `.hero iframe` further
   up, which sets inset: 0 and would otherwise reinstate top/left: 0 while the
   centring transform still applied, throwing the frame half a viewport off. */
.hero--director .hero__frame { overflow: hidden; }
.hero--director .hero__frame iframe {
  position: absolute; top: 50%; left: 50%; right: auto; bottom: auto;
  width: 100vw; height: 56.25vw;
  min-width: 177.78svh; min-height: 100svh;
  transform: translate(-50%, -50%);
  border: 0; pointer-events: none;
}

/* Two scrims, and the short one is a deliberate departure from the live page.
   Live draws nothing until the bio opens, so the white name sits straight on
   the frame — fine over Lance Doty's night baseball field, unreadable over
   the snow two seconds later. The name is the one thing that must survive any
   frame of any reel, so it gets a ground of its own at all times.
   The tall one is the live gradient and still belongs to the bio. */
.hero__scrim {
  position: absolute; inset: auto 0 0 0; pointer-events: none;
  height: 220px;
  background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.78));
}
/* Live ramps this straight from transparent to black across the full 425px,
   which leaves the first line of the bio sitting on whatever the video is
   doing — measured at luminance 72 behind it on a sunlit frame, which white
   body copy cannot win against. It reaches full black by the time the copy
   starts instead. */
.hero__scrim--bio {
  height: 425px;
  background: linear-gradient(rgba(0, 0, 0, 0) 0%,
                              rgba(0, 0, 0, 0.85) 28%,
                              #000 62%);
  opacity: 0; transition: opacity 400ms ease;
}
.hero--director.is-open .hero__scrim--bio { opacity: 1; }

.director__card {
  position: absolute; z-index: 2;
  left: var(--dinset); right: var(--dinset); bottom: 30px;
  max-width: 1100px;
}

.director__head { display: flex; align-items: center; gap: 6px; }

.director__name {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(2.1rem, 4.25vw, 3.8rem);   /* 3.8rem = the live 60.8px */
  line-height: 1; letter-spacing: normal;
  margin: 0; color: var(--white);
}

.director__toggle {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  color: var(--accent); padding: 0 8px; line-height: 0; flex: none;
}
.director__toggle svg {
  width: clamp(22px, 2vw, 28px); height: clamp(22px, 2vw, 28px);
  display: block; transition: transform 320ms cubic-bezier(0.62, 0, 0.24, 1);
}
.hero--director.is-open .director__toggle svg { transform: rotate(90deg); }

/* 0fr to 1fr animates cleanly where a max-height guess would either clip a
   long bio or coast through empty space on a short one. */
.director__bio {
  display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows 420ms cubic-bezier(0.62, 0, 0.24, 1),
              opacity 300ms ease;
}
.hero--director.is-open .director__bio { grid-template-rows: 1fr; opacity: 1; }
.director__bioinner {
  overflow: hidden;
  display: flex; align-items: flex-start; gap: 0;
}

.director__text { flex: 1 1 auto; min-width: 0; max-width: 900px; }
/* White, not a dimmed grey: this is body copy on a black ground and it is
   meant to be read. 16/28 justified is what the live page sets. */
.director__text p {
  margin: 0 0 14px; color: var(--white);
  font-size: 1rem; line-height: 28px; text-align: justify;
}
.director__text p:last-child { margin-bottom: 0; }

/* The ring is baked into the artwork, so no border or radius here. */
.director__portrait {
  flex: none; width: 200px; height: 200px; object-fit: contain;
  margin-left: 0; align-self: flex-start;
}

/* No script means the chevron can never be pressed, so the bio is simply open
   and the control is not offered. */
.no-js .director__toggle { display: none; }
.no-js .director__bio { grid-template-rows: 1fr; opacity: 1; }
.no-js .hero__scrim--bio { opacity: 1; }

/* --------------------------------------------------------------- reelbar */
/* The director's reels as a carousel under the player. Live runs slick.js for
   this; a scroll container with scroll-snap does the same job natively, keeps
   the keyboard and trackpad behaviour the browser already has, and ships no
   library. Live geometry at 1440: items 350x207 on a 360px step. */

/* The title over a second reel block. Live centres it between the two
   galleries at 48px, 32px on a phone, with the line under the first at
   0.8em — "WONDO / DP". Full-strength white; it is a heading, not a label. */
.reelblock__heading {
  font-family: var(--display); font-weight: 500;
  font-size: clamp(2rem, 3.33vw, 3rem);        /* 3rem = the live 48px */
  line-height: 1; text-align: center; text-transform: none;
  letter-spacing: normal; color: var(--white);
  margin: 50px 0 30px; padding: 10px;
}
.reelblock__sub { font-size: 0.8em; }

.reelbar { position: relative; padding: 45px 0 0; }
/* A carousel that follows its own heading has already been given its space. */
.reelblock__heading + .reelbar { padding-top: 0; }
.reelblock__heading + .reelbar .reelbar__nav { top: 103px; }

.reelbar__track {
  display: flex; gap: 10px; margin: 0;
  list-style: none;
  overflow-x: auto; overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory; scroll-behavior: smooth;
  /* Without a scroll-padding the snap aligns the first item to the scrollport
     edge and scrolls the gutter away, resting at a non-zero scrollLeft with
     the first thumbnail flush against the window. It is set with the padding
     above so the two always agree. */
  scrollbar-width: none;
}
.reelbar__track::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: reduce) { .reelbar__track { scroll-behavior: auto; } }

.reelbar__item { flex: 0 0 350px; scroll-snap-align: start; max-width: 78vw; }

.reelbar__link {
  display: block; text-decoration: none; color: var(--white);
  outline-offset: 4px;
}
.reelbar__media {
  display: block; position: relative; overflow: hidden; background: #0d0d0d;
  aspect-ratio: 350 / 207;
}
.reelbar__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 420ms cubic-bezier(0.62, 0, 0.24, 1), opacity 220ms ease;
}
.reelbar__link:hover .reelbar__media img { transform: scale(1.04); }

.reelbar__meta { display: block; padding: 10px 2px 0; }
.reelbar__brand {
  display: block; font-family: var(--display); font-weight: 700;
  text-transform: uppercase; font-size: 1rem; line-height: 1.2;
}
/* The spot title is the quieter of the two, and it is held back by weight and
   size rather than by dimming it toward the background. */
.reelbar__spot {
  display: block; font-size: 0.8rem; line-height: 1.35; font-weight: 400;
  color: var(--white);
}

/* The one that is playing, marked by addition rather than by dimming the
   other eleven: everything in the strip stays fully legible. */
.reelbar__link[aria-current="true"] .reelbar__media {
  outline: 3px solid var(--accent); outline-offset: -3px;
}
.reelbar__link[aria-current="true"] .reelbar__brand { color: var(--accent); }
/* A bar under the thumbnail, so the current item still reads at a glance
   when the strip is scrolling and the outline is edge-on. */
.reelbar__link[aria-current="true"] .reelbar__meta {
  box-shadow: inset 0 3px 0 var(--accent);
  padding-top: 13px;
}

/* The site's own arrow artwork, 75px as on the live page, in the gutter
   beside the strip rather than sitting on top of the first thumbnail. They
   stay put at the ends and go disabled instead of disappearing: a control
   that vanishes is one nobody learns is there. */
.reelbar__nav {
  position: absolute; top: calc(45px + 103px); transform: translateY(-50%);
  z-index: 3; appearance: none; cursor: pointer;
  width: 75px; height: 75px; padding: 0; border: 0; background: transparent;
  line-height: 0; display: grid; place-items: center;
  transition: opacity 200ms ease, transform 200ms ease;
}
.reelbar__nav img { width: 75px; height: 75px; display: block; }
.reelbar__nav:hover:not(:disabled) { transform: translateY(-50%) scale(1.08); }
.reelbar__nav:disabled { opacity: 0.3; cursor: default; }
.reelbar__nav--prev { left: 0; }
.reelbar__nav--next { right: 0; }

/* The strip clears the arrows so a thumbnail never sits under one. */
.reelbar__track { padding-inline: 90px; scroll-padding-inline: 90px; }

/* ------------------------------------------------------ the staged reel */
/* A picked reel is shown the way its own page shows it — the reel's own
   background image, a contained 16:9 player, brand and spot beneath — so it
   borrows .reel wholesale and adds nothing but the swap. A reel is a film to
   sit and watch at a readable size, not wallpaper, which is what a full-bleed
   player turns it into. */
.reel--stage { min-height: auto; }
.reel--stage[hidden] { display: none; }

/* ---------------------------------------------------------------- grid */

.grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px;
  list-style: none; margin: 0 auto; padding: 0 var(--inset);
  max-width: var(--maxw);
}
.card { min-width: 0; }
.card__link { display: block; text-decoration: none; }
.card__media {
  display: block; aspect-ratio: 16 / 9; overflow: hidden; background: #111;
  position: relative;
}
.card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: opacity 0.2s ease, transform 0.4s ease;
}
.card__link:hover .card__media img,
.card__link:focus-visible .card__media img { opacity: 0.6; transform: scale(1.03); }

.card__meta { display: block; padding: 8px 0 0; }
.card__brand {
  display: block; font-family: var(--display); font-weight: 500;
  text-transform: uppercase; font-size: 0.9rem; letter-spacing: 0.02em;
}
.card__spot { display: block; font-size: 0.8rem; color: var(--grey); }

/* ---------------------------------------------------------------- reel */

.reel {
  position: relative;
  min-height: 100vh;
  background-size: cover;
  background-position: center;
  background-attachment: scroll;
  padding: 0 0 80px;
}
.reel__shade { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.72); }
/* Live rhythm at 1440x900, and it is tighter than it was here: name at 108,
   20px to the player, 20px from the player to the brand, 45px from the spot
   to the strip. The bottom padding is zero because whatever follows brings
   its own 45. */
.reel__inner {
  position: relative;
  max-width: var(--maxw); margin: 0 auto;
  padding: clamp(72px, 7.5vw, 108px) var(--inset) 0;
  text-align: center;
}
.reel__director {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.4rem, 3vw, 2rem); letter-spacing: 0.02em; margin: 0 0 20px;
}
.reel__director a { text-decoration: none; }
.reel__director a:hover { color: var(--accent); }

.player {
  position: relative; aspect-ratio: 16 / 9; background: #000;
  max-width: 960px; margin: 0 auto;
}
/* The black under the player is not decoration. A spot that is not 16:9 —
   scope, or the 3:2 the South African work comes in — is pillarboxed or
   letterboxed by the player, and a cross-origin iframe with nothing painted
   behind it is a white rectangle, so those bars came up white against a
   black page. Jason caught it on SHOWMAX, GWM and BIG DADDY; it was every
   spot that is not 16:9. The same reasoning as .hero__frame iframe above. */
.player iframe, .player__start {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
  background: var(--black);
}
.player__start { padding: 0; margin: 0; background: #000; cursor: pointer; display: block; }
.player__start img { width: 100%; height: 100%; object-fit: cover; opacity: 0.85; transition: opacity 0.2s ease; }
.player__start:hover img { opacity: 1; }
.player__play {
  position: absolute; left: 50%; top: 50%; width: 68px;
  transform: translate(-50%, -50%); pointer-events: none;
}
.player__play svg { width: 100%; height: auto; display: block; }

.reel__brand {
  font-family: var(--display); font-weight: 500; text-transform: uppercase;
  font-size: 2rem; line-height: 1.15; margin: 20px 0 0;
}
.reel__spot {
  font-family: var(--display); font-weight: 400; font-size: 1.8rem;
  text-transform: capitalize; margin: 0; line-height: 1.2;
}

.more { position: relative; padding-top: 45px; }   /* live: 45 from the spot */
.more__head {
  font-family: var(--display); font-weight: 500; text-transform: uppercase;
  font-size: 1rem; letter-spacing: 0.08em; color: var(--accent);
  text-align: center; margin: 0 0 20px;
}

/* --------------------------------------------------------------- about */
/* Live at 1440: the copy in an 880px measure from x=130, the Our Oath mark
   on the right at 1145,207 at 165x300. The oath itself is not on this page
   — the mark opens it. */

.about { padding: clamp(100px, 12vw, 117px) var(--inset) 80px;
  /* 1260 plus the 40px inset puts the copy's left edge on the live 130
     and the mark's right edge on the live 1310. */
  max-width: 1260px; margin: 0 auto; }
.about__grid {
  display: grid; grid-template-columns: minmax(0, 880px) 1fr;
  gap: clamp(24px, 4vw, 60px); align-items: start;
}
.about__copy p {
  margin: 0 0 1.1em; color: var(--white);
  font-size: clamp(1rem, 1.35vw, 1.2rem); line-height: 1.75;
}
.about__copy p:last-child { margin-bottom: 0; }

.about__markwrap { margin: clamp(40px, 6vw, 90px) 0 0; justify-self: end; }
.about__mark {
  appearance: none; border: 0; background: transparent; padding: 0;
  cursor: pointer; line-height: 0; display: block;
}
.about__mark img {
  width: 165px; height: 300px; display: block;
  transition: transform 420ms cubic-bezier(0.34, 1.3, 0.5, 1),
              filter 420ms ease;
}
/* The spear leans into the hover and brightens. Live animates this too; the
   exact curve there is locked inside a Webflow interaction, so this is a
   judgement rather than a measurement. */
.about__mark:hover img,
.about__mark:focus-visible img {
  transform: rotate(-4deg) scale(1.06);
  filter: drop-shadow(0 0 22px rgba(204, 73, 79, 0.55));
}

/* The mark is a fixed 165x300 but img carries max-width:100% site-wide, so
   once the right column drops under 165 the width gives and the height does
   not — measured: fine at 1180, down to 114x300 at 1100. Stack before that
   rather than let it distort. */
@media (max-width: 1200px) {
  .about__grid { grid-template-columns: 1fr; }
  .about__markwrap { justify-self: start; margin-top: clamp(32px, 5vw, 56px); }
}

/* ---------------------------------------------------------------- oath */
/* With no script this is an ordinary section at the foot of the page and the
   mark is not offered at all, so the oath is always readable and nothing is
   left behind a button that cannot be pressed. The script turns it into the
   overlay the mark opens. */
.no-js .about__markwrap { display: none; }
.oath { margin: clamp(56px, 8vw, 110px) auto 0; max-width: 820px; }
.oath__panel { position: relative; }
.oath__head {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.8rem, 4vw, 3.2rem); letter-spacing: normal;
  color: var(--accent); margin: 0 0 20px; line-height: 1;
}
.oath__text {
  margin: 0; color: var(--white);
  font-size: clamp(1.05rem, 1.5vw, 1.35rem); line-height: 1.8;
}
.oath__close {
  appearance: none; border: 0; background: transparent; color: var(--white);
  cursor: pointer; padding: 8px; line-height: 0;
  position: absolute; top: -10px; right: -10px;
}
.oath__close svg { width: 26px; height: 26px; display: block; }
.oath__close:hover { color: var(--accent); }

/* --- as an overlay, once a script is there to open and close it --------- */
html:not(.no-js) .oath {
  position: fixed; inset: 0; z-index: 36; margin: 0; max-width: none;
  display: grid; place-content: center; padding: 40px var(--inset);
  background: rgba(0, 0, 0, 0.88);
  visibility: hidden; opacity: 0; transform: translateY(24px);
  transition: opacity 240ms ease,
              transform 320ms cubic-bezier(0.62, 0, 0.24, 1),
              visibility 320ms;
  overflow-y: auto;
}
html:not(.no-js) .oath.is-open { visibility: visible; opacity: 1; transform: translateY(0); }
html:not(.no-js) .oath__panel {
  max-width: 820px; max-height: 78svh; overflow-y: auto;
  padding: clamp(32px, 4.5vw, 52px) clamp(26px, 4vw, 56px);
  background: #0b0b0b;
  border: 1px solid var(--line);
  border-top: 3px solid var(--accent);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
}
html:not(.no-js) .oath__close { top: 10px; right: 12px; }
html:not(.no-js) .oath__close[hidden] { display: block; }

@media (prefers-reduced-motion: reduce) {
  .about__mark img { transition: none; }
  html:not(.no-js) .oath { transition: none; transform: none; }
}

/* ------------------------------------------------------------- connect */
/* The gray ampersand rules the page down the middle, the same device the
   menu uses in red. It is a page background rather than an element so the
   two columns sit either side of it without either having to know it is
   there. */

.connect {
  padding: clamp(110px, 14vw, 170px) var(--inset) 80px;
  max-width: var(--maxw); margin: 0 auto;
  background-repeat: no-repeat;
  background-position: center top clamp(40px, 6vw, 90px);
  background-size: auto clamp(420px, 62vw, 900px);
}

/* Three columns, and the middle one is the ampersand's. Two equal halves put
   the right column at x=744 on a 1440 screen, which is straight over the
   mark; the live page keeps Los Angeles at 266 ending by 570, the ampersand
   569 to 871, and Sales from 909. The side padding is what lands those: the
   grid spans 160 to 1280, so 106 each side leaves 908 for 1fr/302/1fr. */
.connect__cols {
  display: grid;
  grid-template-columns: 1fr clamp(140px, 21vw, 302px) 1fr;
  column-gap: 0;
  padding-inline: clamp(0px, 7.4vw, 106px);
}
/* Placed explicitly, or auto-placement drops the second column into the
   gutter the mark runs down. */
.connect__col { min-width: 0; }
.connect__col:first-of-type { grid-column: 1; }
.connect__col:last-of-type { grid-column: 3; justify-self: center; }

.connect__region {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.6rem, 3.4vw, 2.5rem); color: var(--accent);
  letter-spacing: normal; margin: 0 0 26px; line-height: 1;
}
.connect__sub {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: 1.2rem; letter-spacing: normal; color: var(--accent);
  margin: 30px 0 14px;
}

.people { list-style: none; margin: 0; padding: 0; }
.person { margin: 0 0 26px; }
.person__name {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: 1.3rem; letter-spacing: normal; margin: 0; line-height: 1.2;
}
/* Full strength white, held back by size and weight rather than by dimming
   it toward the background. */
.person__role {
  margin: 4px 0 0; font-size: 0.82rem; line-height: 1.4;
  color: var(--white); font-weight: 500;
}

/* Live butts the two icons together at x=266 and x=295; 5px apart reads
   as two controls rather than one wide one. */
.person__icons { display: flex; align-items: center; gap: 5px; margin: 8px 0 0; }

/* Both icons are a single flat grey with alpha, so they are drawn as masks
   rather than painted pixels. That makes the colour a CSS property: grey at
   rest, accent on hover and while open, with no second artwork to ship and
   no filter chain approximating the red. */
.person__btn {
  appearance: none; border: 0; background: transparent; padding: 0;
  cursor: pointer; line-height: 0; display: block;
  color: #7f7f7f;                     /* the grey the artwork is drawn in */
  transition: color 180ms ease;
}
.icon {
  display: block; width: 30px; height: 30px;
  background-color: currentColor;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;
  -webkit-mask-size: contain;      mask-size: contain;
  transition: transform 200ms ease;
}
.icon--shield {
  width: 29px; height: 29px;
  -webkit-mask-image: url("/images/shield-icon.png");
  mask-image: url("/images/shield-icon.png");
}
.icon--info {
  -webkit-mask-image: url("/images/i-icon.png");
  mask-image: url("/images/i-icon.png");
}
.person__btn:hover,
.person__btn:focus-visible,
.person__btn[aria-expanded="true"] { color: var(--accent); }
.person__btn:hover .icon { transform: scale(1.12); }

/* ------------------------------------------------------------ person bio */
/* With no script this is a plain block under the person and the shield is
   not offered, so every About is simply on the page. The script turns it
   into the popover the shield opens — the same bargain the oath makes. */
.bio { margin: 10px 0 0; max-width: 60ch; }
.bio__panel { position: relative; }
.bio__name {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; font-size: clamp(1.4rem, 2.4vw, 2rem); line-height: 1.1;
  margin: 0; color: var(--white);
}
.bio__role {
  margin: 6px 0 0; font-size: 0.9rem; color: var(--accent); font-weight: 600;
  padding-bottom: 18px; border-bottom: 1px solid var(--line); margin-bottom: 22px;
}
.bio__text { margin: 0 0 1em; font-size: 0.98rem; line-height: 1.75; color: var(--white); }
.bio__text:last-of-type { margin-bottom: 0; }
.bio__note {
  margin: 24px 0 0; padding-top: 20px; border-top: 1px solid var(--line);
  font-size: 0.88rem; line-height: 1.6; color: var(--white);
  display: grid; gap: 12px; justify-items: start;
}
.bio__aicp { width: 110px; height: auto; opacity: 0.92; }
.bio__close {
  appearance: none; border: 0; background: transparent; color: var(--white);
  cursor: pointer; padding: 8px; line-height: 0;
  position: absolute; top: -12px; right: -12px;
}
.bio__close svg { width: 24px; height: 24px; display: block; }
.bio__close:hover { color: var(--accent); }

/* --- as a popover, once a script is there to open and close it ---------- */
html:not(.no-js) .bio {
  position: fixed; inset: 0; z-index: 94; margin: 0; max-width: none;
  display: grid; place-content: center; padding: 40px var(--inset);
  /* Not solid: the page stays faintly legible behind, so the popover reads
     as something laid over the Connect page rather than a new screen. */
  background: rgba(0, 0, 0, 0.88);
  visibility: hidden; opacity: 0;
  overflow-y: auto;
}
html:not(.no-js) .bio__panel {
  max-width: 64ch; max-height: 78svh; overflow-y: auto;
  padding: clamp(28px, 4vw, 44px) clamp(24px, 3.5vw, 48px);
  background: #0b0b0b;
  border: 1px solid var(--line);
  /* The accent edge is the one piece of colour, and it ties the panel to
     the shield that opened it. */
  border-top: 3px solid var(--accent);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
  scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
html:not(.no-js) .bio__panel::-webkit-scrollbar { width: 8px; }
html:not(.no-js) .bio__panel::-webkit-scrollbar-thumb {
  background: var(--line); border-radius: 4px;
}
html:not(.no-js) .bio__close { top: 10px; right: 12px; }
html:not(.no-js) .bio__close[hidden] { display: block; }
.js-ready .bio {
  transform: translateY(16px);
  transition: opacity 220ms ease,
              transform 300ms cubic-bezier(0.62, 0, 0.24, 1),
              visibility 300ms;
}
html:not(.no-js) .bio.is-open { visibility: visible; opacity: 1; transform: translateY(0); }

@media (prefers-reduced-motion: reduce) {
  .js-ready .bio { transition: none; transform: none; }
}

/* The same 0fr to 1fr reveal the director bio uses, for the same reason: it
   animates cleanly whatever the panel turns out to contain. */
.person__card {
  display: grid; grid-template-rows: 0fr; opacity: 0;
  transition: grid-template-rows 320ms cubic-bezier(0.62, 0, 0.24, 1),
              opacity 220ms ease;
}
.person__card.is-open { grid-template-rows: 1fr; opacity: 1; }
/* Every direct child clips, not just the phone list: a 0fr row whose
   child does not hide its overflow still takes up space, which left a
   gap under the icons of anyone carrying a note. */
.person__card > * { overflow: hidden; }
/* The breathing space belongs to the open state. With box-sizing: border-box
   a grid item cannot be shorter than its own padding, so 8px of padding on
   the closed panel floored the row at 8px and left a gap under the icons. */
.person__card > * { padding-top: 0; }
.person__card.is-open > * { padding-top: 8px; }
.person__lines { list-style: none; margin: 0; }
.person__lines li { font-size: 0.9rem; line-height: 1.7; }
.person__lines a { color: var(--white); text-decoration: none; }
.person__lines a:hover { color: var(--accent); }

.connect__h {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  font-size: clamp(1.6rem, 3.4vw, 3rem); color: var(--accent);
  letter-spacing: normal; margin: 0 0 18px; line-height: 1;
}
/* The same inset the columns carry, so Inquiries starts where Los Angeles
   does at 266 rather than at the container edge 106px further left. */
.connect__inq {
  margin: clamp(48px, 7vw, 90px) 0 0;
  padding-inline: clamp(0px, 7.4vw, 106px);
}
.connect__inq a { color: var(--white); font-size: 1.3rem; text-decoration: none; }
.connect__inq a:hover { color: var(--accent); }

.connect__dir {
  margin: clamp(48px, 7vw, 90px) 0 0; text-align: center;
  padding-inline: clamp(0px, 7.4vw, 106px);
}
.connect__addr { margin: 0 0 18px; color: var(--white); }
/* Centred, as live has it: a 900px frame in a 1440 viewport starting at 270. */
.connect__map {
  position: relative; aspect-ratio: 2 / 1; background: #0d0d0d;
  max-width: 900px; margin-inline: auto;
}
.connect__map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.connect__note {
  margin: clamp(40px, 6vw, 70px) auto 0; max-width: 42ch;
  text-align: center; display: grid; justify-items: center; gap: 16px;
}
.connect__note p {
  margin: 0; font-size: 1rem; line-height: 1.6; color: var(--white);
  max-width: 34ch;
}
/* The mark is white line-work on black; it only needs room, not decoration. */
.connect__aicp { width: 150px; height: auto; opacity: 0.92; }

/* No script means the buttons can never be pressed, so every number and note
   is simply on the page and the controls are not offered. */
.no-js .person__btn { display: none; }
.no-js .person__card { grid-template-rows: 1fr; opacity: 1; }

/* --------------------------------------------------- hero video reveal */
/* A cross-origin iframe is a white rectangle until the player paints, and the
   doors now open straight onto it. The script mounts it with .hero__video and
   adds .is-ready once the player itself has said it is playing, so the black
   ground underneath stays the picture until there is actually a video to
   show. A <noscript> iframe never carries the class, so this can never leave
   one invisible.

   The element is black under the player as well. A content blocker that
   stops player.vimeo.com leaves an empty document behind, and an empty
   document is white: on a black page that reads as a flash, which is how
   this was found. Belt and braces, because the two failures are different —
   .is-ready withheld covers a player that never speaks, and this covers one
   that loads an empty frame and gets revealed anyway. */
.hero__frame iframe.hero__video {
  opacity: 0; transition: opacity 400ms ease; background: var(--black);
}
.hero__frame iframe.hero__video.is-ready { opacity: 1; }

/* ---------------------------------------------------------------- post */
/* Rebuilt November 2026 from the template Jason sent. The old page was five
   full-bleed photo bands of justified copy and a section nav; this one is a
   video hero, four service columns, four case studies and a close. Nothing
   of the old page survives but the URL, so its rules went with it.

   Measures and insets are the site's own (--maxw, --inset), not the
   template's, so the page lines up with every other page in the site.

   Where the template asked for white at 72% for secondary copy, this uses
   full-strength white and separates the levels by size and weight instead.
   Dimmed text on black is harder to read and the house does not do it. */

/* --- surviving the deploy ------------------------------------------------
   Every <video> here ships as a <mux-player>, and the rewrite hands it
   inline display:block; width:100%; aspect-ratio:16/9, which would drop the
   hero clip into the flex row beside the headline instead of behind it.
   Inline styles beat a stylesheet, so these need !important — the one place
   in this file where that is the right answer rather than a shortcut. Both
   selectors are listed because locally it is still a <video>. */
.phero__video,
.phero mux-player,
.pcase__video,
.pcase__media mux-player,
/* The full spot, swapped in over the loop when someone asks to watch it.
   It is a player and not decoration, so it keeps its controls and is
   contained rather than cropped — a spot must not lose its edges. */
.pcase__media iframe {
  position: absolute !important;
  inset: 0 !important;
  inset: 0 !important;
  width: 100% !important;
  height: 100% !important;
  max-width: none !important;
  aspect-ratio: auto !important;
  object-fit: cover;
  --controls: none;
  --media-object-fit: cover;
}

.pcase__media iframe {
  border: 0;
  object-fit: contain;
  --controls: auto;
  background: var(--black);
}

/* --- hero --- */
.phero {
  position: relative;
  min-height: 100svh;
  display: grid; align-items: end;
  overflow: hidden;
  background: var(--black);
}
/* The clip runs bright and the copy sits on top of it, so the foot of the
   frame goes to solid black rather than a wash. */
.phero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.28) 40%, #000 100%);
}
.phero__inner {
  position: relative; z-index: 1;
  width: 100%; max-width: var(--maxw); margin: 0 auto;
  padding: 0 var(--inset) clamp(56px, 10vh, 120px);
}
.phero__label {
  font-family: var(--display); font-weight: 500; text-transform: uppercase;
  letter-spacing: normal; color: var(--accent);
  font-size: clamp(1.25rem, 2.4vw, 1.875rem); line-height: 1;
  margin: 0 0 18px;
}
.phero__head {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; line-height: 0.95; margin: 0;
  font-size: clamp(3.25rem, 9vw, 8.25rem);
  max-width: 14ch; color: var(--white);
}
.phero__head .accent { color: var(--accent); }
.phero__body {
  margin: 22px 0 0; max-width: 54ch;
  font-size: clamp(1.0625rem, 1.6vw, 1.25rem); line-height: 1.8; font-weight: 300;
  color: var(--white);
}
.phero__kicker {
  margin: 18px 0 0; font-weight: 700; text-transform: uppercase;
  color: var(--accent); line-height: 1.5;
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
}

/* --- what we do --- */
.psvc { padding: clamp(88px, 12vw, 170px) 0; }
.psvc__inner { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--inset); }
.psvc__head {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; line-height: 1; margin: 0 0 44px;
  font-size: clamp(2.625rem, 5vw, 4.5rem); color: var(--white);
}
.psvc__grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--line);
}
.psvc__item { padding: 28px 24px 32px; border-right: 1px solid var(--line); }
.psvc__item:first-child { padding-left: 0; }
.psvc__item:last-child { border-right: 0; }
.psvc__num {
  display: block; font-family: var(--display); font-weight: 500;
  font-size: 1.125rem; line-height: 1; color: var(--white);
}
.psvc__name {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; font-size: 2.25rem; line-height: 0.95;
  margin: 14px 0; color: var(--white);
}
.psvc__text { margin: 0; font-size: 0.9375rem; line-height: 1.8; color: var(--white); }
/* The fourth column is the point of the page: finishing is the service the
   others lead to. It gets the accent rule, the accent name and a wash. */
.psvc__item--core {
  border-top: 4px solid var(--accent); margin-top: -2px;
  background: linear-gradient(180deg, rgba(204,73,79,0.14), rgba(204,73,79,0) 80%);
}
.psvc__item--core .psvc__name { color: var(--accent); }
.psvc__item--core .psvc__text { font-weight: 500; }

/* --- the work --- */
.pwork__head {
  max-width: var(--maxw); margin: 0 auto;
  padding: clamp(72px, 10vw, 130px) var(--inset) 12px;
  border-top: 1px solid var(--line);
}
.pwork__title {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; line-height: 0.95; margin: 0;
  font-size: clamp(3.5rem, 9vw, 8.125rem); color: var(--white);
}
.pwork__intro {
  margin: 18px 0 0; max-width: 46ch; font-weight: 300;
  font-size: 1.0625rem; line-height: 1.8; color: var(--white);
}

.pcase {
  max-width: var(--maxw); margin: 0 auto;
  padding: clamp(48px, 7vw, 90px) var(--inset) clamp(72px, 10vw, 130px);
}
.pcase__media {
  position: relative; aspect-ratio: 16 / 9;
  background: #0d0d0d; overflow: hidden;
}
.pcase__num {
  position: absolute; left: 18px; top: 10px; z-index: 2;
  font-family: var(--display); font-weight: 700; line-height: 1;
  font-size: clamp(2.75rem, 6vw, 5.25rem); color: var(--white);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.45);
}
/* WCAG 2.2.2: anything that moves by itself for more than five seconds needs
   a way to stop it. Added by site.js so it is never there without a handler. */
.pcase__pause {
  position: absolute; right: 12px; bottom: 12px; z-index: 2;
  width: 44px; height: 44px; display: grid; place-items: center; padding: 0;
  background: rgba(0, 0, 0, 0.55); color: var(--white);
  border: 1px solid rgba(255, 255, 255, 0.5); cursor: pointer;
}
.pcase__pause:hover { background: var(--black); }
.pcase__pause svg { width: 16px; height: 16px; }

.pcase__stills {
  list-style: none; margin: 4px 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px;
}
.pcase__stills a {
  display: block; aspect-ratio: 16 / 9; overflow: hidden; background: #0d0d0d;
}
.pcase__stills img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 600ms ease;
}
.pcase__stills a:hover img { transform: scale(1.04); }

.pcase__body { margin-top: clamp(28px, 4vw, 44px); }
.pcase__headrow {
  display: flex; flex-wrap: wrap; justify-content: space-between;
  align-items: flex-end; gap: 22px 40px;
  padding-bottom: 26px; border-bottom: 1px solid var(--line);
}
.pcase__title {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; line-height: 1; margin: 0;
  font-size: clamp(2.875rem, 5.4vw, 4.75rem); color: var(--white);
}
/* The site's red chevron, set after the last word so it stays with the title
   when it wraps instead of parking at the end of the line. */
.pcase__title::after {
  content: ""; display: inline-block; vertical-align: 0.08em;
  margin-left: 0.2em; width: 0.3em; height: 0.55em;
  background: url("/images/arrow-right.svg") center / contain no-repeat;
}
.pcase__sub {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; font-size: 1.5rem; line-height: 1.1;
  margin: 10px 0 0; color: var(--white);
}
.pcase__desc {
  margin: 26px 0 0; font-weight: 300; line-height: 1.85;
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem); color: var(--white);
}
.pcase__did { margin: 18px 0 0; font-weight: 500; color: var(--white); }
.pcase__k {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; font-size: 1.25rem; color: var(--accent);
  margin-right: 10px;
}

/* --- the button, shared by the cases and the close --- */
.pbtn {
  display: inline-flex; align-items: center; gap: 12px;
  min-height: 52px; padding: 12px 22px 10px;
  background: var(--accent); color: var(--white); text-decoration: none;
  font-family: var(--display); font-weight: 500; text-transform: uppercase;
  letter-spacing: normal; font-size: 1.5rem; line-height: 1;
  transition: background 200ms ease;
}
.pbtn:hover { background: #b33d43; }

/* --- close --- */
.pclose { position: relative; text-align: center; padding: 0 0 clamp(96px, 12vw, 160px); }
.pclose__amp {
  display: block; width: clamp(110px, 13vw, 160px); height: auto;
  margin: 0 auto 36px;
}
.pclose__inner { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--inset); }
.pclose__head {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: normal; line-height: 1.04; margin: 0;
  font-size: clamp(1.75rem, 6.4vw, 5.25rem); color: var(--white);
}
.pclose__text {
  margin: 24px auto 36px; max-width: 44ch; font-weight: 300;
  font-size: 1.0625rem; line-height: 1.8; color: var(--white);
}

/* The template faded each section up on arrival. Nothing else in this site
   does that, so the page would have been the one that behaves differently;
   Jason's notes marked it optional. The still hover is motion enough, and
   it comes off for anyone who has asked for less of it. */
@media (prefers-reduced-motion: reduce) {
  .pcase__stills img { transition: none; }
  .pcase__stills a:hover img { transform: none; }
}

/* ---------------------------------------------------------- responsive */

@media (max-width: 1100px) { .grid { grid-template-columns: repeat(3, 1fr); } }

/* Four service columns stop holding a 36px condensed name somewhere here. */
@media (max-width: 900px) {
  .psvc__grid { grid-template-columns: 1fr 1fr; }
  .psvc__item { border-right: 0; border-bottom: 1px solid var(--line); padding-left: 0; padding-right: 16px; }
  .psvc__item:nth-child(odd) { border-right: 1px solid var(--line); }
  .psvc__item:nth-child(even) { padding-left: 16px; }
}

@media (max-width: 760px) {

  /* Two columns and a divider need width. Below this the ampersand has no
     page to divide, so it goes and the two lists stack. */
  /* Below this there is no page to divide, so the gutter goes with it. */
  .connect__cols { grid-template-columns: 1fr; padding-inline: 0; gap: 40px; }
  .connect__col:first-of-type,
  .connect__col:last-of-type { grid-column: 1; justify-self: start; }

  .menu__cols { grid-template-columns: 1fr; justify-items: center; padding: 0 20px; gap: 26px; }
  /* Both lists are pinned to explicit grid columns higher up, and grid keeps
     honouring those after the template drops to one track: it grows implicit
     tracks to reach column 3, so at 390 the computed template came out
     163.5px 0px 134.5px and the lists stayed side by side over the seam.
     Moving both into column 1 is what actually stacks them. */
  .menu__pages,
  .menu__dirs { grid-column: 1; justify-self: center; }
  .menu__pages { text-align: center; }
  .menu__pages a { line-height: 1.5; }
  .menu__directors a { line-height: 1.75; }
  /* The seam is sized off the viewport height, so on a phone it is 287px of
     ampersand across a 390px screen. With the lists stacked down the middle
     it has nothing left to divide and just sits behind the words. It is only
     on screen here because opening the menu shuts the doors; hiding it in
     that state leaves the doors' own transition alone. */
  .menu-open .doors__seam { display: none; }

  /* Post page on a phone: one service column, two stills per row with the
     third dropped, and the title row stacked so the button is not squeezed
     against a 40px heading. */
  .psvc__grid { grid-template-columns: 1fr; }
  /* :nth-child(n) only to outweigh the odd/even rules at 900px, which would
     otherwise keep indenting every second column and drawing its divider in
     a layout that has one column. */
  .psvc__item:nth-child(n) {
    border-right: 0; border-bottom: 1px solid var(--line); padding: 24px 0;
  }
  .psvc__item--core:nth-child(n) { padding: 24px 14px; }
  .pcase__stills { grid-template-columns: 1fr 1fr; }
  .pcase__stills li:nth-child(3) { display: none; }
  .pcase__num { left: 12px; }
  .pcase__title { font-size: 2.5rem; }
  .pcase__headrow { align-items: flex-start; }
  .pbtn { font-size: 1.25rem; }

  /* Live hides the bio and the portrait outright below 760px — a phone never
     sees a word of either. The copy exists and is worth reading, so it stays.
     The card stops being pinned to a fold it cannot fit inside: the hero grows
     to fit it, the video stays behind as a full-height ground, and the bio
     scrim becomes a flat wash rather than a gradient that would run out. */
  .director__name { font-size: 2rem; }            /* live: 32px at 390 */
  .hero--director.is-open { height: auto; min-height: 100svh; }
  .hero--director.is-open .director__card { position: static; padding: 120px var(--dinset) 40px; }
  .hero--director.is-open .hero__scrim--bio {
    top: 0; height: auto; background: rgba(0, 0, 0, 0.9);
  }
  .director__bioinner { display: block; }
  /* Justified at 310px is all rivers and no benefit. */
  .director__text p { text-align: left; }
  .director__portrait { width: 120px; height: 120px; margin: 18px 0 0; }

  .grid { grid-template-columns: repeat(2, 1fr); gap: 14px; }
  .connect__cols { grid-template-columns: 1fr; gap: 36px; }
  .connect__col { border-left: 0; padding-left: 0; }
  .reel__brand { font-size: 1.5rem; }
  .reel__spot { font-size: 1.3rem; }
  .topbar { padding: 0 30px 0 0; }
  .logo img { width: 44px; }       /* live: 44x54 */
  .burger { padding-top: 31px; }   /* live: top bar at y=31 */
}

@media (max-width: 430px) {
  .grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  /* No doors: the panels never cover the page, so nothing has to uncover it.
     Which means the menu cannot borrow them as its backdrop and needs its
     own, or it would sit transparent over the page. */
  .doors { display: none; }
  .menu, .menu.is-open,
  .js-ready .menu, .js-ready .menu.is-open { background: var(--black); transition: none; }
  .card__media img, .menu, .burger__bars i, .player__start img, .home__directors a { transition: none; }
  .menu, .menu.is-open { transform: none; }
  .card__link:hover .card__media img { transform: none; }
}
