/* ===== FONTS ===== */
@font-face{
  font-family:'Sora';
  src:url('../fonts/Sora-VariableFont_wght.ttf') format('truetype-variations');
  font-weight:100 800;font-style:normal;font-display:swap;
}
@font-face{
  font-family:'Inter';
  src:url('../fonts/Inter-Variable.ttf') format('truetype-variations');
  font-weight:100 900;font-style:normal;font-display:swap;
}

/* ===== TOKENS ===== */
:root{
  /* Pure black and pure white are the primary pair. Red is the accent:
     it marks, highlights and calls to action, and never carries a surface.
     The grey ramp is neutral by design - any hue in it would read as a cast
     against a #000/#FFF base. */
  --bg:#000000;
  --black:#000000;
  --red:#E31E24;
  --red-deep:#B3171C;
  /* The panel steps are wider than their light-mode counterparts were, 16 and
     26 points off the ground against 10 and 20 the other way, because a step
     of equal size reads smaller in the shadows: #0A0A0A on #000 is almost
     invisible where #F5F5F5 on #FFF was a clear surface. */
  --panel:#101010;
  --panel-2:#1A1A1A;
  --ink:#FFFFFF;
  --ink-2:#B4B4B4;   /* 10.1:1 on black - body-weight secondary copy */
  --ink-3:#8C8C8C;   /* 6.2:1 on black; it carries the small mono labels */
  --line:#2A2A2A;
  /* square everywhere — no rounded corners on any section, card, chip or button */
  --r:0;
  --r-lg:0;
  --r-img:0;
  --pad:clamp(1.25rem,5vw,4.5rem);
  --sec:clamp(4.5rem,10vw,9rem);

  --display:'Sora',system-ui,-apple-system,'Segoe UI',sans-serif;
  --body:'Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  /* System stack: no third webfont to download. Carries the labels, counters
     and specs - the parts of the page that are data rather than prose. */
  --mono:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,
         'Liberation Mono',monospace;

  /* SORA — display scale. px targets: 96 / 80 / 64 / 56 / 44 / 32 / 24
     clamp() floors them for small screens; the max is the spec value. */
  --d-xl:clamp(2.75rem,6.2vw,6rem);      /* Display XL  96 / .95 / 600 */
  --d-l:clamp(2.5rem,5.4vw,5rem);        /* Display L   80 / .98 / 600 */
  --d-m:clamp(2.1rem,4.4vw,4rem);        /* Display M   64 / 1.00 / 600 */
  --t-h1:clamp(2rem,3.9vw,3.5rem);       /* H1          56 / 1.05 / 600 */
  --t-h2:clamp(1.7rem,3vw,2.75rem);      /* H2          44 / 1.10 / 600 */
  --t-h3:clamp(1.35rem,2.2vw,2rem);      /* H3          32 / 1.15 / 500 */
  --t-h4:clamp(1.15rem,1.6vw,1.5rem);    /* H4          24 / 1.20 / 500 */

  /* INTER — text scale. px: 20 / 17 / 15 / 15 / 12 / 12 */
  --body-l:1.25rem;                      /* Body Large  20 / 1.55 / 400 */
  --body-m:1.0625rem;                    /* Body Medium 17 / 1.55 / 400 */
  --body-s:.9375rem;                     /* Body Small  15 / 1.50 / 400 */
  --btn:.9375rem;                        /* Button      15 / 1.00 / 500 */
  --label:.75rem;                        /* Label       12 / 1.20 / 500 */
  --caption:.75rem;                      /* Caption     12 / 1.40 / 400 */

  /* Height of the fixed nav. nav.js measures the real bar and writes
     --nav-real, because the height moves with base font size, zoom and
     breakpoint - a hard-coded value drifts and leaves a gap or an overlap.
     The 74px here is only the pre-JS fallback. */
  --nav-real:74px;
  --nav-h:var(--nav-real);
}
/* tells the UA the page is dark: scrollbars, form controls and the
   autofill wash all follow it, which no amount of CSS on our side can do */
html{color-scheme:dark;}
*{box-sizing:border-box;margin:0;padding:0;}
html{scroll-behavior:smooth;}
body{
  background:var(--bg);color:var(--ink);
  font-family:var(--body);font-size:var(--body-m);font-weight:400;line-height:1.55;
  -webkit-font-smoothing:antialiased;
  /* hidden first as the fallback: browsers that know `clip` take it, and clip
     does not create a scroll container, which is what keeps sticky working */
  overflow-x:hidden;overflow-x:clip;
}
body.is-loading{overflow:hidden;}
a{color:inherit;text-decoration:none;}
img{max-width:100%;display:block;}
::selection{background:var(--red);color:#fff;}

/* ===== TYPE ===== */
h1,h2,h3,h4,.display{font-family:var(--display);letter-spacing:-.03em;}
h1{font-size:var(--d-xl);line-height:.95;font-weight:600;}
h2{font-size:var(--d-l);line-height:.98;font-weight:600;}
h2.d-m{font-size:var(--d-l);line-height:.98;}
h3{font-size:var(--t-h3);line-height:1.15;font-weight:500;letter-spacing:-.02em;}
h4{font-size:var(--t-h4);line-height:1.2;font-weight:500;letter-spacing:-.02em;}
.lede{font-size:var(--body-m);line-height:1.55;color:var(--ink-2);max-width:52ch;}
.caption{font-size:var(--caption);line-height:1.4;font-weight:400;color:var(--ink-3);}
h1 em,h2 em,h3 em,h4 em{font-style:normal;color:var(--red);}

/* ===== IMAGE SLOT (developer marker — deleted when real media lands) ===== */
.slot{
  background:var(--panel-2);border-radius:0;
  display:flex;align-items:center;justify-content:center;
  position:relative;overflow:hidden;
}
.slot-label{
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-3);text-align:center;padding:.6rem 1rem;
}
.slot-label b{display:block;color:var(--ink-2);font-weight:500;margin-bottom:.35rem;}

/* --- placeholder media ---------------------------------------------------
   Swap each <img>/<video> src for the real asset. The .slot-label badge
   still carries the original brief; delete the rule below to hide them all. */
.slot img,.slot video{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;z-index:0;}
/* A 16:9 clip in the 2:3 card left ~40% black, which reads as broken. The card
   is a teaser, so it crops to fill; the detail page still shows the full frame. */
.slot video.is-landscape{object-fit:cover;}
/* the cards used to crop their media and drift it inside the frame. They
   stage it whole now, so there is nothing to drift and the rule is gone. */
/* Cards carry no text over the image. The briefs stay in the DOM so you can
   still see which asset each slot wants — add class="briefs" to <body> to
   reveal them while swapping, remove it to go back to clean cards. */
.slot-label{display:none;}
body.briefs .slot-label{
  display:block;position:absolute;left:0;bottom:0;z-index:2;text-align:left;
  background:rgba(10,10,10,.72);color:rgba(255,255,255,.82);
  padding:.42rem .6rem;max-width:100%;
  backdrop-filter:blur(5px);-webkit-backdrop-filter:blur(5px);}
body.briefs .slot-label b{color:#fff;}

/* ===== EYEBROW PILL ===== */
.eyebrow{
  display:inline-flex;align-items:center;gap:.5rem;
  background:var(--panel);border:1px solid var(--line);border-radius:0;
  padding:.45rem .9rem;
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.02em;color:var(--ink);
}
.eyebrow::before{content:"";width:6px;height:6px;background:var(--red);flex-shrink:0;}
.eyebrow + h2{margin-top:1.2rem;}

/* ===== ARROW LINK ===== */
.alink{display:inline-flex;flex-direction:column;gap:.45rem;align-items:flex-start;}
.alink .row{display:inline-flex;align-items:center;gap:.9rem;
  font-size:var(--btn);line-height:1;font-weight:500;}
.alink .ar{font-size:1rem;transition:transform .35s cubic-bezier(.16,1,.3,1);}
.alink:hover .ar{transform:translate(3px,-3px);}
.alink .mk{display:flex;align-items:center;gap:4px;}
.alink .mk::before{content:"";width:3px;height:3px;background:var(--red);}
.alink .mk::after{content:"";width:18px;height:2px;background:var(--red);transition:width .35s cubic-bezier(.16,1,.3,1);}
.alink:hover .mk::after{width:30px;}

/* ===== TAG / BUTTON ===== */
.tag{
  display:inline-flex;align-items:center;border:1px solid var(--line);border-radius:0;
  padding:.4rem .85rem;font-size:var(--label);line-height:1.2;font-weight:500;
  color:var(--ink-2);white-space:nowrap;
}
.btn{display:inline-flex;align-items:center;gap:.6rem;border-radius:0;
  padding:.9rem 1.6rem;font-size:var(--btn);line-height:1;font-weight:500;
  border:1px solid var(--ink);transition:all .3s;}
.btn.solid{background:var(--red);color:#fff;border-color:var(--red);}
.btn.solid:hover{background:var(--red-deep);border-color:var(--red-deep);}
.btn.ghost{background:none;color:var(--ink);border-color:var(--line);}
.btn.ghost:hover{border-color:var(--red);color:var(--red);}

/* ===== REVEALS ===== */
.r{opacity:1;transform:none;}
body.reveals-ready .r{opacity:0;transform:translateY(16px);
  transition:opacity .8s cubic-bezier(.16,1,.3,1),transform .8s cubic-bezier(.16,1,.3,1);}
body.reveals-ready .r.in{opacity:1;transform:none;}
.d1{transition-delay:.06s}.d2{transition-delay:.12s}.d3{transition-delay:.18s}
@media(prefers-reduced-motion:reduce){
  body.reveals-ready .r{opacity:1;transform:none;transition:none;}
  .tick-track{animation:none !important;}
  #loader .ldr-bar::after{animation:none;transform:translateX(0);}
  .hero-meta .rec{animation:none;}
}

/* ===== LOADER ===== */
#loader{position:fixed;inset:0;z-index:9999;background:var(--bg);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1.6rem;}
.ldr-word{font-family:var(--display);font-size:var(--t-h4);line-height:1.2;font-weight:600;letter-spacing:-.02em;}
/* match the nav wordmark: .ai upright and red, not italic and black */
.ldr-word em{font-style:normal;color:var(--red);}
.ldr-bar{width:110px;height:2px;background:var(--line);overflow:hidden;border-radius:0;}
.ldr-bar::after{content:"";display:block;height:100%;background:var(--red);
  transform:translateX(-100%);animation:ldrFill 1.7s cubic-bezier(.6,0,.2,1) forwards;}
@keyframes ldrFill{to{transform:translateX(0);}}
.ldr-count{font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.1em;color:var(--ink-3);}
.ldr-count b{font-weight:500;color:var(--red);}

/* ===== NAV ===== */
#nav{position:fixed;top:0;left:0;right:0;z-index:900;
  display:flex;justify-content:space-between;align-items:center;
  padding:1.15rem var(--pad);
  /* On an all-black page a solid bar is invisible against the hero behind
     it. Translucent with a blur instead: content sliding underneath is
     readable as movement, which is the only cue left that the bar is fixed.
     The flat colour above is the fallback where backdrop-filter is not. */
  background:var(--black);
  background:rgba(0,0,0,.72);
  /* halved, and no saturate: this region is re-filtered every frame as the
     page moves under it */
  -webkit-backdrop-filter:blur(8px);
  backdrop-filter:blur(8px);
  border-bottom:1px solid rgba(255,255,255,.12);
  transition:background .35s ease,transform .35s cubic-bezier(.16,1,.3,1),
             opacity .45s ease;}
/* .solid is still toggled by nav.js on scroll; the bar is one colour now, so
   it has nothing left to change */
/* reading downward: clear the bar out of the way. it comes straight back on
   any upward scroll, so the CTA is never more than a flick away */
#nav.hide{transform:translateY(-100%);}
/* set on <html> alongside #nav.hide */
html.nav-hidden{--nav-h:0px;}
.brand{font-family:var(--display);font-size:var(--t-h4);line-height:1.2;font-weight:600;letter-spacing:-.025em;}
.brand em{font-style:normal;color:var(--red);}
/* ===== CROSS-LINK TO THE .COM STUDIO ===== */
.brand-group{display:flex;align-items:center;gap:.7rem;min-width:0;}
/* The one rounded element on the site. Everything else is square by rule
   (--r:0); this is shaped to match the pill the .com site uses to point back
   here, so the pair reads as one system across two domains. */
.x-cta{display:inline-flex;align-items:center;gap:.5rem;flex-shrink:0;
  border:1px solid var(--line);border-radius:999px;
  padding:.5rem .95rem;font-size:var(--btn);line-height:1;font-weight:500;
  /* transparent, not --bg: a solid black pill on a bar that is now
     translucent would print as a hard black blob over whatever is blurring
     through behind it */
  color:var(--ink);background:none;white-space:nowrap;
  transition:border-color .3s ease,background .3s ease;}
.x-cta:hover{border-color:var(--ink);background:rgba(255,255,255,.09);}
.x-cta .dot{width:7px;height:7px;border-radius:50%;background:var(--red);
  flex-shrink:0;}
.x-cta .ar{font-size:.95em;color:var(--ink-2);
  transition:transform .3s cubic-bezier(.16,1,.3,1),color .3s;}
.x-cta:hover .ar{transform:translate(2px,-2px);color:var(--ink);}

/* ===== LOGOMARK ===== */
/* sized in em so it tracks the wordmark at every breakpoint. Decorative:
   the text beside it already carries the name for screen readers. */
.brand,.ldr-word{display:inline-flex;align-items:center;gap:.5rem;}
.brand .mark,.ldr-word .mark{height:1.25em;width:auto;flex-shrink:0;}
/* The mark is a near-black nib with a red tip. On a black bar the body
   disappears, and invert() would take the red to cyan, so the two places it
   appears on screen load logomark-light.png instead: the same artwork with
   the body remapped to white and the tip untouched. logomark.png stays as it
   is, because it is also the Organization logo in the JSON-LD and Google
   renders that on a white card. */

.navlinks{display:flex;gap:1.9rem;align-items:center;
  font-size:var(--btn);line-height:1;font-weight:500;}
.navlinks a{white-space:nowrap;color:var(--ink);transition:opacity .25s,color .4s ease,border-color .4s ease,background .3s;}
.navlinks a:not(.nav-cta):hover{color:var(--red);opacity:1;}
.nav-cta{border:1px solid var(--red);color:var(--red);padding:.6rem 1.1rem;transition:all .3s;}
.nav-cta:hover{background:var(--red);color:#fff;}
/* No #nav overrides: the base rules invert with the tokens - white wordmark
   with a red .ai, white links that go red on hover, and a red outline CTA. */
.brand,.brand em{transition:color .4s ease;}
/* The pill stays in the bar at every width. On a phone it tightens instead of
   leaving: smaller type and padding so it sits between the wordmark and the
   menu button. The in-menu copy (.x-cta-m) is retired; the bar always has it. */
.x-cta-m{display:none;}
@media(max-width:520px){
  .brand-group{gap:.5rem;}
  .brand{font-size:.86rem;}
  .x-cta{gap:.32rem;padding:.38rem .6rem;font-size:.68rem;}
  .x-cta .dot{width:5px;height:5px;}
  /* the pill draws small, but the tap area still reaches 44px tall */
  .x-cta{position:relative;}
  .x-cta::after{content:"";position:absolute;left:0;right:0;top:50%;
    height:44px;transform:translateY(-50%);}
  .nav-cta{white-space:nowrap;padding:.5rem .7rem;font-size:.8rem;}
  .navlinks{gap:.6rem;}
}
/* the narrowest phones: the arrow goes, the label stays */
@media(max-width:374px){
  .brand{font-size:.8rem;}
  .brand-group{gap:.4rem;}
  .x-cta{padding:.36rem .5rem;}
  .x-cta .ar{display:none;}
}
/* ===== MOBILE MENU =====
   Below 860px the links were display:none - Work, What We Make and FAQ did
   not exist on a phone. The same .navlinks element becomes a panel instead,
   rather than a second list: this nav is lifted onto every other page, and
   two copies would be two things to keep in step. */
.nav-toggle{display:none;}
/* a smaller laptop: the bar keeps every link, just closer together */
@media(min-width:1181px) and (max-width:1360px){.navlinks{gap:1.25rem;}}
/* Work + Showcase on desktop; a single "Work" in the phone menu */
.navlinks .nl-m{display:none;}
/* 1180, not 860: with Work and Showcase both in the bar, seven links plus
   the wordmark and the Brand Studio pill stop fitting below ~1180px and
   start wrapping into each other. Keep in step with nav.js. */
@media(max-width:1180px){
  /* id-strength: the menu's own link rule below would otherwise win */
  #nav .navlinks .nl-d{display:none;}
  .navlinks .nl-m{display:inline;}
  :root{--nav-real:60px;}   /* fallback only; JS measures */
  #nav{padding:.9rem var(--pad);}
  .brand{font-size:1rem;white-space:nowrap;}

  .nav-toggle{display:inline-flex;align-items:center;justify-content:center;
    width:44px;height:44px;margin-right:-.6rem;flex-shrink:0;
    background:none;border:0;padding:0;color:var(--ink);cursor:pointer;
    position:relative;z-index:2;}
  .nt-box{display:block;width:22px;height:13px;position:relative;}
  .nt-box i{position:absolute;left:0;right:0;height:1.5px;
    background:currentColor;
    transition:transform .32s cubic-bezier(.16,1,.3,1);}
  .nt-box i:first-child{top:0;}
  .nt-box i:last-child{bottom:0;}
  /* the two rules meet in the middle and cross */
  body.nav-open .nt-box i:first-child{transform:translateY(5.75px) rotate(45deg);}
  body.nav-open .nt-box i:last-child{transform:translateY(-5.75px) rotate(-45deg);}

  .navlinks{position:fixed;left:0;right:0;top:var(--nav-real);
    height:calc(100svh - var(--nav-real));
    flex-direction:column;align-items:stretch;justify-content:center;
    gap:0;padding:0 var(--pad) 12vh;
    background:rgba(0,0,0,.97);
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    overflow-y:auto;overscroll-behavior:contain;
    opacity:0;pointer-events:none;transform:translateY(-10px);
    transition:opacity .3s ease,transform .35s cubic-bezier(.16,1,.3,1);}
  body.nav-open .navlinks{opacity:1;transform:none;pointer-events:auto;}
  /* the links come back, at a size a thumb can hit without aiming */
  .navlinks a:not(.nav-cta):not(.x-cta-m){display:flex;align-items:center;
    font-family:var(--display);font-size:var(--t-h3);font-weight:500;
    letter-spacing:-.025em;padding:1.15rem 0;
    border-bottom:1px solid var(--line);}
  .navlinks .nav-cta{margin-top:2.2rem;justify-content:center;
    padding:1.15rem 1.4rem;font-size:var(--body-m);}
  /* the page behind must not scroll under an open panel */
  body.nav-open{overflow:hidden;}
}
@media(max-width:380px){
  /* the page gutter is generous for reading but costs the nav 40px it needs */
  #nav{padding-left:.85rem;padding-right:.85rem;}
  .brand{gap:.32rem;}
  .brand .mark{height:1.1em;}
  /* group + CTA measured 293px against 293px of room at 320w — the switch
     gives back the ~13px that stops them touching */
  .brand-group{gap:.3rem;}
  .brand{font-size:.8rem;}
  .nav-cta{padding:.5rem .6rem;font-size:.75rem;}
}

/* ===== FILM CARD AS AN INSTRUMENT PANEL =====
   An index, corner ticks and a mono readout. The ticks are drawn with two
   gradients on one pseudo-element rather than four elements, and only the
   readout moves on hover, so a grid of fifteen cards stays cheap. */


/* ===== GRAIN =====
   One fixed layer of fractal noise over the whole page. Film grain and sensor
   noise at once, which is the right texture for a studio that renders rather
   than shoots. Generated by an inline SVG filter, so it is a few hundred bytes
   and resolution-independent. z-index 5 clears page content (which tops out at
   4) and stays under the sticky filters, the nav and the loader. */
body::before{content:"";position:fixed;inset:0;z-index:5;pointer-events:none;
  /* screen, not a flat veil: noise over #000 lifts speckle into the shadows
     the way film grain does, while white stays white. A plain 5% overlay
     greyed every light section and dulled the red. */
  /* .14 was tuned on a page that was mostly white, where screen blending
     hid the noise everywhere except the hero. On an all-black page every
     pixel takes it, so it comes down to where it reads as emulsion rather
     than as a crawling overlay. */
  /* No blend mode. A blended fixed layer stops the viewport being
     composited as a scrolled layer - it has to be re-blended every frame,
     which is the single most expensive thing a full-page overlay can do.
     screen was chosen when the page was mostly white, to keep the noise
     from greying it; over black it is indistinguishable from normal. */
  opacity:.055;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");background-size:160px 160px;}
@media print{body::before{display:none;}}

/* ===== TEXT REVEAL =====
   decode.js splits the text; the reveal class starts it. Glyphs keep their
   final shapes and only change opacity, blur and offset, so nothing reflows
   while it runs.

   Visible by default in both modes. The hidden state lives in the keyframes
   with a backwards fill, so a line whose reveal never fires still reads - an
   opacity:0 base made the animation a single point of failure for the H1.

   `.in .dc` rather than `h1.in .dc`: the element carrying .r is sometimes the
   text itself (every h2) and sometimes the block around it (a service, an
   engage statement), and a descendant selector covers both. */
.dw{display:inline-block;}
.dc{display:inline-block;}

/* char - display headlines resolving out of blur */
@keyframes dcIn{
  from{opacity:0;filter:blur(12px);transform:translateY(.1em);}
  to{opacity:1;filter:blur(0);transform:none;}}
.in .dc{animation:dcIn .55s cubic-bezier(.2,.75,.2,1) backwards;
  animation-delay:calc(var(--i) * 15ms);}

/* word - everything else that should arrive rather than appear. No blur:
   at label and sub-heading sizes it reads as a focus error rather than as
   a render settling, and it is the expensive half of the effect. */
@keyframes dwIn{
  from{opacity:0;transform:translateY(.38em);}
  to{opacity:1;transform:none;}}
.in .dwa{animation:dwIn .5s cubic-bezier(.2,.8,.2,1) backwards;
  animation-delay:calc(var(--i) * 30ms);}

/* ===== TECHNICAL LAYER =====
   Everything here is metadata the page states about itself: section numbers,
   formats, durations, model names, categories, field labels. Setting it in
   mono separates it from the prose and gives the studio's technical claim a
   voice in the typography rather than only in the copy. */
.eyebrow,.caption,.clients-lab,.ldr-count,.fp-next-head,.next-cat,
.fp-crumb,.hero-meta,.tag,
.fd-details dt,.fp-specs li span,.contact-links .lab{
  font-family:var(--mono);
  font-weight:400;
  letter-spacing:.04em;
}
/* mono runs wide, so the two fixed-width label columns need the room back */
.fd-details dt{width:132px;}
.fd-details dd{padding-left:144px;}
.fp-specs li span{flex:0 0 116px;}
@media(max-width:600px){.fd-details dd{padding-left:0;}}


/* ===== TILES: THE LAST WORD ON THE DATA BLOCKS =====
   Written at four classes and placed after the band rules, because at
   three it tied with `.film-card:not(.is-wide) .fs-l` and lost on source
   order - which painted that rule's 60px black gradient down the left
   quarter of every portrait tile.

   A tile has no bands. Its frame is the clip's own ratio, so there is no
   dead space to scrim and nothing for these blocks to sit in: the left one
   shrinks to the spec flag in the top corner and the right one goes. */
.film-grid.tiles .film-card .fs-l,
.film-grid.tiles .film-card .fs-r{
  position:absolute;top:0;bottom:auto;height:auto;
  width:auto;max-width:none;background:none;
  justify-content:flex-start;gap:.4rem;
  padding:clamp(.6rem,1vw,.9rem);}
.film-grid.tiles .film-card .fs-l{left:0;right:auto;}
.film-grid.tiles .film-card .fs-r{display:none;}

/* ===== SECTION SHELL ===== */
/* .62 because adjacent sections stack top+bottom padding — keeps the visible
   gap between sections at ~1.25x --sec rather than 2x */
/* the anchor clearance follows the bar nav.js measures, instead of a
   hard-coded 72 that was under it at 74 and over it at 60 */
section{padding:calc(var(--sec)*.62) var(--pad);
  scroll-margin-top:calc(var(--nav-real) + 1rem);}
.sec-head{display:flex;justify-content:space-between;align-items:flex-end;gap:2rem;flex-wrap:wrap;}
.sec-head h2{margin-top:1.1rem;}

/* ===== HERO — red gradient field ===== */
/* Two gradient layers only — a soft bloom over a red→deep-red ramp. It stays
   in brand red rather than crashing to black, and carries no shader canvas,
   no scrim and no backdrop-blur, so the weight is paint-only. */
/* flat, not a gradient: the fixed nav is the same solid black, and any ramp
   underneath it showed as a seam where the two met */
/* A measuring grid and corner brackets: a camera viewfinder and a technical
   drawing at once, which is what the studio does. Painted in CSS gradients,
   so it costs no request and no markup. */
.hero{
  position:relative;color:#fff;
  /* One viewport exactly, so nothing of the next section shows under it.
     svh rather than vh: on mobile, vh is the *large* viewport and the hero
     would run under the browser's own chrome.

     A flex column because min-height leaves slack on a tall display, and
     slack at the bottom of a hero reads as a mistake. The foot takes it
     with margin:auto instead. */
  display:flex;flex-direction:column;
  min-height:100vh;min-height:100svh;
  /* measured from the nav rather than guessed at: the old 7.5rem floor was
     larger than the bar it was clearing, and on a short viewport that floor
     still applied at full size */
  padding:calc(var(--nav-real) + clamp(1.1rem,8vh,5rem)) var(--pad)
          clamp(1.6rem,6vh,3.5rem);
  /* flat black: the grid moved onto the film layer below, and this is what
     shows in the moment before the poster frame paints */
  background:var(--black);
}

/* ===== HERO FILM =====
   The studio's own bridal montage, running silent behind the headline. */
.hero-bg{position:absolute;inset:0;z-index:0;overflow:hidden;}
.hero-bg video{display:block;width:100%;height:100%;object-fit:cover;}
/* Scrim and measuring grid in one layer, both over the footage.

   The ramp is left-weighted because that is where the headline and the
   standfirst sit, and its shallow end is still 60% because the montage
   passes through a white-grey beach and a lit pink veil - sized for the
   brightest shot in the film rather than the first one. The second ramp is
   vertical and protects the 15px entity line at the foot of the hero, which
   is the first thing to fail over a bright frame. */
.hero-bg::after{content:"";position:absolute;inset:0;
  background:
    linear-gradient(rgba(255,255,255,.05) 1px,transparent 1px) 0 0/100% 92px,
    linear-gradient(90deg,rgba(255,255,255,.05) 1px,transparent 1px) 0 0/92px 100%,
    linear-gradient(transparent 38%,rgba(0,0,0,.5)),
    linear-gradient(90deg,rgba(0,0,0,.92),rgba(0,0,0,.78) 42%,rgba(0,0,0,.6));}
/* a single pass of a scan line as the hero resolves, then it is gone */
.hero-scan{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:1;}
.hero-scan::after{content:"";position:absolute;left:0;right:0;height:38vh;top:-40vh;
  background:linear-gradient(180deg,transparent,rgba(255,255,255,.055) 55%,
    rgba(227,30,36,.14) 86%,transparent);
  animation:scan 2.1s cubic-bezier(.4,0,.2,1) .35s 1 both;}
@keyframes scan{to{top:105%;}}
@media(prefers-reduced-motion:reduce){.hero-scan{display:none;}}
/* the hero's own children sit above the sweep. The film layer and the sweep
   are scenery and keep their own positioning - without the exclusion this
   rule made .hero-bg position:relative, dropping a 1600x900 video into flow
   and pushing the headline off the bottom of the screen. */
.hero > *:not(.hero-scan):not(.hero-bg){position:relative;z-index:2;}

/* the brackets sit inside the gutter, aligned to the text column */
.hero::before,.hero::after{content:"";position:absolute;width:22px;height:22px;
  border:1px solid rgba(255,255,255,.4);pointer-events:none;z-index:3;}
.hero::before{top:calc(var(--nav-real) + 1.6rem);left:var(--pad);
  border-right:0;border-bottom:0;}
.hero::after{bottom:1.6rem;right:var(--pad);border-left:0;border-top:0;}
@media(max-width:620px){
  .hero-bg::after{background-size:100% 64px,64px 100%,auto;}
  .hero::before,.hero::after{width:14px;height:14px;}
}
.hero ::selection{background:#fff;color:var(--red);}
/* meta row — flat label, replaces the blurred pill */
.hero-meta{display:flex;align-items:center;flex-wrap:wrap;gap:.9rem;
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.02em;
  color:rgba(255,255,255,.72);}
.hero-meta span{display:inline-flex;align-items:center;gap:.55rem;}
/* a record light: red now that the ground is black rather than red */
.hero-meta .rec{width:6px;height:6px;background:var(--red);flex-shrink:0;
  box-shadow:0 0 0 3px rgba(227,30,36,.22);
  animation:blink 1.6s infinite;}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.15}}

/* capped against viewport height as well as width. On a tall screen min()
   returns the display token unchanged; on a 560px viewport it takes the
   headline from 79px to 62px, which is 34px of the overflow on its own. */
.hero h1{font-size:min(var(--d-xl),11vh);
  margin-top:clamp(.85rem,4vh,2.4rem);color:#fff;}
.hero h1 em{font-style:normal;color:#fff;} /* same reason as the marker above */

/* subhead: says plainly what the poetic H1 implies, and carries the phrase
   people actually type. Sized well under the H1 so it reads as a caption. */
/* the margin-bottom is the floor under the foot's margin-top:auto - auto
   resolves to zero when there is no free space, and without this the
   standfirst and the rule below it would meet on a short screen */
.hero-sub{margin-top:clamp(.65rem,2.6vh,1.5rem);
  margin-bottom:clamp(1.3rem,6.5vh,4rem);max-width:34ch;
  font-size:var(--t-h4);line-height:1.25;font-weight:400;letter-spacing:-.02em;
  color:rgba(255,255,255,.88);}
@media(max-width:900px){.hero-sub{max-width:none;font-size:var(--body-l);}}

/* ===== HERO SOUND =====
   Two auto margins in the hero's flex column - this one and the foot's -
   split the free space between them, which parks the control about
   two-thirds down the right edge, clear of the headline above it and the
   actions below. */
.hero-sound{align-self:flex-end;z-index:4;
  /* margin-top:auto pushes it down the column; margin-bottom is the floor
     under the gap to the rule below, because on a short screen both auto
     margins resolve to zero and this would otherwise touch it */
  margin-top:auto;margin-bottom:clamp(1rem,2.6vh,1.8rem);
  display:inline-flex;align-items:center;gap:.6rem;
  padding:.65rem 1.05rem;border-radius:999px;
  border:1px solid rgba(255,255,255,.42);background:rgba(0,0,0,.34);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  color:#fff;cursor:pointer;
  font-family:var(--mono);font-size:var(--label);line-height:1;
  letter-spacing:.1em;text-transform:uppercase;
  transition:border-color .25s ease,background .25s ease;}
.hero-sound:hover{border-color:#fff;background:rgba(0,0,0,.6);}
.hero-sound:focus-visible{outline:2px solid #fff;outline-offset:3px;}
.hero-sound svg{width:17px;height:17px;flex:none;fill:none;
  stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;}
/* the icon states the current one, not the one a click would produce */
.hero-sound .on{display:none;}
.hero-sound[aria-pressed="true"] .on{display:block;}
.hero-sound[aria-pressed="true"] .off{display:none;}

/* Sound on: the page gets out of the film's way. The scrim goes with the
   copy - it is there to keep white type readable over a moving picture,
   and there is no type left to protect. */
.hero-meta,.hero h1,.hero-sub,.hero-foot,.hero-bg::after{
  transition:opacity .55s ease;}
/* Written above the reveal system on purpose: body.reveals-ready .r.in
   pins these to opacity:1 at (0,3,1), and .hero.sound-on h1 is (0,2,1),
   so the plain form alone loses. It is kept as the second half of the
   list for the no-JS case, where nothing ever sets .in. */
body.reveals-ready .hero.sound-on .hero-meta,
body.reveals-ready .hero.sound-on h1,
body.reveals-ready .hero.sound-on .hero-sub,
body.reveals-ready .hero.sound-on .hero-foot,
.hero.sound-on .hero-meta,
.hero.sound-on h1,
.hero.sound-on .hero-sub,
.hero.sound-on .hero-foot{opacity:0;pointer-events:none;}
.hero.sound-on .hero-bg::after{opacity:.18;}
/* The nav is fixed, so it is not inside the hero and cannot be reached
   from .hero.sound-on - the state is mirrored on <body> for it. The
   WhatsApp button goes too: it is the only other thing left floating over
   the film, and it sits right beside the control that started it. */
body.film-sound #nav{opacity:0;pointer-events:none;
  transform:translateY(-100%);}
body.film-sound .wa-fab{opacity:0;pointer-events:none;
  transform:translateY(130%);}
.hero.sound-on::before,.hero.sound-on::after{opacity:0;}
.hero::before,.hero::after{transition:opacity .55s ease;}
@media(max-width:620px){
  .hero-sound{padding:.55rem .85rem;gap:.45rem;}
  .hero-sound svg{width:15px;height:15px;}

  /* The brackets sit at nav + 1.6rem and the eyebrow at nav + 1.1rem, so
     at this width the frame lands on the type. It is a desktop device. */
  .hero::before,.hero::after{display:none;}

  /* 2.75rem is the display scale's floor - a desktop hero size. Four lines
     of it fill a phone. */
  /* 7.6vw keeps “Films without cameras.” on one line down to 360px;
     8.4 broke it back into four lines on a 390 phone, which is the
     density this was meant to fix. */
  .hero h1{font-size:clamp(1.7rem,7.6vw,2.5rem);}
  .hero-meta{font-size:.68rem;letter-spacing:.02em;}
  .hero-sub{font-size:var(--body-m);max-width:30ch;}

  /* Stacked and full width, and the row stops short of the floating
     WhatsApp button rather than running under it. */
  .hero-actions{width:100%;flex-direction:column;align-items:stretch;
    gap:.55rem;}
  .hero-actions .btn{justify-content:center;padding-block:1rem;}
  .hero-foot{padding-bottom:4.4rem;}
}
/* A small screen held sideways. Keyed on width as well as height: on
   height alone this caught any desktop window that was not maximised, and
   compressed a 1280-wide hero as if it were a phone. A desktop window
   keeps the desktop hero at any height - the vh clamps in the base rules
   already size it - and this only handles the case where the screen is
   short *and* small. */
@media(max-height:620px) and (max-width:900px){
  /* The menu gained a fifth link, and on a short phone the panel's
     generous bottom padding tipped the list into scrolling. The list is
     the whole point of the panel, so the padding gives way first. */
  .navlinks{padding-bottom:clamp(.4rem,1.5vh,1.5rem);}
  .navlinks a:not(.nav-cta):not(.x-cta-m){padding:.68rem 0;}
  .navlinks .nav-cta{margin-top:.8rem;}
  .hero{padding-top:calc(var(--nav-real) + clamp(.8rem,3vh,2rem));
    padding-bottom:clamp(1rem,3vh,2rem);}
  /* the width term is carried in too: declared after the width block,
     a height-only cap let a shorter screen show a bigger headline */
  .hero h1{font-size:min(var(--d-xl),9.5vh,7.6vw);
    margin-top:clamp(.5rem,2vh,1.2rem);}
  .hero-sub{font-size:var(--body-m);margin-top:clamp(.45rem,1.5vh,.9rem);
    margin-bottom:clamp(.8rem,3vh,2rem);}
  .hero-foot{padding-top:clamp(.7rem,2vh,1.2rem);
    /* no clearance needed: the row un-stacks below and ends left of the
       floating button instead of running under it */
    padding-bottom:clamp(.6rem,2vh,1.1rem);}
  .hero-actions{flex-direction:row;width:auto;gap:.6rem;}
  .hero-actions .btn{padding-block:.8rem;}
  /* the floor comes down with everything else - a 16px gap is still a
     gap, and on a short screen the height is worth more */
  .hero-sound{margin-top:auto;margin-bottom:.65rem;}
}

/* rule, then copy left / actions right, both sitting on the same baseline */
/* the two-column split was there to sit the entity paragraph beside the
   actions. With the paragraph gone the actions take the row on their own,
   on the left, under the rule and in line with everything above them */
.hero-foot{margin-top:auto;padding-top:clamp(.95rem,4vh,2.4rem);
  border-top:1px solid rgba(255,255,255,.28);
  display:flex;flex-wrap:wrap;
  gap:clamp(1.6rem,4vw,4rem);align-items:end;}
.hero-actions{display:flex;gap:.7rem;flex-wrap:wrap;}
/* inverted pair: red-on-white would vanish, so solid goes white and ghost
   takes a plain white hairline — no blur backdrop needed without the shader */
/* On the old red hero a white button was the only option. Against black the
   accent can do the job it exists for: the page's primary action is the one
   red thing above the fold. */
.hero .btn.solid{background:var(--red);color:#fff;border-color:var(--red);}
.hero .btn.solid:hover{background:var(--red-deep);border-color:var(--red-deep);color:#fff;}
.hero .btn.ghost{background:none;color:#fff;border-color:rgba(255,255,255,.6);}
.hero .btn.ghost:hover{border-color:#fff;background:rgba(255,255,255,.12);color:#fff;}
@media(max-width:900px){
  .hero-foot{align-items:start;}
}

/* ===== WHY AI (centred statement) ===== */
.why{text-align:center;}
.why h2{margin:1.2rem auto 0;max-width:18ch;}
.why .lede{margin:1.3rem auto 0;max-width:46ch;}
/* The model-name row that sat here is gone from the page. The stack is
   still named in the FAQ answer about tools, in llms.txt, and on every
   film page's details - which is where someone who cares about it looks. */

/* ===== WHY LIST (3 pillars) ===== */
.why-list{padding-top:0;}
/* icon over title, hairline rules between the columns */
.why-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:0;}
.why-item{border-left:1px solid var(--line);padding:0 clamp(1.5rem,3.5vw,3.5rem);}
.why-item:first-child{border-left:0;padding-left:0;}
.pill-icon{width:clamp(70px,6vw,96px);height:auto;color:var(--red);
  display:block;margin-bottom:clamp(1.6rem,3vw,2.6rem);}
.why-item h3{font-size:var(--t-h3);line-height:1.2;font-weight:500;max-width:16ch;}
.why-item p{margin-top:1rem;color:var(--ink-2);font-size:var(--body-s);line-height:1.5;max-width:32ch;}
@media(max-width:800px){
  .why-grid{grid-template-columns:1fr;gap:2.6rem;}
  .why-item{border-left:0;padding:0;}
}

/* ===== CARDS (films + services share this format) ===== */
/* chip row -> title -> optional description, image 4:3, View on hover */
/* .fmeta .cat left this group: on a grid of fifteen the filled chip was the
   loudest thing on the page. Film cards read their category as quiet mono
   text instead; the film-page header still gets the chip, where there is one. */
.card-chip,.sbody .idx,.strip-cap .cat,.fp-head .cat{
  display:inline-flex;align-items:center;background:var(--ink);color:var(--bg);
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.01em;
  padding:.34rem .62rem;}


.film-filters{display:flex;gap:.5rem;flex-wrap:wrap;margin-top:clamp(2rem,4vw,3rem);
  position:sticky;top:var(--nav-h);z-index:500;   /* nav is z-900 */
  transition:top .35s cubic-bezier(.16,1,.3,1);
  background:var(--bg);
  /* bleed to the section edges so cards never show beside the pinned row */
  margin-left:calc(var(--pad) * -1);margin-right:calc(var(--pad) * -1);
  padding:.9rem var(--pad);
  border-bottom:1px solid var(--line);}
@media(max-width:860px){
  /* shorter nav on small screens */
}
.chip{border:1px solid var(--line);background:none;
  padding:.45rem 1rem;font-family:var(--body);font-size:var(--label);line-height:1.2;font-weight:500;
  color:var(--ink-2);cursor:pointer;transition:all .25s;}
.chip:hover{border-color:var(--red);color:var(--red);}
.chip.on{background:var(--red);border-color:var(--red);color:#fff;}

/* One project per row. The gap is large because each row is a screenful -
   anything tighter and the next stage crowds the caption of the one above. */
.film-grid{margin-top:clamp(2rem,4vw,3rem);display:grid;
  grid-template-columns:1fr;gap:clamp(3rem,6vw,5.5rem);}


/* ===== FULL-WIDTH STAGE =====
   One project per row. Height-capped rather than ratio-locked: the films are
   9:16 and a full-width 16:9 frame would crop three-quarters of one away. The
   clip keeps its own ratio, centred, and the space either side of it is
   filled with a blurred enlargement of the poster - and then used. */
.film-card{
  /* the stage height, and from it the width a 9:16 frame takes inside it.
     Both are needed as numbers because the side blocks are sized from what
     the film leaves over. */
  --sh:min(76vh,760px);
  --vidw:calc(var(--sh) * 9 / 16);
  position:relative;
  display:grid;grid-template-columns:minmax(0,1fr);
  grid-template-areas:"stage" "meta";
  row-gap:clamp(.8rem,1.4vw,1.1rem);
}
.film-card.is-wide{--vidw:calc(var(--sh) * 16 / 9);}
.film-card.hidden{display:none;}
.film-card.over{display:none;}

.fstage{grid-area:stage;position:relative;height:var(--sh);overflow:hidden;
  display:grid;place-items:center;background:#000;}
.film-card .fthumb{position:relative;z-index:1;height:100%;width:auto;
  max-width:100%;}
/* a landscape film leaves no bands, so it simply spans the row and the stage
   takes its height from the clip rather than from the viewport */
.film-card.is-wide .fstage{height:auto;}
.film-card.is-wide .fthumb{width:100%;height:auto;}
/* A blurred, darkened enlargement of the poster fills the stage behind the
   film. It is a real <img> rather than a background on a pseudo-element: a
   url() carried in a custom property resolves against the stylesheet's
   folder, not the page's. */
.film-card .fbg{position:absolute;inset:0;z-index:0;width:100%;height:100%;
  object-fit:cover;pointer-events:none;
  filter:blur(44px) saturate(125%) brightness(.42);transform:scale(1.2);}
/* Sites are 720x1280 captures - the same 9:16 as the films, so they stage
   the same way. Cropping one to a wide frame showed the top fifth of a phone
   screenshot, which was half a word of a headline. */
.film-card .fthumb video,
.film-card .fthumb img:not(.fbg){position:absolute;inset:0;z-index:1;
  width:100%;height:100%;object-fit:contain;object-position:center;
  /* No transform here. Scaling a composited <video> rasterises the layer
     at its pre-transform size and stretches the raster, which turns the
     one-pixel black border some of these clips carry into a five-pixel
     smear - measured, 76.5 against 16.9 at the same edge. Edge damage is
     cropped out of the files instead. */}

/* the only mark on the picture: a corner arrow that says this is a link */
.fopen{position:absolute;top:0;right:0;z-index:3;
  width:clamp(34px,3vw,44px);aspect-ratio:1;display:grid;place-items:center;
  color:#fff;background:rgba(0,0,0,.35);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  opacity:0;transition:opacity .3s ease,background .25s ease;}
.fopen .ar{font-style:normal;font-size:.95rem;line-height:1;
  transition:transform .3s cubic-bezier(.16,1,.3,1);}
.film-card:hover .fopen{opacity:1;}
.film-card:hover .fopen .ar{transform:translate(2px,-2px);}
.film-card .fthumb:focus-visible .fopen{opacity:1;}
@media(hover:none){.fopen{opacity:1;}}

/* ===== TILE GRID =====
   One landscape and two portraits to a row, every tile the same height,
   every clip at the ratio it was shot in.

   Those three things only agree because the row composition is fixed: the
   ratio sum of 16/9 + 9/16 + 9/16 is the same for every row, so every row
   resolves to the same height without being told what it is.

   Each tile grows by its own ratio from a zero basis, which leaves the
   browser to divide the row - at its precision, not mine. The frame's
   aspect-ratio turns the width it lands on back into a height, so the row
   levels itself. Nothing is measured in JavaScript and nothing is cropped.
   The row breaks are spacers in the markup, after every third tile. */
.film-grid.tiles{--tile-gap:clamp(1.2rem,2vw,2rem);
  display:flex;flex-wrap:wrap;align-items:flex-start;
  column-gap:var(--tile-gap);row-gap:0;}
.film-grid.tiles .film-card{flex:var(--r) 1 0;
  /* a flex item's default min-width is its min-content width - the longest
     word of the title - which would floor a narrow tile and overflow the
     row */
  min-width:0;margin-bottom:var(--tile-gap);}
.rowbreak{flex:1 0 100%;height:0;margin:0;}
/* a filtered subset has no fixed composition left to break */
.film-grid.tiles.is-filtered .rowbreak{display:none;}
.film-grid.tiles .fstage{height:auto;}
.film-grid.tiles .fthumb{width:100%;height:auto;max-width:none;}
.film-grid.tiles .fthumb video,
.film-grid.tiles .fthumb img:not(.fbg){object-fit:cover;}
/* The backdrop stops being a ghost.

   Blurred, darkened and scaled 1.2x, it is built for the full-width stage,
   where it fills real bands either side of a vertical film. A tile's frame
   is cut to the clip's own ratio, so here it fills nothing - it just sits
   behind the clip as a different image, ready to show through anything
   that exposes an edge by a pixel.

   Same crop, same overscan, no blur: pixel-aligned with the clip in front
   of it. An exposed edge then shows the same picture, and it is a better
   thing to look at while the clip loads than a blur was. */
.film-grid.tiles .fbg{filter:none;transform:none;
  object-fit:cover;object-position:center;}
/* ---- a tile says one thing: what the project is called ----
   The index, category and spec rows are the catalogue's job and the film
   page's; here they would be three sizes of mono over a photograph. */
.film-grid.tiles .fs-r,
.film-grid.tiles .fidx,
.film-grid.tiles .frule,
.film-grid.tiles .fcat{display:none;}
/* ...except the spec flag, which is not data but the notice that an ad
   carrying a real brand's name is unofficial. It is positioned in the
   block at the end of this section, which has the specificity to hold. */

/* the title rides the frame instead of sitting under it. Assigned to the
   stage area so the caption row collapses and the card is exactly as tall
   as its picture - which is what keeps the rows level. */
.film-grid.tiles .fmeta{grid-area:stage;position:absolute;z-index:4;
  left:0;right:0;bottom:0;min-height:0;
  padding:clamp(.7rem,1.3vw,1.2rem);}
.film-grid.tiles .fmeta h3{font-size:var(--t-h4);color:#fff;
  text-shadow:0 1px 14px rgba(0,0,0,.5);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;}
.film-grid.tiles .film-card:hover h3{color:#fff;}
/* the scrim the title reads against - the band rule below switches it off
   for a full-width stage, and a tile is not one */
.film-grid.tiles .fstage::after{display:block;height:58%;}
/* Three to a row needs the width for it. Below this each tile takes the
   full column at its own ratio, which is how a phone wants to see a 9:16
   film anyway, and the spacers have nothing to break. */
@media(max-width:820px){
  .film-grid.tiles .film-card{flex:0 0 100%;}
  .rowbreak{display:none;}
}
/* ===== BENTO (works.html - Showcase) =====
   Four columns. A landscape film takes two columns by one row, a vertical
   film one column by two rows, and the first project two by two as the lead.
   The row height is derived from the container width so that a two-column
   cell is exactly 16:9; a vertical cell then lands close to its own 9:16 and
   loses only a sliver to cover-cropping. Dense flow fills the holes the mixed
   shapes leave. */
#films:has(.bento){container-type:inline-size;
  /* the Showcase project area is a white sheet under the black header.
     Re-pointing the tokens recolours the pinned filter row, the chips, the
     count and the note with it; tile titles are white on their own scrim. */
  --bg:#FFFFFF;--ink:#0A0A0A;--ink-2:#3C3C3C;--ink-3:#5E5E5E;--line:#E2E2E2;
  background:var(--bg);color:var(--ink);
  padding-bottom:clamp(3rem,6vw,5rem);}
#films:has(.bento) .film-grid{margin-top:clamp(1.6rem,3vw,2.6rem);}
.film-grid.tiles.bento{--cols:4;--bgap:clamp(1.4rem,2.6vw,2.4rem);
  display:grid;grid-template-columns:repeat(var(--cols),minmax(0,1fr));
  grid-auto-flow:row dense;gap:var(--bgap);
  /* the tile row's flex-start would leave every card 0px tall in a grid */
  align-items:stretch;
  /* width of a two-column span, times 7/16: a landscape tile reads as a
     wide 2.3:1 band and a vertical one lands almost exactly on 9:16 */
  grid-auto-rows:calc(((100cqw - (var(--cols) - 1) * var(--bgap)) / var(--cols) * 2
                       + var(--bgap)) * 7 / 16);}
.film-grid.tiles.bento .film-card{margin:0;grid-column:span 2;
  grid-template-areas:"stage";grid-template-rows:minmax(0,1fr);row-gap:0;}
.film-grid.tiles.bento .film-card:not(.is-wide){grid-column:span 1;grid-row:span 2;}
.film-grid.tiles.bento .film-card.is-lead{grid-column:span 2;grid-row:span 2;}
.film-grid.tiles.bento .film-card.is-lead-tall{grid-column:span 2;grid-row:span 4;}
.film-grid.tiles.bento .fstage{height:100%;}
.film-grid.tiles.bento .fthumb{width:100%;height:100%;aspect-ratio:auto !important;}
/* the lead tile gets a bigger title */
.film-grid.tiles.bento .film-card:is(.is-lead,.is-lead-tall) .fmeta h3{font-size:var(--t-h3);}
@media(max-width:900px){
  .film-grid.tiles.bento{--cols:2;}
  .film-grid.tiles.bento .film-card.is-lead{grid-row:span 1;}
}
/* a phone: one column, each frame back to a fixed shape */
@media(max-width:560px){
  .film-grid.tiles.bento{grid-template-columns:1fr;grid-auto-rows:auto;}
  .film-grid.tiles.bento .film-card,
  .film-grid.tiles.bento .film-card:not(.is-wide),
  .film-grid.tiles.bento .film-card.is-lead,
  .film-grid.tiles.bento .film-card.is-lead-tall{grid-column:auto;grid-row:auto;}
  .film-grid.tiles.bento .fthumb{height:auto;aspect-ratio:16/9 !important;}
  .film-grid.tiles.bento .film-card:not(.is-wide) .fthumb{aspect-ratio:4/5 !important;}
}

/* ===== WORK GRID (work.html) =====
   The tile look, on an even grid: three equal columns, every frame cropped
   to the same 16:9 landscape so every film lines up as a sheet.
   The inline aspect-ratio on each frame is the clip's own, hence !important. */
.film-grid.tiles.grid3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:var(--tile-gap);}
.film-grid.tiles.grid3 .film-card{margin:0;}
.film-grid.tiles.grid3 .fthumb{aspect-ratio:16/9 !important;}
@media(max-width:1000px){
  .film-grid.tiles.grid3{grid-template-columns:repeat(2,minmax(0,1fr));}
}
@media(max-width:560px){
  .film-grid.tiles.grid3{grid-template-columns:1fr;}
}

/* ===== THE CARD'S OWN DATA =====
   Always on the picture, never under it. The two blocks sit in the bottom
   corners of the stage over a scrim; the side bands further down are a
   promotion of the same two blocks, not a different arrangement.

   They are grid items assigned to the stage area, because an absolutely
   positioned grid item is laid out against its grid area rather than the
   grid container - without that, bottom:0 would mean the bottom of the card,
   under the title. */
.fside{grid-area:stage;position:absolute;bottom:0;z-index:3;
  display:flex;flex-direction:column;gap:.5rem;min-width:0;max-width:46%;
  padding:clamp(.9rem,1.8vw,1.6rem);pointer-events:none;}
.fs-l{left:0;align-items:flex-start;text-align:left;}
.fs-r{right:0;align-items:flex-end;text-align:right;}
/* the scrim they read against. Bottom only, and only in overlay mode - the
   side bands carry their own horizontal gradient instead. */
.fstage::after{content:"";position:absolute;left:0;right:0;bottom:0;
  height:42%;z-index:2;pointer-events:none;
  background:linear-gradient(transparent,rgba(0,0,0,.75));}

/* over a frame rather than the page, so every value is set light */
.fidx{font-family:var(--mono);font-size:var(--caption);letter-spacing:.12em;
  color:rgba(255,255,255,.6);line-height:1;}
.frule{display:block;width:26px;height:2px;background:var(--red);}
.fcat{font-family:var(--mono);font-size:var(--caption);line-height:1.35;
  letter-spacing:.04em;color:rgba(255,255,255,.92);}
/* icon then value: the mark says which measurement it is, so the row needs
   no label and three of them stack into a column instead of a sentence */
.fsp{display:inline-flex;align-items:center;gap:.5rem;
  font-family:var(--mono);font-size:var(--caption);letter-spacing:.04em;
  color:rgba(255,255,255,.6);line-height:1;}
.fsp b{font-weight:400;color:rgba(255,255,255,.92);}
.fsp svg{width:14px;height:14px;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round;}
/* the one coloured thing in either block */
.fflag{display:inline-flex;align-items:center;gap:.4rem;
  background:var(--red);color:#fff;padding:.3rem .6rem;
  font-family:var(--mono);font-size:var(--label);line-height:1.2;
  letter-spacing:.04em;}
.fflag.live{background:none;color:rgba(255,255,255,.92);padding:0;}
.fflag.live::before{content:"";width:6px;height:6px;background:var(--red);
  animation:blink 1.6s infinite;}

@media(min-width:1001px){
  /* A vertical film on a wide screen leaves two empty columns of blurred
     poster. The same two blocks fill them instead of sitting in the corners:
     full height, centred, with a horizontal gradient of their own in place
     of the bottom scrim. Everything else about them is unchanged. */
  .film-card:not(.is-wide) .fside{top:0;bottom:auto;height:var(--sh);
    width:calc((100% - var(--vidw)) / 2);max-width:none;
    justify-content:center;gap:.7rem;
    padding:clamp(1.2rem,2.4vw,2.4rem);}
  .film-card:not(.is-wide) .fs-l{left:0;
    background:linear-gradient(90deg,rgba(0,0,0,.6),rgba(0,0,0,.12));}
  .film-card:not(.is-wide) .fs-r{right:0;
    background:linear-gradient(270deg,rgba(0,0,0,.6),rgba(0,0,0,.12));}
  .film-card:not(.is-wide) .fstage::after{display:none;}
  /* ...but never in a tile. --vidw is a slice of the viewport, so inside a
     400px column (100% - var(--vidw)) goes negative and both blocks collapse
     to zero width - the data would simply vanish. A tile puts them back in
     the picture's bottom corners, which is where a landscape card and a
     phone already have them. */
  /* Caption sizes against a stage this big read as fine print, whichever
     arrangement the blocks are in, so the step-up is not scoped to the
     bands - a landscape card's overlay gets it too. */
  .fidx{font-size:.95rem;letter-spacing:.16em;}
  .frule{width:34px;}
  .fcat{font-size:.8rem;line-height:1.5;}
  .fsp{font-size:.8rem;gap:.6rem;}
  .fsp svg{width:16px;height:16px;}
  /* and the overlay needs a little more room under it at that size */
  .film-card.is-wide .fside{padding:clamp(1.2rem,2vw,1.9rem);gap:.6rem;}
  /* band-sized type is fine print's opposite problem in a 400px tile */
}

}
@media(max-width:620px){
  /* two corners of a phone-width frame is about 160px each */
  .fside{max-width:52%;gap:.4rem;padding:.8rem .7rem;}
  .fsp{gap:.4rem;}
  .fsp svg{width:12px;height:12px;}
}

/* the caption is the title alone now - everything else moved up */
.fmeta{grid-area:meta;}
.fmeta h3{font-size:var(--t-h3);line-height:1.12;
  font-family:var(--display);font-weight:500;letter-spacing:-.025em;}
.film-card:hover h3{color:var(--red);}
.fmeta h3{transition:color .25s ease;}


/* centred and solid: this is the one action in the section, so it should read
   as loudly as the hero CTAs rather than as a footnote */
.film-more{display:flex;justify-content:center;
  margin-top:clamp(2.4rem,5vw,3.5rem);
  padding-top:clamp(2rem,4vw,2.8rem);border-top:1px solid var(--line);}
.film-more[hidden]{display:none;}
.film-more .btn{cursor:pointer;font-family:var(--body);
  padding:1.15rem 2.6rem;font-size:var(--body-m);letter-spacing:.01em;}
.film-more .ar{transition:transform .3s;}
.film-more .btn:hover .ar{transform:translateY(3px);}
@media(max-width:600px){
  .film-more .btn{width:100%;justify-content:center;padding:1.05rem 1.4rem;}
}
.film-note{margin-top:2.4rem;font-size:var(--body-s);line-height:1.5;color:var(--ink-3);}
.film-note a{color:var(--red);text-decoration:underline;text-underline-offset:3px;}


/* ===== SERVICES — plain card grid ===== */
/* the gap carries the separation now that nothing is boxed, so it is wider
   than it was between two bordered panels */
.svc-grid{margin-top:clamp(2.5rem,5vw,4rem);display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:clamp(2.2rem,4vw,3.4rem) clamp(2rem,4vw,4rem);}
/* No fill and no box. Each entry had a panel and a border around a letter,
   a title and two lines of text - two frames for something that only needed
   to line up. One rule above it does that. */
.svc{color:var(--ink);position:relative;
  border-top:1px solid var(--line);
  padding:clamp(1.1rem,2vw,1.5rem) 0 0;}
/* number and title share a line, description spans both — no extra markup */
.sbody{display:grid;grid-template-columns:auto 1fr;column-gap:1.1rem;
  align-items:baseline;}
.sbody .idx{grid-row:1;grid-column:1;background:none;color:var(--ink-3);padding:0;
  font-family:var(--display);font-size:var(--t-h4);line-height:1.2;font-weight:500;}
.sbody h3{grid-row:1;grid-column:2;font-size:var(--t-h4);line-height:1.2;
  font-weight:500;letter-spacing:-.02em;color:var(--ink);
  border-left:1px solid var(--line);padding-left:1.1rem;}
.sbody p{grid-column:2;margin-top:.7rem;padding-left:1.1rem;
  color:var(--ink-2);font-size:var(--body-s);line-height:1.5;max-width:44ch;}
@media(max-width:1000px){.svc-grid{grid-template-columns:repeat(2,1fr);}}
@media(max-width:640px){
  .svc-grid{grid-template-columns:1fr;}
  .sbody p{max-width:none;}
}


/* ===== AGENCIES =====
   Reuses the services grid below it. Only the lede needs a rule: the
   centred "Why" block gets its gap from .why, and this one is not that. */
#agency .lede{margin-top:1.3rem;}


/* In the outline, not on the screen. The Work page's cards are h3 and it
   has no visible section heading, so the level they sit under has to
   exist somewhere a screen reader can reach. */
.vh{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);
  white-space:nowrap;border:0;}


/* The homepage carries a short version of the team; this is the way
   through to the full page. */
.sec-more{display:inline-flex;align-items:center;gap:.5rem;
  margin-top:clamp(1.1rem,2vw,1.5rem);
  font-size:var(--body-s);color:var(--ink-2);
  border-bottom:1px solid var(--line);padding-bottom:.3rem;
  transition:color .25s ease,border-color .25s ease;}
.sec-more:hover,.sec-more:focus-visible{color:var(--ink);border-color:var(--red);}
.sec-more .ar{transition:transform .25s ease;}
.sec-more:hover .ar{transform:translateX(3px);}


/* ===== WHO WE ARE =====
   Two entries, so the three-column services grid would leave a hole. Same
   rule-above-the-entry treatment, two columns, one on a phone. */
/* On team.html the heading above this grid is the screen-reader-only one,
   so the gap it is clearing is not there and the page opened with a hole. */
#team .founders{margin-top:0;}
.founders{margin-top:clamp(2.8rem,5.5vw,4.5rem);display:grid;
  grid-template-columns:repeat(2,1fr);
  gap:clamp(2.8rem,5vw,4.2rem) clamp(2.2rem,4vw,4rem);}
/* The portrait sits beside the words. Stacked, the text ran out about
   halfway across a 610px column and left the right third of every card
   empty, which is what made the section read as flat. */
.founder{display:grid;grid-template-columns:auto 1fr;align-items:start;
  gap:clamp(1.3rem,2.4vw,2.2rem);
  border-top:1px solid var(--line);
  padding:clamp(1.3rem,2.2vw,1.8rem) 0 0;}
/* a number and the title, in the mono the rest of the page uses for
   labels - it gives the two cards a rhythm the bare names did not have */
.founder .fmeta{display:flex;align-items:center;gap:.55rem;
  font-family:var(--mono);font-size:var(--label);line-height:1.2;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);}
.founder .fmeta i{font-style:normal;color:var(--red);}
/* Square, so any photo they have crops predictably, and the panel colour
   underneath means a slow load is a dark block rather than a white flash
   on a black page. aspect-ratio holds the space with or without the
   width/height attributes. */
.founder .fphoto{display:block;width:100%;height:auto;aspect-ratio:1/1;
  /* Full column width ran 610px and dominated the section; 220 was too
     small. A share of the card rather than of the window, so it does not
     collapse at the widths where the grid is still two columns. */
  width:clamp(170px,19vw,280px);max-width:none;
  object-fit:cover;object-position:50% 50%;background:var(--panel);
  /* the page carries no other colour photography, and a daylight snapshot
     in full colour on a black page pulls harder than the work does */
  filter:grayscale(1);transition:filter .5s ease;}
/* colour comes back while the visitor is on that person */
.founder:hover .fphoto,.founder:focus-within .fphoto{filter:none;}
/* The same square, holding the space until a portrait arrives. Without it
   the grid equalises the columns and the unpictured name sits hundreds of
   pixels above the other one. */
.founder .fphoto.is-pending{background:var(--panel);
  border:1px solid var(--line);}
.founder .fbio{margin-top:.75rem;color:var(--ink-2);
  font-size:var(--body-m);line-height:1.55;max-width:44ch;}
.founder h3{margin-top:.55rem;font-size:var(--t-h3);line-height:1.15;
  font-weight:500;letter-spacing:-.02em;color:var(--ink);}
/* the row is pulled left by the padding inside each hit area, so the icons
   line up with the name. Hit areas are 44px tall but only 32px wide, so the
   two glyphs sit close together as a pair */
.f-social{display:flex;align-items:center;gap:0;
  flex-wrap:wrap;margin:.85rem 0 0 -6.5px;}
/* Says what the glyphs beside it are for. Deliberately not a button and
   deliberately not bordered - it goes nowhere, and anything box-shaped
   here would get clicked. Same mono label as the line above the name. */
.f-social .f-connect{font-family:var(--mono);font-size:var(--label);
  line-height:1;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);margin:0 .35rem 0 6.5px;}
.f-social a{display:inline-flex;align-items:center;justify-content:center;
  width:32px;height:44px;color:var(--red);
  transition:color .25s ease;}
/* red is the accent everywhere else on the page, so the hover has to go
   somewhere else to read as a state change - white, like every other link */
.f-social a:hover,.f-social a:focus-visible{color:var(--ink);}
.f-social svg{width:19px;height:19px;}
@media(max-width:1000px){.founders{grid-template-columns:1fr;}}
/* below this the card is narrow enough that a column of text beside the
   portrait would be a few words wide, so it stacks */
@media(max-width:560px){
  .founder{grid-template-columns:1fr;gap:0;}
  .founder .fphoto{width:clamp(150px,46vw,210px);
    margin-bottom:clamp(1rem,3.4vw,1.4rem);}
}


/* A campaign page shows every film in it, not just the one the card
   loops. Three verticals side by side on a desktop, stacked on a phone. */
.fd-films{display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(1rem,2vw,1.6rem);margin-top:.4rem;}
.fd-films figure{margin:0;}
.fd-films video{display:block;width:100%;height:auto;aspect-ratio:9/16;
  background:var(--panel);border:1px solid var(--line);}
.fd-films figcaption{margin-top:.7rem;display:flex;flex-direction:column;
  gap:.25rem;}
.fd-films figcaption strong{font-size:var(--body-s);font-weight:500;
  color:var(--ink);}
.fd-films .fd-dur{font-family:var(--mono);font-size:var(--label);
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3);}
.fd-films figcaption em{font-style:normal;font-size:var(--body-s);
  line-height:1.45;color:var(--ink-2);}
@media(max-width:700px){.fd-films{grid-template-columns:1fr;
  gap:clamp(1.4rem,4vw,2rem);}}


/* ===== FAQ ===== */
.faq-grid{display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(2rem,5vw,5rem);align-items:start;}
/* heading pins to the top while the questions scroll past it */
.faq-head{position:sticky;top:110px;align-self:start;}
/* a list of rules, not a stack of boxes - so no gap between them either,
   or the rules read as six separate underlines instead of one list */
.faq-wrap{display:flex;flex-direction:column;gap:0;
  border-top:1px solid var(--line);}
details{background:none;border:0;border-bottom:1px solid var(--line);
  border-radius:0;overflow:hidden;}
summary{list-style:none;cursor:pointer;padding:1.2rem 0;
  display:flex;justify-content:space-between;align-items:center;gap:1.5rem;
  font-size:var(--body-m);line-height:1.55;font-weight:400;}
summary::-webkit-details-marker{display:none;}
summary .m{flex-shrink:0;width:14px;height:14px;position:relative;
  transition:transform .35s cubic-bezier(.16,1,.3,1);font-size:0;}
summary .m::before,summary .m::after{content:"";position:absolute;top:6px;
  width:9px;height:1.5px;background:var(--red);}
summary .m::before{left:0;transform:rotate(45deg);}
summary .m::after{right:0;transform:rotate(-45deg);}
details[open] summary .m{transform:rotate(180deg);}
details p{padding:0 0 1.35rem;color:var(--ink-2);
  font-size:var(--body-s);line-height:1.5;max-width:58ch;}
@media(max-width:860px){
  .faq-grid{grid-template-columns:1fr;gap:2rem;}
  .faq-head{position:static;}
}

/* ===== CONTACT ===== */
.contact{padding-bottom:clamp(2.5rem,5vw,4rem);}

.contact-inner{display:grid;grid-template-columns:minmax(0,1fr) auto;
  gap:clamp(2rem,5vw,5rem);align-items:end;}
.contact-inner h2{max-width:18ch;}
@media(max-width:900px){.contact-inner{grid-template-columns:1fr;gap:2.6rem;}}
.contact-links{display:flex;flex-direction:column;gap:.6rem;font-size:var(--body-s);line-height:1.5;}
.contact-links a,.contact-links span{display:flex;gap:.9rem;align-items:baseline;}
.contact-links a:hover{color:var(--red);}
.contact-links .lab{width:56px;flex-shrink:0;
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.02em;color:var(--ink-3);}

/* ===== CONTACT FORM ===== */
.cform{margin-top:clamp(2.5rem,5vw,4rem);border-top:1px solid var(--line);
  padding-top:clamp(2rem,4vw,3rem);}
.cf-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(1rem,2vw,1.6rem);}
.cf-field{display:flex;flex-direction:column;gap:.5rem;margin:0;}
.cf-wide{grid-column:1/-1;}
.cf-field label{font-size:var(--label);line-height:1.2;font-weight:500;
  letter-spacing:.02em;text-transform:uppercase;color:var(--ink-3);}
.cf-field input,.cf-field textarea{
  font-family:var(--body);font-size:var(--body-m);line-height:1.5;color:var(--ink);
  background:var(--panel);border:1px solid var(--line);border-radius:0;
  padding:.85rem 1rem;width:100%;
  transition:border-color .25s,background .25s;}
.cf-field textarea{resize:vertical;min-height:130px;}
.cf-field input:focus,.cf-field textarea:focus{
  outline:none;border-color:var(--red);background:var(--panel-2);}
/* off-screen rather than display:none — some bots skip hidden inputs */
.cf-honey{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;}
.cf-foot{display:flex;align-items:center;gap:1.2rem;flex-wrap:wrap;
  margin-top:clamp(1.2rem,2.5vw,1.8rem);}
.cf-foot .btn{cursor:pointer;font-family:var(--body);}
.cf-status{font-size:var(--body-s);line-height:1.4;margin:0;}
.cf-status.err{color:var(--red);}
.cform.sending .btn{opacity:.55;pointer-events:none;}
@media(max-width:760px){.cf-grid{grid-template-columns:1fr;}}

/* ===== CLIENT LOGO STRIP ===== */
/* A marquee rather than a grid: sixteen marks in a static wall reads as a
   directory, while one slow lap reads as a reel - and it fits the strip into
   a band instead of a screenful.
   --logo-scale sizes every mark by the same factor. The PNGs are exported at
   2x and optically area-matched, so halving them keeps a long wordmark and a
   round crest looking the same visual weight; a plain height rule would not. */
.clients{background:var(--panel);border-block:1px solid var(--line);
  padding:clamp(1.5rem,2.6vw,2rem) 0;overflow:hidden;--logo-scale:.4;}
/* a label, not a section heading: this band is a credential line between the
   hero and the work, so it should cost a strip of height, not a screenful */
.clients-lab{padding:0 var(--pad);margin-bottom:clamp(.9rem,1.8vw,1.3rem);
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);}

.logo-strip{display:flex;width:max-content;
  /* the marks fade out at both edges instead of being chopped by the viewport */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);}
/* The gap is wide because the list is short. One lap must be wider than the
   viewport or the duplicate track catches up and the same logo shows twice on
   screen at once, which reads as a bug. Six marks total only 555px at strip
   scale, so the spacing carries the difference; narrow this back toward 5vw
   once there are ten or more. */
.logo-track{display:flex;align-items:center;flex:none;
  gap:clamp(2.4rem,13vw,13rem);padding-right:clamp(2.4rem,13vw,13rem);
  animation:logo-marquee 64s linear infinite;}
/* both laps are the same width, so -100% of one lands exactly on the next */
@keyframes logo-marquee{from{transform:translateX(0);}
  to{transform:translateX(-100%);}}
.logo-strip:hover .logo-track{animation-play-state:paused;}

.logo{flex:none;display:block;}
/* Every PNG in the strip is a dark greyscale silhouette on transparent, so
   on black they would read as a row of holes. brightness(0) crushes each one
   to a flat black mask and invert(1) flips it to white - no colour is lost
   because none of them carry any, and the band ends up at one consistent
   weight instead of seven different greys. */
.logo img{display:block;height:auto;width:calc(var(--w) * var(--logo-scale));
  filter:brightness(0) invert(1);
  opacity:.62;transition:opacity .3s ease;}
.logo:hover img{opacity:1;}

@media(max-width:760px){.clients{--logo-scale:.32;}
  .logo-track{animation-duration:48s;}}

@media(prefers-reduced-motion:reduce){
  /* no lap: the strip becomes a static, centred wall and the duplicate goes */
  .logo-strip{width:auto;flex-wrap:wrap;justify-content:center;
    padding:0 var(--pad);-webkit-mask-image:none;mask-image:none;}
  /* flex:none is what lets the moving track stay wider than the viewport;
     released here so the wall can actually wrap instead of being clipped */
  .logo-track{animation:none;flex:1 1 auto;min-width:0;
    flex-wrap:wrap;justify-content:center;
    row-gap:clamp(1.6rem,3vw,2.4rem);padding-right:0;}
  .logo-track[aria-hidden="true"]{display:none;}
  .logo img{opacity:.62;}
}

/* ===== TOUCH TARGETS =====
   A finger is not a cursor. Measured on a phone, the primary action was
   31px tall, the sound control 35, and the footer's link column 23 - all
   under the 44px a thumb needs and the 24px WCAG asks for even as a
   minimum. Inline links inside a sentence are exempt and left alone; a
   column of links is not a sentence.

   Padding rather than height, so nothing changes shape - the text sits
   where it did and the hit area grows around it. */
@media(hover:none),(max-width:860px){
  .brand{padding-block:.6rem;}
  .nav-cta{padding-block:.85rem;}
  .hero-sound{padding-block:.85rem;}
  .alink .row{padding-block:.6rem;}
  .foot-col a,.contact-links a{padding-block:.55rem;}
  /* the caption under a card is the card's link: give the whole strip to
     the thumb rather than just the two lines of title */
  .film-card .fmeta{padding-block:.35rem .6rem;}
  .film-note a{padding-block:.3rem;}
}

/* ===== FLOATING WHATSAPP CTA ===== */
/* WhatsApp green and round: this is a platform affordance people recognise on
   sight, so it follows WhatsApp's convention rather than the site's squares. */
.wa-fab{position:fixed;right:clamp(1rem,3vw,2rem);bottom:clamp(1rem,3vw,2rem);
  z-index:880;display:inline-flex;align-items:center;justify-content:center;
  width:58px;height:58px;border-radius:50%;
  background:#25D366;box-shadow:0 6px 26px rgba(37,211,102,.32);
  transition:transform .4s cubic-bezier(.16,1,.3,1),background .25s,
             opacity .4s ease;}
.wa-fab:hover{background:#1FB855;transform:translateY(-2px) scale(1.04);}
.wa-fab svg{width:30px;height:30px;fill:#fff;}
@media(max-width:620px){.wa-fab{width:52px;height:52px;}
  .wa-fab svg{width:27px;height:27px;}}
@media print{.wa-fab{display:none;}}

/* ===== FOOTER ===== */
/* this used to be the black block at the end of a white page; it is the
   same ground as the rest now, so the hairline is all that separates it */
footer{background:var(--black);color:#fff;overflow:hidden;
  border-top:1px solid var(--line);
  padding:clamp(3.5rem,7vw,6rem) var(--pad) clamp(1.8rem,3vw,2.4rem);}
.foot-top{display:grid;grid-template-columns:2fr 1fr 1.2fr;
  gap:clamp(2rem,5vw,5rem);align-items:start;}
.foot-brand p{margin-top:.55rem;color:rgba(255,255,255,.78);font-size:var(--body-s);line-height:1.5;}
.foot-brand p:first-of-type{margin-top:0;}
/* wordmark spans the full footer width — vw sizing so it always fills edge to edge */
.foot-wordmark{display:block;margin-top:clamp(3rem,7vw,6rem);
  font-family:var(--display);font-weight:700;color:#fff;
  /* derived from the real content width (viewport minus the page gutters),
     with a little slack for the scrollbar, so it fills without overflowing */
  font-size:calc((100vw - 2 * var(--pad) - 20px) / 8.15);
  line-height:.82;letter-spacing:-.045em;white-space:nowrap;}
.foot-wordmark em{font-style:normal;color:#fff;}
.foot-col{display:flex;flex-direction:column;gap:.75rem;
  font-size:var(--body-s);line-height:1.5;}
.foot-col a{color:#fff;transition:opacity .25s;width:fit-content;}
.foot-col a:hover{opacity:.65;}
/* contact column: an icon sits inline ahead of each line, so the icons label
   the links rather than repeating them in a separate row */
.foot-contact a{display:inline-flex;align-items:center;gap:.65rem;}
.foot-contact svg{width:17px;height:17px;flex-shrink:0;
  fill:none;stroke:currentColor;stroke-width:1.6;
  stroke-linecap:round;stroke-linejoin:round;}
.foot-contact svg .fill{fill:currentColor;stroke:none;}
.foot-base{margin-top:clamp(1.6rem,3vw,2.4rem);padding-top:1.4rem;
  border-top:1px solid rgba(255,255,255,.28);
  display:flex;justify-content:space-between;gap:1rem;flex-wrap:wrap;
  font-size:var(--caption);line-height:1.4;color:rgba(255,255,255,.72);}
footer .sig i{font-style:normal;color:#fff;}
footer ::selection{background:#fff;color:var(--red);}
@media(max-width:800px){.foot-top{grid-template-columns:1fr 1fr;gap:2.4rem;}
  .foot-brand{grid-column:1/-1;}
  .foot-wordmark{font-size:calc((100vw - 2 * var(--pad) - 12px) / 8.5);}}
@media(max-width:520px){.foot-top{grid-template-columns:1fr;}}

/* ===== WORK PAGE =====
   The catalogue. It borrows the film-page header and the homepage grid, and
   what it needs of its own is mostly subtraction: the filter row follows the
   header directly, so the three paddings that would otherwise stack between
   them - the header's bottom inset, a section's top inset and the row's own
   margin - come out. */
body.work .fp-head{padding-bottom:clamp(1.2rem,3vh,2rem);}
/* the brackets are a viewfinder for a page built around one frame. On a
   header three lines deep the top-left one lands on the breadcrumb. */
body.work .fp-head::before,body.work .fp-head::after{display:none;}
body.work #films{padding-top:0;}
body.work .film-filters{margin-top:clamp(1rem,2vh,1.6rem);}

.works-count{margin-left:auto;align-self:center;
  font-family:var(--mono);font-size:var(--caption);letter-spacing:.06em;
  color:var(--ink-3);}
@media(max-width:620px){.works-count{margin-left:0;width:100%;}}

/* ===== FILM PAGE ===== */
/* Paths inside this file resolve against the document, so /films/*.html
   re-declares the two @font-face rules with ../ prefixes. Everything else
   here is path-free and shared. */
/* Shares its ground with the stage below it, so header and film read as one
   black block. The crumb and title carry the separation instead of a colour
   change. Carries the hero's measuring grid and brackets so the viewfinder is
   a system across the site, not a homepage flourish. */
.fp-head{position:relative;color:#fff;
  padding:clamp(7rem,14vh,9.5rem) var(--pad) clamp(2.5rem,5vw,4rem);
  background:
    linear-gradient(rgba(255,255,255,.045) 1px,transparent 1px) 0 0/100% 92px,
    linear-gradient(90deg,rgba(255,255,255,.045) 1px,transparent 1px) 0 0/92px 100%,
    var(--black);}
.fp-head::before,.fp-head::after{content:"";position:absolute;width:22px;
  height:22px;border:1px solid rgba(255,255,255,.4);pointer-events:none;}
.fp-head::before{top:calc(var(--nav-real) + .8rem);left:var(--pad);
  border-right:0;border-bottom:0;}
.fp-head::after{bottom:1.4rem;right:var(--pad);border-left:0;border-top:0;}
@media(max-width:620px){
  .fp-head{background-size:100% 64px,64px 100%,auto;}
  .fp-head::before,.fp-head::after{width:14px;height:14px;}
}
.fp-head ::selection{background:#fff;color:var(--red);}
.fp-crumb{display:flex;align-items:center;gap:.6rem;
  font-size:var(--label);line-height:1.2;font-weight:500;letter-spacing:.02em;
  color:rgba(255,255,255,.7);}
.fp-crumb a{transition:color .25s;}
.fp-crumb a:hover{color:#fff;}
/* inline-flex ignores vertical margin against the crumb above it */
.fp-head .cat{display:inline-flex;margin-top:1.4rem;
  background:rgba(255,255,255,.16);color:#fff;}
.fp-head h1{margin-top:1rem;font-size:var(--d-l);color:#fff;}
.fp-log{margin-top:1.2rem;max-width:36ch;
  font-family:var(--display);font-size:var(--t-h4);line-height:1.25;
  font-weight:400;letter-spacing:-.02em;color:rgba(255,255,255,.86);}

/* the film, with suggestions beside it */
/* ===== FILM PAGE: TWO-COLUMN SHELL ===== */
/* The rail runs the whole height of the page rather than stopping at the
   bottom edge of the film. .fp-rail must stretch for that - a sticky inner
   wrapper inside a column that only fits its content unpins immediately. */
.fp-shell{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,400px);
  align-items:stretch;background:var(--bg);}
.fp-main{min-width:0;background:var(--bg);}
.fp-rail{min-width:0;background:var(--bg);color:#fff;
  border-left:1px solid rgba(255,255,255,.14);}
/* Flows with the page rather than sticking: a sticky rail with its own
   max-height becomes a second scroll box inside the page, and the details
   at its foot can only be reached by scrolling the rail instead of the page.
   A player page's rail is simply a long column. */
/* Desktop: the rail pins under the nav and scrolls on its own, so the whole
   list is reachable without the column ever driving the page height. The
   max-width:1000px block below resets this, leaving mobile in normal flow. */
.fp-rail-inner{position:sticky;top:var(--nav-h);
  max-height:calc(100vh - var(--nav-h));overflow-y:auto;
  padding:1.1rem 1.1rem 1.6rem;
  scrollbar-width:thin;scrollbar-color:rgba(255,255,255,.22) transparent;}
.fp-rail-inner::-webkit-scrollbar{width:8px;}
.fp-rail-inner::-webkit-scrollbar-thumb{background:rgba(255,255,255,.22);}
.next-all{display:inline-flex;align-items:center;gap:.5rem;margin-top:.9rem;
  padding:.55rem .1rem;font-size:var(--body-s);font-weight:500;color:#fff;
  border-bottom:1px solid rgba(255,255,255,.3);transition:border-color .25s;}
.next-all:hover{border-color:#fff;}
/* the scrollable rail already reaches everything, so the link is redundant */
@media(min-width:1001px){.next-all{display:none;}}

.fp-stage{padding:0;background:var(--black);display:block;}
/* The stage row is as tall as the suggestions panel. A 9:16 film fills it
   exactly, but the one 16:9 film (Bidyut) is shorter - centre it so it
   letterboxes evenly instead of hanging from the top. */
.fp-player{min-width:0;align-self:stretch;
  display:flex;flex-direction:column;justify-content:center;}
.fp-stage video{display:block;width:100%;max-height:82vh;object-fit:contain;background:#000;}


.fp-next-head{font-size:var(--label);line-height:1.2;font-weight:500;
  letter-spacing:.08em;text-transform:uppercase;color:rgba(255,255,255,.55);
  margin:0 0 .9rem;}
.next-item{display:flex;gap:.7rem;padding:.4rem .5rem;margin:0 -.5rem;
  align-items:flex-start;transition:background .25s;}
.next-item:hover{background:rgba(255,255,255,.08);}
/* 16:9 like a video player's own suggestion rail. The posters are 9:16, so
   they are centre-cropped rather than pillarboxed - a 9:16 still letterboxed
   into a 16:9 slot is a 50px sliver in a 160px box, which reads as an error. */
.next-thumb{flex:0 0 160px;aspect-ratio:16/9;overflow:hidden;background:#111;}
.next-thumb img{width:100%;height:100%;object-fit:cover;object-position:center;
  display:block;}
.next-meta{display:flex;flex-direction:column;gap:.25rem;min-width:0;}
/* clamped so one long title cannot push the row twice as tall as its
   neighbours and break the rhythm of the rail */
.next-title{font-size:var(--body-s);line-height:1.3;font-weight:600;color:#fff;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;
  overflow:hidden;}
.next-cat{font-size:var(--caption);line-height:1.3;color:rgba(255,255,255,.55);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}

@media(max-width:1000px){
  /* not enough width for a rail — it moves under the film */
  .fp-shell{grid-template-columns:1fr;}
  .fp-rail{border-left:0;border-top:1px solid rgba(255,255,255,.14);}
  .fp-rail-inner{position:static;max-height:none;
    padding:1.4rem var(--pad) 1.8rem;}
  .next-item{max-width:520px;}
}

.fp-info{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(0,1fr);
  gap:clamp(2rem,5vw,5rem);align-items:start;}
.fp-syn{font-size:var(--body-l);line-height:1.6;color:var(--ink-2);max-width:56ch;}
.fp-syn strong{color:var(--ink);font-weight:600;}
.fp-specs{list-style:none;border-top:1px solid var(--line);}
.fp-specs li{display:flex;gap:1rem;padding:.7rem 0;border-bottom:1px solid var(--line);
  font-size:var(--body-s);line-height:1.4;}
.fp-specs li span{flex:0 0 104px;font-size:var(--label);font-weight:500;
  letter-spacing:.02em;text-transform:uppercase;color:var(--ink-3);padding-top:.15rem;}
.fp-actions{display:flex;gap:.7rem;flex-wrap:wrap;margin-top:2rem;}
@media(max-width:900px){
  .fp-info{grid-template-columns:1fr;gap:2.4rem;}
  .fp-syn{font-size:var(--body-m);}
}


/* A site page shows a still where a film shows a player, but the stage around
   it behaves identically: same black backing, same 82vh ceiling, same
   suggestions panel. Every rule is scoped to .fp-player - an unscoped
   `.is-site a{display:block}` also hit the suggestion links in the panel
   beside it and blew their thumbnails up to full width. */
.fp-stage.is-site .fp-player{position:relative;align-self:stretch;
  display:flex;align-items:center;justify-content:center;
  max-height:82vh;overflow:hidden;}
.fp-stage.is-site .fp-player a{display:block;width:100%;}
.fp-stage.is-site .fp-player img{display:block;width:100%;height:auto;
  max-height:82vh;object-fit:contain;}

/* ===== FILM PAGE: DISCLAIMER + LONG-FORM BODY ===== */
/* the notice rides in the red header beside the title, so it is read before
   the film rather than after it */
.fp-head.with-note{display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,clamp(260px,26vw,360px));
  gap:clamp(1.6rem,4vw,4rem);align-items:end;}
.fp-head-main{min-width:0;}
.film-disclaimer{margin:0;border-left:2px solid rgba(255,255,255,.55);
  background:rgba(255,255,255,.12);padding:.9rem 1.1rem;
  font-size:var(--caption);line-height:1.55;color:rgba(255,255,255,.82);}
.film-disclaimer strong{color:#fff;font-weight:600;}
@media(max-width:900px){
  .fp-head.with-note{grid-template-columns:1fr;gap:1.8rem;align-items:start;}
}

.fp-detail{border-top:1px solid var(--line);}
.fd-body{max-width:72ch;min-width:0;}
.fd-body h2{font-size:var(--t-h3);line-height:1.15;margin-top:clamp(2.2rem,4vw,3rem);}
.fd-body h2:first-child{margin-top:0;}
.fd-body p{margin-top:1rem;font-size:var(--body-m);line-height:1.65;color:var(--ink-2);}
.fd-scenes,.fd-list{margin-top:1.2rem;padding-left:0;list-style:none;}
.fd-scenes li,.fd-list li{position:relative;padding-left:2.4rem;margin-top:.8rem;
  font-size:var(--body-s);line-height:1.6;color:var(--ink-2);}
.fd-scenes{counter-reset:scene;}
.fd-scenes li{counter-increment:scene;}
.fd-scenes li::before{content:counter(scene,decimal-leading-zero);
  position:absolute;left:0;top:.1rem;
  font-family:var(--display);font-size:var(--label);font-weight:600;
  letter-spacing:.02em;color:var(--red);}
.fd-list li::before{content:"";position:absolute;left:.35rem;top:.62rem;
  width:6px;height:6px;background:var(--red);}
.fd-scenes li strong,.fd-list li strong{color:var(--ink);font-weight:600;}
.fd-details{margin-top:0;border-top:1px solid var(--line);}
.fd-sec-body > .fd-details{border-top:0;}
.fd-details dt{float:left;clear:left;width:120px;padding:.7rem 0;
  font-size:var(--label);font-weight:500;letter-spacing:.02em;
  text-transform:uppercase;color:var(--ink-3);}
.fd-details dd{margin:0;padding:.7rem 0;padding-left:132px;
  border-bottom:1px solid var(--line);
  font-size:var(--body-s);line-height:1.5;color:var(--ink-2);}
.fd-cta{display:flex;gap:.7rem;flex-wrap:wrap;margin-top:clamp(2rem,4vw,2.8rem);}
@media(max-width:600px){
  .fd-details dt{float:none;width:auto;padding-bottom:0;}
  .fd-details dd{padding-left:0;padding-top:.2rem;}
}
/* expandable article sections: the FAQ affordance, but not the FAQ card.
   The global details{} rules give a grey panel, inset padding and small text,
   which suit short answers and fight long-form prose - undone here. */
.fd-sec{border:0;border-bottom:1px solid var(--line);
  background:none;overflow:visible;}
.fd-sec > .fd-sec-body > p{padding:0;font-size:var(--body-m);line-height:1.65;
  max-width:none;}
.fd-sec > .fd-sec-body > ol,
.fd-sec > .fd-sec-body > ul,
.fd-sec > .fd-sec-body > div{padding:0;}
.fd-sec > summary{list-style:none;cursor:pointer;display:flex;
  justify-content:space-between;align-items:center;gap:1.5rem;
  padding:clamp(1.1rem,2vw,1.5rem) 0;}
.fd-sec > summary::-webkit-details-marker{display:none;}
.fd-sec > summary h2{margin:0;font-size:var(--t-h3);line-height:1.15;}
.fd-sec > summary .m{flex-shrink:0;width:14px;height:14px;position:relative;
  font-size:0;transition:transform .35s cubic-bezier(.16,1,.3,1);}
.fd-sec > summary .m::before,.fd-sec > summary .m::after{content:"";
  position:absolute;top:6px;width:9px;height:1.5px;background:var(--red);}
.fd-sec > summary .m::before{left:0;transform:rotate(45deg);}
.fd-sec > summary .m::after{right:0;transform:rotate(-45deg);}
.fd-sec[open] > summary .m{transform:rotate(180deg);}
/* The always-open variant: a campaign's films are the point of the page,
   not an aside, so they get the same rule and spacing with no toggle. */
.fd-sec-open > h2{margin:0;font-size:var(--t-h3);line-height:1.15;
  padding:clamp(1.1rem,2vw,1.5rem) 0;}
.fd-sec-body{padding-bottom:clamp(1.2rem,2.5vw,1.8rem);}
.fd-sec-body > p:first-child{margin-top:0;}
.fd-body h2{margin-top:0;}

.fd-faq{margin-top:0;display:flex;flex-direction:column;gap:0;
  border-top:1px solid var(--line);}
@media(max-width:760px){
  .fd-scenes li,.fd-list li{padding-left:1.9rem;}
}

/* more projects */
.fp-more{border-top:1px solid var(--line);}
.fp-more .film-grid{margin-top:clamp(2rem,4vw,3rem);}
