/* Homepage sections only. The shell (tokens, nav, footer, theme switch) is in
   shell.css, which every page loads. */
/* ── hero: the film first, in a rounded card ───────────────────────────── */
.hero{padding-top:6px}
/* max-width is load-bearing: min-height against an aspect-ratio makes the box
   resolve its WIDTH from that height (420 x 2.22 = 933px), so between 760px and
   ~1030px the card ran wider than the viewport and scrolled the whole page
   sideways. Capping it lets the height win without the width following. */
.herocard{position:relative; border-radius:var(--r); overflow:hidden; max-width:100%;
  /* 16:9, matching the film. The card was 16:7.2, which with object-fit:cover
     threw away 20% of every frame - 10% off the top and 10% off the bottom.
     Nothing is cropped now; the card is simply taller. */
  /* No min-height. Against an aspect-ratio it wins over the ratio on narrow
     desktop widths (761-800px), which made the card 1.70:1 and let object-fit:cover
     shave up to 4.2% off the sides of every frame. The ratio alone crops nothing. */
  background:var(--black); aspect-ratio:16/9}
.herocard>video,.herocard>img{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; opacity:.92}
/* The loop plays until the click; the full film replaces it and takes the whole
   card, uncropped, with the copy out of the way. */
#brandfilm{display:none}
.herocard.playing #heroloop{display:none}
.herocard.playing #brandfilm{display:block; object-fit:contain; opacity:1; background:#000}
/* The video carries opacity below 1, which gives it a stacking context and so
   paints it ABOVE a plain in-flow sibling. Without this the copy sits under the
   video and the button cannot be clicked at all. */
.herocard .inner{position:relative; z-index:1}
/* Once the film is playing the copy goes. It is not a hero any more, it is a
   video, and the headline is in front of it.

   This rule existed and did nothing. A comment had been opened in the middle
   of its selector list, so the whole thing parsed as
   `.herocard.playing .inner, .herocard.playing .herocard .inner
    {position:relative; z-index:1}`
   - the display:none was never there, and what WAS there put the copy on a
   layer above the film while it played. .inner is height:100% and
   justify-content:flex-end, so the part of it sitting over the native control
   bar was the padding at the bottom: invisible, and the reason the only way to
   pause the film was to go full screen. */
.herocard.playing .inner{display:none}
/* flex-end, not center, on the MAIN axis: the copy and the brand-film pill sit
   down at the foot of the card so the film carries the top two thirds. Across
   the card they are centred (12 Oct), which is the other axis and the reason
   both properties are here rather than one doing both jobs.

   900px, not 760. The box is what decides whether the lede fits on one line,
   not the lede's own max-width: at 760 the content box was 648px and the line
   wants 709, so it broke in two. MEASURED in Pilat at 17px, which is the
   binding case - the same line in the Arial fallback is 468px and would have
   fitted at the old width, so sizing this off the fallback would have looked
   right here and wrapped on every machine with the real font installed. */
.herocard .inner{position:relative; height:100%; display:flex; flex-direction:column;
  justify-content:flex-end; align-items:center; text-align:center;
  gap:14px; padding:clamp(24px,4vw,56px); color:var(--white);
  max-width:900px; margin-inline:auto}
/* .pill carries align-self:flex-start for the rest of the site. In a centred
   column that pins it to the left edge of the stack. */
.herocard .inner .pill{align-self:center}
.eyebrow{display:flex; align-items:center; gap:10px; font-size:11px; font-weight:700;
  letter-spacing:.2em; text-transform:uppercase; margin:0}
.eyebrow img{height:13px; width:auto}
/* The hero card is black in both themes, so the eyebrow cannot inherit --muted:
   in light mode that is #575752 on black, 2.89:1. White here matches what
   .filmhero and .depthero already do on their own dark heroes. */
.herocard .eyebrow{color:var(--white)}
.hero h1{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:clamp(34px,6.4vw,92px); line-height:.94; letter-spacing:-.01em; margin:0}
.tag{display:inline-block; align-self:flex-start; background:var(--grad);
  color:var(--white); font-size:10px; font-weight:700; letter-spacing:.16em;
  text-transform:uppercase; padding:6px 13px; border-radius:999px}
/* The Happen Device on the Our Story button. It was &#10095;, a text ornament
   from whatever face the body font resolves to, which is a thin chevron and
   not our mark at all. This is the device itself, lifted verbatim from the
   master logotype in happen-device.svg, so the angles and the weight are the
   real artwork at any size.

   Masked rather than placed as an <img>: an <img> renders the file's own fill
   and would have come out black on a gradient pill, where a mask paints
   `background` through the shape and so takes currentColor. Same technique
   .dodobullet uses. Sized by height against the 13px label, and the viewBox is
   161.334 x 178.671 so the width follows from that ratio rather than being
   guessed. */
/* WHITE until you roll over it, Richard 17 Oct. It sits on the hero film, and
   a gradient pill on top of a moving picture was doing the work the headline
   should. White reads as the one thing to press; the gradient arrives on hover
   as the reward, which is what the nav tabs, the burger and .pill.ghost all
   already do.

   Everything inside is currentColor, so the ring and the device invert with
   the pill for free: black on white at rest, white on the gradient on hover.
   No second set of rules, and nothing to keep in step. */
.pill.happen{position:relative; background:var(--white); color:var(--black);
  --ring:26px; --ringpad:30px;
  /* Shorter than the base pill, Richard 17 Oct. .pill is padding:16px 30px and
     the 26px ring is the tallest thing in it, so the capsule stood 58px. 11px
     of padding-block brings it to 48px. padding-block ONLY: the sides stay at
     30px, which is what --ringpad is measured from, so the device does not
     move. */
  padding-block:11px;
  /* TWO LINES IN THE SAME CAPSULE, Richard 20 Oct: "Watch" over "Our Story".
     line-height:1 is what keeps it at 48px - two 13px lines come to 26px,
     which is exactly the ring's height, so the ring still sets the capsule
     and the padding-block above still resolves to 48. At the default
     "normal" the pair would have run to about 30px and pushed it to 52. */
  line-height:1;
  transition:background .18s ease, color .18s ease}
.pill.happen:hover,.pill.happen:focus-visible{
  background:var(--grad); color:var(--white)}

/* IN A CIRCLE, like the play cue on the films. That one is a 56px ring, 2px of
   white, with the device 22px wide inside it - the device is 39% of the ring.
   Here the ring is 26px against the 13px label, so the device is 10px, and the
   proportion carries across unchanged.

   The ring is ::before and stays in the flex flow, so the label still sits
   beside it and the padding still works. The device is ::after, which lands
   AFTER the label in flow and so has to be taken out of it and placed on the
   ring: left is the pill's own padding plus half a ring. Both are driven by
   the two custom properties above, so changing the ring size moves the device
   with it rather than leaving it stranded.

   Masked rather than an <img>, as before: an <img> renders the file's own fill
   and would be black on the gradient, where a mask paints `background` through
   the shape and so takes currentColor. */
.pill.happen::before{content:""; flex:none; box-sizing:border-box;
  width:var(--ring); height:var(--ring);
  border:2px solid currentColor; border-radius:50%; background:transparent}
.pill.happen::after{content:""; position:absolute; top:50%;
  left:calc(var(--ringpad) + var(--ring) / 2);
  /* a shade right of centre, for the same reason as on the cards: the mark
     points right and its weight sits left of its bounding box */
  transform:translate(calc(-50% + 1px),-50%);
  width:10px; aspect-ratio:161.334/178.671; background:currentColor;
  -webkit-mask:url(/assets/brand/happen-device.svg) no-repeat center/contain;
          mask:url(/assets/brand/happen-device.svg) no-repeat center/contain}
@media (forced-colors:active){
  .pill.happen::after{background:currentColor}
  .pill.happen::before{border-color:currentColor} }
.pill{display:inline-flex; align-items:center; gap:12px; align-self:flex-start;
  background:var(--grad); color:var(--white); border:0; cursor:pointer;
  font-family:var(--body); font-weight:700; font-size:13px; letter-spacing:.1em;
  text-transform:uppercase; padding:16px 30px; border-radius:999px; text-decoration:none}
.dots{position:absolute; right:clamp(24px,4vw,56px); bottom:clamp(24px,4vw,40px);
  display:flex; gap:10px}

/* ── section furniture ─────────────────────────────────────────────────── */
section{padding-block:clamp(34px,4vw,54px)}
.shead{display:flex; align-items:center; gap:16px; margin:0 0 18px}
/* Our partners, set exactly as the genre pages set theirs: site.css .eyebrow
   is Pilat 700 at 12px, .16em, uppercase, in --muted. Same words, same
   treatment, so the home page and a division page agree - Richard, 16 Oct.
   The rule used to carry a note about all four section headings moving
   together; three of those four have since gone and .shead is now this
   heading alone, on the home page and its Welsh mirror. */
.shead h2{font-family:var(--body); font-weight:700; font-size:12px;
  letter-spacing:.16em; text-transform:uppercase; color:var(--muted,var(--grey-01));
  margin:0}
.arrows{margin-left:auto; display:flex; gap:8px}
.arrows button{width:38px; height:38px; border-radius:999px; border:1px solid var(--line);
  background:var(--card); color:var(--fg); cursor:pointer; font-size:15px; line-height:1}
.arrows button:disabled{opacity:.4; cursor:not-allowed}

/* ── browse by genre ───────────────────────────────────────────────────── */
.genres{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:20px;
  list-style:none; margin:0; padding:0}
.genres li{position:relative; border-radius:var(--r); overflow:hidden;
  aspect-ratio:4/3.6; background:var(--black)}
.genres img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover}
.genres .meta{position:relative; height:100%; display:flex; flex-direction:column;
  justify-content:flex-end; gap:6px; padding:22px; color:var(--white);
  background:linear-gradient(180deg,transparent 42%,rgba(0,0,0,.72) 100%)}
.genres h3{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:19px; letter-spacing:.01em; margin:0}
.genres p{margin:0; font-size:13px; line-height:1.4; color:var(--white)}
.genres a{margin-top:8px; font-size:11px; font-weight:700; letter-spacing:.14em;
  text-transform:uppercase; text-decoration:none}

/* ── new from whisper ──────────────────────────────────────────────────── */
.projects{display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:20px;
  list-style:none; margin:0; padding:0}
.projects li{position:relative; border-radius:var(--r); overflow:hidden;
  aspect-ratio:16/10; background:var(--black)}
.projects img{position:absolute; inset:0; width:100%; height:100%; object-fit:cover}
.projects .meta{position:relative; height:100%; display:flex; flex-direction:column;
  justify-content:space-between; padding:14px; color:var(--white);
  background:linear-gradient(180deg,rgba(0,0,0,.45) 0,transparent 38%,rgba(0,0,0,.72) 100%)}
.projects .genre{align-self:flex-end; background:rgba(0,0,0,.62); font-size:9px;
  font-weight:700; letter-spacing:.14em; text-transform:uppercase;
  padding:5px 10px; border-radius:999px}
.projects b{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:15px; letter-spacing:.01em}
/* "{Client} x Whisper" - lowercase x, in the gradient, never an ampersand. */
.lockup{font-size:10px; font-weight:700; letter-spacing:.12em; text-transform:uppercase;
  display:flex; align-items:center; gap:7px}
.lockup .x{background:var(--grad); -webkit-background-clip:text; background-clip:text;
  color:transparent; text-transform:lowercase; font-size:12px; font-weight:400}

/* ── about: the gradient panel ─────────────────────────────────────────── */
.aboutpanel{display:grid; grid-template-columns:280px 1fr; border-radius:var(--r);
  overflow:hidden; background:var(--grad); color:var(--white)}
.aboutnav{display:flex; flex-direction:column; gap:4px; padding:26px 22px;
  border-right:1px solid rgba(255,255,255,.28)}
.aboutnav button{text-align:left; background:none; border:0; cursor:pointer; color:inherit;
  font-family:var(--body); font-weight:700; font-size:12px; letter-spacing:.12em;
  text-transform:uppercase; padding:13px 18px; border-radius:999px;
  display:flex; align-items:center; justify-content:flex-start; gap:10px}
/* The white pill and the arrow both tried to mark the selected tab and neither
   did it well - the arrow was hardcoded onto the first tab and never moved.
   Both are gone. A white Happen Device marks the selection instead, and the
   unselected tabs step back on opacity so the current one still reads. */
/* The selected tab is a WHITE PILL (12 Oct). Opacity alone was too quiet a
   difference on a panel this loud, and since hover now selects, the pill is
   what the pointer lights up as it goes down the list.

   Red on white, not gradient-on-the-word: the pill sits ON the red-to-pink
   panel, so a gradient word inside it would be a second ramp a few pixels from
   the first. One ramp per panel. */
.aboutnav button{opacity:.72; border-radius:999px;
  transition:opacity .15s, background-color .15s, color .15s}
.aboutnav button[aria-selected="true"]{opacity:1;
  background:var(--white); color:var(--red)}
/* focus-visible gets the pill too, so a keyboard sees what a pointer sees.
   It cannot rely on hover selecting, because arrow keys move the selection
   without a pointer ever being involved. */
.aboutnav button:focus-visible{opacity:1}
/* Before the label, not after. Every tab carries the box whether or not it is
   selected - it is only faded out - so the labels stay on one left edge instead
   of shuffling sideways as the selection moves.

   It was a dodo silhouette and is now a plain white bar (12 Oct). At 12px the
   bird was a smudge rather than a mark, and five of them down one panel read
   as decoration.

   The bar has to be a BAR and not just more brightness, because hover now
   takes a tab to full white as well. If the selected state were only opacity,
   selected and hovered would be the same thing on screen and the panel would
   stop saying which one is open the moment the pointer entered it.

   Same .845em box as the bird, so nothing shifts. The gradient paints a 2px by
   1em rule centred inside it: width here is the box the label aligns to, and
   background-size is the mark itself. */
/* The box stays so every label sits on one left edge whether or not its tab is
   the selected one. The rule inside it does not: the pill marks the selection
   now, and a white rule printed inside a white pill is nothing at all. Kept as
   an empty box rather than deleted, because taking it out shifts five labels
   sideways and the alignment is the reason it was there. */
.aboutnav button::before{content:""; flex:none; width:.845em; height:1em}
/* currentColor, not a fixed value: the focus ring is then black on the selected
   white pill and white on the unselected tabs, and never the browser's default
   blue, which is not in the palette. */
.aboutnav button:focus-visible{outline:2px solid currentColor; outline-offset:3px}
/* padding-top is pinned, the other three sides are not.
   38px aligned the selected tab with the panel's KICKER, which was the same
   words repeated. The kicker is display:none now, so that datum no longer
   exists and the tab was left sitting 7px above the headline it should line up
   with. 32px puts the tab's caps level with the caps of A GLOBAL.

   Measured by eye on staging, not from font metrics: the canvas ascent/cap
   numbers put the two within 2px of each other when the screenshot showed 7,
   because the 12px label falls back to a different face from the 56px
   headline. Two passes, 30 then 32, zoomed on the pair. */
.aboutbody{padding:clamp(26px,3.4vw,46px); padding-top:32px}
.aboutbody .kicker{font-size:11px; font-weight:700; letter-spacing:.2em;
  text-transform:uppercase; margin:0 0 14px}
.aboutbody h2{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:clamp(26px,3.9vw,56px); line-height:.97; letter-spacing:-.01em;
  margin:0 0 18px; max-width:15ch}
.aboutbody p{margin:0; font-size:clamp(14px,1.2vw,17px); max-width:62ch}
/* Every panel is in the markup and visible by default, so with JavaScript off
   the section reads as five stacked statements rather than one tab and four
   dead buttons. The script hides the inactive ones on load. */
.apanel + .apanel{margin-top:38px}
/* Once the script takes over, every panel is stacked in ONE grid cell and the
   inactive ones are hidden with visibility rather than removed. visibility
   keeps them in the layout, so the cell is always as tall as the tallest panel
   and the page stops jumping as you move between tabs. Without the script they
   stay in normal flow and simply read one after another. */
.aboutbody[data-tabs]{display:grid}
.aboutbody[data-tabs] .apanel{grid-area:1/1; margin-top:0;
  visibility:hidden; opacity:0; transition:opacity .18s}
.aboutbody[data-tabs] .apanel.is-on{visibility:visible; opacity:1}
/* How we work: three statements, not one headline (deck p7). */
.astack{display:grid; gap:26px}
.astack h3{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:clamp(20px,2.4vw,32px); line-height:.98; letter-spacing:-.01em; margin:0 0 8px}
.acreed{line-height:1.65}


/* ── trusted by ────────────────────────────────────────────────────────── */
/* minmax(0,1fr), not 1fr: a track sized by a 1600px logo PNG is what made
   the page scroll sideways at 1440. */
.logos{display:grid; grid-template-columns:repeat(9,minmax(0,1fr)); gap:14px;
  list-style:none; margin:0; padding:0}
/* padding:0 overrides site.css's v1 band, which puts 16px/10px on each tile.
   That padding squeezed the content box out of 3:2, so the 600x400 image
   overflowed the auto-sized grid row and all of the overflow went downward -
   every PNG sat ~11% below the tile centre while the two SVGs, being smaller
   than the row, stayed put. The normalised files carry their own margin
   (INK_W/INK_H in normalise-logos.py), so the tile needs none. */
.logos li{background:#fff; border:1px solid var(--line); border-radius:10px; padding:0;
  aspect-ratio:3/2; display:grid; place-items:center; overflow:hidden}
/* The normalised files are 600x400, the tile's own ratio, with the padding
   baked in - so they simply fill it and every logo lands at the same optical
   weight. The two SVGs were left as vectors (ImageMagick renders red-bull.svg
   as a black silhouette), so they are sized here to match by eye. The caps
   were 56/38 against an ink box of 0.78x0.60; with the ink box now 0.86x0.76
   they had to go up in step or EA and Red Bull sit at half the weight of
   their neighbours. */
/* max-height:none is load-bearing. site.css's v1 band still sets
   `.logos img{max-height:30px}`, and overriding width/height here does NOT
   clear it - it is a different property, so it survived and clamped every PNG
   to 30px inside a 92px tile, squashing them out of ratio. */
.logos img{width:100%; height:auto; max-height:none; display:block}
.logos .vector{width:auto; max-width:74%; max-height:62%; margin-inline:auto}
/* The PNGs are centred on their ink by normalise-logos.py, but these two stay
   vectors and the browser centres them on their bounding box. Measured by
   rendering each at 600px and comparing ink centroid to bbox centre: EA sits
   5.7% low, Red Bull 9.4% (its wordmark is heavier than the bulls above it).
   The nudge is a percentage of each image's OWN height, so it holds at every
   tile size. Keyed off the filename so no markup has to carry a class. */
.logos img[src$="ea.svg"]{transform:translateY(-5.7%)}
.logos img[src$="red-bull.svg"]{transform:translateY(-9.4%)}

@media (max-width:1100px){
  .genres,.projects{grid-template-columns:repeat(2,minmax(0,1fr))}
  .logos{grid-template-columns:repeat(5,minmax(0,1fr))}
  .aboutpanel{grid-template-columns:1fr}
  .aboutnav{flex-direction:row; flex-wrap:wrap; border-right:0;
    border-bottom:1px solid rgba(255,255,255,.28)}
  .footcols{grid-template-columns:repeat(3,minmax(0,1fr))}
  .navgenre{display:none}
}
/* Landscape phones are wider than 760px (844x390 on an iPhone 14/15/16), so the
   width query above misses them and they were getting the desktop hero: a 16:9
   card 446px tall inside a 390px-tall viewport, with the headline below the fold
   and the copy sitting over the picture. Height is the honest test here. The
   film takes the full card width, uncropped, and the copy sits below it exactly
   as it does in portrait. */
@media (orientation:landscape) and (max-height:520px){
  .herocard{min-height:0; aspect-ratio:auto}
  .herocard > video,.herocard > img{position:static; width:100%; height:auto;
    aspect-ratio:16/9; object-fit:contain; opacity:1}
  .herocard .inner{height:auto; justify-content:flex-start; max-width:none;
    padding-block:clamp(16px,3vw,26px)}
  .dots{position:static; justify-content:flex-end;
    padding:0 clamp(24px,4vw,56px) clamp(14px,3vw,22px)}
}

@media (max-width:760px){
  .genres,.projects{grid-template-columns:1fr}
  .logos{grid-template-columns:repeat(3,minmax(0,1fr))}
  .footcols{grid-template-columns:repeat(2,minmax(0,1fr))}
  .navmain{display:none}
  /* Nothing is cropped on a phone. The card stops being a fixed shape: the
     film sits in its own 16:9 box at full frame, and the copy takes the space
     below it inside the same black card. The old 4:5 card threw away 55% of
     every frame - 27.5% off each side - which took both of the bull's horns
     out of shot. It also means the headline is no longer fighting the picture
     for contrast. */
  .herocard{min-height:0; aspect-ratio:auto}
  .herocard > video,.herocard > img{position:static; width:100%; height:auto;
    aspect-ratio:16/9; object-fit:contain; opacity:1}
  .herocard .inner{height:auto; justify-content:flex-start; max-width:none;
    padding-block:clamp(20px,5.5vw,30px)}
  /* the dots leave the absolute corner, or they land on top of the copy */
  .dots{position:static; justify-content:flex-end;
    padding:0 clamp(24px,4vw,56px) clamp(18px,4vw,24px)}
}
@media (prefers-reduced-motion:reduce){*{animation:none!important; transition:none!important}}

/* ── /work/ : the department rail, then that department's four cases ───── */
.workpage{padding-block:clamp(26px,3.2vw,44px)}
/* OUR WORK reads as a section heading, so it takes --fg like .wprompt below
   rather than the base .eyebrow's muted grey. Scoped: the muted grey is
   deliberate elsewhere, and .enquire sets it explicitly. The dodo mark follows,
   because it is masked to currentColor. */
/* The same size as "Proud to partner with" below it (.shead h2, 22px Pilat
   Bold). They are the same thing - the label on a section - and at 11px
   against 22px they did not read as a pair. 15 Oct. */
.workpage .eyebrow{color:var(--fg); font-family:var(--body); font-weight:700;
  font-size:22px; letter-spacing:.02em}
/* Smaller than the homepage hero on purpose: this page's job is the work
   below it, not the headline. */
/* h2, not h1. This block lives on the home page now, which already has its
   h1 in the hero, and a second h1 would leave the page with two competing
   top-level headings. The size is unchanged. */
.workpage h2{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:clamp(32px,4.8vw,68px); line-height:.94; letter-spacing:-.015em;
  /* 12px under it was fine when an eyebrow sat above; with WE ARE MULTI GENRE
     running straight into the rail the two read as one block. Enough to
     breathe, not a gap - Richard, 16 Oct. */
  margin:6px 0 clamp(22px,2.8vw,38px)}
.wprompt{margin:0 0 clamp(16px,2.2vw,24px); font-family:var(--body); font-weight:700;
  font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--fg)}

/* The rail. Four equal panels, and they stay that way. The selected one used
   to open to 2.8fr with the other three as slats, and grid-template-columns
   was animated so that slid rather than jumped. Replaced 9 Oct: the panels are
   now the same size as each other, the only movement is a 10% lift on the one
   under the pointer, and that department's featured case rolls in underneath
   the four. */
.wrail{display:grid; grid-template-columns:repeat(4,1fr); gap:14px}

.wpanel{position:relative; overflow:hidden; min-width:0;
  display:flex; flex-direction:column; justify-content:flex-end; padding:15px;
  /* 16:9, asked for 15 Oct. Was a clamped height, which made the slats
     portrait at every width. The aspect ratio holds the shape as the rail
     narrows; min-height keeps the open panel's strapline and pill from
     being squeezed out on a narrow desktop. */
  /* max-width:100%, or min-height drives the WIDTH through the ratio: 190 x
     16/9 is 337.8px whatever the track measures, so four panels plus gaps ran
     1394px wide at every viewport. At 1440 the wrap's own margin hid the 34px;
     from about 1398 down it was real horizontal scroll - 57px at 1100, 110px
     at 901. Capped, the panel takes its track (330px at 1440, the figure the
     7% note below was written against) and the ratio yields on width while
     min-height still holds the open panel's strapline and pill. */
  aspect-ratio:16/9; min-height:190px; max-width:100%;
  border-radius:var(--r); background:var(--black); color:var(--white);
  transform-origin:center center;
  transition:transform .24s cubic-bezier(.22,.61,.36,1)}
/* The end two grow inward. At centre origin the first panel pushes past the
   left edge of the wrap and the last past the right, and the rail has no
   overflow to absorb either. Same handling as .workcard on the division
   pages. */
/* 7%, not 10%. At 10% a 329.5px panel grows 16.5px on each side against a
   14px gap, so it ate its neighbours' edges. The most that clears the gap is
   1 + 2*14/329.5 = 1.085; 1.07 leaves about 2.5px of air at 1440 and more at
   every narrower width, because the panels shrink while the gap does not. */
.wpanel:hover,.wpanel:focus-within{transform:scale(1.07); z-index:3}
.wpanel:first-child{transform-origin:left center}
.wpanel:last-child{transform-origin:right center}
/* The button is the tab and covers the whole panel, so the pointer anywhere on
   a panel brings that department's case up below; the Explore link sits above
   it, which is why the pill cannot live inside the button. */
.wpick{position:absolute; inset:0; z-index:2; width:100%; height:100%;
  padding:0; border:0; background:transparent; color:inherit;
  text-align:left; font:inherit; cursor:pointer}
/* z-index:0 matters. `.wpanel > *{z-index:1}` used to catch this img too,
   which put the photograph ABOVE the ::after scrim below - the scrim was
   painting behind the picture and doing nothing at all. The copy stayed
   readable only where a still happened to be dark at the foot, and no DOM
   contrast check could see it, because the measured colours were all correct. */
.wpanel > img{position:absolute; inset:0; z-index:0; width:100%; height:100%;
  object-fit:cover; opacity:.92}
/* The banner clip on a genre tile, over the still and matching its .92 so the
   scrim below reads the same whether it is playing or not. */
.wpanel > video.clip{position:absolute; inset:0; z-index:0; width:100%; height:100%;
  object-fit:cover; opacity:0; transition:opacity .3s ease}
.wpanel > video.clip.on{opacity:.92}
/* Black up from the foot so the copy holds on any still. Not the gradient:
   that is a brand accent, and this is doing a legibility job. */
.wpanel::after{content:""; position:absolute; z-index:1; inset:0;
  background:linear-gradient(180deg,rgba(0,0,0,.45) 0,transparent 30%,
    rgba(0,0,0,.58) 60%,rgba(0,0,0,.93) 100%)}
/* Layers, foot to top: photo 0, scrim 1, the tab button 2, the number and
   name 3, the strapline and Explore link 4. Nothing here relies on
   z-index:auto, because that is what hid the scrim. The name and number sit
   above the button and are pointer-events:none, so a click still reaches it. */
/* .wnum is gone: Richard asked for the 01-04 counters off the home
   carousel, 14 Oct. Removed from the markup and from build-work.py too, so a
   rebuild does not put them back. */
/* Horizontal on every panel now. It ran up the side in vertical-rl because a
   collapsed slat was a fraction of the width these are. Sized to
   DOCUMENTARIES: it is the longest of the four, the panel's overflow:hidden
   would crop it rather than wrap it, and it is the one to measure if the rail
   proportions change. Measured across the clamp - 243.7px of word in a 300px
   panel at 1440, and 163.5px in 185px at 960, which is the narrowest the rail
   gets before it stacks. */
.wname{position:relative; z-index:3; pointer-events:none; margin:0;
  font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:clamp(12px,1.26vw,18px); letter-spacing:.01em; line-height:1.05}
/* Rolls up under the name rather than fading in over it, so the department
   under the pointer stays labelled while its strapline arrives. max-height
   rather than height: the straplines run to one line or two depending on the
   width, and the tallest measured 110.8px at 960, where they all wrap. 140
   clears that with room for a longer line than any of the four carry now. */
.wopen{position:relative; z-index:4; overflow:hidden;
  display:flex; flex-direction:column; align-items:flex-start; gap:10px;
  max-height:0; opacity:0; margin-top:0;
  transition:max-height .28s cubic-bezier(.22,.61,.36,1), opacity .2s,
    margin-top .28s cubic-bezier(.22,.61,.36,1)}
.wpanel:hover .wopen,.wpanel:focus-within .wopen{max-height:140px; opacity:1;
  margin-top:10px}
/* The name clears while the pointer is on a genre - 16 Oct. By then the panel
   has lifted, the clip is running and the Explore pill names it anyway, so
   the word was sitting on top of the thing it labels. Opacity, not display:
   it has to be able to come back when the pointer leaves.
   Hover only, and only where there IS a pointer. Below 900 the panels stack
   and open on selection instead, and the name is the only label the closed
   slats carry. */
@media (min-width:901px){
  .wname{transition:opacity .18s ease}
  .wpanel:hover .wname,
  .wpanel:focus-within .wname{opacity:0}
}
@media (prefers-reduced-motion:reduce){ .wname{transition:none} }
.wpick:focus-visible{outline:2px solid var(--fg); outline-offset:-3px}
/* The whole panel navigates, so it has to look like it does. On the button
   that covers the panel rather than the panel itself: the pill above it sets
   its own, and a cursor on the wrapper would also claim the gap. */
.wpick{cursor:pointer}
.wpanel .pill{z-index:4; font-size:11px; padding:10px 18px}

@media (prefers-reduced-motion:reduce){
  .wpanel,.wopen{transition:none}
  .wpanel:hover,.wpanel:focus-within{transform:none}
}

/* ── that department's featured case study ──────────────────────────────── */
/* One per department, swapping with the rail. Inset to 75% and centred, so the
   carousel stays the loudest thing on the page and the case reads as what
   follows from it rather than as a second, competing grid. The inset is
   released at 900px, where the rail stops being a rail and stacks. */
.wfeat{display:grid; grid-template-columns:1.5fr 1fr; align-items:center;
  gap:clamp(18px,2.6vw,36px); max-width:75%;
  margin:clamp(26px,3.2vw,44px) auto 0}
/* An author `display:grid` outranks the user agent's [hidden]{display:none},
   so without this every department's band renders and the page comes out at
   several times its real height with the rest stacked below the fold. Nothing
   looks broken on screen, which is why it needs the explicit rule. */
/* A fade, done in flow rather than by stacking.

   First attempt held all four bands in one grid cell and crossed their
   opacity. That killed the jump but cost more than it bought: the cell takes
   the height of the TALLEST band, and .wfeat centres its own content, so every
   shorter band floated in the middle of a tall box with black above and below
   it. The four measuring the same when I checked was an artefact of the stack
   I had just introduced, not a fact about the bands.

   So: one band in the layout at a time, natural height, and work.js fades the
   outgoing one out before it swaps and the incoming one in after. Nothing is
   visible at the moment the height changes, which is the only moment it can
   change. The opacity transition is here; the sequencing is in work.js,
   because only it knows which band is leaving.

   display:none is deliberate and the explicit rule is needed: an author
   `display:grid` outranks the user agent's [hidden]{display:none}, and without
   this every band renders at once and the page comes out several times its
   real height. */
.wfeat{transition:opacity .18s ease}
.wfeat[hidden]{display:none}
/* Set by work.js on the band that is on its way out, and on the incoming one
   for a frame before it is cleared. */
.wfeat.is-fading{opacity:0}
@media (prefers-reduced-motion:reduce){ .wfeat{transition:none} }
.wfeatimg{position:relative; display:block; overflow:hidden; min-width:0;
  aspect-ratio:16/9; border-radius:var(--r); background:var(--black)}
.wfeatimg > img{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; transition:transform .5s cubic-bezier(.22,.61,.36,1)}
.wfeat:hover .wfeatimg > img{transform:scale(1.03)}
.wfeatbody{min-width:0}
/* Featured work is the only label left on the section now that Our work has
   gone, so it takes what Our work had: 22px, and Pilat REGULAR rather than
   bold. Pilat ships Regular and Bold only - there is no Light cut in
   assets/fonts - so regular is as light as this gets without faking it. */
/* 18px, matching Our partners and Awards podium. The three are the same
   thing - the label on a section - so they move together. They were briefly
   apart on 16 Oct when Our partners came down a couple of points and this
   did not; Richard asked for the pair to agree again the same afternoon. */
.feyebrow{margin:0 0 12px; font-family:var(--body); font-weight:400; font-size:18px;
  letter-spacing:.02em; text-transform:uppercase; color:var(--fg)}
/* .workpage h2.ftitle, not .ftitle. The featured title is an <h2> inside
   .workpage, so `.workpage h2` at (0,1,1) beat this rule at (0,1,0) and the
   card headline was rendering at the SECTION heading's clamp - 67px at 1400
   rather than the 42px intended here. That is what made the panels jump:
   RIDDICULOUS took one line and MEET THE OWENS took three, a 126px swing in
   the body between two tabs of the same control.
   The min-heights hold two lines of title and two of standfirst whatever the
   copy does, so switching department moves the image and the words and
   nothing else. Measured across 1500, 1200, 820 and 420: every panel the
   same height, and 22px apart at 1000 where Sport's longer line wraps. */
.ftitle,
.workpage h2.ftitle{font-family:var(--display); font-weight:900; text-transform:uppercase;
  font-size:clamp(26px,3vw,44px); line-height:.96; letter-spacing:-.01em;
  margin:0 0 12px; text-wrap:balance; min-height:calc(2 * .96em)}
.wfeat .lockup{margin:0 0 12px; font-family:var(--body); font-weight:700;
  font-size:10px; letter-spacing:.14em; text-transform:uppercase}
.fdesc{margin:0 0 18px; font-size:14px; line-height:1.55; max-width:34ch;
  min-height:calc(2 * 1.55em); color:var(--muted)}
/* Placeholder copy stays visibly unfinished so it cannot ship by accident.
   Per field, not per band: a real title above placeholder copy should not be
   dimmed with it. */
.ftitle.is-placeholder,.fdesc.is-placeholder{opacity:.6; font-style:italic}
.wfeatcta{margin:0}
.wfeatimg:focus-visible{outline:2px solid var(--fg); outline-offset:3px}

@media (max-width:900px){
  /* Four panels in a row stops working before this: below it they stack, the
     unselected ones as 78px slats and the selected one open in place. There is
     no pointer here, so the lift and the strapline reveal both move off hover
     and onto selection. */
  .wrail{grid-template-columns:1fr; gap:9px}
  /* aspect-ratio:auto, or the selected panel sets its own WIDTH from its
     height: 16/9 against the min-height:250px below resolves to 444.4px, and
     at 400 that is 76px past a 368px track, which the rail has no overflow to
     absorb - 60px of horizontal scroll on the home page at phone width. The
     stacked layout is sized by its content, so the ratio has no job here. */
  .wpanel{height:auto; aspect-ratio:auto; min-height:78px; flex-direction:row;
    align-items:center; gap:12px; padding:15px}
  .wpanel:hover,.wpanel:focus-within{transform:none}
  .wpanel:has([aria-selected="true"]){min-height:250px; flex-direction:column;
    align-items:stretch; justify-content:flex-end}
  .wpanel:has([aria-selected="true"]) .wopen{max-height:170px; opacity:1;
    margin-top:10px}
  /* display, not max-height. In the row layout the strapline is a flex item
     across rather than down, so at max-height:0 it still claims width and
     squashes the name. */
  .wpanel:not(:has([aria-selected="true"])) .wopen{display:none}
  .wname{font-size:clamp(17px,4.4vw,22px)}
  /* A 78px slat has no room for the foot-up scrim: the name sits in the part
     of that ramp that is deliberately clear, and over the Branded still it
     measured 2.9:1. Across instead of up, so the name is backed and the right
     of the picture still shows. */
  .wpanel:not(:has([aria-selected="true"]))::after{
    background:linear-gradient(90deg,rgba(0,0,0,.85) 0,rgba(0,0,0,.72) 55%,
      rgba(0,0,0,.35) 100%)}
  /* Stacked: image over copy, and the band goes full width with the rail. */
  .wfeat{grid-template-columns:1fr; max-width:100%; align-items:start;
    gap:clamp(14px,2vw,20px)}
}

/* The panel kicker repeated the selected tab word for word, a few inches
   apart: "WHAT WE DO" in the nav and "WHAT WE DO" again above the headline.
   Gone, and the headline takes its place. With the kicker hidden the h2 is the
   first thing in the panel, so the existing 38px of padding-top puts it exactly
   where the kicker's text was - measured at 1280, 1024 and 768, h2 top 68.5px
   before and 38px after, which is the kicker's own line.

   display:none rather than deleting the markup: the Welsh build mirrors the
   English page, so both lose it together and neither drifts. */
.aboutbody .kicker{display:none}
