:root{
  --ink:#070707;
  --ink-2:#0c0c0d;
  --ink-3:#050506;
  --paper:#f3f1ee;
  --line:rgba(255,255,255,.10);
  --line-2:rgba(255,255,255,.055);
  --dim:rgba(255,255,255,.56);
  --dim-2:rgba(255,255,255,.40);
  --mag:#E4197E;
  --org:#F5821F;
  --accent:linear-gradient(96deg,#E4197E 0%,#F5821F 100%);
  --pad:clamp(1.25rem,5vw,5.5rem);
  --mono:"Fragment Mono",ui-monospace,monospace;
  /* "Archivo Fallback" had no @font-face defining it, so it fell straight
     through to system-ui behind the browser's back. Dropped rather than
     given a metric-adjusted @font-face, which needs measurements against
     Archivo's real metrics that nobody has taken. */
  --disp:"Archivo",system-ui,sans-serif;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
html{-webkit-text-size-adjust:100%}
body{
  background:var(--ink);
  color:#fff;
  font-family:var(--disp);
  font-variation-settings:"wdth" 100,"wght" 400;
  font-size:15px;
  line-height:1.72;
  letter-spacing:.004em;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
::selection{background:var(--mag);color:#fff}

/* ---------- type primitives ---------- */
.mono{
  font-family:var(--mono);
  font-size:10.5px;
  letter-spacing:.20em;
  text-transform:uppercase;
  line-height:1.4;
}
.eyebrow{
  font-family:var(--mono);
  font-size:10.5px;
  letter-spacing:.24em;
  text-transform:uppercase;
  background:var(--accent);
  -webkit-background-clip:text;
  background-clip:text;
  color:transparent;
}
.h-sec{
  font-family:var(--disp);
  font-variation-settings:"wdth" 108,"wght" 760;
  font-size:clamp(2.1rem,5vw,4.3rem);
  line-height:.96;
  letter-spacing:-.028em;
  text-wrap:balance;
  margin:.9rem 0 0;
  max-width:none;
}
.lead{
  font-size:clamp(1rem,1.35vw,1.14rem);
  line-height:1.62;
  color:rgba(255,255,255,.78);
  max-width:74ch;
  text-wrap:pretty;
}
.body{color:var(--dim);max-width:70ch;text-wrap:pretty}
.body b,.lead b{font-variation-settings:"wdth" 100,"wght" 700;color:#fff;font-weight:400}
.nl{display:block}
/* Mirror of .nl-m below. Block on desktop, where a closing or second sentence
   starting on its own line reads correctly, but INLINE on a phone, where the
   narrow column turns that break into a short stub line and the paragraph
   reads squashed. Keep the leading space inside the span: it is collapsed
   while the span is a block and renders once it goes inline. */
.nl-d{display:block}
@media (max-width:640px){ .nl-d{display:inline} }
/* Breaks onto its own line on a phone only, where the sentence would otherwise
   run to three ragged lines. Inline everywhere else. */
.nl-m{display:inline}
@media (max-width:640px){ .nl-m{display:block} }
.rule{height:1px;background:var(--line);width:100%}

/* ---------- grain ---------- */

/* ---------- nav ----------
   Three columns, not space-between. With space-between the middle group is
   only centred when the two outer groups happen to be the same width, and
   they are not: the mark measures 79px and the CTA 189px, which pushed the
   links 54.79px left of the bar centre (measured at 1440). A 1fr/auto/1fr
   grid centres the links against the bar itself, whatever the outer widths
   do, and the explicit column assignments hold the mark left and the CTA
   right even when the links are hidden on a narrow screen. */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:40;
  /* 1fr auto auto, not 1fr auto 1fr: the old third column was elastic, so the
     CTA hung 216px clear of the links with dead space between them. All the
     slack now sits in column one and the two right-hand items travel together. */
  display:grid;grid-template-columns:1fr auto auto;align-items:center;
  gap:1rem;padding:1.1rem var(--pad);
  transition:background .45s ease,border-color .45s ease,padding .45s ease;
  border-bottom:1px solid transparent;
}
/* Opaque, not .94. There is no backdrop-filter behind this bar and the hero is
   fixed behind every page, so 6% of a white display headline came through as a
   grey smear across the mark and the CTA. Translucency without blur is not a
   material, it is a smudge. */
.nav.solid{background:var(--ink);border-bottom-color:var(--line);padding-top:.8rem;padding-bottom:.8rem}
.nav-home{grid-column:1;justify-self:start}
/* links sit hard right, up against the CTA, rather than centred */
.nav-links{grid-column:2;justify-self:end;margin-right:.9rem}
.nav-cta{grid-column:3;justify-self:end}
.nav-mark{height:34px;width:auto;display:block}
.nav-links{display:flex;gap:1.9rem;align-items:center}
.nav-links a{position:relative;color:rgba(255,255,255,.7);transition:color .3s}
.nav-links a::after{
  content:"";position:absolute;left:0;right:0;bottom:-6px;height:1px;
  background:var(--accent);transform:scaleX(0);transform-origin:left;
  transition:transform .45s cubic-bezier(.16,.84,.3,1);
}
.nav-links a:hover{color:#fff}
.nav-links a:hover::after{transform:scaleX(1)}
.nav-cta{color:#fff}

/* ---------- burger + mobile menu ----------
   A lens with the burger lines inside it. The three bars are chords of the
   circle, so they come out short/long/short on their own and the mark reads as
   an iris and as a menu at the same time. A literal cine camera was tried first
   and abandoned: at 24px the silhouette stopped saying "menu" at all.
   Cased in the same pill as the CTA beside it, so the bar has one language.
   It swaps to a cross when open rather than morphing, which needs no animation
   frame and cannot land half way. */
/* No border, no pill. The mark carries itself, and casing it as well as the CTA
   beside it made two competing boxes in a 44px-tall bar. */
.nav-burger{
  display:none;align-items:center;justify-content:center;
  width:44px;height:44px;margin-right:-.6rem;
  border:0;background:none;color:#fff;cursor:pointer;padding:0;
}
.nav-burger svg{
  width:26px;height:26px;display:block;
  transition:transform .6s cubic-bezier(0,0,.2,1);
}
/* One continuous S-path carries the whole animation. Closed, a 12-unit dash
   with a 63-unit gap exposes only the top and bottom straights, which read as
   two bars either side of the fixed middle line. Open, the dash grows and the
   offset walks it onto the curved centre of the same path, so the bars travel
   into a single hooked stroke instead of cutting to a cross. */
.nav-burger .bg-morph{
  stroke-dasharray:12 63;
  transition:stroke-dasharray .6s cubic-bezier(0,0,.2,1),stroke-dashoffset .6s cubic-bezier(0,0,.2,1);
}
.nav-burger[aria-expanded="true"] svg{transform:rotate(-45deg)}
.nav-burger[aria-expanded="true"] .bg-morph{stroke-dasharray:20 300;stroke-dashoffset:-32.42px}
.nav-burger:focus-visible{outline:2px solid var(--mag);outline-offset:2px;border-radius:6px}
@media(prefers-reduced-motion:reduce){
  .nav-burger svg,.nav-burger .bg-morph{transition:none}
}

/* The menu is a viewfinder readout, not a wall. A full-bleed black panel with
   11vw caps was built first and rejected as shouty: it threw away the language
   the rest of the page already speaks. This one borrows the hero rail instead,
   a floating card with a hairline edge, a REC eyebrow and mono shot numbers,
   so the menu looks like it belongs to the same camera as everything else. */
.nav-menu{
  position:fixed;top:56px;right:12px;z-index:39;
  width:min(300px,calc(100vw - 24px));
  padding:1rem 1.1rem 1.1rem;
  border:1px solid var(--line);border-radius:12px;
  background:rgba(10,10,12,.82);
  -webkit-backdrop-filter:blur(20px) saturate(140%);
  backdrop-filter:blur(20px) saturate(140%);
  opacity:0;visibility:hidden;
  transform:translateY(-6px) scale(.98);transform-origin:100% 0;
  transition:opacity .28s ease,transform .28s cubic-bezier(.16,.84,.3,1),visibility .28s;
}
/* Scrim, painted behind the card's own background but across the whole screen,
   so the page reads as parked rather than covered. */
.nav-menu::before{
  content:"";position:fixed;inset:0;z-index:-1;
  background:rgba(4,4,5,.55);
  opacity:0;transition:opacity .28s ease;
}
.nav-menu.open{opacity:1;visibility:visible;transform:none}
.nav-menu.open::before{opacity:1}

.nav-menu .nav-menu-head{
  display:flex;align-items:center;gap:.5rem;
  font-family:var(--mono);font-size:.58rem;letter-spacing:.24em;
  text-transform:uppercase;color:var(--dim-2);
  padding-bottom:.7rem;border-bottom:1px solid var(--line-2);
}
.nav-menu .nav-menu-head .dot{
  width:5px;height:5px;border-radius:50%;background:var(--mag);flex:0 0 auto;
}
.nav-menu a{
  display:flex;align-items:baseline;gap:.8rem;
  padding:.66rem 0;
  border-bottom:1px solid var(--line-2);
  font-family:var(--disp);font-variation-settings:"wdth" 100,"wght" 620;
  font-size:1.02rem;letter-spacing:-.004em;color:rgba(255,255,255,.84);
  transition:color .3s ease;
}
.nav-menu a:hover{color:#fff}
/* The row you are on has to be unmistakable, so it carries the marker as well
   as the colour. The marker sits right, away from the label, and only the
   current row shows it filled. */
.nav-menu a{justify-content:space-between}
.nav-menu a::after{
  content:"";width:6px;height:6px;border-radius:50%;
  background:currentColor;opacity:0;transition:opacity .3s ease;
  align-self:center;flex:0 0 auto;
}
.nav-menu a[aria-current="page"]{color:var(--org)}
.nav-menu a[aria-current="page"]::after{opacity:1}
.nav-menu a:hover::after{opacity:.45}
.nav-menu .nav-menu-cta::after,.nav-menu .nav-menu-mail::after{content:none}
/* The CTA is off the bar at this width, so it has to carry real weight here. */
.nav-menu .nav-menu-cta{
  justify-content:center;border-bottom:0;
  margin-top:1rem;padding:.8rem 1rem;border-radius:7px;
  background:var(--accent);color:#08080a;
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;
  font-variation-settings:normal;
}
.nav-menu .nav-menu-cta::before{content:none}
.nav-menu .nav-menu-mail{
  justify-content:center;border-bottom:0;padding-bottom:0;margin-top:.85rem;
  font-family:var(--mono);font-size:.62rem;letter-spacing:.14em;
  color:var(--dim);font-variation-settings:normal;
}
.nav-menu .nav-menu-mail::before{content:none}

/* ---------- hero (text-behind-subject rig, adapted) ---------- */
/* Height is pinned to the LARGE viewport, not the dynamic one. With inset:0 the
   hero tracked the live viewport, so the moment a phone collapsed its URL bar on
   the first scroll the box grew, object-fit:cover rescaled the plate inside it,
   and the whole image appeared to zoom and jump. 100lvh is a constant: it is the
   viewport with the browser chrome retracted, so the box never changes size and
   the image never rescales. It also matches .after's 100svh offset staying put.
   The plain 100vh above it is the fallback for engines without lvh. */
#hero{position:fixed;top:0;left:0;right:0;height:100vh;height:100lvh;z-index:0;overflow:hidden}
.hero-media{position:absolute;inset:0;z-index:0}
.hero-media img{
  width:100%;height:100%;object-fit:cover;object-position:center;
  /* base plate is black and white. Colour only arrives through the lens. */
  filter:grayscale(1);
}
/* the lens canvas must fill the hero exactly, or it cannot register with the
   plate underneath it. Explicit sizing, never the canvas default. */
.hero-lens{width:100%;height:100%;display:block}

/* The greyscale plate exists so the pointer lens can reveal colour through it.
   Touch devices have no pointer, so there is no reveal to drive. They get the
   plate itself: black and white, the same as the desktop base. Operator asked
   for black and white twice; 22% saturation was not it. */
/* The bleed is the treatment on every device now, so a phone gets the same
   plate rather than a flat greyscale one. Reverses the earlier all-grey mobile
   rule on the operator's instruction, 2026-08-10. The lens still goes: there is
   no pointer to drive it. */
@media (hover:none),(pointer:coarse){
  .hero-lens{display:none}
}


.hero-scrim{
  position:absolute;inset:0;z-index:1;pointer-events:none;
  background:
    /* 1. the travelling bottom fade, on its OWN layer.
       Every stop here is relative to --fd, the top edge of the incoming section
       as a percentage of the hero. It has to be alone in its layer: CSS forces
       each stop to be >= the one before it, so when this sat in the same
       gradient as the fixed 26% stop below, calc(var(--fd) - 46%) fell behind
       26% once --fd dropped under about 72% and every travelling stop clamped
       onto it. The fade then went solid and stopped moving, which is the black
       mark that always appeared at the same place.
       Ends on #070707, byte-identical to --ink, so the section's top edge meets
       a plate of the same colour and there is no seam. */
    linear-gradient(180deg,
      rgba(7,7,7,0) calc(var(--fd,100%) - 46%),
      rgba(7,7,7,.55) calc(var(--fd,100%) - 28%),
      rgba(7,7,7,.90) calc(var(--fd,100%) - 12%),
      #070707 var(--fd,100%),
      #070707 100%),
    /* 2. top darkening under the nav, fixed */
    linear-gradient(180deg,rgba(7,7,7,.80) 0%,rgba(7,7,7,0) 26%),
    /* 3. vignette */
    radial-gradient(120% 78% at 50% 42%,rgba(0,0,0,0) 34%,rgba(4,4,5,.62) 100%);
}

.hero-lens{position:absolute;inset:0;z-index:2;pointer-events:none}
/* The headline is ONE solid layer. The subject-mask knockout that split each
   letter between fill and outline was rejected: derived from a flat photo it
   tore the letterforms into ragged fragments and read as broken, not designed.
   Do not reinstate it on the hero. */
.hl{
  margin:0;
  font-family:var(--disp);
  line-height:.84;
  letter-spacing:-.038em;
  text-transform:uppercase;
}
.hl .l{display:block;overflow:hidden;padding-bottom:.18em;margin-bottom:-.14em}
/* Display sizes below are approx, tuned by eye against the rendered hero so
   both lines fill the frame without wrapping. Re-check on any copy change. */
.hl .l{white-space:nowrap}
.hl .l1{font-size:clamp(1.55rem,5.6vw,4.8rem);font-variation-settings:"wdth" 108,"wght" 820}
.hl .l2{font-size:clamp(1.55rem,5.6vw,4.8rem);font-variation-settings:"wdth" 108,"wght" 820}
.hl .l2{color:rgba(255,255,255,.88)}
.hl .w{display:inline-block}
.hl .w i{
  display:inline-block;font-style:normal;
  transform:translateY(105%);opacity:0;filter:blur(20px);
}
.hero-ready .hl .w i{
  transform:translateY(0);opacity:1;filter:blur(0);
  transition:transform 1.3s cubic-bezier(.05,.9,.1,1),opacity 1.3s cubic-bezier(.05,.9,.1,1),filter 1.3s cubic-bezier(.05,.9,.1,1);
}
.fill .hl{color:#fff}
.line .hl{
  color:transparent;
  -webkit-text-stroke:1.4px rgba(255,255,255,.92);
}
@media(min-width:900px){.line .hl{-webkit-text-stroke-width:2px}}

/* ---------- VIDEO VILLAGE ----------
   The hero is the director's monitor. A producer reads a viewfinder overlay
   before they read a headline, so the production metadata frames the image
   instead of sitting under it, and the credit chain lives IN the hero.
   The image is a held frame today; a cleared clip drops into .hero-media
   later with no other change. */
.vv{
  position:absolute;inset:0;z-index:6;pointer-events:none;
  display:flex;flex-direction:column;
  padding:clamp(4.4rem,10.5vh,6rem) var(--pad) clamp(1.3rem,3.6vh,2.1rem);
  /* The viewfinder travels with the scroll. --hy is written to #hero on every
     scroll frame and used to be read by .hero-inner, which no page carries, so
     the value was computed and thrown away and the frame sat pinned while the
     plate moved under it. This is a transform, so it does not disturb the
     reserved rail widths that stop the slate resizing the frame. */
  transform:translate3d(0,var(--hy,0px),0);
  will-change:transform;
}
/* The bottom rail cycles through four productions and each has a different
   text length. As a flex child that changed the rail's height, which resized
   the viewfinder frame above it: the frame visibly jumped every few seconds.
   Fix the rail's height so the frame can never move, and centre the overflow
   line rather than letting it reflow the column. */
.vv-rail.vv-bot{
  flex:0 0 auto;
  min-height:2.1rem;
  align-items:center;
  align-content:center;
  overflow:hidden;
}
.vv-rail.vv-bot > span{white-space:nowrap}
@media (max-width:760px){
  .vv-rail.vv-bot{justify-content:center;text-align:center;min-height:2.6rem}
  .vv-rail.vv-bot .sp{display:none}
}
.vv-rail{
  display:flex;align-items:center;gap:clamp(.9rem,1.9vw,1.9rem);flex-wrap:wrap;
  font-family:var(--mono);font-size:.645rem;letter-spacing:.185em;text-transform:uppercase;
  color:var(--dim-2);
}
.vv-rail .sp{flex:1 1 auto;min-width:0}
.vv-rail .k{color:rgba(255,255,255,.34)}
.vv-rail .v{color:#fff;margin-left:.5rem}
.vv-rail .v.sw{transition:opacity .26s ease,transform .26s cubic-bezier(.16,.84,.3,1)}
.vv-rail .v.sw.out{opacity:0;transform:translateY(-6px)}
.rec{display:inline-flex;align-items:center;gap:.5rem;color:#fff}
.rec .dot{
  width:7px;height:7px;border-radius:50%;background:var(--mag);
  animation:recpulse 2.1s ease-out infinite;
}
@keyframes recpulse{
  0%{box-shadow:0 0 0 0 rgba(228,25,126,.55)}
  70%{box-shadow:0 0 0 9px rgba(228,25,126,0)}
  100%{box-shadow:0 0 0 0 rgba(228,25,126,0)}
}
.tc{color:#fff;font-variant-numeric:tabular-nums;letter-spacing:.12em}
/* The location label lives in the viewfinder rail, not floating over the
   photograph. It sits on the dark scrim so it is legible by construction
   rather than rescued with a backing plate. */
/* White on every device, and settled. The plate is black and white throughout,
   and the rail reads cleaner plain than with the accent gradient on it. */
.vv-loc,.vv-svc{
  color:#fff;
  font-weight:600;
}

/* ---------- mobile: the viewfinder top rail ----------
   The rail wrapped to two lines on a phone, dropping the timecode below the
   location. It has to read as one strip of camera data, so: no wrap, tighter
   tracking, and the location loses the gradient. Gradient text is clipped to
   the glyphs, and at 10px over a photograph the accent has almost no contrast
   left, so it goes flat white where it can actually be read. */
@media (max-width:640px){
  /* REC keeps its own line, top left, exactly where it was. The rest of the
     camera data forms ONE line beneath it: location hard left, timecode hard
     right. Cramming all three onto a single row pushed the timecode off the
     right edge of a 375px screen. */
  .vv-rail.vv-top{
    flex-wrap:wrap;
    row-gap:.35rem;
    column-gap:.6rem;
    font-size:.56rem;
    letter-spacing:.1em;
  }
  /* Two rows, not three: REC and the timecode share the first line, the two
     coloured strings share the second. REC used to take a full-width row of its
     own, which pushed the timecode onto a third line and left the rail looking
     stretched. .sp is the existing spacer, re-used here as a zero-height
     full-width flex item purely to force the break in the right place. */
  .vv-rail.vv-top .rec{flex:0 0 auto;order:1}
  .vv-rail.vv-top .tc{flex:0 0 auto;order:2;margin-left:.9rem}
  .vv-rail.vv-top .sp{display:block;order:3;flex:0 0 100%;height:0;margin:0}
  .vv-rail.vv-top .vv-svc{order:4}
  .vv-rail.vv-top .vv-loc{flex:0 0 auto;order:5;margin-right:auto}
  .vv-rail.vv-top > *{white-space:nowrap}

  /* Section headings ran to three cramped lines on a phone, and because .st .w
     pulls .18em back per line with line-height .96, the ink of the last line
     sat below the heading's own box and the image beneath overlapped it.
     Measured at a 323px heading: 33.6px gives three lines, 27-30px gives two.
     The frame also gets its own clearance rather than relying on that margin. */
  .h-sec{font-size:clamp(1.6rem,7.4vw,2.1rem)}
  /* The gap goes on the heading, not the figure. The figure's own top margin
     collapses against the heading block and resolves to 0, and padding on the
     figure pushed its absolutely-positioned .tag up out of the image. Padding
     under the heading cannot collapse and leaves the figure's internals alone. */
  #services .sec-head{padding-bottom:3.6rem}

  /* The hero lines are nowrap so they fill the frame on desktop. On a phone
     that ran the headline straight off the right edge, clipping the last word
     of both lines. Let it wrap and drop the width axis so it still fits. */
  .hl .l{white-space:normal}
  /* Phone only: four fixed lines, The company / that runs / your shoot in /
     Thailand. Desktop is untouched and stays two lines. */
  .hl .l .w{display:block}

  /* The headline shrank 2.9x from desktop (71.68px to 24.8px) while the lede
     stayed at 15.2px, so on a phone a five-line paragraph out-weighed the
     headline and the block read as crushed. Measured at a 303px box: 24.8px
     gives a 68px headline, 32px gives 115px, which restores the proportion
     against the lede. Sits after the base .hl .l1 rule, which is the same
     specificity, so source order decides it. */
  /* PHONE ONLY. Four short lines leave the frame mostly empty at 8.6vw, so the
     type goes up to fill it. The longest group is "The company"; the value
     below is the largest that clears the frame, measured not guessed. */
  .hl .l1,.hl .l2{font-variation-settings:"wdth" 92,"wght" 800;font-size:clamp(2.1rem,11.2vw,4rem)}
  /* Each .l is overflow:hidden for the reveal mask, so a wide offset shadow is
     sliced flat at the line box and reads as a grey slab. Tight blur, almost no
     offset, stays inside the line. */
  .hl{text-shadow:0 1px 12px rgba(0,0,0,.55)}
  .hero-lead{margin-top:1.4rem}
  /* Solid white on a phone, reversing the earlier decision to keep the gradient
     here. The hero is getting a colour bleed down the frame, and a gradient
     rail sitting on top of a gradient plate is two effects fighting. Operator
     call, 2026-08-10. */
  .tc{letter-spacing:.06em}
}


/* the viewfinder frame itself */
.vv-frame{position:relative;flex:1 1 auto;margin:.85rem 0;display:flex;align-items:flex-end}
.vv-frame::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  border:1px solid rgba(255,255,255,.13);
}
.vv-c{position:absolute;width:24px;height:24px;border:2px solid rgba(255,255,255,.88)}
.vv-c.tl{top:-1px;left:-1px;border-right:0;border-bottom:0}
.vv-c.tr{top:-1px;right:-1px;border-left:0;border-bottom:0}
.vv-c.bl{bottom:-1px;left:-1px;border-right:0;border-top:0}
.vv-c.br{bottom:-1px;right:-1px;border-left:0;border-top:0}
.vv-cross{position:absolute;top:50%;left:50%;width:18px;height:18px;transform:translate(-50%,-50%);opacity:.42}
.vv-cross::before,.vv-cross::after{content:"";position:absolute;background:#fff}
.vv-cross::before{left:50%;top:0;bottom:0;width:1px;transform:translateX(-50%)}
.vv-cross::after{top:50%;left:0;right:0;height:1px;transform:translateY(-50%)}

.vv-body{
  position:relative;z-index:2;width:100%;
  /* No transform here. .vv is the one element that carries --hy now, and this
     applied it a second time on a descendant, so the headline travelled at
     twice the rate of the frame around it and pulled away from it on scroll. */
  padding:0 clamp(1rem,2.4vw,2.2rem) clamp(1.2rem,3.4vh,2.4rem);
}
.hero-lead{
  max-width:56ch;margin-top:1.15rem;
  color:rgba(255,255,255,.82);font-size:clamp(.95rem,1.15vw,1.06rem);line-height:1.6;
  pointer-events:auto;text-wrap:pretty;
}
@media (prefers-reduced-motion:reduce){
  .rec .dot{animation:none}
  .vv-rail .v.sw{transition:none}
}
@media (max-width:640px){
  .vv-cross{display:none}
  .vv-rail{font-size:.58rem;letter-spacing:.14em;gap:.75rem}
  .vv-rail .hide-s{display:none}
}

/* ---------- EXPANDABLE GALLERY ----------
   rig: expandable-gallery (creative/rigs/expandable-gallery/EXTRACTION.md)
   Held to the rig's own dials rather than invented ones:
     hovered flex 2, siblings 0.5, resting 1, easeInOut 0.5s
     per-tile black overlay fades 0.3 -> 0 so the focused image BRIGHTENS
   Rebuilt on flex + opacity + transform in plain CSS and JS, no framer-motion,
   because this ships as static files with no framework runtime. */
.gal{
  display:flex;gap:14px;margin-top:clamp(1.8rem,4vw,3rem);
  height:clamp(300px,54vh,470px);
}
.gal-p{
  position:relative;flex-grow:1;flex-shrink:1;flex-basis:0;
  min-width:0;width:0;overflow:hidden;border-radius:14px;
  transition:flex-grow .5s ease-in-out,flex-basis .5s ease-in-out;
  cursor:pointer;border:0;padding:0;background:none;
}
/* Hover and keyboard focus only, exactly as the rig specifies. An earlier
   version carried an .on class plus a :has() patch so tests could fake a
   hover; the patch out-ranked real :hover, so pointing at one tile lit its
   caption while a different tile stayed expanded. Verified by eye, not by
   reading computed styles, which showed nothing wrong. */
.gal:hover .gal-p,.gal:focus-within .gal-p{flex-grow:.5}
/* The hovered tile must out-specify the sibling rule above. `:hover` counts in
   the class column, so `.gal:hover .gal-p` is (0,3,0) and was beating a plain
   `.gal-p:hover` at (0,2,0): every tile resolved to 0.5 and nothing expanded,
   while the veil and caption still responded, which is what made it look like
   the rig was applied when it was not. */
.gal:hover .gal-p:hover,.gal:focus-within .gal-p:focus-visible{flex-grow:2}
.gal-p:focus-visible{outline:2px solid var(--mag);outline-offset:3px}

.gal-art{position:absolute;inset:0;display:block}
.gal-art img{width:100%;height:100%;object-fit:cover}

/* PHONE ONLY. The open tile is 140px wide against a 438px gallery, so a
   900x600 plate is cropped to about 21% of its width, and the closed tiles to
   about 5%. At the default 50% that centre slice landed on empty water, bare
   deck and open hillside in four of the five frames, and clipped the group in
   the fifth: the subject is almost never dead centre in a landscape photo.
   Each value below is the horizontal centre of that frame's actual subject,
   read off the plate.

   Keyed to the filename, NOT nth-child, for the same reason the client logo
   heights are: a positional selector hands the wrong focal point to every
   tile after any insertion or removal, and nothing shows up in the diff.
   Scoped to 560px so desktop and tablet keep the centre crop they have.

   NOTE THE ARITHMETIC, it is not intuitive. `object-position:X%` does NOT put
   the X% point of the photo in the middle of the tile. It aligns the X% point
   of the IMAGE with the X% point of the BOX, so to centre a subject sitting at
   P of the frame you need:

       X = (P * scaledImageWidth - boxWidth / 2) / (scaledImageWidth - boxWidth)

   Measured geometry for the open tile: box 140x438, plate 900x600 scaled to
   cover = 658 wide, overflow 518. Setting X = P directly (the obvious guess)
   lands the subject at the tile's edge instead of its middle. */
@media (max-width:560px){
  .gal-art img[src*="gal-jungle"]   {object-position:22% center} /* cuts the dead trunk at 10-16% off the left edge */
  .gal-art img[src*="gal-coast"]    {object-position:12% center} /* P .20 the three seated cast, centred           */
  .gal-art img[src*="gal-unit"]     {object-position:75% center} /* P .70 the operator sitting on the dolly        */
  .gal-art img[src*="gal-night"]    {object-position:40% center} /* P .42 crowd, opened out to the left            */
  .gal-art img[src*="gal-highland"] {object-position:45% center} /* the CAMERA at P .48, sat at 59% of the tile    */
}

/* the rig's one treatment: a flat black veil that lifts on the focused tile */
.gal-p::before{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:#000;opacity:.3;transition:opacity .5s ease-in-out;
}
.gal-p:hover::before,.gal-p:focus-visible::before{opacity:0}

/* caption rides the focused tile only, centred at its foot */
.gal-cap{
  position:absolute;left:0;right:0;bottom:0;z-index:2;
  padding:2.6rem 1rem 1.4rem;text-align:center;pointer-events:none;
  background:linear-gradient(transparent,rgba(0,0,0,.30) 38%,rgba(0,0,0,.82));
  opacity:0;transform:translateY(8px);
  transition:opacity .5s ease-in-out,transform .5s ease-in-out;
}
.gal-p:hover .gal-cap,.gal-p:focus-visible .gal-cap{opacity:1;transform:none}
.gal-cap .t{
  display:block;font-family:var(--mono);font-size:.7rem;
  letter-spacing:.34em;text-transform:uppercase;color:#fff;
}
.gal-cap .s{
  display:block;margin-top:.45rem;font-family:var(--mono);font-size:.62rem;
  letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.62);
}

/* No mobile variant. The gallery is the same flex row at every width and simply
   gets smaller: five tiles side by side, no horizontal scrolling, and a tap
   expands a tile in place. A previous version turned it
   into a scroll-snap carousel below 760px, which was a different component with
   a different shape and meant the row ran off the side of the screen. */

/* No pointer means no hover, so the resting state has to be arranged rather
   than waited for. Every caption used to be forced visible here, which gave a
   phone all five at once and nothing to discover. Instead the row opens in the
   state a desktop visitor sees mid-hover: one tile expanded with its caption
   showing, the other four resting and silent. Tapping any tile moves that
   state to it. Nothing opens an overlay, on any device.
   `.gal .gal-p` is (0,2,0) against the base `.gal-p` at (0,1,0), so it wins on
   specificity and does not depend on where it sits in the file. */
@media (hover:none){
  .gal-p::before{opacity:.16}
  /* The desktop rig already does exactly this on hover. A touch device has no
     hover, so the same rules are driven by an .on class instead: identical
     flex-grow values, identical transition, identical caption behaviour. Scoped
     inside (hover:none) so it can never out-rank real :hover on a pointer
     device, which is what broke an earlier .on class in this file. */
  .gal .gal-p{flex-grow:.5}
  .gal .gal-p.on{flex-grow:2}
  .gal .gal-p.on::before{opacity:0}
  .gal .gal-p.on .gal-cap{opacity:1;transform:none}
}
@media (prefers-reduced-motion:reduce){
  .gal-p,.gal-p::before,.gal-cap{transition:none}
  .gal{scroll-behavior:auto}
}

/* ---------- named credits: title art against the show it belongs to ---------- */
/* Was clamp(2.4rem,5vw,4rem), which stacked ~53px on top of the section's own
   60px padding and left a band of empty black above the header for no reason.
   The section padding is the spacing; this does not need to repeat it. */
.creds{margin-top:0}

/* The header here is a real .sec-head + .h-sec h2, the same as #work and
   #clients. It used to be a `.rubric mono` paragraph, which no .rubric rule
   in this file even reached (they are all scoped to .slate), so it fell
   through to the generic .mono utility at 10.5px and read as a caption
   rather than a section heading. Do not re-style it here: matching the other
   two sections is what keeps the page consistent. */
.cred-list{list-style:none;margin-top:1.4rem;position:relative}
/* the rules are her gradient, not a flat hairline. Drawn as pseudo-elements
   because a gradient cannot be a border-colour and border-image would paint
   all four sides. */
.cred-list::before{content:"";position:absolute;left:0;right:0;top:0;height:1px;background:var(--accent)}
/* Title, then the chain, then the artwork hard right. The art is the thing
   the eye lands on, so it anchors the end of the row rather than the start.
   Always in colour: these are the shows' own title cards and desaturating
   them just made them look unfinished. */
.cred{
  display:grid;align-items:center;gap:1.4rem;
  grid-template-columns:minmax(0,1fr) minmax(0,auto) minmax(0,auto) 148px;
  padding-block:1.1rem;position:relative;
}
.cred::after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--accent)}
.cred-art{
  grid-column:4;justify-self:end;
  width:148px;height:auto;border:1px solid var(--line-2);border-radius:4px;
  transition:transform .5s ease;
}
.cred:hover .cred-art{transform:scale(1.04)}
.cred-t{
  font-family:var(--disp);font-variation-settings:"wdth" 96,"wght" 700;
  font-size:clamp(1.05rem,1.7vw,1.5rem);letter-spacing:-.015em;text-transform:uppercase;color:#fff;
}
.cred-m{font-family:var(--mono);font-size:.68rem;letter-spacing:.12em;text-transform:uppercase;color:#fff;white-space:nowrap}
.cred-m .k{color:var(--dim-2);margin-right:.5rem}
@media (max-width:760px){
  /* Phone only. A <ul> carries a 40px browser default padding-inline-start that
     nothing reset, so every row sat 40px in from the left while its right edge
     stayed flush, pushing the block across. At this width that inset is most of
     the margin, so it goes; the wide view was fine and keeps it. */
  .cred-list{padding:0}
  /* Art is 92px wide here rather than 148, so the desktop min-height would
     tower over the rows it is meant to match. Re-set for this width. */
  .cred{grid-template-columns:minmax(0,1fr) 92px;gap:.9rem;row-gap:.3rem}
  .cred-art{width:92px;grid-column:2;grid-row:1 / span 3;align-self:start}
  .cred-t{grid-column:1}
  .cred-m{grid-column:1;white-space:normal}
}


/* ---------- page body over the pinned hero ---------- */
.after{position:relative;z-index:1;margin-top:100svh;background:var(--ink)}

section{position:relative}
.wrap{padding-left:var(--pad);padding-right:var(--pad)}
.sec-head{display:flex;flex-direction:column;gap:.2rem}

/* reveal primitives */
.rev{opacity:0;transform:translateY(32px) scale(.965);transition:opacity .7s ease,transform .85s cubic-bezier(.16,.84,.3,1)}
.rev.in{opacity:1;transform:none}
/* The reveal mask is overflow:hidden on each word, and .h-sec runs a
   line-height below 1, so the line box is shorter than the font and
   descenders (y, g, p) were being sliced off. Extend the clip box below
   the baseline and pull the same amount back so layout is unchanged. */
.st .w{display:inline-block;overflow:hidden;vertical-align:top;padding-bottom:.18em;margin-bottom:-.18em}
.st .w i{display:inline-block;font-style:normal;transform:translateY(110%);opacity:0;transition:transform .72s cubic-bezier(.16,.84,.3,1),opacity .4s ease}
.st.in .w i{transform:translateY(0);opacity:1}
.draw{height:1px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .9s cubic-bezier(.16,.84,.3,1)}
.draw.in{transform:scaleX(1)}

/* ---------- 01 credits, the centrepiece ---------- */
#work{
  background:var(--ink);
  padding:clamp(5rem,11vh,8.5rem) 0 clamp(4.5rem,10vh,7.5rem);
  border-top:1px solid var(--line-2);
}
#work::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background-image:linear-gradient(90deg,var(--line-2) 1px,transparent 1px);
  background-size:calc(100% / 6) 100%;
  mask-image:linear-gradient(180deg,transparent,#000 12%,#000 78%,transparent);
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 12%,#000 78%,transparent);
}
#work>*{position:relative}
.work-head{display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;flex-wrap:wrap}
/* The .credits/.credit archive layout that sat here is deleted: no markup
   used it, and Productions now has its own slate cards in archive.css.
   The NDA guard below stays, because it is a safety net rather than a
   layout: any element carrying the flag is hidden whatever it is. */
/* NDA hold. Scoped to the attribute alone, never to one class: the previous
   rule was `.credit[data-cleared="false"]` and the held titles are `.t`, so it
   matched nothing and both held shows rendered in plain view. Any element
   carrying the flag is hidden, whatever it is. Publishing is a one-word change
   on the record. */
[data-cleared="false"]{display:none !important}

.slate{margin-top:clamp(2.4rem,5vh,3.6rem)}
.slate .rubric{color:var(--dim-2);display:flex;align-items:center;gap:1rem}
.slate .rubric .rule{flex:1}

/* ---------- 02 on location ---------- */
#location{
  background:var(--ink-3);
  padding:clamp(5rem,12vh,9rem) 0;
  overflow:hidden;
}
/* About carries a single plate now: its second photograph was the same one
   sitting behind the client wall on Home, and no image on this site appears
   twice. One full-width plate reads as deliberate; a lone half-width cell in
   a two-column grid reads as a missing image. */
.plate{position:relative;overflow:hidden;background:#101012}
.plate img{width:100%;height:auto;filter:saturate(.86) contrast(1.05) brightness(.9)}
.plate .tag{position:absolute;left:1rem;bottom:.9rem;z-index:2}
.plate::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(0,0,0,0) 46%,rgba(0,0,0,.55) 100%)}
.plate.a{transform:translateY(calc(var(--pc,0) * -58px))}
.plate.b{transform:translateY(calc(var(--pc,0) * 58px))}

/* ---------- 03 services ---------- */
#services{
  background:var(--ink-2);
  padding:clamp(5rem,12vh,9rem) 0;
  overflow:hidden;
  border-top:1px solid var(--line-2);
}
/* margin-top here collapses to 0 against the heading block above, so the space
   lives as padding on #services .sec-head instead, where it cannot collapse.
   Kept on the element as well for any context where it does not collapse. */
.svc-frame{
  position:relative;margin-top:clamp(2.4rem,6vh,4rem);
  transform:translateY(calc(var(--sc,0) * 34px));
}
/* Aspect-ratio, not a fixed height. The band was cut for a 1.83:1 frame;
   its replacement is 3:2 and a short fixed height crushed it to 5.5:1. */
/* The gap under this heading measured -29px at 1440 and -5px at 390: the frame's
   own top margin collapses away, and the frame then parallaxes up to 34px
   further up. Padding on the heading cannot collapse, so it holds the space. The
   value is set so the gap below matches the 27px gap above it. */
#services .sec-head{padding-bottom:3.5rem}
.svc-frame img{width:100%;aspect-ratio:21/9;height:auto;max-height:52vh;object-fit:cover;object-position:center 46%;filter:saturate(.8) brightness(.72)}
.svc-frame::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,rgba(12,12,13,.72) 0%,rgba(12,12,13,.18) 45%,rgba(12,12,13,.72) 100%)}
.svc-frame .tag{position:absolute;left:1.1rem;top:1rem;z-index:2}
.svc-list{margin-top:clamp(2.2rem,5vh,3.4rem);border-top:1px solid var(--line-2)}
.svc{
  display:flex;align-items:baseline;gap:1.4rem;
  padding:clamp(.85rem,1.9vh,1.35rem) 0;
  border-bottom:1px solid var(--line-2);
  font-variation-settings:"wdth" 104,"wght" 620;
  font-size:clamp(1.3rem,3.1vw,2.6rem);
  line-height:1.02;letter-spacing:-.026em;
}
.svc .n{font-family:var(--mono);font-size:10px;letter-spacing:.2em;color:var(--dim-2);flex:0 0 2.6rem}
.svc:nth-child(odd){transform:translateX(calc(var(--sc,0) * 46px))}
.svc:nth-child(even){transform:translateX(calc(var(--sc,0) * -46px))}
.svc-note{margin-top:clamp(1.8rem,4vh,2.6rem)}

/* ---------- 04 clients ---------- */
#clients{
  position:relative;
  padding:clamp(5rem,12vh,9rem) 0;
  overflow:hidden;
  background:#08080a;
}
#clients .bg{position:absolute;inset:0;z-index:0}
#clients .bg img{width:100%;height:100%;object-fit:cover;object-position:center 40%;filter:saturate(.7) brightness(.62) contrast(1.04)}
#clients .veil{
  position:absolute;inset:0;z-index:1;
  background:
    linear-gradient(180deg,rgba(8,8,10,.92) 0%,rgba(8,8,10,.34) 34%,rgba(8,8,10,.40) 62%,rgba(8,8,10,.94) 100%);
}
#clients>.wrap{position:relative;z-index:2}
/* ---------- clients: the real logos ----------
   The brands' own vectors, except BBC StoryWorks, which is a sub-brand with no
   vector anywhere and comes from her own client sheet.

   Seven dark marks are reversed to white at the asset, in two different ways
   because one formula cannot do both: BBC, Ford and Warner Bros. have light
   detail INSIDE a dark shape, so those map dark to white and light to
   transparent, keeping the letters, the script and the monogram punched out.
   Cartier, Hyundai and Toyota are flat art, so those become a solid white
   silhouette on their original alpha. Reversing them all the first way turned
   the mid-tone reds and navies half-transparent.

   Sizing is normalised on AREA, not height. The marks run from 0.78:1 (the
   Warner Bros. shield) to 10.3:1 (BBC StoryWorks), a 13x spread, so a single
   height cap made the wide ones hit their width limit and shrink to nothing
   while the square ones sat marooned in empty grid cells. h = sqrt(3150/aspect),
   clamped 22-44px, gives every mark the same optical weight. Flex, not grid, so
   there are no fixed cells left blank. */
.logos{
  --s:1;
  list-style:none;margin:clamp(2.4rem,6vh,4rem) 0 0;padding:0;
  display:flex;flex-wrap:wrap;justify-content:center;align-items:center;
  gap:clamp(1.8rem,3vw,3rem);
}
.logos li{display:flex;align-items:center;justify-content:center}
.logos img{width:auto;display:block}
/* Keyed to the file, NOT to nth-child. These heights are per-mark, worked out
   from each logo's own aspect ratio, so a positional selector silently hands the
   wrong height to every logo after any insertion or removal. Removing Tourism
   Malaysia at position 11 would have shifted Toyota onto its 31px and Banijay
   onto Toyota's 23px, with nothing to see in the diff. Netflix and Endemol are
   still to be added, so this stays keyed to the asset. */
  .logos img[src*="bbc-storyworks"]{height:calc(var(--s) * 22px)}
  .logos img[src*="itv"]          {height:calc(var(--s) * 44px)}
  .logos img[src*="amazon-prime"] {height:calc(var(--s) * 44px)}
  .logos img[src*="netflix"]      {height:calc(var(--s) * 29px)}
  .logos img[src*="endemol"]      {height:calc(var(--s) * 44px)}
  .logos img[src*="warner-bros"]  {height:calc(var(--s) * 44px)}
  .logos img[src*="banijay"]      {height:calc(var(--s) * 30px)}
/* Simulated against the real wrap before choosing: at --s:.72 the block ran
   five rows and 218px on a 335px container. At .50 with a 12px gap it wraps to
   three rows of 4/6/3 and 84px, which is the same wall at a size that suits a
   phone rather than a page of it. */
/* The forced wrap. A flex line breaks where the items stop fitting, which on a
   phone put five marks up and stranded two underneath. Operator's call is four
   then three, in the order Warner Bros., Amazon Prime, Netflix, ITV. A
   full-width, zero-height flex item after the fourth is the only way to say
   "break here" in flexbox, since ::before and ::after can only sit at the ends
   of the container. Hidden everywhere except the phone, so the wide view is
   untouched and still wraps on its own. */
/* Scoped to .logos on purpose. A bare .logo-break loses to .logos li{display:flex}
   on specificity, so the item stayed in the flow at zero width and the wall paid
   for it in gap: measured 74px between ITV and BBC StoryWorks where every other
   pair sits at 37px, a double gap in the middle of the wide view. */
.logos .logo-break{display:none}

@media (max-width:900px){ .logos{--s:.72;gap:1.4rem 1.8rem} }
@media (max-width:560px){
  /* PHONE ONLY. --s was .50, chosen back when the wall wrapped on its own and
     .72 ran to five rows. The .logo-break above now pins the layout to 4 then
     3, so that wrap cannot happen any more and the old cap was just making the
     marks small for no reason: measured, the four-mark row used 229px of a
     335px container and left 106px empty. At .72 with a 10px gap that row
     measures 308px of 335, so it fills the width without touching the edges.
     Desktop and tablet are untouched, both live above this breakpoint. */
  .logos{--s:.72;gap:10px;margin-top:clamp(1.5rem,4vh,2.4rem)}
  .logos .logo-break{display:block;flex-basis:100%;height:0;margin:0}
}


/* ---------- 05 questions ---------- */
#questions{background:#060607;padding:clamp(5rem,12vh,9rem) 0;border-top:1px solid var(--line-2)}
#questions::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(90% 55% at 12% 0%,rgba(228,25,126,.10) 0%,rgba(6,6,7,0) 62%);
}
#questions>.wrap{position:relative}
.qa{margin-top:clamp(2.6rem,6vh,4.2rem);display:grid;grid-template-columns:1fr 1fr;gap:clamp(1.8rem,4vw,3.6rem) clamp(2rem,5vw,4.5rem)}
.q{opacity:0;transform:translateY(10px);transition:opacity .6s ease,transform .6s cubic-bezier(.16,.84,.3,1)}
.q.in{opacity:1;transform:none}
.q .line{height:1px;background:var(--accent);transform:scaleX(0);transform-origin:left;transition:transform .85s cubic-bezier(.16,.84,.3,1);margin-bottom:1.1rem}
.q.in .line{transform:scaleX(1)}
.q h3{
  margin:0 0 .75rem;
  font-variation-settings:"wdth" 100,"wght" 660;
  font-size:clamp(1.08rem,1.72vw,1.5rem);
  line-height:1.16;letter-spacing:-.02em;text-wrap:balance;
}
.q p{margin:0;color:var(--dim);font-size:.94rem;line-height:1.68;text-wrap:pretty}
.q p b{color:rgba(255,255,255,.95);font-variation-settings:"wdth" 100,"wght" 640;font-weight:400}

/* ---------- 06 contact ---------- */
#contact{position:relative;overflow:hidden;background:#070708}
#contact .bg{position:absolute;inset:0;z-index:0}
#contact .bg img{width:100%;height:100%;object-fit:cover;object-position:center 55%;filter:grayscale(1) brightness(.42)}
/* Body copy sits over this photograph, so the veil has to carry it. */
/* The contact panel is now a form people have to read and type into, so the
   photograph has to sit right back. It is atmosphere, not a focal point. */
#contact .veil{position:absolute;inset:0;z-index:1;background:linear-gradient(180deg,rgba(7,7,8,.95) 0%,rgba(7,7,8,.90) 30%,rgba(7,7,8,.96) 100%)}
#contact .body{color:rgba(255,255,255,.86)}
/* No top padding here. #contact already carries section padding further down
   this file, and the two stacked: 86px on the section plus 130px on the wrap
   put 291px of dead black between the last FAQ line and "Next step" at
   1900px wide. Measured, not guessed. */
#contact>.wrap{position:relative;z-index:2;padding-top:0;padding-bottom:0}
.foot{
  position:relative;z-index:2;
  display:flex;align-items:flex-end;justify-content:space-between;gap:2rem;flex-wrap:wrap;
  padding:clamp(2.2rem,6vh,3.6rem) var(--pad) clamp(1.6rem,4vh,2.4rem);
  border-top:1px solid var(--line-2);
}
.foot .meta{color:var(--dim-2);text-align:right;line-height:1.9}

/* ---------- responsive ---------- */
@media(max-width:1080px){
  .credit{grid-template-columns:3.2rem minmax(0,1fr) 16rem}
}
@media(max-width:860px){
  .nav-links{display:none}
  /* The links are hidden here, so the burger is the ONLY route to Productions,
     About and Privacy on a phone. Before it existed this breakpoint was a dead
     end: the bar carried the mark and the CTA and nothing else.
     The CTA comes off the bar too and lives inside the menu. Carrying both left
     a 163px pill and a toggle fighting for a 390px bar, and the toggle is what
     the CTA now sits behind. */
  .nav{grid-template-columns:auto 1fr}
  .nav-home{grid-column:1}
  .nav-cta{display:none}
  .nav-burger{grid-column:2;justify-self:end;display:flex}
  .nav-mark{height:28px}
  .credit{grid-template-columns:2.6rem minmax(0,1fr);gap:.55rem 1rem}
  .credit .m{grid-column:2;margin-top:.85rem;gap:.9rem;transform:none}
  .credit .t{transform:none}
  .plate.a,.plate.b{transform:none}
  .svc:nth-child(odd),.svc:nth-child(even){transform:none}
  .svc-frame{transform:none}
  .qa{grid-template-columns:1fr}
  .foot{align-items:flex-start}
  .foot .meta{text-align:left}
}
@media(max-width:520px){
  body{font-size:14.5px}
  .credit .m{grid-template-columns:1fr}
}

@media(prefers-reduced-motion:reduce){
  .hl .w i,.hero-ready .hl .w i{transform:none!important;opacity:1!important;filter:none!important;transition:none!important}
  .rev,.st .w i,.q,.credit .cell{opacity:1!important;transform:none!important;transition:none!important}
  .draw,.credit .sweep,.q .line{transform:scaleX(1)!important;transition:none!important}
  .plate.a,.plate.b,.svc,.svc-frame,.credit .t,.credit .m{transform:none!important}
}

/* ================= multi-page furniture =================
   Three dedicated pages, as sold: Home, About, Past productions.
   Only Home carries the pinned video-village hero, so the inner pages
   need their own top and must not reserve a viewport for one. */
.after--nohero{margin-top:0}

/* The nav is fixed and 73px tall, so this padding is nav clearance PLUS the
   gap. At 11rem it left a 76px empty band under the bar, which read as the
   header being padded twice. 7.5rem gives ~44px of clearance below the nav. */
.pagehead{padding:clamp(5.5rem,11vh,7.5rem) 0 clamp(2.4rem,6vh,4rem);border-bottom:1px solid var(--line)}
.pagehead .ph-t{
  font-family:var(--disp);font-variation-settings:"wdth" 108,"wght" 820;
  font-size:clamp(2rem,6vw,4.6rem);line-height:.98;letter-spacing:-.03em;
  text-transform:uppercase;color:#fff;margin:.9rem 0 0;text-wrap:balance;
}
.pagehead .ph-l{margin-top:1.2rem;max-width:60ch;color:var(--dim);font-size:1.06rem;line-height:1.6;text-wrap:pretty}

.nav-home{display:inline-flex;align-items:center}
/* The right padding is short by half the tracking and the left padding long by
   the same. Letter-spacing adds its gap after the LAST character too, so with
   even padding the visible text sits 0.98px left of the middle of the pill and
   reads off-centre. .09em at this size is that half-gap. */
.nav-cta{
  font-size:.68rem;letter-spacing:.18em;text-transform:uppercase;color:#fff;
  white-space:nowrap;text-align:center;
  padding:.6rem calc(.95rem - .09em) .6rem calc(.95rem + .09em);
}

/* Must sit above and opaque over the pinned hero. Without a background and
   a stacking context the fixed hero's viewfinder rail showed through
   underneath the footer at the bottom of every page. */
.site-foot{position:relative;z-index:1;background:var(--ink);border-top:1px solid var(--line);padding:clamp(1.6rem,3.4vh,2.4rem) 0}
.site-foot .wrap{display:flex;align-items:center;justify-content:space-between;gap:1rem 2rem;flex-wrap:wrap}
.foot-r{display:flex;align-items:center;gap:1.6rem;flex-wrap:wrap;justify-content:flex-end}
.foot-nav{display:flex;gap:1.2rem;flex-wrap:wrap}
.foot-nav a{font-family:var(--mono);font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;color:var(--dim-2)}
.foot-nav a:hover{color:#fff}
.site-foot img{width:clamp(96px,9vw,132px);height:auto;display:block}
/* the one non-clickable string in the footer, sitting to the left of the
   links so everything tappable is grouped at the far right */
.site-foot p{font-size:.7rem;letter-spacing:.12em;text-transform:uppercase;color:var(--dim-2);margin:0}
/* #7a7a7a, not --dim-2. At 11.2px on the #070707 footer, --dim-2 (#6a6a6a)
   measured 3.72:1 against the 4.5:1 WCAG AA minimum. 4.69:1 here, computed
   rather than eyeballed. The links beside it are #fff and already pass.
   Scoped as `.site-foot .foot-txt` (0,2,0) because a bare `.foot-txt`
   (0,1,0) loses to `.site-foot p` (0,1,1) and silently did nothing. */
.site-foot .foot-txt{white-space:nowrap;color:#7a7a7a}
.site-foot a{color:#fff}
.site-foot a:hover{background:var(--accent);-webkit-background-clip:text;background-clip:text;color:transparent}

@media (max-width:640px){
  /* The CTA stays. Hiding it left a phone with a logo and nothing else in the
     bar, and no way to reach the enquiry form from the top of the page. It
     measures 189px against 350px of usable width, so it fits. */
  .nav-cta{font-size:.62rem;letter-spacing:.14em;padding:.55rem calc(.8rem - .07em) .55rem calc(.8rem + .07em)}
  /* 44px minimum touch target (WCAG 2.5.5). .nav-home is ~65x28px here (the
     mark itself is set to 28px tall at the 860px breakpoint above) and
     .nav-cta's pill falls a few px short on height too. An invisible ::after,
     centred on each link and floored at 44px, extends the tappable area
     without touching the box that actually paints, so neither the mark nor
     the pill changes size. Both base rules are (0,1,0) with nothing else
     setting `position` on either, so position:relative here is uncontested;
     the two new ::after rules are (0,1,1), also uncontested. */
  .nav-home,.nav-cta{position:relative}
  .nav-home::after,.nav-cta::after{
    content:"";position:absolute;top:50%;left:50%;
    width:max(100%,44px);height:44px;
    transform:translate(-50%,-50%);
  }
  .foot-r{gap:.5rem 1.1rem}
}

/* ---------- footer, narrow ----------
   On the wide view this is one row: mark, then the grey line and the links
   running to the right edge. Below about 1100px that row cannot hold, and
   `justify-content:flex-end` then pushed each wrapped piece to a different
   right edge, so the grey line sat indented while the links underneath sat
   flush left. That mismatch is the whole defect.
   Here it stacks deliberately instead: mark, then three lines all starting on
   the mark's own left edge. Nothing is centred and nothing is right-aligned,
   so every line shares one margin. */
@media(max-width:760px){
  .site-foot .wrap{flex-direction:column;align-items:flex-start;gap:1.1rem}
  .foot-r{
    flex-direction:column;align-items:flex-start;justify-content:flex-start;
    gap:.75rem;width:100%;
  }
  .foot-nav{gap:.4rem 1.1rem}
}

/* ================= enquiry form =================
   Replaces the bare mailto. Captures what a service producer needs before
   they can answer with a real number: what, where, when, who is travelling
   and which parts of the job they want. Posts to brief.php, which is PHP
   because the site sits on the client's existing Bluehost shared hosting.
   No Node, no third-party form service, no new bill. */
.enq{margin-top:clamp(2rem,5vh,3.2rem);max-width:1000px;margin-inline:auto}
.enq-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1.1rem 1.6rem}
.f{display:flex;flex-direction:column;gap:.5rem}
.f-wide{margin-top:1.6rem}
.fl{
  font-family:var(--mono);font-size:.64rem;letter-spacing:.18em;text-transform:uppercase;
  color:var(--dim-2);
}
.fl em{font-style:normal;color:rgba(255,255,255,.28)}
.enq input,.enq select,.enq textarea{
  width:100%;font:inherit;font-size:.95rem;color:#fff;
  background:rgba(255,255,255,.04);
  border:1px solid var(--line);border-radius:8px;
  padding:.85rem .95rem;transition:border-color .3s ease,background .3s ease;
}
.enq textarea{resize:vertical;min-height:110px}
.enq input::placeholder,.enq textarea::placeholder{color:rgba(255,255,255,.3)}
.enq input:focus,.enq select:focus,.enq textarea:focus{
  outline:none;border-color:var(--mag);background:rgba(255,255,255,.07);
}
.enq select{appearance:none;cursor:pointer;
  background-image:linear-gradient(45deg,transparent 50%,rgba(255,255,255,.5) 50%),linear-gradient(135deg,rgba(255,255,255,.5) 50%,transparent 50%);
  background-position:calc(100% - 19px) calc(50% + 2px),calc(100% - 13px) calc(50% + 2px);
  background-size:6px 6px,6px 6px;background-repeat:no-repeat}
.enq select option{background:#111;color:#fff}

.f-set{border:0;padding:0;margin:1.7rem 0 0}
/* Uniform cells, not ragged pills. Two columns on a phone, three from tablet up.
   SIX chips, and six is the reason there is no :last-child span anywhere here:
   it divides exactly by both 2 and 3, so every row is full at both widths.

   Worth knowing before changing the count. It was nine, which also divided by
   three, and the wide view had no span for that reason. The client's revision
   cut casting, equipment hire and post-production, which left seven, and seven
   is prime: "Full service" stranded alone in a third row. A span was added to
   close that, and then dropping the fixer-services chip took it to six, at which
   point the span itself created the hole it had been added to fix. The rule is
   the count, not the span: pick a chip count that divides by 2 and by 3. */
.chips{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.55rem;margin-top:.75rem}
@media (min-width:768px){
  .chips{grid-template-columns:repeat(3,minmax(0,1fr))}
  .chip span{white-space:nowrap}
}
/* the label is what a finger actually hits, and it was inline, so its box
   followed line-height (42px) regardless of the pill inside it. */
.chip{position:relative;cursor:pointer;display:flex;align-items:stretch;min-height:44px}
.chip input{position:absolute;opacity:0;width:0;height:0}
.chip span{
  /* inline-flex with a 44px floor, not inline-block: these measured 42px tall,
     2px under the WCAG 2.5.5 minimum for a tappable control, and there are nine
     of them on a phone. The pill still hugs its label horizontally. */
  display:flex;align-items:center;justify-content:center;text-align:center;
  width:100%;min-height:44px;
  padding:.55rem .95rem;border:1px solid var(--line);border-radius:999px;
  font-size:.82rem;color:var(--dim);transition:all .28s ease;
}
.chip input:checked + span{background:var(--accent);border-color:transparent;color:#0a0a0b;font-weight:600}
.chip input:focus-visible + span{outline:2px solid var(--mag);outline-offset:2px}

.enq-foot{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;margin-top:1.9rem}
.btn-send{
  font:inherit;font-size:.72rem;letter-spacing:.18em;text-transform:uppercase;font-weight:600;
  padding:1rem 1.7rem;transition:transform .3s ease;
}
.btn-send:hover{transform:translateY(-2px)}
/* .btn-wa and .wa-i lived here and are gone with the WhatsApp buttons the
   client removed in the 2026-08-14 revision. Nothing referenced them. */

/* direct email: a producer who will not fill in a form should not have to hunt
   for the address. Her accent on hover. */
.btn-mail{
  display:inline-flex;align-items:center;gap:.6rem;
  font-size:.72rem;letter-spacing:.15em;text-transform:uppercase;color:var(--dim);
  border:1px solid var(--line);border-radius:8px;padding:.95rem 1.3rem;
  transition:border-color .3s ease,color .3s ease;
}
.btn-mail:hover{border-color:var(--mag);color:#fff}
.mail-i{width:15px;height:11px;flex:none;border:1.5px solid currentColor;border-radius:2px;position:relative}
.mail-i::after{content:"";position:absolute;inset:-1.5px auto auto -1.5px;width:15px;height:11px;
  background:linear-gradient(135deg,transparent 46%,currentColor 46%,currentColor 54%,transparent 54%)}
.enq-note{margin-top:1.1rem;font-size:.64rem;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.34)}

@media (max-width:760px){
  .enq-grid{grid-template-columns:1fr}
  .enq-foot{flex-direction:column;align-items:stretch}
  .btn-send,.btn-mail{width:100%;text-align:center;justify-content:center}
}
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* Single call to action on the brochure pages. The form itself lives on its
   own page, so nothing competes with it and the other pages stay a read. */
.btn-start{
  display:inline-block;margin-top:clamp(1.6rem,4vh,2.4rem);
  font-family:var(--mono);font-size:.74rem;letter-spacing:.18em;text-transform:uppercase;font-weight:500;
  padding:1.15rem 2rem;
}

/* ---------- buttons: travelling border light ----------
   One hairline of light walks the whole perimeter, rather than stepping between
   four fixed positions. A conic gradient whose start angle is an animated
   custom property does it in CSS alone, with no per-frame JS.

   @property is what makes that angle animatable: registered as an <angle> it
   interpolates, and without the registration it is just a string, so the
   keyframe does nothing and the ring sits still. That is the fallback, and it
   is a perfectly good resting state on an old browser.

   The face is a solid plate inset by 1px, so only the outer hairline shows the
   light. Both plate and ring sit at negative z-index inside an isolated
   stacking context, which puts them behind the label without needing a wrapper
   element in the markup on all seven pages.

   No orange. The accent gradient's orange end read as tacky at button size, so
   the light is white at rest and her magenta on hover. */
@property --hbg-a{syntax:"<angle>";inherits:false;initial-value:0deg}
@keyframes hbg-travel{to{--hbg-a:360deg}}

.btn-start,.btn-send,.nav-cta,.nav-menu .nav-menu-cta{
  position:relative;z-index:0;isolation:isolate;
  border:0;background:none;color:#fff;
  border-radius:999px;cursor:pointer;
  transition:color .35s ease;
}
.btn-start::before,.btn-send::before,.nav-cta::before,.nav-menu .nav-menu-cta::before{
  content:"";position:absolute;inset:0;z-index:-2;border-radius:inherit;
  background:conic-gradient(from var(--hbg-a),
    rgba(255,255,255,.11) 0deg,rgba(255,255,255,.11) 296deg,
    #fff 338deg,rgba(255,255,255,.11) 360deg);
  animation:hbg-travel 4s linear infinite;
}
.btn-start::after,.btn-send::after,.nav-cta::after,.nav-menu .nav-menu-cta::after{
  content:"";position:absolute;inset:1px;z-index:-1;border-radius:inherit;
  background:#0a0a0c;transition:background .35s ease;
}
.btn-start:hover::before,.btn-send:hover::before,.nav-cta:hover::before,
.nav-menu .nav-menu-cta:hover::before{
  background:conic-gradient(from var(--hbg-a),
    rgba(228,25,126,.30) 0deg,rgba(228,25,126,.30) 280deg,
    #E4197E 336deg,rgba(228,25,126,.30) 360deg);
}
.btn-start:hover::after,.btn-send:hover::after,.nav-cta:hover::after,
.nav-menu .nav-menu-cta:hover::after{background:#121016}
@media(prefers-reduced-motion:reduce){
  .btn-start::before,.btn-send::before,.nav-cta::before,
  .nav-menu .nav-menu-cta::before{animation:none}
}

/* ================= self-hosted type =================
   The Google Fonts <link> is gone from the pages. Two files, 87.4KB together,
   served from the client's own Bluehost account: no third-party connection on
   first paint, no DNS and TLS to fonts.gstatic.com before any text can render.
   swap so the copy is readable while they arrive. */
@font-face{
  font-family:"Archivo";
  font-style:normal;
  font-weight:300 900;
  font-stretch:62% 125%;
  font-display:swap;
  src:url("../assets/fonts/archivo-variable-latin.woff2") format("woff2");
}
@font-face{
  font-family:"Fragment Mono";
  font-style:normal;
  font-weight:400;
  font-display:swap;
  src:url("../assets/fonts/fragment-mono-latin.woff2") format("woff2");
}

/* Thanks page: the two routes sit side by side, the credits and a direct email. */
.thanks-actions{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;margin-top:clamp(1.6rem,4vh,2.4rem)}
.thanks-actions .btn-start{margin-top:0}
@media (max-width:560px){
  .thanks-actions{flex-direction:column;align-items:stretch}
  /* One button here now. The WhatsApp one was removed at the client's request
     and nothing replaced it, because she did not ask for a replacement. */
  .thanks-actions .btn-start{width:100%;text-align:center;justify-content:center}
}

/* ================= section rhythm =================
   Every section padded top AND bottom, so each shared boundary paid twice:
   measured gaps of 78, 56, 104 and 118px between visible content on Home.

   These MUST be written at ID specificity. The first attempt used
   `main > section + section`, which loses to the existing `#work`, `#clients`,
   `#services` rules every time, so it changed nothing and the gaps grew.
   ID selectors beat element selectors regardless of source order. */
#work, #credits, #services, #questions, #location{
  padding-top:clamp(3.2rem,7vh,5.5rem);
  padding-bottom:clamp(2.6rem,5vh,4rem);
}
/* these sit on their own photograph or fill, where the space is the design */
/* #clients keeps the full rhythm: it carries a photograph and needs the air.
   #contact does not. Whatever precedes the CTA band already pays a
   padding-bottom, so 86px on top of that put 140px of dead black between
   the last FAQ line and "Next step". The band has its own ground, so it
   does not need to be pushed away from the section above as well. */
#clients{
  padding-top:clamp(4.5rem,10vh,7.5rem);
  padding-bottom:clamp(4.5rem,10vh,7.5rem);
}
#contact{padding-top:clamp(1.8rem,3.6vh,2.7rem);padding-bottom:clamp(2.4rem,5vh,3.6rem)}
  /* The page head was eating 143px above the fold before the form began, which
     on a phone pushed the first field most of a screen down. The heading and
     the lead still lead the page, they just stop reserving desktop air. */
  .pagehead{padding:clamp(4.4rem,8vh,5.4rem) 0 clamp(1.3rem,3vh,1.9rem)}
  .pagehead .ph-l{margin-top:.85rem;font-size:1rem}
@media (max-width:640px){
  #work, #credits, #services, #questions, #location{padding-top:2.6rem;padding-bottom:2rem}
  #clients, #contact{padding-top:3.4rem;padding-bottom:3.4rem}
}

/* ---------- provisional values ----------
   Two credits are cleared to publish but their studio and broadcaster have not
   come back from the client, and there is no title card for either. Rather than
   invent a chain for two real shows, the fields carry a placeholder that is
   deliberately obvious. This MUST be gone before launch: replace the values and
   delete the class. Grep for "tbc" to find every one. */
.tbc{
  color:var(--org);
  font-style:normal;
  opacity:.85;
}
.tbc::after{content:" *";opacity:.7}
