/* ============================================================
   Fonts, served from this origin

   Previously loaded from fonts.googleapis.com, which handed
   Google every visitor's IP on every pageview. Self-hosted, the
   site makes no third-party request for type at all. Latin
   subset, woff2 only — every browser in use supports it.

   font-display: swap so text paints immediately in the fallback
   and reflows when the face arrives, rather than sitting
   invisible while it downloads.
   ============================================================ */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/space-grotesk-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/space-grotesk-latin-600-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/space-grotesk-latin-700-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/inter-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/inter-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/inter-latin-600-normal.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-latin-400-normal.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-latin-500-normal.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-latin-600-normal.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("../fonts/ibm-plex-mono-latin-700-normal.woff2") format("woff2");
}

/* ============================================================
   Magek Filmworks — Design tokens
   Palette: black-on-black studio, one signature accent pulled
   from a tungsten film light (3200K) — the warm lamp that means
   "we're rolling." Same structural language as gekjr.pro, its
   own voice: dark instead of paper, a square REC mark instead
   of a round tally, amber instead of red.
   ============================================================ */

:root {
  /* The deliberate inverse of gekjr.pro. Its --ink (#131313) is this
     site's --paper; its --paper (#fbfbf9) is this site's --ink. Same
     token names as the Site Bible so the two stylesheets read as one
     system: personal site on paper, company site on screen. */
  --paper: #131313;
  --paper-raised: #1b1b1a;
  --paper-sunk: #0c0c0c;
  --ink: #fbfbf9;
  --ink-soft: #a09e97;
  --ink-faint: #6c6a65;
  --line: #2c2c2a;
  --line-soft: #212120;
  --tally: #df4929;
  --tally-dim: rgba(223, 73, 41, 0.16);
  --tally-glow: rgba(223, 73, 41, 0.38);

  /* Accent for small text. On the dark ground the mark colour is
     legible as type; on the light grounds it is not (3.9:1), so the
     light sections darken this one token and leave fills alone. */
  --tally-ink: var(--tally);

  /* A real grain plate rather than a gradient trick: 96px of gaussian
     noise, tiled and held at low opacity over every photograph. */
  --grain: url("data:image/png;base64,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");

  --font-display: "Space Grotesk", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Inter", "Helvetica Neue", Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;

  --maxw: 1100px;
  /* Side padding. Only bites once the viewport is narrower than
     --maxw plus two gutters — below that it is all that stands
     between the text and the edge of the screen. */
  --gutter: 44px;

  /* Shared indent for the header wordmark and the footer disc. */
  /* Flush with the content column. The header mark, the hero headline,
     every section's copy and the footer mark now share one left edge
     instead of the mark sitting slightly proud of everything below it. */
  --brand-indent: 0px;
  /* The lockup's own height. This file is 95.3% ink, so the box and the
     visible wordmark are near enough the same thing. */
  --logo-h: 84px;

  /* Header height — also drives the mobile menu offset and the
     scroll offset for every in-page anchor. */
  --header-h: 108px;
}

* { box-sizing: border-box; }

/* Component display rules (grid/flex/inline-flex) outrank the UA's
   [hidden] rule, so anything toggled with el.hidden stays visible
   without this. Every hidden step, reveal block and swapped button
   depends on it. */
[hidden] { display: none !important; }

html { scroll-behavior: smooth; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

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

a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.1;
  margin: 0 0 0.5em;
  letter-spacing: -0.01em;
}

p { margin: 0 0 1em; }

.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* The masthead and the colophon span the page; the content column does
   not. So the mark sits on the page's own left margin rather than on
   the text column's, which is the point — a wordmark that lines up with
   the body copy reads as part of it. The gutter is still respected, so
   the mark is never hard against the edge, and the footer carries the
   same class so the two brand marks stay on one line as before. */
.wrap--bleed { max-width: none; }

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

/* ---------- Eyebrow ----------
   No bullet mark. gekjr.pro leads its labels with a tally dot; this site
   deliberately does not, so the two read as related but not the same. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  margin-bottom: 14px;
}

/* ---------- Viewfinder corners: secondary signature ----------
   Bracket corners like a camera frame guide. Used around the
   hero frame and on hover states, never as pure decoration. */
.frame {
  position: relative;
}
.frame::before,
.frame::after,
.frame-tr,
.frame-bl {
  content: "";
  position: absolute;
  width: 22px;
  height: 22px;
  border: 2px solid var(--tally);
  opacity: 0.85;
}
.frame::before { top: -1px; left: -1px; border-right: none; border-bottom: none; }
.frame::after { bottom: -1px; right: -1px; border-left: none; border-top: none; }
.frame-tr { top: -1px; right: -1px; border-left: none; border-bottom: none; }
.frame-bl { bottom: -1px; left: -1px; border-right: none; border-top: none; }

/* ---------- Header ---------- */
/* Solid, and the same ground as the page. The old gradient scrim read
   as a separate bar floating over the site; this reads as the top of
   the page itself, which suits a flat single-ground layout. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  background: #000;
}

.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--header-h);
}

/* Nudged in off the gutter so the mark sits inside the column, not hard
   against its edge. The footer disc uses the same token, so the two
   stay on one line.

   Note the visible gap runs ~3px wider than this value: the lockup PNG
   carries about 1% of transparent margin on its left edge. That is in
   the file, not the CSS, so it is left alone. */
.brand {
  /* No optical offset any more: this lockup is transparent and carries
     only 1.06% dead space on its left edge, so the image box and the
     visible wordmark start in the same place. The compensation here was
     for the black-plated file, which sat 6.16% in. */
  margin-left: var(--brand-indent);
}

.brand img {
  height: var(--logo-h);
  width: auto;
  /* Scaled from the left edge, not the centre: the mark sits on the
     page's left margin, and growing from the middle would slide it off
     that line for the length of the hover or the flare. */
  transform-origin: left center;
  /* Overshoots a touch on the way back, so the release reads as the
     mark springing up rather than sliding up. */
  transition: transform 0.42s cubic-bezier(0.34, 1.4, 0.64, 1),
              filter 0.42s ease;
}

/* Same micro-scale the footer disc uses, so both marks behave alike. */
@media (hover: hover) {
  .brand:hover img {
    transform: scale(1.06);
    filter: brightness(1.18);
  }
}

/* Held down. A click drives the mark to the bottom of its travel and
   leaves it there — it does not spring back on a timer. The release is
   the visitor moving the mouse, which is what a physical key does: it
   stays where your finger put it until your finger goes somewhere else.

   Snapping down is quick; the return is the slower base transition, so
   press and release feel like different actions rather than one
   symmetrical wobble. */
.brand.is-held img {
  transform: scale(0.93);
  filter: brightness(1.42);
  transition-duration: 90ms, 90ms;
}

/* Declared after the hover rule on purpose: identical specificity, so
   source order is what lets a press beat the hover it happens inside. */
.brand:active img { transform: scale(0.93); }

@media (prefers-reduced-motion: reduce) {
  .brand img { transition: none; }
  .brand.is-held img,
  .brand:active img { transform: none; filter: none; }
}

/* Its own query, not folded into the block above: a :hover rule outside
   (hover: hover) sticks on a touch screen. */
@media (prefers-reduced-motion: reduce) and (hover: hover) {
  .brand:hover img { transform: none; filter: none; }
}

.header-nav {
  display: flex;
  align-items: center;
  gap: 28px;
}

.header-nav a {
  font-family: var(--font-mono);
  /* Up from 0.78rem, and set to the size every other mono label on the
     site uses — the section eyebrows and the form's step labels. With
     Home gone there is one link and one button up there, so it can
     carry the weight. */
  font-size: 0.86rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  transition: color 0.15s ease;
}

.header-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border: 1px solid var(--tally);
  color: var(--ink) !important;
  border-radius: 2px;
}

.nav-toggle {
  display: none;
  background: none;
  border: none;
  color: var(--ink);
  padding: 6px;
  cursor: pointer;
}

@media (max-width: 1024px) {
  .header-nav {
    display: none;
    position: absolute;
    top: var(--header-h);
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    background: var(--paper-raised);
    border-bottom: 1px solid var(--line);
    padding: 8px var(--gutter) 20px;
  }
  .header-nav.is-open { display: flex; }
  .header-nav a { padding: 12px 0; width: 100%; }
  .header-cta { margin-top: 8px; }

  .nav-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-right: -10px;
    padding: 0;
  }
}

/* ---------- Hero (full-bleed image) ---------- */
.hero {
  position: relative;
  min-height: 78vh;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--paper);
}

.hero-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
}

/* The frame is no longer dimmed. The photograph runs at full opacity and
   is thrown out of focus instead, so the type sits in front of it the way
   a title sits over a rack-focus pull rather than over a grey wash. */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Shadows carry most of the legibility; this closes the gap on the
     brightest frames. Deepened just enough to pay for toning the type
     off pure white: measured against all four frames, #e6e2da over this
     scrim reads at 2.38:1 worst-case, where pure white over the old one
     managed 2.30 — so the softer colour costs nothing. Going further
     would clear the 3:1 large-text threshold, but only by darkening the
     photographs more than the type problem justifies. */
  background: linear-gradient(180deg, rgba(10, 10, 10, 0.44) 0%, rgba(10, 10, 10, 0.54) 55%, rgba(10, 10, 10, 0.78) 100%);
}

.hero .wrap {
  position: relative;
  z-index: 1;
  /* Full width, not max-content. As a shrink-to-fit flex item it was
     only as wide as its longest line, and `margin: 0 auto` then centred
     that box — so the hero's left edge floated with the copy instead of
     sitting on the site's content axis, drifting 168px right of the
     header logo at 1440 and a different distance at every width. The
     original max-content was there to stop a long line clipping; the
     column is 1012px wide and the longest line is 644px, so there is
     nothing left to clip. */
  width: 100%;
  display: grid;
  gap: 15px;
  /* The frame is a fixed height and the copy sits at its foot, so this
     reserved band is the only thing keeping the headline out from under
     the fixed header. */
  padding-top: calc(var(--header-h) + 84px);
  padding-bottom: 72px;
}

.hero .eyebrow { color: #cfcdc5; }

.hero h1 {
  font-size: clamp(2.1rem, 7vw, 4.2rem);
  max-width: 14ch;
  color: #fff;
}

.hero h1 em {
  font-style: normal;
  color: var(--tally);
}

.hero-sub {
  font-size: clamp(1.12rem, 3.4vw, 1.35rem);
  color: #d6d4cc;
  max-width: 46ch;
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.7),
    0 2px 10px rgba(0, 0, 0, 0.6);
  transition: opacity 0.45s ease, transform 0.45s ease;
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 4px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 14px 22px;
  border-radius: 2px;
  border: 1px solid var(--line);
  /* Stated, not inherited. .btn is used on both <a> and <button>, and a
     <button> with no background declared takes the browser's own — a
     light grey that showed as a filled panel on every dark ground.
     .btn-primary sets its own fill and is unaffected. */
  background: transparent;
  transition: border-color 0.15s ease, background 0.15s ease;
}

.btn-primary {
  background: var(--tally);
  color: #0a0a09;
  border-color: var(--tally);
  font-weight: 600;
}

.btn-ghost { color: #fff; border-color: rgba(255,255,255,0.35); }

@media (min-width: 900px) {
  .hero { min-height: 86vh; }
}

/* ---------- Section shell ----------
   scroll-margin-top lives here rather than in its own rule: the header
   is fixed, so an untreated jump parks the section title underneath it.
   One rule per selector, per the Bible's duplicate-selector lesson. */
#top { scroll-margin-top: calc(var(--header-h) + 12px); }

.section {
  padding: 92px 0;
  scroll-margin-top: calc(var(--header-h) + 12px);
}

/* No alternating bands. One ground, and spacing does the separating —
   the same way the reference site holds together without dividers. */
.section-alt { background: transparent; }
/* Centred, so the "full capabilities" link sits under the title rather
   than beside it — side by side, a width-limited centred heading and a
   right-hand link read as two things that missed each other. */
.section-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 18px;
  margin-bottom: 46px;
}

/* Section titles run at display scale — they are the only thing
   competing with the hero for the page's voice. */
.section-head h2 {
  font-size: clamp(2.4rem, 6.4vw, 4.6rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.02;
  margin: 0;
  max-width: 16ch;
}

.see-all {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  white-space: nowrap;
}

/* ---------- The rundown ----------
   An ID column, a short meta column, and the content. A rundown is the
   document a technical director actually reads on a show, so the page is
   organised the way the work is rather than by decorative numbering.
   No rules between rows — spacing separates them. */
.rundown {
  display: grid;
  gap: 62px;
}

.rundown-row {
  display: grid;
  gap: 8px 26px;
  align-items: start;
}

@media (min-width: 760px) {
  .rundown-row { grid-template-columns: 88px 156px 1fr; }
}

.rd-id {
  font-family: var(--font-mono);
  font-size: clamp(0.95rem, 1.5vw, 1.15rem);
  font-weight: 600;
  letter-spacing: 0.14em;
  color: var(--tally-ink);
  padding-top: 5px;
}

.rd-meta {
  font-family: var(--font-mono);
  font-size: clamp(0.84rem, 1.2vw, 0.98rem);
  letter-spacing: 0.08em;
  color: var(--ink-soft);
  padding-top: 6px;
}

.rd-body h3 {
  font-size: clamp(1.45rem, 2.4vw, 1.8rem);
  letter-spacing: -0.015em;
  margin-bottom: 12px;
}

.rd-body p {
  color: var(--ink-soft);
  font-size: 1rem;
  max-width: 60ch;
  margin-bottom: 0;
}

.rd-systems {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 2;
  letter-spacing: 0.02em;
  color: var(--ink-faint, #6c6a65);
  margin-top: 16px !important;
  max-width: 66ch;
}

/* A link in a rundown row — today only gekjr.pro in the crew bio. It had
   a 1px bottom border in --line, which is the site's rule colour: right
   for a divider, invisible as a link. Nothing announced it was clickable,
   so nobody clicked it.
   Same treatment as .reel-credit a, deliberately — one way of saying
   "this goes somewhere" across the whole site. */
.rd-body a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--tally);
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}

@media (hover: hover) {
  .rd-body a:hover { color: var(--tally); }
}

/* The crew list runs the same grid, closer together — three short rows
   rather than four long ones. */
.rundown-tight { gap: 40px; }
.rundown-tight .rd-body h3 { font-size: clamp(1.25rem, 2vw, 1.5rem); margin-bottom: 8px; }
.rundown-tight .rd-body p { font-size: 0.95rem; }

@media (min-width: 760px) {
  .rundown-tight .rundown-row { grid-template-columns: 72px 124px 1fr; }
}

@media (max-width: 640px) {
  .rundown { gap: 46px; }
  .rd-id, .rd-meta { padding-top: 0; }
  .rd-body h3 { font-size: 1.32rem; }
}

/* ---------- Testimonials (red-rule pull-quotes) ----------
   The Bible's pull-quote component rather than another boxed card —
   same reasoning as the editorial list: the page reads editorially,
   and a rule carries the quote without fencing it in. */
.testimonial-grid {
  display: grid;
  gap: 40px;
}

.testimonial {
  padding: 0;
}

.testimonial p {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 500;
  line-height: 1.45;
  margin-bottom: 16px;
  max-width: 52ch;
}

.testimonial cite {
  font-style: normal;
  font-family: var(--font-mono);
  font-size: 0.76rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}

/* Three quotes, three columns — a 2-up grid left one hanging on its own
   row with a hole beside it. */
@media (min-width: 900px) {
  .testimonial-grid { grid-template-columns: repeat(3, 1fr); gap: 32px; }
}

@media (max-width: 640px) {
  .testimonial-grid { gap: 30px; }
  .testimonial p { font-size: 1.08rem; }
}

/* ---------- Contact ----------
   It's the last section, so on tall screens the page ran out of
   scroll before the anchor could bring it to the top — the jump
   stopped short with the section parked mid-page. Giving it a
   viewport's worth of height means #contact can actually reach
   the top; the content stays vertically centered in the space. */
#contact {
  min-height: calc(100vh - var(--header-h));
  display: grid;
  grid-template-columns: 1fr;
  align-content: center;
}

/* margin:0 auto on a grid item disables stretch and centres it at its
   max-content width — the same trap the hero hit. Fill the row so the
   1100px column rule applies and the block lines up with the sections. */
#contact > .wrap { width: 100%; }

/* vh on mobile can measure against the pre-collapse address-bar
   height, pushing bottom-anchored content off-screen. dvh as a
   progressive-enhancement pair, per the Site Bible. */
@supports (height: 100dvh) {
  #contact { min-height: calc(100dvh - var(--header-h)); }
}

/* ---------- Contact ---------- */
.contact-grid {
  display: grid;
  gap: 40px;
}

.contact-lead h2 {
  font-size: clamp(2.2rem, 5.6vw, 3.8rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.03;
  max-width: 15ch;
}
.contact-lead p { color: var(--ink-soft); font-size: clamp(1.04rem, 1.4vw, 1.2rem); max-width: 42ch; }

.contact-direct {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 22px;
}

.contact-direct a {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 600;
  border-bottom: 1px solid transparent;
  width: fit-content;
}

.contact-form {
  display: grid;
  gap: 14px;
}

.contact-form label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-bottom: 6px;
}

/* Controls read as panel inserts: a recessed ground with one hairline
   of light along the top edge, the way a machined bezel catches it. */
.contact-form input,
.contact-form textarea {
  width: 100%;
  box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  color: var(--ink);
  padding: 12px 14px;
  font-family: var(--font-body);
  font-size: 0.95rem;
  border-radius: 2px;
}
.contact-form input:focus,
.contact-form textarea:focus {
  outline: none;
  border-color: var(--tally);
  box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05),
              0 0 0 3px var(--tally-dim);
}

.contact-form textarea { min-height: 110px; resize: vertical; }

@media (min-width: 900px) {
  .contact-grid { grid-template-columns: 1fr 1.1fr; }
}

/* ---------- Footer ---------- */
.site-footer {
  padding: 64px 0 48px;
  /* Still lifted off the page ground (#131313) so the mark's black disc
     reads as a shape rather than a hole, but darker than the #33332f it
     was: that had been pushed to the absolute ceiling, where the footer
     text sat at 4.73:1 with no margin left. This trades some of the
     disc's separation back for a quieter band and easier text. */
  background: #222220;
}

/* The disc closes the page where the wordmark opens it. */
.footer-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-left: var(--brand-indent);
}

.footer-brand p { margin: 0; }

/* The disc is a control now, not an ornament. The link wraps tightly so
   the mark keeps the left edge it shares with the header wordmark. */
.footer-mark-link {
  display: inline-flex;
  flex: none;
  line-height: 0;
  border-radius: 50%;
}

.footer-mark-link .footer-mark {
  transition: transform 0.42s cubic-bezier(0.34, 1.4, 0.64, 1),
              filter 0.42s ease;
}

/* Hover lifts and warms it rather than moving it: this sits at the very
   foot of the page beside a line of type, and anything that travels
   would shove that line around. */
@media (hover: hover) {
  .footer-mark-link:hover .footer-mark {
    transform: scale(1.06);
    filter: brightness(1.18);
  }
}

.footer-mark-link:active .footer-mark { transform: scale(0.97); }

/* Held down the same way the header wordmark is, so both marks answer
   alike. */
.footer-mark-link.is-held .footer-mark {
  transform: scale(0.93);
  filter: brightness(1.42);
  transition-duration: 90ms, 90ms;
}

@media (prefers-reduced-motion: reduce) {
  .footer-mark-link .footer-mark,
  .footer-mark-link.is-held .footer-mark {
    transition: none;
    animation: none;
    transform: none;
    filter: none;
  }
}

/* The hover reset needs its own query rather than riding along in the
   block above: it is a :hover rule, and a :hover rule outside
   (hover: hover) sticks on a touch screen — tapping fakes a hover-in
   with no hover-out. Specificity means it has to exist, so it exists
   in the right place. */
@media (prefers-reduced-motion: reduce) and (hover: hover) {
  .footer-mark-link:hover .footer-mark {
    transform: none;
    filter: none;
  }
}

.footer-mark {
  height: 72px;
  width: auto;
  flex: none;
}

.footer-grid {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: flex-start;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.footer-social {
  display: flex;
  gap: 16px;
}

@media (min-width: 640px) {
  .footer-grid { flex-direction: row; justify-content: space-between; align-items: center; }
}

/* ============================================================
   Mobile & tablet refinements
   The frame above was drawn desktop-first; this block fits it to
   small screens without changing the design language — same
   palette, same type, same REC marks, just sized for a phone.
   ============================================================ */

h1, h2, h3 { text-wrap: balance; }

/* Never let a wide child push the page sideways. */
html, body { overflow-x: hidden; }

/* ---------- Tablet ---------- */
@media (max-width: 1024px) {
  :root {
    --header-h: 92px;
    --logo-h: 64px;
  }
  .section { padding: 72px 0; }

  /* Narrower crop pulls the bright sky behind the eyebrow — lift the scrim. */
  .hero::after {
    background: linear-gradient(180deg,
      rgba(8, 8, 7, 0.42) 0%,
      rgba(8, 8, 7, 0.62) 45%,
      rgba(8, 8, 7, 0.93) 100%);
  }
  .hero-bg { object-position: 58% 42%; }
}

/* ---------- Phone ---------- */
@media (max-width: 760px) {
  :root {
    --gutter: 28px;
    /* Flush on phones: the footer stacks, so any indent would
       misalign the brand row against the social row below it. */
    --brand-indent: 0px;
    /* Taller, to carry the bigger mark with the same air around it. */
    --header-h: 108px;
  }

  /* Respect notches and rounded corners in landscape. */
  .wrap {
    padding-left: max(var(--gutter), env(safe-area-inset-left));
    padding-right: max(var(--gutter), env(safe-area-inset-right));
  }

  /* The limit on phones is width, not height: the lockup is 3.42:1, so
     every pixel of height costs 3.4 of width, and the hamburger is
     fixed at the other end of the bar. Narrow phones step down below.
     Wrapped in :root because a custom property has to live inside a
     rule — bare in the media block it is dropped silently, and phones
     would quietly inherit the desktop size. */
  :root { --logo-h: 76px; }

  .header-nav {
    top: var(--header-h);
    background: var(--paper-sunk);
    border-bottom: 1px solid var(--line);
    padding: 6px var(--gutter) 18px;
  }
  .header-nav a { padding: 14px 0; }
  .header-cta {
    width: fit-content;
    padding: 12px 20px;
    margin-top: 10px;
  }

  /* Hero: svh so mobile browser chrome doesn't crop the frame. */
  .hero { min-height: 82vh; }

  /* Darker scrim — the headline was landing on open sky. */
  .hero::after {
    background: linear-gradient(180deg,
      rgba(8, 8, 7, 0.55) 0%,
      rgba(8, 8, 7, 0.68) 42%,
      rgba(8, 8, 7, 0.94) 100%);
  }

  /* Favor the stage-left structure over the bright sky. */
  .hero-bg { object-position: 62% 45%; }

  .hero .wrap {
    padding-top: calc(var(--header-h) + 52px);
    padding-bottom: 78px;
    gap: 16px;
  }

  .hero .eyebrow {
    font-size: 0.62rem;
    letter-spacing: 0.05em;
    gap: 7px;
    margin-bottom: 8px;
  }

  .hero h1 { max-width: 18ch; }
  .hero-sub { font-size: 1rem; }

  /* Equal-width buttons read as a pair instead of a ragged row. */
  /* justify-items:start on the hero grid shrinks this row, which
     would undo the equal-width button pair — hold it full width. */
  .hero-actions { gap: 10px; width: 100%; }
  .hero-actions .btn {
    flex: 1 1 0;
    min-width: 0;
    justify-content: center;
    padding: 15px 12px;
    font-size: 0.76rem;
  }

  /* Sections: tighter rhythm, roomier cards. */
  .section { padding: 58px 0; }
  .section-head { margin-bottom: 18px; }



  .contact-grid { gap: 32px; }
  .contact-lead h2 { max-width: 20ch; }
  .contact-direct { gap: 2px; }
  .contact-direct a { padding: 8px 0; font-size: 1.08rem; }

  .contact-form input,
  .contact-form textarea { font-size: 16px; } /* stops iOS zoom-on-focus */
  .contact-form .btn { justify-self: stretch; justify-content: center; }

  .site-footer { padding: 24px 0 32px; }
  .footer-grid { gap: 18px; }
  /* .footer-grid stacks its rows with align-items: flex-start, so every
     row starts pinned left by default. align-self overrides that per
     row, giving each the full row width to center within — otherwise
     centering the content inside has no extra space to work with.
     The brand row goes column (mark above copyright line, both
     centered) rather than staying a left-to-right pair, since a wide
     copyright line next to a small mark reads as left-aligned text
     with a logo beside it, not a centered block. */
  .footer-brand { flex-direction: column; gap: 14px; align-self: stretch; text-align: center; }
  .footer-mark { height: 58px; }
  .footer-social { flex-wrap: wrap; gap: 14px; justify-content: center; align-self: stretch; }
  /* Was 27px tall — under the thumb floor, and these are the only
     outbound links on the page. */
  .footer-social a {
    padding: 12px 0;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* ---------- Narrow phones ---------- */
@media (max-width: 380px) {
  .hero .eyebrow { font-size: 0.58rem; }
  .hero-actions { flex-direction: column; }
  .hero-actions .btn { flex: none; width: 100%; }
}

/* At 360 and below a 76px mark runs into the hamburger — they actually
   collide at 344 — so the mark and the bar step back down together.
   The step is at 370 rather than 380 because 375 (iPhone SE, 8) still
   clears the toggle by 28px at full size, and that is a common enough
   screen to be worth the extra query. This block sits after the 760px
   one on purpose: same specificity, so source order decides. */
@media (max-width: 370px) {
  :root { --header-h: 94px; --logo-h: 62px; }
}

@supports (height: 100dvh) {
  @media (max-width: 760px) {
    .hero { min-height: 82dvh; }
  }
}

/* ============================================================
   Client intake wizard
   Four short steps instead of one long form. Same materials as
   the rest of the page: mono labels, square marks, 2px radii,
   tungsten only where it means "this is selected" or "this is
   the way forward".
   ============================================================ */

.contact-note {
  font-size: 0.92rem;
  color: var(--ink-soft);
  max-width: 40ch;
}

/* ---------- Progress ---------- */
.wizard-head {
  display: flex;
  /* Baseline, not centre. Centre made the row's height — set by the
     larger counter — push the step labels 3.4px down, which broke their
     line with the eyebrow in the column opposite. Baseline puts the
     labels and the counter on one line of type regardless of how tall
     either gets. */
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 6px;
  flex-wrap: wrap;
}

.wizard-steps {
  display: flex;
  align-items: center;
  gap: 18px;
  list-style: none;
  margin: 0;
  padding: 0;
  flex-wrap: wrap;
}

/* Where you are is carried by the type itself and a rule under the
   current step — no bullet beside every label. Four dots to say four
   steps, next to a counter already reading "01 / 04", was the same
   fact stated three ways. */
.wizard-steps li {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  /* Matched to the "Get in touch" eyebrow opposite: these two are the
     same kind of label — one names the section, one names the step —
     so they read at the same weight across the split. */
  font-size: 0.86rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  padding-bottom: 6px;
  border-bottom: 2px solid transparent;
  transition: color 0.2s ease, border-color 0.2s ease;
}

/* Behind you: legible, but no longer asking for attention. */
.wizard-steps li.is-done { color: var(--ink-soft); }

.wizard-steps li.is-current {
  color: var(--ink);
  border-bottom-color: var(--tally);
}

/* The counter is the one place in the form that answers "how much more
   of this is there", so it reads at a size worth glancing at rather
   than the size of the labels beside it. Tabular figures so 01 and 04
   occupy identical width and the row does not shift as it advances. */
.wizard-count {
  font-family: var(--font-mono);
  font-size: 1.15rem;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  margin: 0;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* Where you are is the live number; the total is the fixed part it is
   counting against, so only the first half takes full ink. */
#wizard-current {
  color: var(--ink);
  font-weight: 600;
}

/* ---------- Steps ---------- */
.contact-form.intake { gap: 22px; }

.step {
  border: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 20px;
  min-width: 0;
}

.step-legend {
  font-family: var(--font-display);
  font-size: 1.18rem;
  font-weight: 600;
  color: var(--ink);
  padding: 0;
  margin-bottom: 2px;
}

.field { display: grid; gap: 8px; min-width: 0; }

.field-row {
  display: grid;
  gap: 20px;
  min-width: 0;
}

@media (min-width: 640px) {
  .field-row { grid-template-columns: 1fr 1fr; }
}

/* Beats the base .contact-form label rule. */
.contact-form .field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-bottom: 0;
}

.contact-form .field-label em {
  font-style: normal;
  color: var(--tally-ink);
  margin-left: 6px;
  font-size: 0.92em;
}

.field-hint {
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--ink-soft);
  margin: 2px 0 0;
  max-width: 52ch;
}

/* The skip-ahead line at the foot of step 1. Deliberately quiet: it is
   an escape hatch, not a second call to action competing with Next. */
.step-skip {
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--ink-soft);
  margin: 4px 0 0;
}

/* A button that reads as a link. It has to be a <button> — a bare <a>
   with no href is not reachable by keyboard, and an href="#" would put
   a junk entry in the visitor's history on every click. */
.linklike {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--tally-ink);
  text-decoration-thickness: 1px;
}

.linklike:hover,
.linklike:focus-visible { color: var(--tally-ink); }

.field-error {
  display: none;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  color: #ff8a6a;
  margin: 0;
}
.field-error.is-shown { display: block; }

.contact-form input.is-invalid,
.contact-form select.is-invalid { border-color: #ff8a6a; }

/* ---------- Select ---------- */
.contact-form select {
  width: 100%;
  box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05);
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  color: var(--ink);
  padding: 12px 38px 12px 14px;
  font-family: var(--font-body);
  font-size: 0.95rem;
  border-radius: 2px;
  appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23a8a69f' stroke-width='1.6' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
}
.contact-form select:focus {
  outline: none;
  border-color: var(--tally);
  box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05),
              0 0 0 3px var(--tally-dim);
}

.contact-form input[type="date"],
.contact-form select { min-height: 45px; }

/* ---------- Option chips ---------- */
.opt-grid { display: grid; gap: 8px; }

@media (min-width: 480px) {
  .opt-grid-2 { grid-template-columns: 1fr 1fr; }
  .opt-grid-3 { grid-template-columns: repeat(3, 1fr); }
}

.contact-form .opt {
  display: block;
  position: relative;
  margin: 0;
  min-width: 0;
}

/* Kept in the layout for hit area and focus, visually replaced
   by the chip so the control matches the rest of the page. */
.contact-form .opt input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}

.contact-form .opt span {
  display: flex;
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: normal;
  align-items: center;
  min-height: 46px;
  padding: 10px 14px;
  background: var(--paper-sunk);
  border: 1px solid var(--line);
  border-radius: 2px;
  box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05);
  font-size: 0.92rem;
  color: var(--ink-soft);
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}


.contact-form .opt input:checked + span {
  border-color: var(--tally);
  background: var(--tally-dim);
  color: var(--ink);
}

.contact-form .opt input:focus-visible + span {
  outline: 2px solid var(--tally);
  outline-offset: 2px;
}

/* ---------- Actions ---------- */
.wizard-actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding-top: 4px;
}

.wizard-actions .btn { cursor: pointer; }

/* Type and a direction, not a filled block. A solid rectangle is the
   heaviest object the page owns, and spending that weight on "next"
   — a step in the middle of a form, not the thing being asked for —
   made the wizard feel like the loudest thing on the site.

   They stay <button> elements: still focusable, still in the tab order,
   still announced as buttons. Only the paint changes. */
/* Selectors carry .contact-form deliberately. The light-ground rules
   further down the file — .section--paper .btn-ghost — match these
   buttons at the same specificity and sit later, so a plain
   `.wizard-actions .btn` lost to them and the border came back. The
   extra class outranks them instead of relying on source order. */
.contact-form .wizard-actions .btn {
  background: transparent;
  border-color: transparent;
  border-width: 0;
  padding: 12px 0;
  /* No box to hit, so the target is built from padding alone. */
  min-height: 44px;
  font-size: 0.86rem;
  letter-spacing: 0.1em;
  gap: 10px;
}

.wizard-actions { gap: 30px; }

.contact-form .wizard-actions #wizard-next,
.contact-form .wizard-actions #wizard-send {
  color: var(--tally);
  font-weight: 600;
}
.contact-form .wizard-actions #wizard-back { color: var(--ink-soft); }

@media (hover: hover) {
  .contact-form .wizard-actions .btn:hover {
    background: transparent;
    border-color: transparent;
    color: var(--ink);
  }
}

/* Chevrons drawn with borders rather than a glyph: the mono face has no
   arrow worth using, and this needs no icon file. Only on the two
   navigation buttons — Send inquiry is a destination, not a direction. */
#wizard-next::after,
#wizard-back::before {
  content: "";
  width: 8px;
  height: 8px;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  flex: none;
  transition: transform 0.15s ease;
}

#wizard-next::after { transform: rotate(45deg) translate(-1px, 1px); }
#wizard-back::before { transform: rotate(-135deg) translate(-1px, 1px); }

@media (hover: hover) {
  #wizard-next:hover::after { transform: rotate(45deg) translate(1px, -1px); }
  #wizard-back:hover::before { transform: rotate(-135deg) translate(1px, -1px); }
}

.form-status {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.03em;
  color: var(--ink-soft);
  margin: 0;
  min-height: 1em;
}
.form-status.is-error { color: #ff8a6a; }

/* ---------- Confirmation ---------- */
.intake-done {
  border: 1px solid var(--tally);
  background: var(--paper-sunk);
  padding: 32px 28px;
  border-radius: 2px;
  align-self: start;
}

.intake-done h3 { font-size: 1.35rem; margin-bottom: 12px; }
.intake-done p { color: var(--ink-soft); margin-bottom: 10px; }
.intake-done p:last-child { margin-bottom: 0; }

.intake-done-direct a {
  color: var(--ink);
  border-bottom: 1px solid var(--tally);
}

/* ---------- Phone ---------- */
@media (max-width: 760px) {
  .wizard-head { gap: 10px; padding-bottom: 14px; }
  .wizard-steps { gap: 12px; }
  /* Was 0.66rem — 10.6px, the smallest type on the site, on the one
     screen where a visitor has to track where they are. Merged into
     this rule rather than added above it: an earlier declaration in
     the same block was being overridden by this one. */
  /* No size step on a phone: the eyebrow it is matched to does not
     take one either, and the two must stay level. */
  .wizard-steps li { gap: 6px; }
  .wizard-count { font-size: 1.05rem; }
  .field-hint, .step em { font-size: 0.78rem; }
  .step { gap: 18px; }
  .step-legend { font-size: 1.08rem; }
  .field-row { gap: 18px; }
  .contact-form select { font-size: 16px; }
  .contact-form .opt span { font-size: 0.95rem; }
  /* Text links, so they size to their words rather than stretching
     across the row the way filled buttons did. */
  .wizard-actions { gap: 24px; }
  .wizard-actions .btn { flex: 0 0 auto; }
  .intake-done { padding: 26px 20px; }
}

/* Spam trap — off-screen rather than display:none, which some bots skip. */
.honey {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Anchoring the wizard when a step changes. */
#intake-form { scroll-margin-top: calc(var(--header-h) + 20px); }

/* While a clip is on screen the headline stands down. Both want the
   middle of the frame, and measured at every width the play control's
   caption ran straight through "Put it everywhere." Offsetting one
   around the other works at one width and fails at the next; showing
   one thing at a time works everywhere.

   The transition lives on the .is-screening rule, not on .hero-seq, so
   it only applies on the way OUT. Coming back, the headline snaps to
   opacity 1 and its own keyframes do the entrance — the same rise it
   performs at first paint. Fading the container in as well would have
   layered a 0.45s fade under a 0.84s rise, and the W would have arrived
   twice. */
.hero.is-screening .hero-seq {
  opacity: 0;
  transform: translateY(-8px);
  pointer-events: none;
  transition: opacity 0.45s ease, transform 0.45s ease;
}

/* The subhead stays. It is the one line that says what the company does,
   and it should be readable on every slide rather than only on the
   stills — a visitor who lands on a clip slide would otherwise get a
   play button and no sentence. Only the headline steps aside, because
   only the headline was in the play control's way. */

@media (prefers-reduced-motion: reduce) {
  .hero.is-screening .hero-seq { transform: none; transition: none; }
}

/* ------------------------------------------------------------------
   Hero play panel

   Shown only while a video poster is the active slide. It sits on the
   right because the headline owns the left — the two never share space,
   so nothing has to be hidden or dimmed when a video comes round.

   It is a [data-reel] button, so the lightbox handler that already
   listens on those runs it with no new code. The slider rewrites its
   dataset as slides change; the handler reads that at click time.
   ------------------------------------------------------------------ */
/* Centred in the frame, not tucked to one side. A play mark in the
   middle of a picture is the one control everybody already recognises,
   and it is what tells a visitor the frame is a video at all — which
   the YouTube slide especially needed, since its poster is a
   photograph like any other. */
.hero-play {
  position: absolute;
  z-index: 3;
  left: 50%;
  top: 50%;
  /* Plate on top, ring under it — and the RING is what sits on the
     frame's centre line, not the control as a whole.

     The ring is the LAST child, so its bottom edge is the block's bottom
     edge. translateY(-100%) puts that bottom on the 50% line; adding
     half a ring back pushes it down until the ring's own centre lands
     there. Percentages in a transform resolve against the element's own
     size, so this stays exact whether a clip carries one credit or
     three — the plate above can weigh whatever it likes.

     --play-lift is added by main.js, and only when the control would
     otherwise reach the subhead. */
  transform: translate(-50%, calc(var(--ring) / -2 + var(--play-drop, 0px) - var(--play-lift, 0px)));
  display: grid;
  justify-items: center;
  gap: 16px;
  --ring: 88px;
  /* How far below the frame's centre line the ring sits. Dead centre
     read as too high once the plate moved above the ring: the block's
     visual weight is all in the plate, so the pair only looks settled
     when the ring is carried a little past the middle. This is where it
     wants to be — the clamp in main.js still decides where it ends up if
     the subhead or the masthead disagrees. */
  --play-drop: 0px;
  padding: 0;
  text-align: center;
  background: none;
  border: 0;
  cursor: pointer;
  animation: hero-play-in 460ms cubic-bezier(0.22, 1, 0.36, 1);

  /* An absolutely positioned box with left: 50% and no width shrinks to
     fit the space from that line to the right edge — half the viewport.
     On a phone that squeezed the plate to about 190px and wrapped the
     runtime onto its own line. max-content lets it size to its contents
     and overflow that half, and the max-width is what actually governs
     how wide it gets. */
  width: max-content;
  max-width: min(460px, 88vw);
}

/* Nothing behind the caption. No plate, no scrim, no shadow — the type
   sits on the frame and the frame shows through it.

   That is a deliberate trade, and it costs measured contrast: on a light
   passage the small mono lines drop under 4.5:1. If it ever needs
   fixing, the fix is a grade across the WHOLE frame — the .vignette
   element is already there for it — never a darkening shaped like the
   text. A shadow that follows the words is the thing that reads as a
   shadow. */

@keyframes hero-play-in {
  from { opacity: 0;
         transform: translate(-50%, calc(var(--ring) / -2 + var(--play-drop, 0px) - var(--play-lift, 0px) + 12px)); }
  to   { opacity: 1;
         transform: translate(-50%, calc(var(--ring) / -2 + var(--play-drop, 0px) - var(--play-lift, 0px))); }
}

/* Short phones. On a 667px-tall screen a clip carrying two credit lines
   leaves no room between the masthead and the subhead: the clamp in
   main.js ends up holding the control back from both, and the gap under
   the caption falls to 16px. The control has to be smaller there rather
   than better-placed — there is no placement that fits.

   Keyed to height, not width. It is the vertical room that runs out;
   a 375px-wide phone in a tall window is fine. */
@media (max-height: 700px) {
  /* .hero .hero-play, not .hero-play. A short phone matches this block
     AND the max-width: 760px block further down the file, both score one
     class, and source order hands it to the later one — which sets
     --ring back to 66px. This is the fifth time that has happened in
     this stylesheet; count the classes before changing the value. */
  .hero .hero-play { --ring: 52px; gap: 11px; }
  .hero .hero-play-mark::after {
    border-width: 9px 0 9px 14px;
    margin-left: 3px;
  }
  .hero .hero-play-title { font-size: 0.98rem; }
  .hero .hero-play-meta { font-size: 0.68rem; }
  .hero .hero-play-credits { margin-top: 6px; padding-top: 7px; }
  .hero .hero-credit { font-size: 0.76rem; }
}

/* A ring, so the triangle reads against a busy frame without needing a
   panel behind it.

   It sits BELOW the caption plate. The whole control is one button, so
   the plate has always been clickable too — but it does not look like a
   button, and a ring above a block of text asks the pointer to travel
   across that text to reach the thing that plays. Underneath, the
   approach is clear, and the block falls into reading order: what the
   clip is, then the way in. */
.hero-play-mark {
  width: var(--ring);
  height: var(--ring);
  display: grid;
  place-items: center;
  border: 2px solid rgba(251, 251, 249, 0.9);
  border-radius: 50%;
  background: rgba(10, 10, 9, 0.42);
  transition: background 0.25s ease, border-color 0.25s ease, transform 0.25s ease;
}

.hero-play-mark::after {
  content: "";
  width: 0;
  height: 0;
  margin-left: 6px;
  border-style: solid;
  border-width: 15px 0 15px 25px;
  border-color: transparent transparent transparent var(--ink);
  transition: border-left-color 0.25s ease;
}

/* The caption sits on the frame with nothing behind it — no plate, no
   scrim, no shadow. */
.hero-play-body {
  display: grid;
  gap: 5px;
  /* Centred under the ring. Everything else on the site ranges left, so
     this is the one exception — but the ring is a centred object and a
     left-ranged block hanging off a circle has no edge to hang from.
     Centred type under a centred mark at least reads as one unit. */
  justify-items: center;
  text-align: center;
  width: 100%;
  /* Only enough to hold the block clear of the ring above it. There is
     no box edge left for padding to sit inside. */
  padding: 2px 0 0;
  background: none;
  border: 0;
}

.hero-play-kicker {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* White, not tally. Tally needs a ground of about rgb(20) to clear
     4.5:1, which is a plate — no photograph and no grade gets there. The
     accent survives as the dot below, and a dot is decoration rather
     than text, so it carries no contrast requirement. */
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 8px;
}

.hero-play-kicker::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--tally);
  flex: none;
}

.hero-play-title {
  font-family: var(--font-display);
  font-size: 1.22rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: var(--ink);
}

.hero-play-meta {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  /* Everything on this control is --ink now. --ink-soft was chosen when
     there was a plate under it with a known ground; on a photograph it
     measures about 3:1 even after the grade. The hierarchy is carried by
     size, case and face instead of by weight of colour — which is how a
     title card does it anyway. */
  color: var(--ink);
}

/* A dot between the tags and the runtime rather than a wider gap. Two
   mono strings with only space between them read as one broken string;
   a mark says they are two different facts. */
.hero-play-meta > * + *::before {
  content: "";
  display: inline-block;
  width: 3px;
  height: 3px;
  margin-right: 10px;
  border-radius: 50%;
  background: var(--tally);
  vertical-align: 0.25em;
}

.hero-play-time { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The tag list breaks between tags if it has to, never inside one. */
.hero-play-tags { word-spacing: 0.2em; }

/* ------------------------------------------------------------------
   Collaboration credits

   A live show is rarely one house's work. Where a clip is somebody
   else's production that we streamed — or ours that somebody else
   streamed — the caption says so, in the shape a film credit uses:
   role small and faint, house in ink. pages.py refuses to build a clip
   that names a partner without also naming what we did.

   The rule above them separates the credit from the clip's own facts:
   title and runtime describe the video, credits describe who made it.
   ------------------------------------------------------------------ */
.hero-play-credits {
  display: grid;
  gap: 3px;
  justify-items: center;
  width: 100%;
  margin-top: 9px;
  padding-top: 10px;
  /* The rule divides the clip's own facts from who made it: title and
     runtime describe the video, the credits describe the houses. */
  border-top: 1px solid rgba(251, 251, 249, 0.22);
}

.hero-credit {
  display: flex;
  align-items: baseline;
  justify-content: center;
  /* "Technical Director" and "George E. Kennedy, Jr." do not fit on one
     line on a phone. Wrapping puts the name under its role, which is
     how a credit roll sets it anyway. */
  flex-wrap: wrap;
  gap: 2px 7px;
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--ink);
}

/* The role never breaks across lines — "STREAMED / BY" stacked above
   the house it belongs to read as two separate credits. */
.hero-credit i,
.reel-slide-credit i {
  white-space: nowrap;
  opacity: 0.82;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
}

/* The panel credit keeps the quieter grey: it sits on a known dark
   ground there, not on a photograph. */
.reel-slide-credit i { color: var(--ink-soft); opacity: 1; }

/* Same credit, quieter setting. In the panel the clip already has a
   caption bar of its own, so the credits join it as another line rather
   than getting a rule and a block of their own. */
.reel-slide-credit {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin-top: 4px;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--ink-soft);
}

.reel-slide-credit:first-of-type { margin-top: 8px; }

@media (hover: hover) {
  .hero-play:hover .hero-play-title { color: var(--tally); }

  .hero-play:hover .hero-play-mark {
    background: rgba(10, 10, 9, 0.6);
    border-color: var(--tally);
    transform: scale(1.06);
  }
  .hero-play:hover .hero-play-mark::after { border-left-color: var(--tally); }
}

/* On a phone the headline fills the frame, so the panel goes to the
   foot of the hero as a full-width bar rather than floating beside
   copy it would land on top of. */
@media (max-width: 899px) {
  /* Still centred, just smaller — the headline sits lower on a phone so
     the middle of the frame is free. */
  .hero-play { gap: 12px; }
  .hero-play-title { font-size: 1.05rem; }
  .hero-play-body { padding: 2px 0 0; }
  /* One number: the ring's size and the offset that centres it are the
     same token, so they cannot drift apart. */
  .hero-play { --ring: 66px; --play-drop: 0px; }
  .hero-play-mark::after { border-width: 11px 0 11px 18px; margin-left: 4px; }
}

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

/* ============================================================
   Hero sequence
   One "We", held, and four things it does. The subject is stated
   once at display scale and every beat below inherits it, so the
   word repeats itself in the reading without being reprinted —
   and each beat is a verb rather than a sentence.

   Dropping the repeated "We " also cut the longest beat from 25
   characters to 18, which is what lets every one of them hold a
   single line at far more widths than the old sentences did.
   ============================================================ */

.hero h1.hero-seq {
  /* The beats sit smaller than the old sentences did. They are
     three-word verb phrases now, not clauses, and the sketch reads
     with the beats clearly subordinate to the word above them. */
  font-size: clamp(1.6rem, 5.9vw, 3.5rem);
  line-height: 1.05;
  max-width: none;
  /* The same token as body copy on every dark section, rather than a
     hardcoded colour of its own — so the hero and the page read as one
     white instead of two that nearly match. The deepened scrim behind
     it is what pays for the extra brightness. */
  color: var(--ink);
}

/* The opening beat. Set 2x the others, the relationship in the sketch —
   dominant enough to read as the subject the whole list hangs off, not
   merely a larger first line. It rises first and clears with the rest,
   so each cycle starts from an empty frame. */
.seq-we {
  display: block;
  font-size: 2em;
  animation: seq-beat 12s ease-in-out infinite;
  animation-delay: 0s;
  opacity: 0;
  line-height: 0.92;
  letter-spacing: -0.045em;
  margin-bottom: 0.06em;
}

/* Ranged in off the W, so the beats read as subordinate to it rather
   than as a continuation of the same column. */
.seq-lines {
  display: block;
  padding-left: 0.28em;
}

.seq-line {
  display: block;
  white-space: normal;
  /* Every line holds its space at all times — only opacity and
     transform animate, so the hero never changes height and the
     subhead below it never shifts. */
  animation: seq-beat 12s ease-in-out infinite;
  opacity: 0;
}

/* Scaled with the cycle every time it moves (20s -> 16s -> 12s, see THE
   ONE DIAL below) by whatever factor the cycle moved, so each delay
   stays the same fraction of the loop it always was — 2.5% / 5% / 7.5%
   / 10% — and the stagger reads identically, just compressed.
   Originally tightened from 0.75s: at 0.75s (on the old 20s cycle) the
   last beat was still leaving 3.8s after the W, which put it well into
   the following slide. At 0.5s the whole sequence cleared within 2.6s
   of the slide change and read as one movement rather than a queue —
   that proportion is what 0.3s now preserves on the current cycle. */
.seq-line:nth-child(1) { animation-delay: 0.3s; }
.seq-line:nth-child(2) { animation-delay: 0.6s; }
.seq-line:nth-child(3) { animation-delay: 0.9s; }
.seq-line:nth-child(4) { animation-delay: 1.2s; }

.seq-line-accent { color: var(--tally); }

/* THE ONE DIAL: the duration on .seq-we and .seq-line above. Everything
   here is a percentage of it, and the percentages are recalculated for
   each new duration rather than left alone — leaving them would stretch
   the build and the hold too, and the beats would arrive in slow
   motion. Only the rest at the end changes.

   THIS DURATION AND THE SLIDER'S HOLD ARE ONE NUMBER, NOT TWO. The hold
   is 6s (data-hold in pages.py); the cycle is 12s, which is two slides,
   and a video slide at two holds is exactly one cycle. Left free-running
   at a mismatched ratio they drift, and the headline can land on a
   fresh slide already mid-cycle and start fading a second later — this
   was 10s/20s originally, then 8s/16s, and every number below has
   scaled down each time to keep it locked.

   The hold percentage is pinned to the slide change, not to a number
   that looked right. At 12s:
     0.00s  rises
     0.50s  fully up
     6.00s  still up  <- 50%, exactly when the slide changes
     6.66s  the W has cleared
     7.86s  the last beat has cleared (1.2s delay)
    12.00s  starts again
   So nothing fades in the middle of a bar. The headline holds for the
   whole of its slide and leaves on the cut, and the slide that follows
   is the rest — a little over four empty seconds, which is what the
   subhead underneath needs to get read. Sequence on one slide, silence
   on the next.

   Tried along the way, back when the cycle was 20s — 12s gave no gap at
   all, 18s about six seconds, 24s about twelve, which was too long a
   wait for the headline. (That 12s was a candidate CYCLE length being
   tested against a fixed hold, not the current 12s cycle above — a
   coincidence of the same number, not the same thing.) */
@keyframes seq-beat {
  0%     { opacity: 0; transform: translate3d(0, 16px, 0); }
  4.2%   { opacity: 1; transform: translate3d(0, 0, 0); }
  50%    { opacity: 1; transform: translate3d(0, 0, 0); }
  55.5%  { opacity: 0; transform: translate3d(0, -12px, 0); }
  100%   { opacity: 0; transform: translate3d(0, -12px, 0); }
}

/* Motion is the whole point here, so the reduced-motion fallback is
   the finished sentence rather than a permanently blank hero. */
@media (prefers-reduced-motion: reduce) {
  .seq-line,
  .seq-we {
    animation: none;
    opacity: 1;
    transform: none;
  }
}


/* ============================================================
   Hover states
   Scoped to real pointers. On touch, plain :hover fakes a
   hover-in with no hover-out and the state sticks — the Site
   Bible logged this on gekjr.pro; this build had inherited it
   on every one of these rules.
   ============================================================ */

@media (hover: hover) {
  .clients-grid a:hover { color: var(--tally); }
  .clients-grid .is-pinned:hover { color: #ef5c3d; }
  .crew a:hover { border-bottom-color: var(--tally); }
  .clients summary:hover .clients-cue { border-color: var(--ink); }
  .header-nav a:hover { color: var(--ink); }
  .header-cta:hover { background: var(--tally-dim); }
  .btn-primary:hover { background: #ef5c3d; }
  .btn-ghost:hover { border-color: var(--tally); color: var(--tally); }
  .see-all:hover { color: var(--tally); }
  .contact-direct a:hover { border-bottom-color: var(--tally); color: var(--tally); }
  .footer-social a:hover { color: var(--tally); }
  .contact-form .opt:hover span { border-color: var(--ink-soft); color: var(--ink); }
}

/* ============================================================
   Who we are — statement, then the people
   ============================================================ */

.about-grid {
  display: grid;
  gap: 44px;
}

@media (min-width: 900px) {
  .about-grid { grid-template-columns: 1fr 1.15fr; gap: 60px; }
}

/* The people. One row per person: their photograph, then their entry.

   This was a single portrait beside the whole list, which was right
   while the list was one person. With two it belonged to neither — a
   face next to a name that is not theirs reads as a mistake, and it is
   one. */
.crew {
  display: grid;
  gap: 40px;
}

.crew-row {
  display: grid;
  gap: 18px;
  align-items: start;
}

.crew-portrait {
  margin: 0;
  /* 4:5 for both, so two different source ratios present as one set.
     Per-person object-position rides on the element — an nth-child rule
     would point at the wrong person the moment somebody is inserted
     above, which is exactly what happened to the hero slides. */
  aspect-ratio: 4 / 5;
  max-width: 300px;
  overflow: hidden;
  background: var(--paper-sunk);
}

.crew-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 42%;
  display: block;
}

/* The call-sheet code and the title on one line: with a face above the
   name, three stacked labels for one person was two too many. */
.crew-tag {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 6px;
  display: flex;
  gap: 12px;
  align-items: baseline;
}

.crew-tag i {
  font-style: normal;
  color: var(--tally-ink);
  font-weight: 500;
}

.crew-text h3 {
  font-size: clamp(1.25rem, 2vw, 1.5rem);
  margin-bottom: 8px;
}

.crew-text p:not(.crew-tag) {
  font-size: 0.95rem;
  color: var(--ink-soft);
  line-height: 1.6;
}

/* Was .rd-body a, which stopped matching when the crew left the rundown
   markup — the gekjr.pro link went back to looking like plain text.
   Same treatment as .reel-credit a: one way of saying "this goes
   somewhere" across the site. */
.crew-text a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--tally);
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}

@media (hover: hover) {
  .crew-text a:hover { color: var(--tally); }
}

@media (min-width: 700px) {
  .crew-row {
    grid-template-columns: 168px 1fr;
    gap: 30px;
  }
  .crew-portrait { max-width: none; }
}

.statement {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin-bottom: 18px;
  max-width: 20ch;
}

.about-lead p:last-child {
  color: var(--ink-soft);
  max-width: 46ch;
  margin-bottom: 0;
}

/* ============================================================
   Clients & Collaborators
   A disclosure rather than a permanent wall: the names matter to the
   people checking, and take space from everyone else. Names, not
   logos — displaying another company's mark implies a relationship
   that a project credit doesn't.
   ============================================================ */

.clients summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 12px;
  /* The whole disclosure control was 26px tall. It is the one thing on
     the About page a visitor is meant to tap. */
  min-height: 44px;
}
.clients summary::-webkit-details-marker { display: none; }



.clients-cue {
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--ink-soft);
  border-bottom: 2px solid var(--ink-soft);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.2s ease;
  flex: none;
}
.clients[open] .clients-cue { transform: rotate(-135deg) translate(-3px, -3px); }

/* The two collaborators stand outside the disclosure: they are the
   strongest thing in this section and were hidden behind a click. The
   heading now reads as a table of contents for what follows — the red
   word, then the red names, then a control labelled "Clients" holding
   the other twenty-two. */
.clients-head {
  font-size: clamp(1.15rem, 1.8vw, 1.35rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0 0 4px;
}

/* Two names do not want four columns. Doubled class deliberately: the
   base .clients-grid column counts sit in media queries further down
   the file, so a single-class modifier lost to them and these two names
   were being squeezed into 70px columns and wrapping. */
.clients-grid.clients-grid--collab a { font-weight: 600; }

.clients-grid.clients-grid--collab {
  columns: 2;
  column-gap: 32px;
  max-width: 380px;
  margin-top: 18px;
}

.clients { margin-top: 26px; }

/* The summary is a control now, not a heading, so it reads at label
   scale — the same mono the panel labels use. */
.clients-more {
  font-family: var(--font-mono);
  font-size: 0.86rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.clients-more b {
  color: var(--ink-faint);
  font-weight: 400;
  margin-left: 8px;
  font-variant-numeric: tabular-nums;
}

@media (hover: hover) {
  .clients summary:hover .clients-more { color: var(--ink); }
}

/* A nudge on the cue, at every width. It used to be phones only, on the
   reasoning that a pointer finds this by hovering — but the control is
   now a small "CLIENTS 22" label rather than the section heading, so it
   wants the same prompt on a desktop.

   Three bounces, not a loop. Something that moves forever stops being a
   prompt and becomes a tic, and it competes with the reel deck cycling
   further up the page. It only runs while the list is shut, and only
   once it has been scrolled to. */
@keyframes cue-nudge {
  0%, 55%, 100% { transform: rotate(45deg) translate(-2px, -2px); }
  25%           { transform: rotate(45deg) translate(2px, 2px); }
  40%           { transform: rotate(45deg) translate(0, 0); }
}

.clients:not([open]) .clients-cue.is-nudging {
  animation: cue-nudge 1.5s ease-in-out 3;
}

/* The selector has to match the one above, not just name the class.
   `.clients:not([open]) .clients-cue.is-nudging` scores four classes;
   a bare `.clients-cue.is-nudging` scores two and lost, so the nudge
   kept running for people who had asked for no motion. */
@media (prefers-reduced-motion: reduce) {
  .clients:not([open]) .clients-cue.is-nudging { animation: none; }
}

.clients summary:focus-visible {
  outline: 2px solid var(--tally);
  outline-offset: 4px;
}

/* Ruled rows rather than a filled grid: an odd number of names would
   otherwise leave a lit, empty cell at the end of the last row. */
/* A roster, not a run-on. gekjr.pro sets the same names inline with
   middots — right there, where it is one line of a personal page. Here
   the list is the credential, so it gets its own shape: one name per
   row, hairline-ruled, flowing down columns like a call sheet.

   CSS columns rather than a grid: the list grows as work comes in, and
   columns reflow a changing count without anyone editing a template. */
.clients-grid {
  list-style: none;
  margin: 30px 0 0;
  padding: 0;
  /* Mobile first. The base used to be 4, so phones fell straight
     through to it and got four columns of squeezed names. */
  columns: 1;
  column-gap: 44px;
}

.clients-grid li {
  /* Without this a name can be split across the column break. */
  break-inside: avoid;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-size: 0.92rem;
  line-height: 1.4;
  color: var(--ink-soft);
  /* 11px, not 9: at 9 the row came out 40px and every linked name was
     a 39px tap target, under the 44px floor. */
  padding: 11px 0;
}

/* A linked name hands its padding to the anchor, so the whole row is
   the tap target rather than just the glyphs. */
.clients-grid li:has(a) { padding: 0; }

/* A link means a collaborator. Only the two firms Magek actually works
   alongside carry one, so red and clickable now say the same thing and
   the list needs no extra class to tell them apart. Everyone else is a
   client: named, credited, not linked. */
.clients-grid a {
  display: block;
  /* 12, where a plain row uses 11: the row carries a 1px rule that the
     anchor sits inside, so matching the padding left the link 43px —
     one pixel under the floor. */
  padding: 12px 0;
  color: var(--tally-ink);
}

/* The heading is the key. "Collaborators" carries the same colour the
   collaborator names do, so a visitor is told what the accent means
   instead of having to guess — which is work no colour can do alone.

   Same token deliberately, not the brighter --tally. At 24px the
   heading could carry the full brand red (3.11:1 clears the 3:1 large
   text needs), but then the key and the thing it keys would be two
   different reds, and the connection is the entire point. */
.clients-head .is-collab { color: var(--tally-ink); }




@media (min-width: 560px)  { .clients-grid { columns: 2; } }
@media (min-width: 900px)  { .clients-grid { columns: 3; } }
@media (min-width: 1180px) { .clients-grid { columns: 4; } }

/* Capability strip under an editorial-list entry — the Bible's cert-strip
   idea: mono, dotted, scannable, no boxes. */
.editorial-list .spec {
  font-family: var(--font-mono);
  border-top: none;
  font-size: 0.76rem;
  line-height: 1.9;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  max-width: 68ch;
  margin: 18px 0 0;
}

/* Mission sits between the statement and the body copy — labelled, so the
   founding year and the mission read as a record rather than a sentence. */
.mission {
  color: var(--ink);
  font-size: clamp(1.12rem, 1.7vw, 1.35rem);
  max-width: 44ch;
  margin-bottom: 20px;
}

.mission-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--tally-ink);
  margin-bottom: 8px;
}

/* ============================================================
   Split-hero — interior page header
   Photo beside eyebrow/title/subtitle, stacking below 900px.
   The homepage keeps the full-bleed cinematic hero; interior
   pages get this quieter editorial version.
   ============================================================ */

/* The header is fixed and only the homepage hero is meant to sit under
   it. Interior pages clear it. */
.split-hero {
  padding: calc(var(--header-h) + 44px) 0 72px;
}

.split-hero-grid {
  display: grid;
  gap: 32px;
  align-items: center;
}

@media (min-width: 900px) {
  .split-hero { padding: calc(var(--header-h) + 52px) 0 56px; }
  .split-hero-grid { grid-template-columns: 1fr 1fr; gap: 56px; }
}

.split-hero h1 {
  font-size: clamp(2.3rem, 5.2vw, 4rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.02;
  max-width: 14ch;
  margin-bottom: 18px;
}

.split-hero-sub {
  color: var(--ink-soft);
  font-size: clamp(1.08rem, 1.5vw, 1.28rem);
  max-width: 46ch;
  margin-bottom: 0;
}

.split-hero-media {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--paper-sunk);
}

.split-hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 900px) {
  .split-hero-media { aspect-ratio: 5 / 4; }
}

/* ============================================================
   Closing CTA band
   ============================================================ */

.cta-band {
  background: var(--paper-raised);
  text-align: left;
}

/* Same sentence as the contact section's heading, so it takes the same
   scale. It was running a step larger here, which put a CTA band above
   the page title it sat under. */
.cta-band h2 {
  font-size: clamp(2.2rem, 5.6vw, 3.8rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.03;
  max-width: 15ch;
  margin-bottom: 20px;
}

/* The contact heading carries the same sentence but sits in a narrower
   grid column beside the form, so an identical measure still broke it
   differently. Above 900px the band needs one character less to land on
   the same three lines; below it, 15ch already matches. Measured, not
   guessed — and split on the breakpoint the layout already uses rather
   than a new one invented for a line break. */
@media (min-width: 900px) {
  .cta-band h2 { max-width: 14ch; }
}

.cta-band p {
  color: var(--ink-soft);
  font-size: clamp(1.05rem, 1.5vw, 1.24rem);
  max-width: 44ch;
  margin-bottom: 26px;
}

/* Current page marker, header and footer alike. */
/* Current page reads by weight and colour, not a rule under it. */
.header-nav a[aria-current="page"] {
  color: var(--ink);
}

@media (min-width: 640px) {
  .footer-grid { flex-wrap: wrap; }
}

/* The contact page's lead column carries the page h1, so it takes the
   heading scale the split-hero would otherwise have set. */
.contact-page { padding-top: calc(var(--header-h) + 40px); }

.contact-page .contact-lead h1 {
  font-size: clamp(2.2rem, 5.6vw, 3.8rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.03;
  max-width: 15ch;
  margin-bottom: 18px;
}

.header-cta[aria-current="page"] {
  background: var(--tally-dim);
}


/* ============================================================
   Cinematic frame
   Photographs are treated as footage, not illustrations: scope
   ratio, grain and a vignette.

   A slate line used to sit at the foot of the hero reading
   "Magek Filmworks · Washington, D.C. · Since 1998". It was
   removed: the panel directly beneath it opens with "Washington,
   D.C. Metro Area and beyond, since 1998" — the two were touching,
   with no gap at all — and the company name is the logo in the
   header above. Three facts, all repeated within one screen.
   ============================================================ */

/* Grain sits over the picture, under the type. */
.hero::before,
.split-hero-media::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background-image: var(--grain);
  background-size: 96px 96px;
  opacity: 0.16;
  mix-blend-mode: overlay;
  pointer-events: none;
}

.hero { position: relative; }

/* Vignette: the falloff of a real lens, not a drop shadow. */
.hero .vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background-color: transparent;
  background-image: radial-gradient(118% 90% at 50% 44%,
    rgba(0, 0, 0, 0) 34%,
    rgba(0, 0, 0, 0.38) 74%,
    rgba(0, 0, 0, 0.68) 100%);
  transition: background-color 0.45s ease;
}

/* The grade. While a clip is being offered the whole frame steps down a
   third of a stop — every pixel of it, evenly, which is why it reads as
   the shot being darker rather than as something behind the words.

   It has to be a flat colour and not the vignette itself: the vignette
   is fully transparent out to 34%, which is exactly where the caption
   sits. A vignette darkens the edges; the caption needs the middle.

   Only while screening, so the photographs keep their contrast when
   nothing is being offered over them. */
/* 0.26, not 0.34. Swept against contrast on all three clip slides: the
   AAMC watercolour is the binding one — a bright frame where the credit
   line is the first thing to fail. It clears 4.5:1 by 2.0 here and fails
   at 0.20, so this is the lightest grade with real margin left, not the
   lightest that squeaks past.

   The frame is ~12% brighter than it was. If it ever needs to come up
   further, the lever is the credit role's opacity (0.82) rather than
   this number: the roles are what fail first, and they are dimmed on
   purpose rather than by necessity. */
.hero.is-screening .vignette { background-color: rgba(4, 4, 4, 0.26); }

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

/* Scope ratio on wide screens. 2.39:1 is the frame a feature is shot
   in; the hero holds it rather than filling an arbitrary viewport
   fraction. */
@media (min-width: 900px) {
  .hero {
    /* Scope ratio expressed as a height, not aspect-ratio. With
       aspect-ratio, min-height fed back into the width and the box grew
       to 1510px on a 1440px screen — wider than the viewport, which
       pushed the right-hand slate off the edge. Deriving height from
       viewport width keeps the width authoritative. */
    height: clamp(calc(var(--header-h) + 712px), calc(100vw / 2.39), 86vh);
    min-height: 0;
  }
}


/* Interior frames get the same treatment, quieter. */
.split-hero-media { position: relative; }
.split-hero-media::before { opacity: 0.13; }

@media (prefers-reduced-motion: reduce) {
  .hero::before, .split-hero-media::before { opacity: 0.1; }
}

/* ============================================================
   Centred lower sections
   Everything below the frame is centred on the page axis. The
   rundown gives up its three-column grid to do it — the ID and
   meta stack above the name instead of sitting beside it — so
   the structure survives as a label group rather than columns.

   Form controls stay left-aligned inside the centred column:
   centred field labels force the eye to re-find the start of
   every line, which measurably slows form completion.
   ============================================================ */

/* Ranged left, like everything on the homepage. Centred, the crew read
   as a list of awards rather than people, and every line made the eye
   hunt for a new starting point. */
.rundown-row,
.rundown-tight .rundown-row {
  grid-template-columns: 1fr;
  justify-items: start;
  text-align: left;
  gap: 4px;
}

.rd-id { padding-top: 0; }
.rd-meta { padding-top: 2px; margin-bottom: 8px; }
.rd-body { display: grid; justify-items: start; }
.rd-body p,
.rd-systems { margin-inline: 0; }

.testimonial { text-align: center; }
.testimonial p { margin-inline: auto; }

/* About was the last centred page on the site. Its heading, its crew,
   its client list and its closing call to action all ranged to a
   different axis than the homepage, which is most of why the two never
   read as one site. They all range left now. */
.about-lead { text-align: left; }
.about-lead .mission,
.about-lead p { margin-inline: 0; }

.clients summary { justify-content: flex-start; }
.clients-grid { text-align: left; }

.cta-band { text-align: left; }
.cta-band h2,
.cta-band p { margin-inline: 0; }

/* The lead column and the form are two columns of one row, so their
   first lines should start together.

   They did not. .eyebrow is inline-flex, which puts it inside a line
   box and hands it half-leading above — 5px of it — so the left column
   began below the step strip it sits beside. Block-level flex has no
   line box, and fit-content keeps it sized to its text rather than
   stretching the column. Sized to match the strip's 24px row so the
   two align as bands, not just as top edges. */
#contact .contact-lead .eyebrow,
.contact-page .contact-lead .eyebrow {
  display: flex;
  width: fit-content;
  /* Same box a step label builds: text, then 6px, then a 2px rule. The
     rule is transparent here — there is nothing to mark — but it keeps
     the two labels the same shape, which is what puts their type on one
     line across the split. */
  padding-bottom: 6px;
  border-bottom: 2px solid transparent;
  /* Was 11.5px, the same size as a field caption. This is the label on
     the whole section. */
  font-size: 0.86rem;
  letter-spacing: 0.1em;
}

/* ------------------------------------------------------------------
   Contact map

   The frame carries data-src, not src, and js/main.js fills it in when
   the map is about to come into view. loading="lazy" alone was measured
   and does not work here: Chromium fetched the map on page load anyway,
   2818px below the fold. Not the click-to-load treatment the reel gets
   — a map that needs a click to become a map is a worse map — but a
   visitor who never scrolls this far never hands Google a request.

   The query is coordinates with a label — 38.9072,-77.0369
   (Washington, D.C.) — not a place name. A place-name query centres
   the map on the region and may only outline it; coordinates always
   drop a marker, which is the part that was asked for.

   Greyscaled to sit with the photography, which is all deep-contrast
   black and white. A full-colour Google map in the middle of this
   palette reads as something pasted in from another site.
   ------------------------------------------------------------------ */
.contact-map {
  margin-top: 28px;
  aspect-ratio: 16 / 10;
  /* Fills the column rather than a fixed 460px. The hardcoded width
     happened to land 3px short of the column at 1440 and 40px wide of
     the paragraph measure at 1180 — a ragged right edge that changed
     with the viewport. Matching the column means it lines up with the
     heading above it, which is the element that also spans the column;
     the paragraphs sit inside that on their own reading measure, as
     prose should. */
  width: 100%;
  border: 1px solid var(--line);
  background: var(--paper-sunk);
  overflow: hidden;
}

.contact-map iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
  filter: grayscale(1) contrast(1.05);
}

@media (max-width: 899px) {
  .contact-map { aspect-ratio: 16 / 9; margin-bottom: 4px; }
}

/* ------------------------------------------------------------------
   Prose column

   For pages that are only reading: one measure, generous leading, and
   nothing else competing. Capped at 68 characters — long enough not to
   feel choppy, short enough that the eye finds the next line without
   hunting.
   ------------------------------------------------------------------ */
.prose { max-width: 68ch; }

.prose h1 {
  font-size: clamp(2.2rem, 5.6vw, 3.5rem);
  font-weight: 700;
  letter-spacing: -0.03em;
  line-height: 1.03;
  max-width: 15ch;
  margin-bottom: 18px;
}

.prose h2 {
  font-size: clamp(1.15rem, 1.8vw, 1.35rem);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 44px 0 12px;
}

/* One level under h2, for a page like the FAQ that groups terms inside a
   section rather than giving each term its own h2 — a term name should
   read as a term, not as a new topic the way an h2 does. */
.prose h3 {
  font-size: 1rem;
  font-weight: 600;
  margin: 24px 0 6px;
}

.prose p { margin: 0 0 16px; color: var(--ink-soft); }
.prose .prose-lead { font-size: 1.2rem; color: var(--ink); margin-bottom: 8px; }

.prose ul {
  margin: 0 0 16px;
  padding-left: 20px;
  color: var(--ink-soft);
}
.prose li { margin-bottom: 10px; }
.prose li::marker { color: var(--tally-ink); }
.prose b { color: var(--ink); font-weight: 600; }

.prose a {
  color: var(--tally-ink);
  border-bottom: 1px solid currentColor;
}

.prose .prose-meta {
  margin-top: 44px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  font-size: 0.94rem;
  color: var(--ink-faint);
}

@media (hover: hover) {
  .prose a:hover { color: var(--ink); }
}

/* The contact column stays left-ranged: it is a block of reading copy
   sitting beside a form, and the form's fields start on the left edge.
   Centring it broke the shared left margin between the two. */
.contact-lead { text-align: left; }
.contact-lead h1,
.contact-lead h2,
.contact-lead p { margin-inline: 0; }
.contact-direct { align-items: flex-start; }

.contact-page .contact-lead h1 { max-width: 20ch; }

/* The intake form itself keeps its reading order. */
.contact-form { text-align: left; }
/* The form is a left-ranged column: its step strip and its buttons
   belong on the same edge as its fields, not floating in the middle.
   Centred, the labels started 61px right of every other thing in the
   column and 61px right of the heading opposite them. */
.wizard-head { justify-content: flex-start; }
.wizard-actions { justify-content: flex-start; }
.intake-done { text-align: center; }

/* The larger labels need the gaps back a little in the narrowest
   desktop band: at 900 the column is ~365px and the counter was
   dropping to a second line. 13/12 is the loosest pair that holds one
   line all the way down to the breakpoint — measured, not guessed. */
@media (min-width: 900px) and (max-width: 1023px) {
  .wizard-steps { gap: 13px; }
  .wizard-head { gap: 12px; }
}

/* On a phone the step strip and the counter were competing for one line. */
@media (max-width: 640px) {
  .wizard-head {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
  .wizard-steps { justify-content: flex-start; }
}


/* .section-head still centres its own column; the CTA band does not. */
.section-head h2 { margin-inline: auto; }

@media (max-width: 640px) {
  /* .cta-band h2 is deliberately not in here: it carries the same
     sentence as the contact heading, so it keeps the same measure and
     wraps the same way. */
  .section-head h2 { max-width: 14ch; }
}

/* Three quotes, three alignments: the row ranges left, centre, right
   across the page. The middle one holds the axis the rest of the
   section is centred on. */
.testimonial:first-child {
  text-align: left;
}
.testimonial:first-child p,
.testimonial:first-child cite { margin-inline: 0; }

.testimonial:last-child {
  text-align: right;
}
.testimonial:last-child p {
  margin-inline-start: auto;
  margin-inline-end: 0;
}

/* ============================================================
   Tonal segmentation
   Sections carry their own palette by re-declaring the tokens
   rather than overriding component styles. Every component
   already reads --paper / --ink / --line, so a section that
   swaps those swaps everything inside it — buttons, inputs,
   quotes, labels — with no per-component light variants.

   The light values are gekjr.pro's, exactly: this site's dark
   ground is that site's ink, and its light sections are that
   site's paper. The two remain inverses, just interleaved.
   ============================================================ */

.section--paper,
.section--grey {
  /* --ink-faint at #625f5a clears 4.5:1 on both light grounds, and the
     accent darkens to #b23219 for type — buttons and marks keep the
     exact logo red, which is legible as a fill but not as small text. */
  --ink-faint: #625f5a;
  --tally-ink: #b23219;
  --paper-raised: #f8f7f3;
  --paper-sunk: #e9e8e2;
  --ink: #131313;
  --ink-soft: #4a4a48;
  --ink-faint: #7a7873;
  --line: #e2e0da;
  --line-soft: #ecebe6;
  --tally-dim: rgba(223, 73, 41, 0.12);
  background: var(--paper);
  color: var(--ink);
}

.section--paper { --paper: #f2f1ec; }
.section--grey  { --paper: #e2e0da; --paper-sunk: #eeece7; --line: #cfcdc6; }

/* True black, matching .panel--void so the page closes on the same
   ground the checkerboard uses.

   The one inversion that matters: on the page's own #131313 a form
   field recedes into --paper-sunk, which is darker than its
   surroundings. Against black there is nothing darker to recede into,
   so the field has to lift instead — --paper-sunk goes lighter than
   the ground it sits on, and the border comes up with it. Keep the
   name; the role is what flips. */
.section--void {
  --paper: #000;
  --paper-sunk: #141413;
  --paper-raised: #1a1a18;
  --line: #302f2c;
  --line-soft: #201f1d;
  background: var(--paper);
  color: var(--ink);
}

/* ------------------------------------------------------------------
   Split ground: reading copy on paper, the form on black.

   The boundary sits at 50% because that is where the checkerboard's
   divider sits — the panels run a 1fr / 1fr grid the full width of the
   page, so their seam is exactly half. This section is directly below
   them, and any other value leaves two vertical edges a few pixels
   apart down the same page, which reads as a mistake rather than a
   choice. It was previously 50vw - 23px, centred in the grid gutter,
   and was 22px out from the panels at every width.

   50% still lands inside that gutter: the lead column ends before it
   and the form's own padding starts after it, checked at every width
   rather than assumed.
   ------------------------------------------------------------------ */
@media (min-width: 900px) {
  .section--split {
    background: linear-gradient(90deg,
      var(--paper) 0 50%,
      #000 50% 100%);
  }

  /* The form sits on black inside an otherwise light section, so it
     takes the dark palette back. Same inversion as .section--void:
     against black a field has to lift rather than recede, so
     --paper-sunk goes lighter than the ground behind it. */
  .section--split .contact-form {
    --paper: #000;
    --paper-raised: #1a1a18;
    --paper-sunk: #141413;
    --ink: #fbfbf9;
    --ink-soft: #a09e97;
    --ink-faint: #6c6a65;
    --line: #302f2c;
    --line-soft: #201f1d;
    --tally-ink: var(--tally);
    color: var(--ink);
    padding-left: 40px;
  }

  /* The light-ground overrides further down key off .section--paper,
     which this section still carries — so the form would inherit a
     light-ground inset shadow and a dark select arrow on black. Put
     the dark-ground versions back. */
  .section--split .contact-form input,
  .section--split .contact-form select,
  .section--split .contact-form textarea,
  .section--split .contact-form .opt span {
    box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05);
  }

  .section--split .contact-form select {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23a09e97' stroke-width='1.6' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  }

  /* The confirmation panel replaces the form in the same column, so it
     takes the same dark palette and the same 40px inset. Without this
     it kept the light palette and appeared as a pale card on black —
     the column changed character the instant you pressed Send.

     It also showed a seam. The black half of this section is a gradient
     at 50% of the VIEWPORT; the panel starts at the GRID COLUMN edge,
     and those two lines sit about 3px apart at 1440. Nothing revealed
     that while a form — which has no background of its own — was there.
     Give the panel the ground it stands on and the 3px has nothing left
     to show. */
  .section--split .intake-done {
    --paper: #000;
    --paper-raised: #1a1a18;
    --paper-sunk: #141413;
    --ink: #fbfbf9;
    --ink-soft: #a09e97;
    --ink-faint: #6c6a65;
    --line: #302f2c;
    --line-soft: #201f1d;
    --tally-ink: var(--tally);
    color: var(--ink);
    margin-left: 40px;
  }
}

/* Stacked, the last testimonial panel (#f2f1ec) runs straight into the
   contact section (#f2f1ec) with no seam — the two read as one 2100px
   block of off-white. The checkerboard does that work on desktop, where
   the panels alternate side to side; in a single column there is
   nothing left to alternate against.

   A border rather than a spacer element: it paints outside the padding
   box, spans the full width without fighting the gutter, and needs no
   markup. */
@media (max-width: 899px) {
  #contact { border-top: 64px solid #000; }

  /* The stacked form is its own full-bleed black card with a 36px inset
     of its own (see .section--split .contact-form above); left alone,
     the section's ordinary bottom padding reopens once that card ends
     — a slab of plain paper between the black card and the dark
     footer, with nothing in it. Scoped to :has(form) rather than
     #contact outright: once the form is sent, form[hidden] flips and
     the confirmation panel takes its place — a bordered card on the
     paper ground, not a full-bleed one — and that panel still needs
     the section's normal padding underneath it. */
  #contact:has(.contact-form:not([hidden])) { padding-bottom: 0; }
}

/* Stacked, a vertical split would cut across both blocks instead of
   between them. The form becomes a black band across the full width,
   bled out past the gutter so it reads as its own ground. */
@media (max-width: 899px) {
  .section--split .contact-form {
    --paper: #000;
    --paper-raised: #1a1a18;
    --paper-sunk: #141413;
    --ink: #fbfbf9;
    --ink-soft: #a09e97;
    --ink-faint: #6c6a65;
    --line: #302f2c;
    --line-soft: #201f1d;
    --tally-ink: var(--tally);
    background: #000;
    color: var(--ink);
    margin-left: calc(-1 * var(--gutter));
    margin-right: calc(-1 * var(--gutter));
    padding: 36px var(--gutter);
  }

  .section--split .contact-form input,
  .section--split .contact-form select,
  .section--split .contact-form textarea,
  .section--split .contact-form .opt span {
    box-shadow: inset 0 1px 0 rgba(251, 251, 249, 0.05);
  }

  .section--split .contact-form select {
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%23a09e97' stroke-width='1.6' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
  }
}

/* On a light ground the ghost button needs a real edge; on dark it was
   borrowing the photograph behind it. */
.section--paper .btn-ghost,
.section--grey .btn-ghost {
  color: var(--ink);
  border-color: var(--line);
}

/* The recessed-panel highlight is a light catching a dark bezel. On a
   light ground it reads as haze, so it inverts to a soft shadow. */
.section--paper .contact-form input,
.section--paper .contact-form select,
.section--paper .contact-form textarea,
.section--paper .contact-form .opt span,
.section--grey .contact-form input,
.section--grey .contact-form select,
.section--grey .contact-form textarea,
.section--grey .contact-form .opt span {
  box-shadow: inset 0 1px 2px rgba(19, 19, 19, 0.06);
}

/* The select arrow is drawn in the dark palette's soft ink. */
.section--paper .contact-form select,
.section--grey .contact-form select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%234a4a48' stroke-width='1.6' d='M1 1.5 6 6.5 11 1.5'/%3E%3C/svg%3E");
}

@media (hover: hover) {
  .section--paper .btn-ghost:hover,
  .section--grey .btn-ghost:hover {
    border-color: var(--tally);
    color: var(--tally);
  }
}

/* ============================================================
   Panel grid
   Full-bleed cells in a two-column checkerboard, no gutters
   between them — the tone change is the divider. Each panel
   re-declares the palette, so its contents invert with it.
   Below 900px the grid becomes one column and the checkerboard
   reads as an alternating stack.
   ============================================================ */

.panels {
  display: grid;
  grid-template-columns: 1fr;
  /* A 2px rule between cells, drawn by letting the container's own
     colour show through the gap. Reads as a grid rather than as blocks
     butted together, and works on light and dark cells alike. */
  gap: 2px;
  background: #2c2c2a;
}

@media (min-width: 900px) {
  .panels { grid-template-columns: 1fr 1fr; }
}

.panel {
  display: flex;
  flex-direction: column;
  /* Top-aligned, not centred. Grid rows take the height of their tallest
     cell, so centring pushed a short panel's content down into dead space
     — on a 16-inch screen that put "What we do" below the fold while the
     empty half of its cell sat above it. */
  justify-content: flex-start;
  padding: clamp(34px, 4.4vw, 68px);
  min-height: clamp(280px, 24vw, 400px);
  background: var(--paper);
  color: var(--ink);
}

/* Light cells carry gekjr.pro's paper values and the readable
   secondary/accent pair worked out for light grounds. */
.panel--light {
  --paper: #f2f1ec;
  --paper-raised: #f8f7f3;
  --paper-sunk: #e9e8e2;
  --ink: #131313;
  --ink-soft: #4a4a48;
  --ink-faint: #625f5a;
  --line: #e2e0da;
  --tally-ink: #b23219;
}

.panel--grey {
  --paper: #e2e0da;
  --paper-raised: #f2f1ec;
  --paper-sunk: #eeece7;
  --ink: #131313;
  --ink-soft: #4a4a48;
  --ink-faint: #625f5a;
  --line: #d8d6cf;
  --tally-ink: #b23219;
}

/* One cell goes to true black. Used once per page, on the panel
   that should stop the eye. */
.panel--void { --paper: #000000; }

/* ------------------------------------------------------------------
   The reel deck

   Slides stack on one another and only the active one is shown, so the
   cell holds one fixed height however many clips there are — adding a
   third does not change the layout. Nothing plays here: a slide is a
   button that opens the lightbox, which already knows how to run both
   a local file and a YouTube frame. That means no video element and no
   iframe on this page until someone asks for one.
   ------------------------------------------------------------------ */
.panel--reel { justify-content: flex-start; }

.reel-deck {
  position: relative;
  margin-top: 20px;
  /* Stated, so the cell is the right shape before any thumbnail has
     loaded and nothing jumps when they arrive. */
  aspect-ratio: 16 / 9;
  background: #000;
  border: 1px solid var(--line);
  overflow: hidden;
}

.reel-slide {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease;
}

.reel-slide.is-active { opacity: 1; visibility: visible; }

.reel-thumb {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  filter: brightness(0.62);
  transition: filter 0.3s ease, transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Same triangle the hero button draws, scaled up and centred. */
.reel-slide-play {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  margin: -17px 0 0 -11px;
  border-style: solid;
  border-width: 17px 0 17px 28px;
  border-color: transparent transparent transparent var(--ink);
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.65));
  transition: border-left-color 0.2s ease;
}

/* The reel's own label and its captions take the accent. Everywhere
   else on this page the accent marks the numbered service IDs; here it
   marks the one cell that is a moving picture rather than prose. */
.panel--reel .panel-label span { color: var(--tally); }

/* A solid bar, not a gradient over the picture. The accent is a mid
   red, so on a scrim it depends on what happens to be underneath —
   measured 4.77 over the multiviewer and 1.63 over the second clip's
   marked-up rundown, which is white paper. Deepening the gradient to
   fully opaque still only reached 4.49 there. A bar takes the poster
   out of the equation: the caption always sits on the same ground and
   always measures the same. */
.reel-slide-meta {
  z-index: 1;
  color: var(--tally);
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: #0a0a09;
  padding: 10px 16px;
  /* Title left, tag and runtime right. Wraps rather than truncating:
     on a narrow column the tag belongs under the title, not cut off. */
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 4px 14px;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  text-align: left;
}

.reel-slide-tags {
  color: var(--ink-faint);
  white-space: nowrap;
}

/* The service the clip belongs to, plus any descriptors. Hash-tagged
   rather than spelled out as "ProAV Production" — a tag reads as a
   filter, which is what these become once there are enough clips to
   filter. The first is always the service. */
.reel-tag {
  color: var(--ink-soft);
  font-weight: 400;
  margin-right: 10px;
}

/* Descriptors sit back from the service tag they qualify. */
.reel-tag ~ .reel-tag { color: var(--ink-faint); }

@media (hover: hover) {
  .reel-slide:hover .reel-thumb { filter: brightness(0.78); transform: scale(1.03); }
  .reel-slide:hover .reel-slide-play { border-left-color: var(--tally); }
}

.reel-deck-nav {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
}

/* Chevrons drawn with borders, like the form's Back and Next. */
.deck-arrow {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  border: 1px solid var(--line);
  border-radius: 2px;
  cursor: pointer;
  transition: border-color 0.15s ease;
}

.deck-arrow::before {
  content: "";
  width: 8px;
  height: 8px;
  border-top: 2px solid var(--ink-soft);
  border-right: 2px solid var(--ink-soft);
  transform: rotate(-135deg) translate(-2px, -2px);
}

.deck-arrow--next::before { transform: rotate(45deg) translate(-2px, -2px); }

@media (hover: hover) {
  .deck-arrow:hover { border-color: var(--ink-faint); }
  .deck-arrow:hover::before { border-color: var(--ink); }
}

.reel-deck-count {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.reel-deck-count b { color: var(--ink); font-weight: 600; }

/* ---------- panel contents ---------- */

.panel-title {
  font-size: clamp(2.6rem, 6.4vw, 5.4rem);
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 0.98;
  margin: 0;
  max-width: 12ch;
}

.panel-lead {
  color: var(--ink-soft);
  font-size: clamp(1rem, 1.4vw, 1.15rem);
  max-width: 38ch;
  /* The title runs up to 86px, so 20px underneath read as crowding. */
  margin: 30px 0 0;
}

.panel-label {
  display: flex;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 20px;
  font-family: var(--font-mono);
  font-size: clamp(0.9rem, 1.3vw, 1.05rem);
  letter-spacing: 0.14em;
}

.panel-label b {
  color: var(--tally-ink);
  font-weight: 600;
}

.panel-label span {
  color: var(--ink-soft);
  letter-spacing: 0.06em;
}

.panel h3 {
  font-size: clamp(1.7rem, 3vw, 2.5rem);
  font-weight: 700;
  letter-spacing: -0.025em;
  line-height: 1.04;
  margin: 0 0 16px;
  max-width: 16ch;
}

.panel p {
  color: var(--ink-soft);
  margin: 0;
  max-width: 46ch;
}

.panel .rd-systems {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 2;
  color: var(--ink-faint);
  margin-top: 20px;
  max-width: 44ch;
}

/* The Cloud/REMI panel's transport diagram — an SRT feed leaving a
   venue's MC and landing in the cloud. currentColor on the SVG plus
   var() on the two path classes means it needs no light/dark rule of
   its own: whichever palette the panel is already carrying (this one
   is .panel--light) is what it inherits. */
/* Stacked, not side-by-side: the caption started as three short words
   and is now six pipeline stages. Beside the icon it had to fight for a
   narrow leftover column and wrapped to three or four cramped lines on
   a phone. Full-width beneath the icon, it wraps the same text in
   about half as many lines and reads as a caption rather than a squeeze. */
.transport-diagram {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-top: 22px;
}

.transport-diagram svg {
  width: 150px;
  height: auto;
  flex-shrink: 0;
}

.transport-diagram span {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  line-height: 1.6;
  color: var(--ink-faint);
  max-width: 40ch;
}

.td-ink { color: var(--ink); }
.td-accent { color: var(--tally-ink); }

/* The line never stops moving — a static dashed line reads as "connected,"
   which is only half the fact. This is the same stroke-dasharray walk
   used for a marching-ants selection outline: cheap, GPU-friendly, and
   needs no JS. */
.td-flow {
  stroke-dasharray: 5 7;
  animation: transport-flow 1.1s linear infinite;
}

@keyframes transport-flow {
  to { stroke-dashoffset: -24; }
}

/* Motion is the whole point, so the fallback is the finished picture
   rather than a blank one: the dashes stay put, still reading as a path
   to the cloud, just not walking. */
@media (prefers-reduced-motion: reduce) {
  .td-flow { animation: none; }
}

.panel blockquote {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.2rem, 1.9vw, 1.55rem);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ink);
  max-width: 30ch;
}

.panel cite {
  display: block;
  margin-top: 22px;
  font-style: normal;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.panel .btn {
  align-self: flex-start;
  margin-top: 28px;
}

.panel--light .btn-ghost,
.panel--grey .btn-ghost {
  color: var(--ink);
  border-color: var(--line);
}

@media (hover: hover) {
  .panel--light .btn-ghost:hover,
  .panel--grey .btn-ghost:hover { border-color: var(--tally); color: var(--tally); }
}

/* ============================================================
   Hero slider
   Hard cuts, no crossfade — the same choice gekjr.pro's Site
   Bible records, and the right one for frames from a live show.
   A segmented bar underneath fills across the hold, so the page
   shows its own progression rather than cutting without warning.
   ============================================================ */

.hero-slider {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.hero-slide {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The default; a slide overrides it with --focus / --focus-m, set
     inline from HERO_SLIDES in pages.py. */
  object-position: var(--focus, center 46%);
  opacity: 0;
  /* Defocused, and scaled a touch past the frame so the blur has pixels
     to sample at the edges instead of feathering into nothing. */
  /* Sharp and undimmed. The type carries a shadow instead, so the frame
     is a photograph rather than a darkened backdrop. */
  filter: none;
  /* Same curve and duration as a beat of the headline sequence, so the
     frame and the words change together rather than on separate clocks. */
  transition: opacity 630ms ease-in-out;
}

.hero-slide.is-active { opacity: 1; }

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

/* A video slide fills the frame the same way a photograph does — the
   rule above is element-agnostic, but a <video> also needs its own
   background so a frame gap never shows the page through. */
video.hero-slide { background: #0a0a09; }

/* Was .hero-slide:nth-child(2) and (3). An nth-child focal point points
   at whatever photograph happens to sit in that position, so inserting a
   slide anywhere above it re-aims the crop of two unrelated images with
   no error and no visible cause. They live on the elements now. */

/* A phone crops a 16:9 frame hard from the sides — the hero is taller
   than it is wide, so cover scales to fill the height and most of the
   picture is off screen left and right. A slide whose subject is not in
   the middle needs its own answer there, which is what --focus-m is.
   Falls back to the all-widths focus, then to the default. */
@media (max-width: 760px) {
  .hero-slide { object-position: var(--focus-m, var(--focus, center 46%)); }
}
.hero-slide:nth-child(4) { object-position: 46% 42%; }

/* ---------- the load bar ---------- */
/* The strip sits by its rail, not by its buttons. Each button carries
   --tick-pad above and below the 3px rail to make a real tap target, so
   the strip is 45px tall and its bottom edge has to come up by that
   padding or the rail would float 21px higher than it used to. */
.hero-progress {
  --tick-pad: 21px;
  position: absolute;
  z-index: 2;
  left: var(--gutter);
  right: var(--gutter);
  bottom: calc(52px - var(--tick-pad));
  display: flex;
  align-items: center;
  gap: 6px;
  /* The strip spans the frame; only the buttons in it take clicks, so a
     drag across the photo is not intercepted by the gaps between them. */
  pointer-events: none;
}

/* 3px is a bar, not a control. The button is 45px tall and invisible;
   the rail inside it is what you see. */
.hero-tick {
  flex: 1;
  display: flex;
  align-items: center;
  padding: var(--tick-pad) 0;
  background: none;
  border: 0;
  cursor: pointer;
  pointer-events: auto;
  -webkit-tap-highlight-color: transparent;
}

.hero-tick-rail {
  display: block;
  width: 100%;
  height: 3px;
  /* Normal state: a visible neutral rail, so the bar reads as a scale
     even before anything fills it. Orange means loaded, nothing else. */
  background: rgba(251, 251, 249, 0.34);
  overflow: hidden;
  transition: background 0.2s ease, height 0.2s ease;
}

.hero-tick i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--tally);
}

/* Hovering says "this is a control" without moving anything on the page:
   the rail thickens in place, inside padding that already exists. */
@media (hover: hover) {
  .hero-tick:hover .hero-tick-rail {
    height: 5px;
    background: rgba(251, 251, 249, 0.6);
  }
}

/* Keyboard focus needs to be visible against a photograph that could be
   any brightness, so the ring is drawn on the button box, well clear of
   the rail, in the one colour nothing on the frame competes with. */
.hero-tick:focus-visible {
  outline: 2px solid var(--tally);
  outline-offset: -14px;
  border-radius: 2px;
}

/* Only the live tick carries orange. A finished one drops back to the
   normal rail, so the bar reads as "loading now" rather than as a
   progress meter that accumulates. */

@media (max-width: 760px) {
  .hero-progress { bottom: calc(44px - var(--tick-pad)); gap: 5px; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-tick i { transition: none !important; }
}


/* Short laptops. The block is a line of type taller than it used to be,
   which on a 1440x800 screen pushed the foot of the hero under the
   fold — the frame's 86vh ceiling never bound, because the floor was
   always the larger of the two. Rather than let the hero outgrow the
   scale steps down where there is no height to spend. Values measured,
   not guessed: this pair is the one that keeps the headline clear of
   the header and the frame's foot above the fold at every size. */
@media (min-width: 900px) and (max-height: 880px) {
  .hero { height: clamp(calc(var(--header-h) + 660px), calc(100vw / 2.39), 94vh); }
  .hero h1.hero-seq { font-size: clamp(1.6rem, 5.2vw, 3.05rem); }
}

/* "Put it everywhere." is 18 characters where the old longest beat was
   25, so every beat now holds one line on its own down to about 340px
   without being told to. The nowrap rule and the taller floor that used
   to cover the 900-1139 band both went with the sentences that needed
   them — measured across fourteen widths, nothing wraps. */



/* ============================================================
   Shadowed hero type
   A layered shadow rather than a single soft blur: a tight, dark
   one to hold the letterform edge against detail directly behind
   it, and a wide, faint one to sink the whole line away from the
   frame. Cheaper than darkening the photograph, and it travels
   with the type rather than sitting over the picture.
   ============================================================ */

.hero h1 .seq-line,
.hero h1 .seq-we {
  text-shadow:
    0 1px 2px rgba(0, 0, 0, 0.62),
    0 3px 10px rgba(0, 0, 0, 0.55),
    0 0 42px rgba(0, 0, 0, 0.42);
}


/* The video slide sits in the same stack as the photographs and is
   cropped the same way. Poster shows while the clip loads. */
video.hero-slide {
  object-fit: cover;
  object-position: center 50%;
  background: var(--paper);
}

/* ============================================================
   Reel lightbox
   The clip lives here rather than in the slider: a 38-second
   slide stalls the rotation, and a browser that can't start it
   stalls the rotation for good. In a dialog it plays with sound,
   under the viewer's control, while the frames keep moving.
   ============================================================ */

/* The hero used to carry two labelled buttons under the headline for
   the same two clips the slider now offers itself. Once a video slide
   shows its own play control the row was the third place the same clip
   appeared on one screen, so it went. */

/* Rebuilt on open, so it has no intrinsic size until the metadata
   arrives — the frame around it carries the shape instead.

   Absolutely positioned, exactly as the YouTube iframe above it is.
   That asymmetry was a real bug: on `height: 100%` the percentage
   resolves against the frame's aspect-ratio height (1180 / 16 * 9 =
   663px at full width) and NOT against the height `max-height: 66vh`
   actually clamped it to (602px). So the player came out 62px taller
   than its own frame and, with nothing clipping it, laid that 62px over
   the caption — the title vanished behind the video.

   `inset: 0` sizes the player to the frame's real box, whichever of the
   two rules won. The overflow: hidden on the frame is the belt to this
   pair of braces: no player, whatever it is built from, gets out. */
.reel-video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #000;
}

.reel {
  width: min(1180px, 94vw);
  max-height: 90vh;
  padding: 0;
  border: none;
  background: #000;
  color: var(--ink);
  overflow: visible;
}

/* Two jobs, and the alpha alone only did one of them. At 0.88 the page
   behind measured a spread of 7.3 across the strip beside the dialog and
   a range of 7-37 — dim, but every edge still there and the copy still
   readable, which is what made it compete with the clip.

   Raising the alpha drops the brightness; the blur is what destroys the
   legibility, and it is the legibility that was the distraction. Both
   together: spread 2.3, range 6-19. Not flat black — the page is still
   faintly there, which is what says "this is on top of something" rather
   than "the site went away" — but nothing on it can be read.

   backdrop-filter is unsupported in a few places; those fall back to
   0.95 flat, which is dark enough on its own. */
.reel::backdrop {
  background: rgba(6, 6, 6, 0.90);
  backdrop-filter: blur(6px);
}

/* Stated rather than inferred, so the box is the right shape before the
   player has loaded anything and nothing jumps when it does. */
.reel-frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  /* Down from 86vh to leave the caption a band. At 86 against the
     dialog's 90vh the credits had nowhere to go on a short window and
     pushed the player off the bottom. */
  max-height: 66vh;
  background: #000;
  display: grid;
  place-items: center;
  overflow: hidden;
}

.reel-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.reel-frame a { color: var(--ink); }

/* ------------------------------------------------------------------
   Lightbox caption

   The frame was capped at 86vh against a 90vh dialog, which left no
   room for anything under it. The cap comes down so the caption has a
   band of its own rather than pushing the player off the bottom of a
   short window.
   ------------------------------------------------------------------ */
.reel-cap {
  padding: 16px 22px 18px;
  background: var(--paper);
  border-top: 1px solid var(--line);
}

.reel-cap-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.12rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.reel-cap-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  margin: 4px 0 0;
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.reel-cap-meta > * + *::before {
  content: "";
  display: inline-block;
  width: 3px;
  height: 3px;
  margin-right: 10px;
  border-radius: 50%;
  background: var(--tally);
  vertical-align: 0.25em;
}

.reel-cap-credits:not(:empty) {
  margin-top: 11px;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}

.reel-credit {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 2px 8px;
  margin: 0 0 3px;
  font-family: var(--font-body);
  font-size: 0.86rem;
  color: var(--ink);
}

.reel-credit:last-child { margin-bottom: 0; }

.reel-credit i {
  white-space: nowrap;
  font-family: var(--font-mono);
  font-style: normal;
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* The only linked credit on the site, so it has to say so on its own —
   there is no surrounding body copy to inherit link treatment from. */
.reel-credit a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--tally);
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
}

@media (hover: hover) {
  .reel-credit a:hover {
    color: var(--tally);
    text-decoration-color: var(--tally);
  }
}

@media (max-width: 760px) {
  .reel-cap { padding: 13px 15px 15px; }
  .reel-cap-title { font-size: 1rem; }
}

.reel-close {
  position: absolute;
  top: -44px;
  right: 0;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  color: var(--ink);
  font-size: 1.9rem;
  line-height: 1;
  cursor: pointer;
}

@media (hover: hover) {
  .reel-close:hover { color: var(--tally); }
}

@media (max-width: 760px) {
  .reel { width: 96vw; }
  .reel-close { top: -40px; }
}
