/* Deverett Media Group
   ---------------------------------------------------------------------------
   Design notes, so this doesn't drift back:

   The brand purple (#5043bd, sampled from the client's own logo) is FIXED.
   Violet is also the most recognisable signature of AI-generated UI, which
   traces to Tailwind UI shipping bg-indigo-500 as a placeholder. The fix is not
   to abandon the colour, it is to stop treating it as a theme: no gradients,
   no purple slabs, no lavender-tinted neutrals. Purple appears as rules,
   hovers, one band, and small marks. Ink carries the weight.

   Deliberately avoided: gradient fills, coloured card-edge strips, checkmark
   bullets, uniform shadow on every surface, glassmorphism, Inter.
   --------------------------------------------------------------------------- */

:root {
  --purple: #5043bd;
  --purple-pressed: #3e3493;
  --lavender: #a5a4ef;
  --lavender-bright: #b9b8f4;

  --ink: #1a1a1c;            /* true neutral, not tinted toward the accent */
  --ink-soft: #3d3d42;
  --grey: #5c5c63;
  --line: #e2e2e4;           /* neutral hairline */
  --line-strong: #c9c9cd;
  --paper: #f3f2fa;          /* the previous build's pale purple wash, restored */
  --dark: #141416;
  --white: #ffffff;

  --font-display: "Big Shoulders", "Haettenschweiler", "Arial Narrow", sans-serif;
  --font-body: "Schibsted Grotesk", -apple-system, "Segoe UI", sans-serif;
  --font-serif: "Source Serif 4", "Iowan Old Style", Georgia, serif;

  --measure: 60ch;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body { -webkit-tap-highlight-color: rgba(80, 67, 189, .12); }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

body {
  font-family: var(--font-body);
  color: var(--ink);
  background: var(--white);
  line-height: 1.62;
  font-size: 17px;
  font-feature-settings: "kern" 1;
  -webkit-font-smoothing: antialiased;
}

img, svg, video { display: block; max-width: 100%; }
.container { max-width: 1120px; margin: 0 auto; padding: 0 24px; }

/* ---------- type ----------
   Display is condensed and optically sized: the 13px label and the 76px
   headline are different drawings of the same face, which is the part cheap
   typography can't fake. */
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  text-transform: uppercase;
  line-height: .96;
  letter-spacing: .005em;
  text-wrap: balance;
}
/* max pulled back from 5.6rem: "The Truth & Reality About the Film Business" is
   four lines where "The Truth About..." was three, and the hero still has to
   clear the fold on a laptop. */
h1 { font-size: clamp(3rem, 7vw, 5rem); font-variation-settings: "opsz" 72; }
h2 { font-size: clamp(2.3rem, 5vw, 3.6rem); font-variation-settings: "opsz" 60; }
h3 { font-size: clamp(1.4rem, 2.6vw, 1.75rem); font-variation-settings: "opsz" 36; letter-spacing: .015em; }

/* Purple accent word, set in italic serif. The colour is the client's brand
   device; the serif is what keeps it from reading as a templated highlight. */
.accent {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: 400;
  text-transform: none;
  letter-spacing: -.015em;
  font-size: 1.04em;
  color: var(--purple);
}
.on-dark .accent, .band-purple .accent { color: var(--lavender-bright); }

/* Hero headline only: the client wants "Truth & Reality" to read as the
   dominant phrase, so it goes bold (not italic) and the surrounding words
   shrink to give it more contrast. Scoped to #hero-h so the podcast H2,
   which reuses .accent italic, is untouched. */
#hero-h .accent { font-style: normal; font-weight: 600; font-size: 1.15em; }
#hero-h .hero-lede { font-size: .64em; }

/* Doubled from .95rem at the client's request (2026-10-29). */
.eyebrow {
  font-family: var(--font-display);
  font-size: 1.9rem; font-weight: 600; letter-spacing: .2em; text-transform: uppercase;
  font-variation-settings: "opsz" 14;
  color: var(--purple); margin-bottom: 1.1rem;
  display: flex; align-items: center; gap: .55em;
}
/* The double chevron from the Indie Film Community logo (and the Indie Film
   Stream one modelled on it) in front of each eyebrow, replacing the
   three-dash sprocket tick (Zach, 2026-10-29). Fitted to the pixels of
   ifc_logo.tiff (97% overlap): on the logo's 43-unit height each arm is 17
   across with a 19.5 run to the point, the second chevron steps 25.25 right,
   and every corner is rounded at 2.5. The path is the shape inset by 2.5 and
   stroked back out with a round join, which is what rounds the corners.
   A mask, so it takes the eyebrow's colour on any band, and sized in em so it
   tracks the label from desktop down to phone. Its height is the label's cap
   height. */
.eyebrow::before {
  content: ""; flex: none; width: 1.074em; height: .8em;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3.14 0 57.73 43'%3E%3Cpath d='M5.64 2.5L15.89 2.5 33.12 21.5 15.89 40.5 5.64 40.5 22.88 21.5zM30.89 2.5L41.14 2.5 58.37 21.5 41.14 40.5 30.89 40.5 48.13 21.5z' stroke='%23000' stroke-width='5' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='3.14 0 57.73 43'%3E%3Cpath d='M5.64 2.5L15.89 2.5 33.12 21.5 15.89 40.5 5.64 40.5 22.88 21.5zM30.89 2.5L41.14 2.5 58.37 21.5 41.14 40.5 30.89 40.5 48.13 21.5z' stroke='%23000' stroke-width='5' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Phones get 1.5x instead: at the full 2x the labels nearly matched the h2
   under them and "Straight answers" broke onto two lines with the mark
   floating between them. The longest label still wraps below ~450px, so the
   lines are tightened, balanced, and the chevron hangs off the first one. */
@media (max-width: 600px) {
  .eyebrow { font-size: 1.4rem; line-height: 1.25; align-items: flex-start; text-wrap: balance; }
  .eyebrow::before { margin-top: calc((1.25em - .8em) / 2); }
}
.on-dark .eyebrow { color: var(--lavender); }
.on-dark .eyebrow { color: var(--lavender); }

.lede {
  font-family: var(--font-serif);
  font-size: clamp(1.14rem, 1.9vw, 1.32rem);
  line-height: 1.52;
  color: var(--ink-soft);
  max-width: 54ch;
  font-variation-settings: "opsz" 24;
}
.on-dark { color: #cfcfd6; }
.on-dark .lede { color: #cfcfd6; }
.on-dark h1, .on-dark h2, .on-dark h3 { color: var(--white); }

a { color: var(--purple); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--purple); outline-offset: 3px; border-radius: 1px;
}
.on-dark a:focus-visible, .on-dark .btn:focus-visible { outline-color: var(--lavender-bright); }
.band-purple a:focus-visible, .band-purple .btn:focus-visible { outline-color: var(--white); }
.site-footer a:focus-visible { outline-color: var(--lavender-bright); }

[id] { scroll-margin-top: 92px; }
/* the same offset for focus-driven and programmatic scrolling, so Tab cannot
   park an element behind the sticky header */
html { scroll-padding-top: 92px; }
@media (max-width: 680px) { [id] { scroll-margin-top: 76px; } }

/* ---------- motion ----------
   Rules that govern everything below:
   1. Content is VISIBLE by default. The reveal states only exist once JS puts
      .js-reveal on <html>, so a script failure shows the page, not a blank one.
   2. Nothing translates horizontally. Sideways entrances are the tell.
   3. Text never parallaxes. Only decorative surfaces drift.
   4. prefers-reduced-motion turns all of it off, including the scroll timelines.
*/

/* section-level reveal */
.js-reveal [data-reveal] { opacity: 0; transform: translateY(14px); }
.js-reveal [data-reveal].in {
  opacity: 1; transform: none;
  transition: opacity .55s cubic-bezier(.2,.6,.3,1), transform .55s cubic-bezier(.2,.6,.3,1);
}

/* child stagger: --i is set by the script, so the order reads as choreography
   rather than one block appearing */
.js-reveal [data-stagger] > * { opacity: 0; transform: translateY(18px); }
.js-reveal [data-reveal].in [data-stagger] > *,
.js-reveal [data-stagger].in > * {
  opacity: 1; transform: none;
  transition: opacity .6s cubic-bezier(.2,.6,.3,1), transform .6s cubic-bezier(.2,.6,.3,1);
  transition-delay: calc(var(--i, 0) * 90ms);
}

/* the eyebrow's chevrons slide forward into place once the section lands.
   They used to scale out from zero width, which suited the dashes but would
   squash the chevrons. */
.js-reveal [data-reveal] .eyebrow::before { opacity: 0; transform: translateX(-.4em); }
.js-reveal [data-reveal].in .eyebrow::before {
  opacity: 1; transform: none;
  transition: opacity .5s cubic-bezier(.65,0,.35,1) .25s, transform .5s cubic-bezier(.65,0,.35,1) .25s;
}

/* ---------- scroll-driven (no JS, GPU, guarded) ---------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* decorative surfaces drift against the scroll */
    .video-slot {
      animation: slot-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    /* translate only. A scale here overflows horizontally once the slots go
       full-width below 900px: 6% of a 728px slot is 44px past the container. */
    @keyframes slot-drift {
      from { transform: translateY(48px); }
      to   { transform: translateY(-48px); }
    }
    /* the grain creeps the other way, so the texture feels like it sits on glass */
    .video-slot::after {
      animation: grain-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    @keyframes grain-drift {
      from { transform: translateY(-26px); }
      to   { transform: translateY(26px); }
    }
    /* The play ring swells through the middle of its pass.
       Scale ONLY: animating opacity here would outrank the static opacity:0
       that hides the glyph behind the leader countdown, and the play mark
       would show through the count. */
    .video-slot .play {
      animation: ring-swell linear both;
      animation-timeline: view();
      animation-range: entry 20% exit 80%;
    }
    @keyframes ring-swell {
      0%   { transform: scale(.86); }
      50%  { transform: scale(1); }
      100% { transform: scale(.86); }
    }

    /* prices wipe up from behind their own baseline.
       Headings no longer use this: they are split into words and staggered,
       and running both would fight each other. */
    .engagement .price {
      animation: wipe-up linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 65%;
    }
    @keyframes wipe-up {
      from { clip-path: inset(0 0 100% 0); transform: translateY(26px); }
      to   { clip-path: inset(0 0 -12% 0); transform: translateY(0); }
    }

    /* the dark band's light source travels as you pass it */
    .band-dark::before {
      content: ""; position: absolute; inset: 0; pointer-events: none;
      background: radial-gradient(60% 50% at 50% 0%, rgba(80,67,189,.30), transparent 70%);
      animation: glow-travel linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    @keyframes glow-travel {
      from { transform: translate3d(-16%, -12%, 0); opacity: .35; }
      50%  { opacity: 1; }
      to   { transform: translate3d(16%, 14%, 0); opacity: .35; }
    }

    /* the footer lifts into place */
    .site-footer .container {
      animation: foot-lift linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 90%;
    }
    @keyframes foot-lift { from { transform: translateY(34px); opacity: 0; } to { transform: none; opacity: 1; } }
  }
}

/* ---------- word-level heading reveal ----------
   Headings are split into words by script and staggered on entry. Scoped to
   .js-split, which only exists once that script runs, so a failure leaves
   ordinary headings. */
.js-split .word { display: inline-block; }
.js-split [data-split] .word {
  opacity: 0; transform: translateY(.5em) rotate(1.2deg);
}
.js-split [data-split].shown .word {
  opacity: 1; transform: none;
  transition: opacity .5s cubic-bezier(.2,.6,.3,1), transform .62s cubic-bezier(.16,.84,.32,1);
  transition-delay: calc(var(--w, 0) * 34ms);
}
@media (prefers-reduced-motion: reduce) {
  .js-split [data-split] .word { opacity: 1; transform: none; }
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {

    /* credential rule wipes in, and each figure lifts on its own offset */
    .creds {
      animation: rule-in linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 85%;
    }
    @keyframes rule-in { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
    .creds .item {
      animation: cred-lift linear both;
      animation-timeline: view();
      animation-range: entry 15% entry 95%;
    }
    @keyframes cred-lift { from { transform: translateY(18px); opacity: 0; } to { transform: none; opacity: 1; } }
    .creds .item:nth-child(3)  { animation-range: entry 20% entry 98%; }
    .creds .item:nth-child(5)  { animation-range: entry 25% entry 100%; }
    .creds .item:nth-child(7)  { animation-range: entry 30% entry 100%; }

    /* On a phone the About band is ONE column, so a sideways open would push
       past the viewport and strand the copy at opacity 0. Vertical there. */
    /* Movement only, no opacity. This column is taller than a phone viewport,
       and an opacity ramp tied to a view() range left it sitting at 0 while on
       screen. Nothing that carries copy gets faded by scroll position. */
    .about-grid > * {
      animation: about-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }
    .about-grid > *:last-child { animation-range: entry 6% entry 80%; }
    @keyframes about-rise { from { transform: translateY(38px); } to { transform: none; } }

    /* the opposing-sides gesture only exists where there are two columns */
    @media (min-width: 900px) {
      .about-grid > *:first-child {
        animation: open-left linear both;
        animation-timeline: view();
        animation-range: entry 0% entry 80%;
      }
      .about-grid > *:last-child {
        animation: open-right linear both;
        animation-timeline: view();
        animation-range: entry 5% entry 90%;
      }
      @keyframes open-left  { from { transform: translateX(-34px); opacity: 0; } to { transform: none; opacity: 1; } }
      @keyframes open-right { from { transform: translateX(34px);  opacity: 0; } to { transform: none; opacity: 1; } }
    }

    /* quotes rise and settle */
    .quote {
      animation: quote-rise linear both;
      animation-timeline: view();
      animation-range: entry 5% entry 75%;
    }
    .quote:nth-child(2) { animation-range: entry 12% entry 88%; }
    @keyframes quote-rise { from { transform: translateY(40px) scale(.985); opacity: 0; } to { transform: none; opacity: 1; } }

    /* the topic columns arrive on different beats (three of them since the
       Development offering was added, so this is nth-child, not first/last) */
    .split > * { animation: col-rise linear both; animation-timeline: view(); }
    .split > *:nth-child(1) { animation-range: entry 0% entry 70%; }
    .split > *:nth-child(2) { animation-range: entry 6% entry 78%; }
    .split > *:nth-child(3) { animation-range: entry 12% entry 86%; }
    @keyframes col-rise { from { transform: translateY(52px); opacity: 0; } to { transform: none; opacity: 1; } }

    /* the dark band's own surface shifts, so the section feels like it has depth */
    .band-dark {
      animation: band-shift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    @keyframes band-shift {
      from { background-position: 50% 0%; }
      to   { background-position: 50% 100%; }
    }
  }
}

/* ---------- the film strip ----------
   A running band of what he does, pulled sideways by your scroll. It is the
   logo's film strip at full width, and it is the one loud moment on the page. */
.filmstrip {
  position: relative;
  /* clip, NOT hidden: overflow:hidden makes this a scroll container, which
     makes view() inside it resolve against this band instead of the page and
     pins the animation at 100%. clip contains the track without doing that. */
  overflow: clip;
  /* belt and braces: keeps the oversized track from ever contributing to the
     document's scroll width, independent of how overflow:clip is implemented */
  contain: paint;
  background: var(--ink);
  border-block: 1px solid var(--ink);
  padding: 1.4rem 0;
  view-timeline-name: --strip;
  view-timeline-axis: block;
}
/* sprocket holes top and bottom */
.filmstrip::before, .filmstrip::after {
  content: ""; position: absolute; left: 0; right: 0; height: 9px;
  background: repeating-linear-gradient(90deg, var(--paper) 0 13px, transparent 13px 30px);
}
.filmstrip::before { top: 6px; }
.filmstrip::after  { bottom: 6px; }
.filmstrip-track {
  display: flex; align-items: center; gap: 2.6rem;
  width: max-content; white-space: nowrap;
  will-change: transform;
}
.filmstrip-track span {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.4vw, 2.6rem); font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--white); font-variation-settings: "opsz" 48;
}
.filmstrip-track span.alt { color: transparent; -webkit-text-stroke: 1px rgba(255,255,255,.55); }
.filmstrip-track i {
  flex: none; width: 9px; height: 9px; border-radius: 50%;
  background: var(--purple); display: block;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .filmstrip-track {
      animation: strip-pull linear both;
      animation-timeline: --strip;      /* the band's timeline, measured against the page */
      animation-range: cover 0% cover 100%;
    }
    @keyframes strip-pull {
      from { transform: translate3d(2%, 0, 0); }
      to   { transform: translate3d(-40%, 0, 0); }
    }
  }
}

/* reading-progress hairline. Pure CSS where supported; hidden otherwise. */
.scroll-progress { display: none; }
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .scroll-progress {
      display: block; position: fixed; inset: 0 0 auto 0; height: 2px; z-index: 80;
      background: var(--purple); transform-origin: 0 50%; transform: scaleX(0);
      animation: progress linear both;
      animation-timeline: scroll(root block);
    }
    @keyframes progress { to { transform: scaleX(1); } }
  }
}

@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal],
  .js-reveal [data-stagger] > * { opacity: 1; transform: none; }
  .js-reveal [data-reveal] .eyebrow::before { opacity: 1; transform: none; }
}

/* ---------- buttons ----------
   Purple, flat. The colour is restored at full strength; what stays gone is the
   gradient/shadow finish that read as generated. The hover is a wipe from the
   left rather than a colour swap, which is the bit that feels made rather than
   defaulted. */
.btn {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-block;
  font-family: var(--font-display);
  font-size: 1.02rem; font-weight: 600; letter-spacing: .17em; text-transform: uppercase;
  font-variation-settings: "opsz" 16;
  line-height: 1; text-decoration: none; white-space: nowrap;
  padding: 1.22rem 2.5rem;
  border: 1px solid transparent; border-radius: 1px;
  transition: color .22s cubic-bezier(.2,.6,.3,1), border-color .22s cubic-bezier(.2,.6,.3,1);
}
/* the wipe */
.btn::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  transform: scaleX(0); transform-origin: left center;
  transition: transform .34s cubic-bezier(.65,0,.35,1);
}
.btn:hover::after { transform: scaleX(1); }

.btn-primary { color: var(--white); background-color: var(--purple); border-color: var(--purple); }
.btn-primary::after { background-color: var(--ink); }
.btn-primary:hover { border-color: var(--ink); }

.btn-ghost { color: var(--ink); background: transparent; border-color: var(--line-strong); }
.btn-ghost::after { background-color: var(--purple); }
.btn-ghost:hover { color: var(--white); border-color: var(--purple); }

.on-dark .btn-primary { color: var(--dark); background-color: var(--lavender-bright); border-color: var(--lavender-bright); }
.on-dark .btn-primary::after { background-color: var(--white); }
.on-dark .btn-primary:hover { color: var(--dark); border-color: var(--white); }
.on-dark .btn-ghost { color: #dcdce2; border-color: rgba(220, 220, 226, .34); }
.on-dark .btn-ghost::after { background-color: var(--lavender-bright); }
.on-dark .btn-ghost:hover { color: var(--dark); border-color: var(--lavender-bright); }

.band-purple .btn-primary { color: var(--purple); background-color: var(--white); border-color: var(--white); }
.band-purple .btn-primary::after { background-color: var(--ink); }
.band-purple .btn-primary:hover { color: var(--white); border-color: var(--ink); }
.band-purple .btn-ghost { color: #fff; border-color: rgba(255, 255, 255, .42); }
.band-purple .btn-ghost::after { background-color: var(--white); }
.band-purple .btn-ghost:hover { color: var(--purple); border-color: #fff; }

.btn-row { display: flex; flex-wrap: wrap; gap: .9rem; margin-top: 2.2rem; }

/* a real arrow character on the baseline, only on text links */
.link-arrow {
  font-family: var(--font-display); font-weight: 600; font-size: 1.02rem;
  letter-spacing: .15em; text-transform: uppercase; text-decoration: none;
  color: var(--ink); border-bottom: 1px solid var(--line-strong);
  padding-bottom: .35rem; display: inline-block;
  transition: color .2s ease, border-color .2s ease;
}
.link-arrow::after { content: " \2192"; }
.link-arrow:hover { color: var(--purple); border-color: var(--purple); }

/* ---------- header ---------- */
/* header condenses once you leave the hero */
.site-header .container { transition: padding .28s cubic-bezier(.2,.6,.3,1); }
.site-header .brand img { transition: height .28s cubic-bezier(.2,.6,.3,1); }
/* set for a frame by the header script when it condenses ahead of a fragment jump */
.site-header.no-anim .container, .site-header.no-anim .brand img { transition: none; }
.site-header.is-scrolled { border-bottom-color: var(--line-strong); }
.site-header.is-scrolled .container { padding-top: .45rem; padding-bottom: .45rem; }
.site-header.is-scrolled .brand img { height: 38px; }
.site-header.is-scrolled .header-act .btn { --btn-y: .62rem; padding: var(--btn-y) 1.15rem; }
@media (max-width: 480px) { .site-header.is-scrolled .brand img { height: 34px; } }

.site-header {
  position: sticky; top: 0; z-index: 50;
  background: var(--white);
  border-bottom: 1px solid var(--line);
}
/* Three columns: mark left, sections centred, action right. The old two-part
   flex left 658px of dead air at 1440 and had nowhere to put the CTA. */
/* Mark left, action right. The section links that used to sit between them are
   gone: the page is one scroll and the nav was duplicating what scrolling does.
   Two tracks, not three — with `auto 1fr auto` the action would auto-place into
   column 2 and strand the right-hand third of the header. */
.site-header .container {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  column-gap: 2rem; padding-top: .85rem; padding-bottom: .85rem;
}
.header-act { display: flex; align-items: center; justify-content: flex-end; }
.header-act .btn { font-size: .92rem; letter-spacing: .14em; padding: 1rem 1.7rem; }
@media (max-width: 1180px) { .site-header .container { column-gap: 1.2rem; } }
/* on a phone the mark centres and the sticky bar owns the action */
@media (max-width: 680px) {
  .site-header .container { grid-template-columns: 1fr; justify-items: center; }
  .header-act { display: none; }
}
.brand { display: flex; align-items: center; text-decoration: none; flex: none; }
/* The mark used to spin one revolution on hover. Removed: on a touch screen
   :hover sticks after a tap, so the mark spun on first touch and then sat
   stuck mid-rotation. A letterhead doesn't move. */
/* 52px so the MEDIA GROUP line under the wordmark is actually legible;
   at 44 it rendered around 7px tall */
.brand img { height: 52px; width: auto; display: block; transition: height .28s cubic-bezier(.2,.6,.3,1); }
@media (max-width: 480px) { .brand img { height: 42px; } }
@media (max-width: 360px) { .brand img { height: 38px; } }
/* Home page only, at the top: the mark at roughly 2.5x so a visitor knows at a
   glance whose site this is (Jeff, round 2). It condenses to the usual 38px
   once you scroll; the hysteresis that keeps that from flickering is in the
   header script in index.html. .site-header.is-scrolled .brand img is (0,3,1)
   and still wins, so no :not() is needed here. */
.home .brand img { height: clamp(76px, 6vw + 46px, 128px); }
/* ---------- cross-document view transitions ----------
   The header and logo persist across the navigation instead of hard-cutting.
   Browsers without support just navigate normally. */
@view-transition { navigation: auto; }
.site-header { view-transition-name: site-header; }
.brand img    { view-transition-name: brand-mark; }
.site-footer  { view-transition-name: site-footer; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .32s; }
@media (max-width: 680px) {
}

/* ---------- sections ----------
   Rhythm is deliberately irregular. White → paper → white → ink → white →
   purple is a pattern; varying the weight of each band is composition. */
section { padding: clamp(4.5rem, 9vw, 7.5rem) 0; }
.band-paper { background: var(--paper); }
/* overflow-x: clip so the opposing-sides slide inside can never escape the
   viewport. The container only has 24px of side padding, so a 34px translate
   overflows on anything narrower than about 1190px. clip (not hidden) leaves
   vertical flow alone. */
/* position: relative is load-bearing. The ::before glow is absolutely
   positioned; without a positioned ancestor it sized against the page and its
   16% translate pushed the document 63px wider than the viewport.
   overflow-x: clip then contains both the glow and the opposing-sides slide. */
/* clip on BOTH axes, not just x. The ::before glow is translated up to -12% of
   the band's height by glow-travel, and with overflow-y visible it escaped
   above the band and washed pale purple over the white section overhead.
   overflow: clip (unlike hidden) still does not create a scroll container, so
   the view() timelines inside keep resolving against the page. */
.band-dark { background: var(--dark); position: relative; overflow: clip; }
.band-purple { background: var(--purple); }
.band-purple h2, .band-purple p { color: var(--white); }

/* ---------- hero ---------- */
/* Was clamp(3.5rem, 7vw, 6rem) on both sides, which ended the band within 39px
   of the fold on a 900px laptop: the page looked like it stopped at the
   credential rule.

   vh, not vw, and this is the one place on the page that earns it. The hero's
   job is to fit above the fold with the next band showing under it, and that is
   a question about viewport HEIGHT. Tying it to width meant a 1440x800 laptop
   got the same 96px of air as a 1440x1200 monitor and lost the peek entirely.
   Bottom is lighter than top because the credential rule carries its own rules
   and already reads as a closing edge. */
.hero { padding-top: clamp(2.25rem, 4.5vh, 4rem); padding-bottom: clamp(1.75rem, 3.25vh, 3.25rem); }
/* One column. The intro film used to sit in a second 9fr track here; it is
   the first of the four films below now, so the hero is the headline, the three offerings
   and the CTA. The h1 runs the full container deliberately - it is 80px
   condensed display type and the width is the point. The list underneath is
   capped: it is a label/value table and its value column becomes an unreadable
   ~100ch if it inherits the same width. */
.hero-grid { display: grid; gap: 3rem; grid-template-columns: minmax(0, 1fr); align-items: center; }
/* The headline deliberately answers to WIDTH only, via the global
   clamp(3rem, 7vw, 5rem). Tying it to height as well was tried and reverted:
   the line breaks at 1440px wide somewhere between 74px and 77px, so a
   height-aware size flipped the headline between one line and two as the
   window was resized vertically. Type that reflows when you drag the bottom
   edge of a window is worse than a shorter peek on a short screen. */

/* ---------- hero offering list ----------
   Replaces the old lede paragraph. Same label/value anatomy as the About
   credential list, so the page states its three offerings the same way twice
   rather than inventing a second device for it. */
/* Three across, not three stacked rows. When the intro film moved down the page
   the hero lost its second column; a capped list left the right third of the
   band empty, which read as something removed rather than as space. Running the
   offerings across closes the width honestly, and it rhymes with the three
   topic films below. */
.hero-points {
  list-style: none; margin-top: clamp(1.15rem, 2.3vh, 1.9rem);
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  display: grid; grid-template-columns: minmax(0, 1fr);
}
.hero-points li { padding: .8rem 0; border-bottom: 1px solid var(--line); }
.hero-points li:last-child { border-bottom: 0; }
@media (min-width: 760px) {
  .hero-points { grid-template-columns: repeat(3, minmax(0, 1fr)); column-gap: 2.8rem; }
  /* the two full-width rules do the closing now, so the rows drop their own */
  .hero-points li { padding: 1.3rem 0 1.6rem; border-bottom: 0; }
}
.hero-points .k {
  font-family: var(--font-display); font-weight: 600; text-transform: uppercase;
  letter-spacing: .15em; font-size: .95rem; color: var(--purple);
  font-variation-settings: "opsz" 14;
  text-decoration: none;
  /* its own line above the description, so the three columns align on a
     common baseline instead of each label eating a fixed 9.5rem gutter */
  display: inline-block; margin-bottom: .5rem;
  border-bottom: 1px solid transparent; padding-bottom: .12rem;
  transition: border-color .2s ease;
}
.hero-points .k:hover { border-color: var(--purple); }
.hero-points span {
  display: block;
  font-family: var(--font-serif); font-size: 1.05rem; line-height: 1.5;
  color: var(--ink-soft); font-variation-settings: "opsz" 20;
}

/* credential strip: one line of prose across the full width, between rules.
   Deliberately not a 4-up grid of big numbers. */
/* Flex is safe here ONLY because every child is an element. With bare text
   nodes it swallowed the space after each <b> ("500+films bought and sold").
   Flex also gives the wrap points that plain inline flow lost when the
   markup was written without whitespace between the spans. */
.creds {
  margin-top: clamp(1.15rem, 2.4vh, 2rem); padding: 1rem 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  font-size: 1.02rem; color: var(--grey); line-height: 1.75;
  display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: .75rem; row-gap: .35rem;
}
.creds b {
  font-family: var(--font-display); font-weight: 700; color: var(--ink);
  font-size: 1.28rem; font-variation-settings: "opsz" 24;
  font-feature-settings: "tnum" 1;
}
.creds .sep { color: var(--line-strong); }
.creds .item { white-space: nowrap; }   /* never split a figure from its label */
/* on the narrowest phones drop the slashes and let each credential own a line */
@media (max-width: 430px) {
  /* four stacked credentials ran 193px on a 375px screen, with the 1.28rem
     figures setting a 36px line box each. Same four lines, less air. */
  .creds { flex-direction: column; row-gap: .1rem; line-height: 1.4; font-size: .92rem; }
  .creds b { font-size: 1.12rem; }
  .creds .sep { display: none; }
}

/* ---------- video placeholder ----------
   Flat ink. The old 135deg indigo ramp was the loudest AI tell on the site. */
/* Purple-tinted ink plus real film grain. Deliberately NOT the 135deg
   blue-to-violet ramp that reads as generated: the tint is off-axis and the
   texture is what carries it. */
.video-slot {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  background:
    radial-gradient(120% 100% at 18% 8%, #322b6b 0%, transparent 58%),
    var(--dark);
  border: 1px solid var(--dark);
  display: flex; align-items: center; justify-content: center;
}
.video-slot::after {   /* grain */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  opacity: .16; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
}
.video-slot .play {
  position: relative; z-index: 1;
  width: 58px; height: 58px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .38);
  display: flex; align-items: center; justify-content: center;
  transition: border-color .25s ease, background-color .25s ease, transform .25s ease;
}
.video-slot:hover .play { border-color: var(--lavender-bright); background-color: rgba(185,184,244,.14); transform: scale(1.06); }

/* ---------- Academy leader ----------
   The countdown that runs before a reel. Stands in for the video that has not
   been shot yet, so the empty frame reads as deliberate rather than unfinished.

   FAIL-SAFE: the markup is injected by JS and every rule below is scoped to
   .js-leader. With no JS there is no leader element and the play glyph is
   simply visible, which is exactly the state before this feature existed. */
.leader { position: absolute; inset: 0; z-index: 1; display: grid; place-items: center; opacity: 0; }
.leader svg { width: min(44%, 176px); height: auto; overflow: visible; }
.leader .ring      { fill: none; stroke: rgba(255,255,255,.28); stroke-width: 1.1; }
.leader .crosshair { stroke: rgba(255,255,255,.28); stroke-width: 1.1; }
.leader .sweep     { fill: rgba(255,255,255,.11); transform-origin: 50px 50px; }
.leader .num {
  font-family: var(--font-display); font-weight: 700; font-size: 30px;
  fill: #fff; text-anchor: middle; dominant-baseline: central;
  font-variation-settings: "opsz" 48; opacity: 0;
}
.video-slot .slot-tag { z-index: 2; }

/* only once the script has taken over does the play glyph defer to the count */
.js-leader .video-slot .play { opacity: 0; transition: opacity .5s ease; }
.js-leader .video-slot.counting .leader { opacity: 1; }
.js-leader .video-slot.counted .play { opacity: 1; }
.js-leader .video-slot.counted .leader { opacity: 0; transition: opacity .6s ease; }

@media (prefers-reduced-motion: no-preference) {
  /* the sweep hand: one revolution per count, three counts */
  .js-leader .video-slot.counting .sweep { animation: leader-sweep 1s linear 3; }
  @keyframes leader-sweep { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
  /* 3, then 2, then 1, one per revolution */
  .js-leader .video-slot.counting .n3 { animation: num-3 3s steps(1) forwards; }
  .js-leader .video-slot.counting .n2 { animation: num-2 3s steps(1) forwards; }
  .js-leader .video-slot.counting .n1 { animation: num-1 3s steps(1) forwards; }
  @keyframes num-3 { 0%,33.32% { opacity:1 } 33.33%,100% { opacity:0 } }
  @keyframes num-2 { 0%,33.32% { opacity:0 } 33.33%,66.65% { opacity:1 } 66.66%,100% { opacity:0 } }
  @keyframes num-1 { 0%,66.65% { opacity:0 } 66.66%,100% { opacity:1 } }
  /* a gate pulse on each count */
  .js-leader .video-slot.counting .leader svg { animation: leader-tick 1s ease-out 3; }
  @keyframes leader-tick { 0% { opacity:.5 } 10% { opacity:1 } 100% { opacity:.85 } }
}
/* reduced motion: no sweep, no counting, straight to the play glyph */
@media (prefers-reduced-motion: reduce) {
  .js-leader .video-slot .play { opacity: 1; }
  .js-leader .video-slot .leader { display: none; }
}
.video-slot .play svg { width: 18px; height: 18px; margin-left: 2px; }
.slot-tag {
  position: absolute; left: 14px; bottom: 14px;
  font-family: var(--font-display); font-size: .82rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: rgba(255,255,255,.62);
}

/* ---------- video embed ----------
   A real <video>, standing in for the not-yet-shot Scott footage. Same
   16:9 box and ink fill as .video-slot so the layout doesn't shift when a
   slot gets swapped one at a time; the slot-tag moves to the top so it
   doesn't sit on top of the native control bar. */
.video-embed {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  background: var(--dark);
  border: 1px solid var(--dark);
}
.video-embed video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.video-embed .slot-tag { top: 14px; bottom: auto; }
/* the podcast's YouTube player fills the same box */
.video-embed iframe {
  position: absolute; inset: 0;
  width: 100%; height: 100%; border: 0; display: block;
}

/* ---------- the four films ----------
   One after the other, every player on the page (Jeff, round 2). They were a
   film-strip tablist with a single player; his read was that his audience
   would not know the strip was something to click, and he is the audience.

   Player left, copy right from 900px, each film closed off by a rule so the
   four read as a sequence. Below that the copy simply follows its player. Kept
   in one orientation rather than alternating sides: a zig-zag makes the eye
   hunt for the next player, and the point of the change is that nobody has
   to hunt. */
.films { margin-top: 3rem; border-top: 1px solid var(--line); }
.film {
  display: grid; gap: 1.4rem; align-items: center;
  grid-template-columns: minmax(0, 1fr);
  padding: 2.4rem 0;
  border-bottom: 1px solid var(--line);
  /* html's scroll-padding-top already clears the sticky header; the global
     [id] scroll-margin on top of it would land #financing etc. ~180px down,
     with half the previous film still on screen */
  scroll-margin-top: 0;
}
.film-no {
  margin: 0 0 .5rem;
  font-family: var(--font-display); font-weight: 700;
  font-size: .9rem; letter-spacing: .18em;
  color: var(--purple); font-variation-settings: "opsz" 14;
  font-feature-settings: "tnum" 1;
}
.film h3 { margin: 0 0 1rem; }
/* the lower-case half of Jeff's titles, "INTRODUCTION to Filmmaking" */
.film h3 .accent { font-size: .9em; }
.film-copy > p:not(.film-no) {
  font-family: var(--font-serif); font-size: 1.06rem; line-height: 1.55;
  color: var(--ink-soft); margin: 0 0 1.4rem; max-width: var(--measure);
  font-variation-settings: "opsz" 20;
}
/* Every row closes itself with its own rule, so the list ends on a line at
   any length. */
.film ul { list-style: none; margin: 0 0 1.6rem; border-top: 1px solid var(--line); }
.film li {
  padding: .62rem 0; margin: 0; color: var(--ink-soft);
  border-bottom: 1px solid var(--line); font-size: 1.01rem;
}
@media (min-width: 900px) {
  .film { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: 3.2rem; padding: 3rem 0; }
}

/* a section lede sitting directly under its heading */
h2 + .lede { margin-top: 1.1rem; }

/* ---------- services ----------
   The seven short service films, each its own card-less cell in a 3-column
   grid (Jeff, round 2: "a 3 x 3 matrix so that it is clear that each one is
   separate"). Seven today, room for nine. Two columns on a tablet, one on a
   phone. A ruled top edge on each cell rather than a box: the page has no
   cards anywhere and this is not the place to start. */
.services {
  list-style: none; margin: 2.6rem 0 0; padding: 0;
  display: grid; gap: 2.4rem 1.8rem;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 640px) { .services { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 980px) { .services { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
/* With seven, the last clip sat alone in the bottom-left cell. It goes in the
   middle of that row instead, spot 8 of the 9 (Jeff, 2026-10-26). The
   :last-child half means the rule switches itself off once an eighth clip
   arrives, and the grid fills left to right again. */
@media (min-width: 980px) { .service:nth-child(7):last-child { grid-column: 2; } }
.service { margin: 0; }
.service h3 {
  margin: 1rem 0 .3rem;
  font-size: clamp(1.1rem, 1.6vw, 1.3rem);
  font-variation-settings: "opsz" 28;
}
/* the running time, set like the slot tags rather than like body copy: it is a
   label on the film, not a sentence about it */
.clip-len {
  margin: 0;
  font-family: var(--font-display); font-weight: 600;
  font-size: .8rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--grey);
  font-feature-settings: "tnum" 1;
}

/* the cover on each service clip and each of the four films
   (the base below was the services' glyph-only cover; services now carry a
   photo and films a poster, both further down)
   Every clip is Jeff on the same purple backdrop, so the posters read as one
   image repeated. The cover stands in for the frame: flat ink (the same
   --dark as every other player here), one line glyph, and the play mark taken
   straight from .video-slot, hover included. No grain, no gradient, no purple
   fill; the brand colour only turns up on hover, as it does everywhere else.
   Lives inside .video-embed, which supplies the 16:9 box and the ink fill.
   The title stays outside it, in the caption under a service or in the copy
   beside a film, so it outlasts the cover. See the services comment in
   index.html for the no-script case. */
.svc-cover {
  position: absolute; inset: 0; z-index: 1;
  display: block; width: 100%; height: 100%;
  margin: 0; padding: 0; border: 0; border-radius: 0;
  background: var(--dark); color: rgba(255, 255, 255, .92);
  font: inherit; cursor: pointer;
  -webkit-appearance: none; appearance: none;
}
.svc-cover[hidden] { display: none; }
.svc-glyph {
  position: absolute; left: 50%; top: 47%;
  width: 25%; height: auto; aspect-ratio: 1;
  transform: translate(-50%, -50%);
  fill: none; stroke: currentColor; stroke-width: 3.2;
  stroke-linecap: round; stroke-linejoin: round;
  transition: color .25s ease;
}
.svc-play {
  position: absolute; right: 14px; bottom: 14px;
  width: 46px; height: 46px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .38);
  display: flex; align-items: center; justify-content: center;
  transition: border-color .25s ease, background-color .25s ease, transform .25s ease;
}
.svc-play svg { width: 16px; height: 16px; margin-left: 2px; fill: currentColor; }
.svc-cover:is(:hover, :focus-visible) .svc-glyph { color: var(--lavender-bright); }
.svc-cover:is(:hover, :focus-visible) .svc-play {
  border-color: var(--lavender-bright); background-color: rgba(185, 184, 244, .14); transform: scale(1.06);
}
.svc-cover:focus-visible { outline: 2px solid var(--lavender-bright); outline-offset: -6px; }

/* On a film the thumbnail stays Jeff's frame (a glyph-only cover was tried
   here and taken back out), so the cover is painted with that same poster
   (inline background-image, set per film in index.html) and the glyph sits
   straight on the frame in the top-left corner, like a broadcast bug. It had
   a flat ink tab behind it first; that read as a black box on the photo, so it
   is now bare, larger, and a little see-through. The corner is always plain
   backdrop in these four frames, so a light glyph reads cleanly; a faint
   shadow keeps it legible if a frame is ever swapped for a brighter one.
   The cover is painted rather than clear because a clear overlay would show
   the browser's own control bar through it; the <video> underneath keeps the
   same poster for the no-script case and for the moment after the click.
   The play mark sits on a photo too, so it keeps a dark fill and a bright rim,
   lighter than before so it does not read as another black shape. The
   glyph's stroke is pinned in screen pixels so it does not scale with it. */
.film .svc-cover {
  background-color: var(--dark);             /* only visible while the poster loads */
  background-size: cover; background-position: center;
}
.film .svc-glyph {
  left: 20px; top: 20px; transform: none;
  width: 76px; height: 76px;
  color: #fff; stroke-width: 3px;
  opacity: .82;
  filter: drop-shadow(0 1px 4px rgba(20, 20, 22, .35));
  transition: opacity .25s ease;
}
.film .svc-glyph :is(path, rect, circle, ellipse, line) { vector-effect: non-scaling-stroke; }
/* lavender on the violet backdrop would vanish, so hover brightens instead */
.film .svc-cover:is(:hover, :focus-visible) .svc-glyph { color: #fff; opacity: 1; }
.film .svc-play {
  right: 20px; bottom: 20px; width: 58px; height: 58px;
  background-color: rgba(20, 20, 22, .5); border-color: rgba(255, 255, 255, .8);
}
.film .svc-play svg { width: 20px; height: 20px; }
.film .svc-cover:is(:hover, :focus-visible) .svc-play { background-color: rgba(20, 20, 22, .72); }
@media (max-width: 899px) {
  .film .svc-glyph { left: 14px; top: 14px; width: 58px; height: 58px; }
  .film .svc-play { right: 14px; bottom: 14px; width: 52px; height: 52px; }
}

/* On a service the cover is a photo of the subject (Zach, 2026-10-27), with
   no glyph: the photo says what the clip is about. It is an <img> so it can
   load lazily (see the services comment in index.html); the ink fill behind
   it shows until it arrives. The play mark takes the film treatment, a dark
   fill and a bright rim, because it sits on a photo here too. */
.svc-photo {
  position: absolute; inset: 0;
  display: block; width: 100%; height: 100%;
  object-fit: cover;
}
.service .svc-play { background-color: rgba(20, 20, 22, .5); border-color: rgba(255, 255, 255, .8); }
.service .svc-cover:is(:hover, :focus-visible) .svc-play { background-color: rgba(20, 20, 22, .72); }

/* ---------- full service ----------
   Was a one-line note when full production was a twice-a-year ask; it is a
   product now. Two columns, because the single-column version left the pitch
   stranded against a half-empty page and wrapped the discipline row onto a
   near-empty second line. The list is a spec sheet on the right, which is what
   "here is everything included" should look like. */
.fs-grid, .pod-grid { display: grid; gap: 2.8rem; grid-template-columns: minmax(0, 1fr); }
.fs-act { margin-top: 0; }
@media (min-width: 900px) {
  .fs-grid, .pod-grid { grid-template-columns: minmax(0, 11fr) minmax(0, 9fr); gap: 2.2rem 4.5rem; align-items: start; }
  /* the photo-and-list column spans both rows so it stays top-aligned beside
     the pitch, and the CTA sits under the copy where it reads on a desktop.
     Now that the photo makes that column the taller side, the second row is
     1fr so it takes up the difference. With both rows auto the grid split the
     extra height across both, and the CTA floated ~120px below the copy. */
  .fs-grid { grid-template-rows: auto 1fr; }
  .fs-copy { grid-column: 1; grid-row: 1; }
  .fs-side { grid-column: 2; grid-row: 1 / span 2; }
  .fs-act  { grid-column: 1; grid-row: 2; align-self: start; }
}
/* Jeff with a full cast and crew, above the list: the section promises "we
   help you make the film", and this is the "we". Uncropped, because the group
   runs edge to edge of the frame. */
.fs-photo { margin: 0 0 2.4rem; }
.fs-photo img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--line-strong);
}
.fs-photo figcaption { margin-top: .6rem; font-size: .88rem; color: var(--grey); }
.fs-close { margin-top: 1.6rem; font-size: 1.06rem; color: var(--ink-soft); max-width: 46ch; }
/* Jeff set SERVICE and EVERYTHING in caps. The page carries emphasis with weight
   and ink rather than shouting, so both words get the same treatment. */
.fs-close strong,
.fullservice .lede strong { color: var(--ink); font-weight: 600; }
/* "What we can help you with" carries more weight than the generic section
   rhythm gives it, so it keeps a bigger clamp than the clamp(4.5rem, 9vw, 7.5rem)
   every other section uses. Full service used to share this bump and no longer
   does: it was the most padded section on the page while saying the least, and
   the extra chrome was part of why it felt oversized on a phone. */
/* Top padding is lighter than the bottom so the band's eyebrow sits close
   enough to its own top edge to be legible where it crosses the fold. That
   peek is the whole point of trimming the hero. */
.topics { padding: clamp(3.75rem, 7.5vw, 6rem) 0 clamp(5.5rem, 11vw, 9.5rem); }

.fs-list-title {
  font-family: var(--font-display); font-size: .95rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--grey);
  font-variation-settings: "opsz" 14; margin-bottom: .6rem;
}
/* grid-auto-flow: column so the eye reads DOWN each column in production order,
   not across the pair */
.disciplines {
  list-style: none; display: grid;
  grid-auto-flow: column; grid-template-rows: repeat(5, auto);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 2rem; border-top: 1px solid var(--line-strong);
}
.disciplines li {
  display: flex; align-items: baseline; gap: .75rem;
  padding: .72rem 0; border-bottom: 1px solid var(--line-strong);
  font-family: var(--font-display); font-weight: 600; font-size: 1.02rem;
  letter-spacing: .13em; text-transform: uppercase; color: var(--ink);
  font-variation-settings: "opsz" 24;
}
/* the film strip's own dot, at bullet scale */
.disciplines li::before {
  content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%;
  background: var(--purple); transform: translateY(-.18em);
}
.disciplines .etc {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  text-transform: none; letter-spacing: 0; font-size: 1.08rem; color: var(--grey);
  font-variation-settings: "opsz" 20;
}
.disciplines .etc::before { background: transparent; }
/* Deliberately still two columns on a phone. Collapsing to one turned ten rows
   into a 507px wall you had to thumb past; the type steps down instead so
   DISTRIBUTION still fits a ~147px column at 375px. */
@media (max-width: 560px) {
  .disciplines { column-gap: 1.1rem; }
  .disciplines li { font-size: .88rem; letter-spacing: .08em; gap: .5rem; padding: .62rem 0; }
  .disciplines .etc { font-size: .95rem; letter-spacing: 0; }
}
/* at 320 the italic "and everything else" wrapped, and because the two columns
   share grid rows that dragged PRODUCING's row to 70px beside it */
@media (max-width: 360px) {
  .disciplines .etc { font-size: .82rem; }
}

/* The rules above are --line-strong because the section sits on the paper
   band since 2026-10-10 (moved below the podcast, and re-banded so the page
   still alternates). --line is 1.16:1 against #f3f2fa; --line-strong 1.49:1. */

/* ---------- disciplines: the row is a control ----------
   Ten single words were a tall, inert list: SCRIPTING tells a filmmaker nothing
   they did not already know. Each row now swaps the line underneath it, so the
   same space carries nine sentences instead of nine nouns.

   No JS. The lines are real markup and the browser does the wiring, so the only
   failure mode is the pre-:has() one handled by the @supports gate below. */
.disciplines button {
  /* the whole row is the target, not just the glyphs */
  appearance: none; -webkit-appearance: none; background: none; border: 0;
  padding: 0; margin: 0; flex: 1; min-width: 0; text-align: left; cursor: pointer;
  font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit;
  transition: color .18s ease;
}
.disciplines li:is(:hover, :focus-within) button { color: var(--purple); }
/* the sprocket hole opens up on the active row */
.disciplines li:is(:hover, :focus-within)::before {
  transform: translateY(-.18em) scale(1.75); transition: transform .18s ease;
}
.disciplines button:focus-visible { outline: 2px solid var(--purple); outline-offset: 4px; }

.d-readout { margin-top: 1.15rem; }
.d-detail {
  font-family: var(--font-serif); font-size: 1rem; line-height: 1.5;
  color: var(--ink-soft); max-width: 42ch;
}
.d-detail + .d-detail { margin-top: .55rem; }
.d-label {
  display: block; font-family: var(--font-display); font-size: .78rem;
  font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
  color: var(--purple); font-variation-settings: "opsz" 14; margin-bottom: .22rem;
}

/* Everything that hides a line sits behind .js-disc, which the script adds, so
   the no-JS page keeps all ten sentences rather than showing an empty box.

   A pure-CSS version with :has() works too and was the first attempt; it is a
   legitimate alternative if you would rather drop the script. The script wins
   here only because it reads the mapping from each button's aria-describedby,
   so adding a discipline is a markup edit, where :has() needs a matching
   nth-child pair added to the stylesheet every time. */
.js-disc .d-readout { display: grid; pointer-events: none; }
/* all ten share one grid cell: the box is as tall as the longest line, so
   nothing reflows between rows and no height is hard-coded */
.js-disc .d-detail { grid-area: 1 / 1; opacity: 0; transition: opacity .2s ease; }
.js-disc .d-detail + .d-detail { margin-top: 0; }
.js-disc .d-detail.is-on { opacity: 1; }

/* ---------- about ---------- */
/* Filmmaker / Teacher / Podcaster / Consultant, sitting under the name.
   Same slash separator as the credential rule in the hero. */
.role-line {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .4rem .8rem;
  margin-top: 1.1rem;
  font-family: var(--font-display); font-weight: 600; font-size: 1rem;
  letter-spacing: .18em; text-transform: uppercase; color: var(--lavender);
  font-variation-settings: "opsz" 16;
}
/* the slash rides on the end of the preceding word, so a wrapped line never
   opens with a dangling separator */
.role-line span:not(:last-child)::after { content: " /"; color: rgba(165, 164, 239, .45); }

.about-grid { display: grid; gap: 3rem; grid-template-columns: minmax(0, 1fr); margin-top: 3rem; }
@media (min-width: 900px) { .about-grid { grid-template-columns: minmax(0, 9fr) minmax(0, 11fr); gap: 4.5rem; align-items: start; } }
.cred-list { list-style: none; margin-top: 2.2rem; }
.cred-list li {
  display: grid; grid-template-columns: 10.5rem 1fr; gap: 1.5rem;
  padding: 1rem 0; border-top: 1px solid #303036; color: #cfcfd6; font-size: 1rem;
}
.cred-list li:last-child { border-bottom: 1px solid #303036; }
.cred-list .k {
  font-family: var(--font-display); font-weight: 600; text-transform: uppercase;
  letter-spacing: .15em; font-size: .95rem; color: var(--lavender);
  font-variation-settings: "opsz" 14;
}
@media (max-width: 640px) {
  .cred-list li { grid-template-columns: 1fr; gap: .3rem; }
}
/* the podcast and reference rows are the only links in this list */
.cred-list a {
  color: var(--lavender-bright); text-decoration: none;
  border-bottom: 1px solid rgba(185, 184, 244, .35); padding-bottom: .08rem;
  transition: border-color .2s ease;
}
.cred-list a:hover { border-color: var(--lavender-bright); }
.ref-links { display: flex; flex-wrap: wrap; gap: .3rem 1.4rem; }

/* the media column: portrait first, then the slot the About video drops into */
.about-media { display: grid; gap: 1.6rem; align-content: start; }
/* height:auto is load-bearing. The width/height attributes on the <img> (there
   to reserve space and stop layout shift) map to a presentational height, which
   outranks aspect-ratio and stretches the frame. */
.portrait {
  width: 100%; height: auto; aspect-ratio: 4 / 5;
  object-fit: cover; object-position: center 25%;
  border: 1px solid #303036;
}

/* ---------- the films ----------
   Posters vary between 2:2.7 and 2:3, so they are cropped to a common 2:3 to
   give the row one baseline. */
.posters-label { margin-top: 4.5rem; }
.posters {
  list-style: none; display: grid; gap: 1.5rem 1.4rem; margin-top: 1.5rem;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 560px) { .posters { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 900px) { .posters { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1.8rem; } }
.posters li { position: relative; }
.posters img {
  width: 100%; height: auto; aspect-ratio: 2 / 3; object-fit: cover;
  border: 1px solid #303036; background: #0d0d10;
}
/* corner flag for the 3 titles picked up by Netflix, named rather than the
   trademarked glyph so nothing but the poster art itself is reproduced */
.posters li.on-netflix::before {
  content: "NETFLIX"; position: absolute; bottom: .55rem; right: .55rem; z-index: 1;
  background: #E50914; color: #fff; line-height: 1;
  font-family: var(--font-display); font-weight: 700; font-size: .62rem;
  letter-spacing: .1em; text-transform: uppercase;
  padding: .3rem .5rem; border-radius: 2px;
}
.posters span {
  display: block; margin-top: .7rem;
  font-family: var(--font-display); font-size: .88rem; font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: #a9a9b2;
  font-variation-settings: "opsz" 14;
}

/* ---------- podcast + follow ----------
   The socials were one footer row and one credential line, which is not a
   presence. This band names the podcast, and the list is the single place every
   channel lives, so the same rows can be repeated in the footer. */
.follow { list-style: none; border-top: 1px solid var(--line-strong); }
.follow li { border-bottom: 1px solid var(--line-strong); }
.follow a {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; padding: .8rem 0; text-decoration: none; color: var(--ink);
  transition: color .2s ease;
}
.follow .net {
  flex: none;
  font-family: var(--font-display); font-weight: 600; font-size: 1.02rem;
  letter-spacing: .13em; text-transform: uppercase;
  font-variation-settings: "opsz" 24;
}
/* The handle is the proof the link goes where it says it does.
   flex: 1 so it fills the gap between the name and the arrow and the text
   actually right-aligns. Without it the handle box shrank to its own text and
   space-between split the slack evenly on both sides, so every row's value
   landed at a different x: IMDb and Wikipedia both read "Jeff Deverett" and
   still did not line up, because their names are different widths. */
.follow .handle {
  flex: 1; min-width: 0;
  color: var(--grey); font-size: .93rem; text-align: right;
  overflow-wrap: anywhere;
}
.follow a::after {
  content: "\2197"; flex: none; color: var(--line-strong);
  transition: color .2s ease, transform .2s ease;
}
.follow a:hover { color: var(--purple); }
.follow a:hover::after { color: var(--purple); transform: translate(2px, -2px); }
@media (hover: none) { .follow a:hover { color: var(--ink); } }
/* With the episode player in the band (2026-10-26) the desktop layout is copy
   beside the player, then the channels as one ruled list across the full
   width. The player sat on top of the list in the right column first, which
   left a column of blank paper under the copy. The column ratio flips from the
   shared 11/9 so the player gets the wider side, and the list repeats the same
   ratio and gap so its two halves line up under the two columns above it.
   Stacked, it reads copy, player, channels. */
@media (min-width: 900px) {
  .pod-grid { grid-template-columns: minmax(0, 9fr) minmax(0, 11fr); row-gap: 3.2rem; }
  /* The player's top edge lines up with the tops of INDIE FILMMAKING, not with
     the eyebrow (Zach, 2026-10-26). That is the eyebrow's line box (1.9rem at
     the body's 1.62), its 1.1rem margin, and the ~.2rem between the h2's line
     box and the tops of its capitals. */
  .pod-video { margin-top: calc(1.9rem * 1.62 + 1.1rem + .2rem); }
  .pod-grid .follow {
    grid-column: 1 / -1;
    display: grid; grid-auto-flow: column; grid-template-rows: repeat(3, auto);
    grid-template-columns: minmax(0, 9fr) minmax(0, 11fr); column-gap: 4.5rem;
  }
}
/* Narrow phones stack the handle under the name. Grid rather than flex-wrap:
   wrapping put the arrow on a third line of its own. */
@media (max-width: 460px) {
  .follow a {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline; gap: .15rem .8rem;
  }
  .follow .net { grid-column: 1; grid-row: 1; }
  .follow a::after { grid-column: 2; grid-row: 1; }
  .follow .handle { grid-column: 1 / -1; grid-row: 2; text-align: left; }
}

/* ---------- testimonials ----------
   No coloured left edge. The quote carries itself. */
.quotes { display: grid; gap: 2.5rem; grid-template-columns: minmax(0, 1fr); margin-top: 2.75rem; }
@media (min-width: 900px) { .quotes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 3rem 3.5rem; } }
/* A flex column with the attribution pushed to the bottom, so the names line
   up across a row whatever the quotes weigh. The grid sizes every cell in a row
   to its tallest quote; without this the shorter quote's cell stretched but its
   content did not, leaving the name floating 66px above its neighbour's and an
   uneven gap before the next row. It also releases the copy: the note in the
   markup asking future editors to keep all four within 40-55 words of each
   other was a layout constraint being enforced by hand on Jeff's testimonials. */
.quote { position: relative; padding-top: 1.6rem; display: flex; flex-direction: column; }
.quote figcaption { margin-top: auto; }
/* sprocket tick instead of the AI-tell coloured left border */
.quote::before {
  content: ""; position: absolute; top: 0; left: 0; width: 34px; height: 8px;
  background: repeating-linear-gradient(90deg, var(--purple) 0 5px, transparent 5px 11px);
}
.quote blockquote {
  font-family: var(--font-serif);
  font-size: clamp(1.2rem, 2.1vw, 1.45rem); line-height: 1.42;
  color: var(--ink); font-variation-settings: "opsz" 32;
  text-indent: -.42em;                       /* hang the opening quote mark */
}
.quote cite {
  display: block; padding-top: 1rem; font-style: normal;
  font-family: var(--font-display); font-size: .95rem; font-weight: 600;
  letter-spacing: .15em; text-transform: uppercase; color: var(--grey);
}

/* ---------- FAQ ---------- */
/* Sits on the paper band, so the rules use --line-strong; --line is invisible
   against #f3f2fa. Native <details> rather than a JS accordion: the answers stay
   in the DOM closed or open, which is the whole point of the section.

   Three groups instead of one long list. It was a single 60ch column, 624px
   wide in a 1120px container, and 1831px tall on a laptop - 821px of that the
   four open answers alone. Now the basics run 2x2 across the full width and
   Financing / Distribution sit side by side. Each group is its own stack of
   <details>, never one list split with CSS columns: with multicol, opening a
   question reflows items between columns and the one you clicked can jump. */
.faq-groups {
  margin-top: 2.6rem;
  display: grid; gap: 2.4rem 3rem;
  grid-template-columns: minmax(0, 1fr);
}
.faq-group-title {
  margin: 0 0 .7rem;
  font-family: var(--font-display); font-weight: 600;
  font-size: .95rem; letter-spacing: .15em; text-transform: uppercase;
  color: var(--purple); line-height: 1.2;
  font-variation-settings: "opsz" 14;
}
.faq-item { border-bottom: 1px solid var(--line-strong); }
.faq-list > .faq-item:first-child { border-top: 1px solid var(--line-strong); }
.faq-item summary {
  position: relative; cursor: pointer; list-style: none;
  padding: .9rem 2.4rem .9rem 0;
  font-family: var(--font-body); font-weight: 600; font-size: 1.04rem;
  line-height: 1.4; color: var(--ink);
}
/* Safari still paints its own triangle without this. */
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+"; position: absolute; right: .3rem; top: 50%;
  transform: translateY(-50%);
  font-size: 1.5rem; font-weight: 400; line-height: 1; color: var(--purple);
}
.faq-item[open] summary::after { content: "\2212"; }   /* minus, not a hyphen */
.faq-item summary:focus-visible {
  outline: 2px solid var(--purple); outline-offset: 3px; border-radius: 1px;
}
.faq-item p { margin: 0 0 1.1rem; color: var(--ink-soft); line-height: 1.6; }

@media (min-width: 760px) {
  .faq-groups { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .faq-group--basics { grid-column: 1 / -1; }
  /* the four open answers, 2x2. Default stretch, so the rules under each pair
     line up across the row whatever the two answers weigh. */
  .faq-group--basics .faq-list {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 3rem;
  }
  /* the top-right cell needs the opening rule the first child gets */
  .faq-group--basics .faq-item:nth-child(2) { border-top: 1px solid var(--line-strong); }
}
.faq .btn-row { margin-top: 2.2rem; }

/* ---------- final CTA ---------- */
/* Smaller than the generic section rhythm: this band is a closing note, not
   another full chapter, so it doesn't need the same clamp(4.5rem, 9vw, 7.5rem)
   every other section gets. */
.final-cta { text-align: center; padding: clamp(3.2rem, 6vw, 5rem) 0; }
.final-cta .lede { margin: 1.2rem auto 0; }
.final-cta .btn-row { justify-content: center; margin-top: 1.6rem; }
.band-purple .lede { color: #e9e8fa; }

/* Four reasons, 2x2. The block is centred but the lines are not: ragged-centred
   sentences of uneven length is the thing that makes a CTA look templated. */
.cta-points {
  list-style: none; display: grid; gap: 1rem 3rem;
  max-width: 780px; margin: 1.8rem auto 0; text-align: left;
}
@media (min-width: 760px) { .cta-points { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.cta-points li {
  position: relative; padding-left: 2.4rem; color: #e9e8fa;
  font-family: var(--font-serif); font-size: 1.1rem; line-height: 1.45;
  font-variation-settings: "opsz" 20;
}
/* Jeff set EXPERIENCE in caps, the same way he set SERVICE and EVERYTHING in the
   full-service section. Same answer here: weight and pure white against the band's
   #e9e8fa body text, rather than shouted caps. */
.cta-points li strong { color: var(--white); font-weight: 600; }
/* the sprocket tick again, in place of a checkmark bullet */
.cta-points li::before {
  content: ""; position: absolute; left: 0; top: .6em; width: 26px; height: 7px;
  background: repeating-linear-gradient(90deg, rgba(255, 255, 255, .9) 0 4px, transparent 4px 9px);
}

/* ---------- booking ---------- */
.engagements { margin-top: 3.5rem; border-top: 1px solid var(--line); }
.engagement {
  display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.4rem;
  padding: 2.6rem 0; border-bottom: 1px solid var(--line);
}
@media (min-width: 820px) {
  .engagement { grid-template-columns: 15rem minmax(0, 1fr) auto; gap: 3rem; align-items: start; }
}
.engagement .meta { }
.engagement .price {
  font-family: var(--font-display); font-weight: 700;
  font-size: 3.4rem; line-height: .9; color: var(--ink);
  font-variation-settings: "opsz" 60; font-feature-settings: "tnum" 1;
}
.engagement .dur {
  font-family: var(--font-display); font-size: .95rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--grey); margin-top: .6rem;
}
.engagement .body p { color: var(--ink-soft); font-size: 1.04rem; max-width: 52ch; }
.engagement .body p + p { margin-top: .85rem; }
.engagement .act { align-self: center; }
@media (max-width: 819px) { .engagement .act .btn { display: block; width: 100%; text-align: center; } }
.fine-print { margin-top: 2.4rem; color: var(--grey); font-size: .99rem; max-width: 64ch; }

/* ---------- thanks ---------- */
.centered { text-align: center; }
.centered .lede { margin-left: auto; margin-right: auto; }
.centered-row { justify-content: center; }
.confirm {
  max-width: 640px; margin: 3rem auto 0; text-align: left;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); padding: 2rem 0;
}
.confirm .when {
  font-family: var(--font-serif); font-size: 1.5rem; line-height: 1.35;
  color: var(--ink); overflow-wrap: anywhere;
}
.prep-title {
  font-family: var(--font-display); font-size: 1rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; color: var(--grey);
  margin-top: 2rem; margin-bottom: .4rem;
}
.prep-list { list-style: none; }
.prep-list li { padding: .8rem 0; border-top: 1px solid var(--line); color: var(--ink-soft); }

/* ---------- footer ----------
   Columns rather than one row of links, and identical on all four pages, so the
   channels are somewhere predictable rather than a different set per page. */
.site-footer { background: var(--dark); color: #a9a9b2; padding: 4rem 0 2.6rem; font-size: .98rem; }
.site-footer .container {
  display: grid; gap: 2.6rem 3rem; grid-template-columns: minmax(0, 1fr); align-items: start;
}
@media (min-width: 760px) {
  .site-footer .container { grid-template-columns: minmax(0, 1.5fr) auto auto; column-gap: 4rem; }
  .foot-legal { grid-column: 1 / -1; }
}
.site-footer a { color: #dcdce2; text-decoration: none; }
.site-footer a:hover { color: var(--lavender-bright); text-decoration: underline; }
.foot-brand { display: block; }
/* 40px until Jeff's round 2 notes (2026-10-14) asked for a bigger mark down
   here too; ~2.4x on a laptop, still inside a 375px phone column */
.foot-brand img { height: clamp(68px, 4vw + 40px, 96px); width: auto; display: block; }
.foot-note { margin-top: 1rem; max-width: 32ch; color: #8e8e97; font-size: .95rem; line-height: 1.55; }
.foot-h {
  font-family: var(--font-display); font-size: .9rem; font-weight: 600;
  letter-spacing: .18em; text-transform: uppercase; color: var(--lavender);
  font-variation-settings: "opsz" 14;
}
.foot-col ul { list-style: none; }
.foot-col a {
  font-family: var(--font-display); font-size: .95rem; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; white-space: nowrap;
  padding: .5rem 0; display: inline-block; min-height: 44px; line-height: 1.7;
}
.foot-legal {
  color: #7d7d86; font-size: .9rem;
  border-top: 1px solid #26262b; padding-top: 1.6rem;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: 8px; top: -48px; z-index: 100;
  background: var(--ink); color: #fff; padding: .6rem 1rem;
  text-decoration: none; transition: top .15s ease;
}
.skip-link:focus { top: 0; }

/* ============================================================
   MOBILE
   ============================================================ */
@media (hover: none) {
  .btn-primary:hover { background-color: var(--ink); border-color: var(--ink); }
  .btn-ghost:hover { color: var(--ink); border-color: var(--line-strong); }
  .band-purple .btn-primary:hover { background-color: var(--white); color: var(--purple); }
  .link-arrow:hover { color: var(--ink); border-color: var(--line-strong); }
  .site-footer a:hover { color: inherit; }
}

/* Sticky bar is visible by default; JS only hides it while the hero CTA is
   on screen. If the script fails you keep a CTA rather than lose one. */
.mobile-cta { display: none; }
@media (max-width: 720px) {
  .mobile-cta {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
    padding: .75rem 18px calc(.75rem + env(safe-area-inset-bottom));
    background: var(--white); border-top: 1px solid var(--line);
    transform: translateY(0); opacity: 1;
    transition: transform .24s cubic-bezier(.2,.6,.3,1), opacity .24s ease;
  }
  .mobile-cta.is-hidden { transform: translateY(120%); opacity: 0; pointer-events: none; }
  .mobile-cta .btn { display: block; width: 100%; text-align: center; padding: 1.05rem 1rem; }
  /* The room the bar needs belongs to the footer, not the body. On the body it
     rendered as an 82px band of white page background BELOW the dark footer, so
     the page appeared to end twice and the bar sat in the seam. Moved here, the
     footer's dark fill runs to the bottom of the document and the bar floats
     over it. 82px clears the 77px bar with a little air; the last footer line
     ends ~47px above it.

     Scoped to .has-sticky-cta because only the home page carries the bar. The
     old body rule was unscoped, so /book/, /thanks/ and 404.html reserved 82px
     for a bar they do not have. */
  .has-sticky-cta .site-footer { padding-bottom: calc(2.6rem + 82px + env(safe-area-inset-bottom)); }
}


@media (max-width: 899px) {
  section { padding: 3.4rem 0; }
  .hero { padding-top: 1.9rem; padding-bottom: 2rem; }
  .container { padding: 0 20px; }
  h1 { font-size: clamp(2.7rem, 10.5vw, 3.4rem); }
  h2 { font-size: clamp(2.1rem, 8.5vw, 2.7rem); }
  .split { gap: 2.6rem; margin-top: 2.6rem; }
  /* 19.2px serif at 1.42 is generous for a 375px column; four quotes ran
     1.61 screens, so you met two at a time */
  .quotes { gap: 2.1rem; margin-top: 2.2rem; }
  .quote { padding-top: 1.25rem; }
  .quote blockquote { font-size: 1.1rem; line-height: 1.38; }
  .quote cite { padding-top: .75rem; font-size: .88rem; }
  .site-footer .container { gap: 2rem; }
  .posters-label { margin-top: 3rem; }
  /* the portrait is a person, not a decorative surface: cap it so it does not
     eat a whole phone screen before the copy starts */
  .portrait { max-height: 62vh; }
}

@media (max-width: 560px) {
  .btn-row { flex-direction: column; align-items: stretch; gap: .5rem; }
  .btn-row .btn-primary { display: block; width: 100%; text-align: center; }
  .btn-row .btn-ghost { border-color: transparent; padding: .85rem 0; }
  .creds { font-size: .95rem; }
  .engagement .price { font-size: 2.9rem; }
  .cta-points li { font-size: 1.04rem; padding-left: 2.1rem; }
}
