/* ---------------------------------------------------------------
 * Framing marks. The deck's corner brackets, used as the structural
 * device throughout. Pure CSS, no JavaScript.
 * ------------------------------------------------------------- */

.frame { position: relative; }

.frame__mark {
	position: absolute;
	width: 26px; height: 26px;
	border: 2px solid var(--local-accent, var(--accent));
	pointer-events: none;
}
.frame__mark--tl { top: 12px; left: 12px;  border-right: 0; border-bottom: 0; }
.frame__mark--tr { top: 12px; right: 12px; border-left: 0;  border-bottom: 0; }
.frame__mark--bl { bottom: 12px; left: 12px;  border-right: 0; border-top: 0; }
.frame__mark--br { bottom: 12px; right: 12px; border-left: 0;  border-top: 0; }

.frame--lg .frame__mark { width: 38px; height: 38px; }

/* ---------------------------------------------------------------
 * Buttons
 * ------------------------------------------------------------- */

/* Two sizes, one behaviour. Every button is magnetic. */
.btn {
	display: inline-flex; align-items: center; gap: var(--s3);
	font-family: var(--serif); font-weight: 500; font-size: 18px;
	line-height: 1.25; text-decoration: none;
	padding: 12px 28px 14px; border-radius: 2px; border: 0; cursor: pointer;
	will-change: transform;
}
.btn--primary { background: var(--accent); color: var(--green-950); }
.btn--primary:hover { background: var(--accent-s2); color: var(--green-950); }

.btn--ghost {
	background: transparent; color: var(--fg);
	border: 2px solid currentColor;
	/* Keep the ghost the same height as a filled button despite the border. */
	padding: 10px 26px 12px;
}
.btn--ghost.btn--sm { padding: 5px 17px 7px; }
.hero .btn--ghost { border-color: rgba(240, 235, 223, .60); }
.hero .btn--ghost:hover { border-color: var(--accent); }
.btn--ghost:hover { border-color: var(--local-accent); color: var(--local-accent); }

.btn__meta { font-family: var(--serif); font-size: 11px; opacity: .72; }

/* ---------------------------------------------------------------
 * Hero
 * ------------------------------------------------------------- */

.hero {
	/* The hero is not inside a .band, so it carries the dark token set itself. */
	--bg: var(--green-brand);
	--fg: var(--ink-dk);
	--body: var(--body-dk);
	--mute: var(--mute-dk);
	--rule: var(--green-700);
	--rule-soft: #005C33;
	--surface: var(--green-900);
	--surface-2: var(--green-950);
	--local-accent: var(--accent);
	--local-accent-ink: var(--accent-ink);

	position: relative;
	min-height: min(88vh, 940px);
	display: flex; align-items: center;
	background: var(--green-brand);
	overflow: hidden;
	isolation: isolate;
}

.hero__media { position: absolute; inset: 0; z-index: -2; }

/* Both layers are absolutely positioned so they overlay. In normal flow they
   stack vertically instead, and the video ends up a full hero height below the
   poster, playing happily where nobody can see it. */
.hero__media img, .hero__media video {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover;
}
.hero__video { opacity: 0; }
.hero__video.is-ready { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
	.hero__video { transition: opacity 600ms ease; }
}

.hero__scrim {
	position: absolute; inset: 0; z-index: -1;
	background:
		linear-gradient(to top,  rgba(0,46,26,.94) 0%, rgba(0,46,26,.55) 46%, rgba(0,46,26,.18) 100%),
		linear-gradient(to right, rgba(0,46,26,.78) 0%, rgba(0,46,26,.34) 38%, rgba(0,46,26,0) 66%);
}

/* The scroll cue. Sits at the foot of the hero, on the same bone as the copy,
   and breathes rather than bounces: a bounce competes with the headline wipe
   for attention at exactly the moment the headline should have it. */
.scroll-cue {
	position: absolute; left: 50%; bottom: clamp(20px, 3.4vh, 40px);
	transform: translateX(-50%);
	display: flex; flex-direction: column; align-items: center; gap: 10px;
	/* The accent, not the body bone: on a dark hero this is the one thing that
	   should catch the eye once the headline has landed. */
	color: var(--accent); opacity: 1;
	font-family: var(--serif); font-size: 14px; letter-spacing: .1em;
	text-transform: uppercase;
	pointer-events: none; z-index: 3;
	transition: opacity 420ms var(--ease-out);
}
@keyframes scroll-cue {
	0%, 100% { transform: translateY(0);   }
	50%      { transform: translateY(6px); }
}
/* Once the reader has started, it has done its job. */
.scroll-cue.is-done { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
	.scroll-cue__arrow { animation: none; transform: none; }
}
@media (max-width: 700px) {
	.scroll-cue { font-size: 11px; }
	.scroll-cue__disc { width: 38px; height: 38px; }
	.scroll-cue__disc svg { width: 14px; height: 14px; }
}

/* The LinkedIn button. The mark sits on the type's own baseline via 1em sizing,
   so it scales with the label instead of needing its own breakpoints. */
/* The official wordmark, white, so the button has to be dark for it to read.
   Green rather than LinkedIn blue: the blue would be the only foreign colour on
   the site, and the mark is recognisable enough without borrowing its ground. */
a.linkedin-btn {
	background: var(--green-950); color: #fff;
	padding: 14px 26px 15px;
}
a.linkedin-btn:hover { background: var(--green-brand); color: #fff; }
.linkedin-btn .icon-linkedin { width: 108px; height: auto; display: block; }
.contact-linked { margin-top: var(--s5); }
.contact-linked a { text-decoration: none; }

.u-visually-hidden {
	position: absolute; width: 1px; height: 1px;
	overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* The scroll cue's arrow now sits in a filled circle, which is what makes it
   read as a control rather than as a stray mark on the photograph. */
.scroll-cue__disc {
	width: 46px; height: 46px; border-radius: 50%;
	background: #fff; color: var(--green-950);
	display: grid; place-items: center;
	animation: scroll-cue 2000ms var(--ease-out) infinite;
}
.scroll-cue__disc svg { width: 17px; height: 17px; display: block; }

/* The team hero's subheading. Steps down from the headline to a subheading, as
   asked on 18/09/26, and takes the copy reveal rather than the wipe. */
.page-hero__sub {
	font-family: var(--display); font-weight: 700;
	font-variation-settings: "wdth" 108;
	font-size: clamp(22px, 2.6vw, 38px);
	line-height: 1.16; letter-spacing: -.02em;
	color: var(--fg); opacity: .88;
	/* Indented off the headline, asked for as "about 100 pixels" on 21/09/26.
	   7vw lands on 100px at a 1440 viewport and scales down with the page
	   rather than holding a fixed 100px against a 390px phone. */
	margin: var(--s5) 0 0 clamp(28px, 7vw, 100px); max-width: 22ch;
}

/* The helicopter faces the wrong way for this layout: the nose pointed off the
   left edge, away from the copy. Flipped so it points into the page. */
.page-hero--flip .page-hero__media img { transform: scaleX(-1); }

/* Crew at sunset: the default centre crop cut the heads off the top of the
   band, so the crop is anchored high. 22% still sliced the caps of the two
   figures at the right; 12% cleared them but pushed the sunset water off the
   bottom. 15% is the highest value that keeps every head in at the widest
   viewport (the worst case, where the band shows least of the frame) while
   leaving a band of the orange water below them. */
.page-hero--top .page-hero__media img { object-position: 50% 15%; }

/* The shared scrim is heaviest at the BOTTOM (94%), which is where this
   picture keeps its sunset. Asked on 21/09/26 to let some of the orange water
   through, so this hero eases the vertical gradient and leans on the
   horizontal one instead: the left stays dark enough for the headline, the
   bottom right opens up. */
.page-hero--top .page-hero__scrim {
	background:
		/* Heavy through the middle, where the type sits, and released at the
		   very bottom, where the water is. The shared scrim does the opposite
		   and that is what was flattening the sunset. */
		linear-gradient(to bottom,
			rgba(0,10,6,.12) 0%, rgba(0,10,6,.28) 24%,
			rgba(0,10,6,.28) 76%, rgba(0,10,6,.02) 100%),
		linear-gradient(to right,
			rgba(0,10,6,.46) 0%, rgba(0,10,6,.20) 48%, rgba(0,10,6,.015) 78%);
}

/* A picture behind a page hero, scrimmed the same way the home page's video
   is, so a headline's contrast never depends on the photograph. The band's own
   colour shows through wherever the picture does not reach. */
.page-hero__media { position: absolute; inset: 0; overflow: hidden; z-index: 0; }
.page-hero__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The scrim darkens, it does not tint. It used to be laid on in the brand
   green, which dragged every photograph round the colour wheel: measured on
   the team banner, a median hue shift of 48 degrees, so a sunset arrived on
   the page olive. A near-neutral dark holds the type off the picture just as
   well (better, in fact, per unit of alpha, because it is darker than the
   green) and leaves the photograph its own colour. */
.page-hero__scrim {
	position: absolute; inset: 0;
	background:
		linear-gradient(to top,  rgba(0,10,6,.72) 0%, rgba(0,10,6,.44) 46%, rgba(0,10,6,.20) 100%),
		linear-gradient(to right, rgba(0,10,6,.62) 0%, rgba(0,10,6,.30) 40%, rgba(0,10,6,.06) 72%);
}
/* The band's own children sit over it. The mark keeps its place between the
   two, so it still reads as a watermark rather than jumping in front. */
.page-hero > .wrap { position: relative; z-index: 2; }
.page-hero > .band-mark { z-index: 1; }

/* Over a photograph the mark has to change what it is made of. The band tints
   are picked to sit a hair off their own flat ground, so #00351D is invisible
   on green and a hard green slab across a picture: it showed up only where it
   should have shown up least. A low-alpha bone wash lifts the photo and the
   flat ground by the same small amount, so the shape reads as one watermark
   across the whole band instead of a cut-out following the picture's edge. */
.page-hero:has(.page-hero__media) > .band-mark { --mark-tint: rgba(240, 235, 223, .12); }

.hero__inner {
	position: relative;
	width: 100%; max-width: var(--wrap); margin: 0 auto;
	padding: var(--s8) var(--s5);
}
@media (min-width: 900px) { .hero__inner { padding-left: var(--s8); padding-right: var(--s8); } }

/* ---------------------------------------------------------------
 * Capability line
 *
 * What the studio can do, one phrase at a time, sitting between the
 * headline and the standfirst. Every phrase occupies the same grid cell,
 * so the slot is as wide and tall as the longest of them and nothing
 * below it moves when they change. The dot beats on each change.
 * ------------------------------------------------------------- */

.capline {
	/* An inline run rather than a flex row, so the dot can use
	   vertical-align: middle. Flexbox can only centre against the line box,
	   which sits higher than the text looks and leaves the dot riding above
	   the x-height. */
	display: block; white-space: nowrap;
	margin: 0 0 0 var(--s4);
	color: var(--ink-dk);
	/* Set like the button it sits beside: same face, weight and size. */
	font-family: var(--serif); font-weight: 500;
	font-size: 18px; line-height: 1.25;
	text-shadow: 0 1px 14px rgba(0, 30, 17, .6);
}

.capline__dot {
	/* vertical-align: middle puts the dot's centre on the baseline plus half
	   the x-height, which is where the eye reads the middle of the word. */
	display: inline-block; vertical-align: middle;
	width: 8px; height: 8px; border-radius: 50%;
	background: var(--accent);
	margin-right: 11px;
}
/* Beats continuously rather than only on the change. JS restarts it on each
   phrase so the beat and the word never drift apart. */
.js .capline__dot { animation: capline-beat 2500ms var(--ease-out) infinite; }

.capline__slot { display: inline-grid; vertical-align: baseline; }
.capline__item {
	grid-area: 1 / 1;
	white-space: nowrap;
	clip-path: inset(-0.35em 100% -0.35em 0);
}
.capline__item.is-on { clip-path: inset(-0.35em 0 -0.35em 0); }

/* Without JS, or with motion off, the first phrase simply stands. */
.js .capline__item.is-on { animation: capline-in 520ms var(--ease-out) forwards; }

@keyframes capline-in {
	from { clip-path: inset(-0.35em 100% -0.35em 0); }
	to   { clip-path: inset(-0.35em 0 -0.35em 0); }
}

/* One beat per phrase: a quick expanding ring, then rest until the next. */
@keyframes capline-beat {
	0%   { transform: scale(1);   box-shadow: 0 0 0 0 var(--accent); }
	7%   { transform: scale(1.5); box-shadow: 0 0 0 8px rgba(0, 0, 0, 0); }
	20%  { transform: scale(1);   box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
	100% { transform: scale(1);   box-shadow: 0 0 0 0 rgba(0, 0, 0, 0); }
}

@media (prefers-reduced-motion: reduce) {
	.js .capline__item.is-on,
	.js .capline__dot { animation: none; }
}

/* The headline runs a line-height below 1, so its box sits tighter than
   the glyphs look. It needs more room under it than the number suggests. */
.hero__heading { color: var(--ink-dk); margin-bottom: var(--s7); }
.hero__standfirst {
	color: var(--ink-dk); max-width: min(42ch, 100%);
	margin-bottom: var(--s7); font-size: var(--t-copy);
	text-shadow: 0 1px 18px rgba(0, 30, 17, .55);
	/* A wrapper, not a paragraph: the copy is one paragraph per line now, and
	   the group still reveals as a single piece. */
	display: grid; gap: var(--s4);
}
.hero__standfirst p { margin: 0; }

/* The standfirst and the actions are inset from the headline. Only once there
   is room for it, so nothing crowds a narrow screen. */
@media (min-width: 1100px) {
	.hero__standfirst, .hero__actions { margin-left: 100px; }
}
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s4); align-items: center; }

/* A short phone in portrait, an iPhone SE being the case that mattered. The
   hero needed 742px of a 667px screen, so "Watch the reel" sat 39px BELOW the
   fold: the client tapped where the button should be and hit nothing, which
   reads as an unresponsive button rather than a layout problem. Nothing covers
   it; there is simply no room. Tightening the padding and the two gaps buys
   back about 100px and brings it properly on screen. */
@media (max-width: 700px) and (max-height: 760px) {
	.hero__inner { padding-top: var(--s5); padding-bottom: var(--s5); }
	.hero__heading { margin-bottom: var(--s4); font-size: clamp(34px, 9vw, 42px); }
	.hero__standfirst {
		margin-bottom: var(--s4); gap: var(--s3);
		font-size: 16px; line-height: 1.5;
	}
	/* The rotating capability line adds a whole row to the button group and is
	   decoration: the same phrases are already in the page for screen readers.
	   On a short screen that row is the difference between the reel button
	   being on screen and being under the browser's own toolbar. */
	.hero__actions .capline { display: none; }
}
@media (min-width: 900px) { .hero__actions { flex-wrap: nowrap; } }


/* ---------------------------------------------------------------
 * Credential line
 * ------------------------------------------------------------- */

/* Four columns: the heading takes the first, the five broadcasters fill the
   other three. The list uses display:contents so its items sit in this grid
   directly, which is what keeps all four columns exactly equal. */
.credentials { display: grid; gap: var(--s6); }

/* Heading on the left, names in three even columns beside it.
   Previously the names were grid items of this container, flowing down four
   columns of two. That only lands tidily on exactly eight names: at nine the
   last one opened an implicit fifth column and sat outside the rhythm. The
   list is now its own grid, so the column count no longer depends on how many
   broadcasters there happen to be. */
/* 18/09/26: the title sits ABOVE the names now, centred, rather than in a
   column beside them. One column, so the heading spans the full band and the
   names run underneath it. */
@media (min-width: 720px) {
	.credentials {
		grid-template-columns: minmax(0, 1fr);
		gap: var(--s7) 0;
		align-items: start;
		justify-items: center;
	}
/* Opens on arrival with the pictures rather than tracking the scroll bar. On the
   scroll-linked version these sat part open whenever the reader stopped, which
   on a word rather than a photograph means a brand name cut off mid-letter. */
.js .credentials__heading,
.js .credentials__name,
.js .cta__heading { clip-path: inset(-0.1em calc((1 - var(--p, 0)) * 100%) -0.1em 0); }
@media (prefers-reduced-motion: reduce) {
	.js .credentials__heading,
	.js .credentials__name,
	.js .cta__heading { clip-path: none; --p: 1; }
}

.credentials__heading { align-self: end; }
	.credentials__list {
		/* Flowing and centred, not a grid.
		   A grid forces "C4" and "National Geographic" into equal columns, and
		   with names this uneven the result is a ragged block of whitespace
		   that reads as accidental. Letting them flow means each line packs
		   with whatever fits and centres itself, so the ragged edge becomes a
		   balanced shape instead of a broken column. Names are separated by
		   space alone; at this size and weight they do not need rules between
		   them to read as separate. */
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		align-items: baseline;
		width: 100%;
	}
}

/* Both of these used to sit inside the min-width:720px block above, indented
   flat against its left margin so they read as top-level rules. They were
   therefore desktop-only, and neither the centred title nor the matched
   "and more" had ever appeared on a phone. */
.credentials__heading { text-align: center; }

li.credentials__name--more {
	/* Deliberately the HEADING's size, not the names' size: the client asked for
	   "and more" to match "Broadcast credits", which is what makes it read as
	   the sign-off to the list rather than one more broadcaster. */
	font-family: var(--display); font-weight: 700;
	font-size: clamp(20px, 2.3vw, 30px);
	line-height: 1.14;
	color: var(--local-accent-ink, var(--fg));
}

.credentials__heading {
	font-family: var(--display); font-weight: 700;
	font-size: clamp(20px, 2.3vw, 30px); line-height: 1.14;
	font-variation-settings: "wdth" 104;
	letter-spacing: -.015em;
	color: var(--local-accent-ink);
	margin: 0; max-width: none;
}

.credentials__list {
	list-style: none; margin: 0; padding: 0;
	display: flex; flex-wrap: wrap; justify-content: center;
	/* This is the gap that applies. The rule inside the min-width media query
	   above is declared EARLIER in the file, so this one overrides it at every
	   width: changing the gap there does nothing at all. */
	align-items: baseline; gap: var(--s5) var(--s9);
	/* Re-swept for the 96px gaps: the ten names pack into two even lines, four
	   then five, anywhere from about 1250px up. Narrower and the first line
	   drops to three and the block breaks into four ragged rows. */
	max-width: min(100%, 1300px);
	margin-inline: auto;
}

/* 96px between names is right on a wide screen and absurd on a phone, where it
   stacks the list into seven rows. Placed AFTER the base rule because that is
   the one that wins here; a min-width query earlier in the file would do
   nothing. */
@media (max-width: 700px) {
	.credentials__list { gap: var(--s4) var(--s6); }
}

/* Each name's box hugs its own word. The wipe insets by a percentage of the
   box, so in a 284px grid cell "C4" was revealing 57px of letters and then
   240px of nothing: four fifths of its reveal happened after the name was
   already fully there. fit-content, not a display change, because these are
   grid items and their placement is doing real work here; measured, not one
   of the nine moves by a pixel. */
.credentials__list li { width: fit-content; }
/* ...except the sign-off, which takes a line to itself and centres on it. The
   fit-content above is a leftover from the grid layout and constrains the flex
   basis, so it has to be overridden explicitly rather than just set alongside. */
.credentials__list li.credentials__name--more {
	flex: 0 0 100%;
	width: 100%;
	text-align: center;
}

.credentials__name {
	font-family: var(--display); font-weight: 800;
	font-size: clamp(28px, 3.1vw, 40px); line-height: 1.2;
	font-variation-settings: "wdth" 106;
	letter-spacing: -.02em; color: var(--fg);
	margin: 0;
}

/* The about page still runs the names as a single line. */
.credentials__line {
	font-family: var(--display); font-weight: 800;
	font-size: clamp(24px, 4.4vw, 54px); line-height: 1.08;
	font-variation-settings: "wdth" 108;
	letter-spacing: -.02em; color: var(--fg); text-wrap: balance;
	margin: 0; max-width: none;
}

/* ---------------------------------------------------------------
 * Telemetry counters
 * ------------------------------------------------------------- */

.telemetry {
	display: grid;
	gap: var(--s7);
}
/* Same column template, same gap and same breakpoint as .credentials above,
   so the counters sit directly under the broadcaster columns. */
@media (min-width: 720px) {
	.telemetry { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.telemetry__cell { padding: 0; }
.telemetry__n {
	/* Held in a variable so a word cell can match the figures' box height. */
	--fig: clamp(40px, 6.2vw, 76px);
	display: block;
	font-family: var(--display); font-weight: 800;
	font-variation-settings: "wdth" 104;
	font-size: var(--fig); line-height: 1; letter-spacing: -.025em;
	color: var(--local-accent); font-variant-numeric: tabular-nums;
}
/* A word in the figure slot. "Multiple" at the numeral size would run past
   its column, so it steps down and keeps the same colour and weight. */
.telemetry__n--word {
	font-size: clamp(28px, 3.3vw, 42px);
	letter-spacing: -.02em;
	font-variant-numeric: normal;
	/* Must stay a block: ::first-letter does not apply to a flex container.
	   The cap below sets the line box to the figures' height, and the rest of
	   the word sits on the same baseline, so all four labels stay on one line. */
	display: block; height: var(--fig); line-height: 1;
}

/* The initial takes the numerals' size, so the cell still reads as a figure
   at a glance. Done with ::first-letter rather than a span, because the
   counter script rewrites this element's textContent and would drop markup. */
.telemetry__n--word::first-letter {
	font-size: var(--fig);
	line-height: 1;
	letter-spacing: -.025em;
}

.telemetry__k {
	display: block; margin-top: var(--s3);
	font-family: var(--serif); font-size: 16px; color: var(--mute); line-height: 1.5;
}

/* ---------------------------------------------------------------
 * Film card, with the hover scrub
 * ------------------------------------------------------------- */

.film-card { display: block; text-decoration: none; color: inherit; position: relative; }

.film-card__viewport {
	position: relative; aspect-ratio: 16 / 9;
	background: var(--green-950); overflow: hidden;
}
.film-card__poster { width: 100%; height: 100%; object-fit: cover; }
.film-card__proxy {
	position: absolute; inset: 0;
	width: 100%; height: 100%; object-fit: cover;
	opacity: 0; pointer-events: none;
}
.film-card.is-scrubbing .film-card__proxy { opacity: 1; }

.film-card__progress {
	position: absolute; left: 0; bottom: 0; height: 2px; width: 0;
	background: var(--accent);
}

.film-card__body {
	display: flex; flex-wrap: wrap; align-items: baseline;
	justify-content: space-between; gap: var(--s3);
	padding-top: var(--s4);
}
.film-card__title {
	font-family: var(--display); font-weight: 700;
	font-size: clamp(19px, 2.4vw, 26px); line-height: 1.12;
	color: var(--fg); letter-spacing: -.01em; margin: 0;
}
.film-card__client {
	font-family: var(--serif); font-size: 11px;
	color: var(--mute); margin: var(--s2) 0 0; max-width: none;
}
.film-card__tc {
	font-family: var(--serif); font-size: 12px;
	color: var(--local-accent-ink); font-variant-numeric: tabular-nums; flex: none;
}

.film-card--wide .film-card__title { font-size: clamp(26px, 4.4vw, 46px); }

/* Live signal, the colour that is not the system accent */

.live-dot {
	display: inline-flex; align-items: center; gap: 8px;
	font-family: var(--serif); font-size: 10.5px; color: var(--fg);
}
.live-dot::before {
	content: ""; width: 8px; height: 8px; border-radius: 50%;
	background: var(--signal); flex: none;
}

/* ---------------------------------------------------------------
 * Service rows
 * ------------------------------------------------------------- */

.services { border-top: 1px solid var(--rule); }

.service-row {
	display: grid; gap: var(--s4);
	padding: var(--s7) 0;
	border-bottom: 1px solid var(--rule);
	position: relative;
	text-decoration: none; color: inherit;
}
@media (min-width: 860px) {
	.service-row {
		grid-template-columns: 64px 1fr 300px;
		gap: var(--s6); align-items: center;
	}
}

/* The icon replaces the old row number. Sized in em so it tracks the row
   title, and it takes its colour from the accent like the number did. */
.service-row__icon {
	display: block;
	color: var(--local-accent-ink);
}
.svc-icon {
	width: clamp(26px, 2.4vw, 34px);
	height: auto;
	display: block;
	overflow: visible;
}
.service-row__title {
	/* 800, matching the display headings elsewhere; these rows are the only
	   thing left in that section now the summaries and chips have gone. */
	font-family: var(--display); font-weight: 800;
	font-size: clamp(31px, 4.6vw, 50px); line-height: 1.1;
	font-variation-settings: "wdth" 106;
	letter-spacing: -.02em; color: var(--fg); margin: 0;
}
.service-row__summary { color: var(--body); margin: var(--s3) 0 0; font-size: var(--t-small); max-width: min(52ch, 100%); }

/* A link, not a button. The row itself is no longer clickable, so this is the
   only thing to click and it underlines to say so. */
.service-row__more {
	display: inline-block;
	margin-top: var(--s4);
	font-family: var(--serif); font-size: var(--t-small);
	color: var(--local-accent-ink, var(--accent-ink));
	text-decoration: underline; text-underline-offset: 4px;
	text-decoration-thickness: 1px;
	transition: color var(--d-fast) ease;
}
.service-row__more:hover { color: var(--fg); }
.service-row__more:focus-visible { outline: 2px solid var(--local-accent, var(--accent)); outline-offset: 3px; }
.service-row__still { overflow: hidden; aspect-ratio: 16 / 10; background: var(--surface); }
/* Same arrival wipe as the collage and the portraits. */
.js .service-row__still { clip-path: inset(0 calc((1 - var(--p, 0)) * 100%) 0 0); }
@media (prefers-reduced-motion: reduce) { .js .service-row__still { clip-path: none; --p: 1; } }
.service-row__still img { width: 100%; height: 100%; object-fit: cover; }

/* ---------------------------------------------------------------
 * Call to action band
 * ------------------------------------------------------------- */

/* Centred, per the 16/09/26 client edit 8. The heading is capped at 16ch, so
   it needs auto margins as well as text-align to sit in the middle. */
.cta { text-align: center; }
/* One line, not two, and larger with it. The size is driven by the viewport so
   the line always fits: at 22 characters this face needs roughly 7.6vw to sit
   on one line, so that is the cap. Below 700px it is allowed to wrap again,
   because nowrap there would force the type down to an unreadable size. */
.cta__heading {
	font-family: var(--display); font-weight: 800;
	/* One line, but quieter: pulled back on 21/09/26 to about 86px at a 1440
	   viewport, roughly three quarters of the band rather than filling it.
	   6vw matches the wrapping branch below at the 700px breakpoint, so the
	   size does not jump as the screen narrows past it. */
	font-size: min(6vw, 94px); line-height: 1.04;
	font-variation-settings: "wdth" 110;
	letter-spacing: -.03em; color: var(--fg); margin: 0 0 var(--s6);
	max-width: 100%;
	margin-inline: auto;
	/* AFTER text-wrap, deliberately. `white-space` and `text-wrap` are both
	   shorthands for `text-wrap-mode`, so a `text-wrap: balance` declared
	   below this one silently resets nowrap to wrap. It used to sit below,
	   and the heading only stayed on one line because a 16ch max-width
	   happened to be narrower than the copy. Balance now lives in the
	   wrapping branch, where it is the only place it can do anything. */
	white-space: nowrap;
}
.cta__body {
	font-family: var(--serif);
	font-size: var(--t-copy); line-height: 1.55;
	color: var(--fg); opacity: .82;
	max-width: 46ch; margin: 0 0 var(--s7);
}
/* The page's single call to action, and the only button on it, so it is sized
   to be the thing you reach for rather than another line of type. */
.cta .btn--primary {
	background: var(--green-950); color: var(--accent-ink);
	font-size: clamp(19px, 1.6vw, 23px);
	padding: clamp(16px, 1.5vw, 21px) clamp(34px, 3.4vw, 48px) clamp(18px, 1.7vw, 23px);
}
.cta .btn--primary:hover { background: var(--green-brand); color: var(--accent-ink); }

/* No indent now the band is centred: that step existed to offset the body and
   button from a left-aligned heading, and centred it only pushes them off
   centre. Kept as a rule so the intent is on the record. */
@media (min-width: 900px) {
	.cta__body,
	.cta .btn--primary { margin-left: auto; margin-right: auto; }
}

/* ---------------------------------------------------------------
 * The work grid
 *
 * Two across on a wide screen. Each tile plays where it sits rather than
 * sending the reader to another page for it.
 * ------------------------------------------------------------- */

.work-grid { display: grid; gap: var(--s9); margin-top: var(--s7); }
.work-col { display: grid; gap: var(--s9); min-width: 0; }

@media (min-width: 900px) {
	.work-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--s10);
		align-items: start;
	}
	.work-col { gap: var(--s10); }

	/* The right column starts lower, so the two never read as a neat 2x2. */
	.work-col--b { margin-top: var(--s10); }

	/* And each drifts at its own rate, so they slide past one another rather
	   than travelling together. Transform only, so it costs nothing to paint. */
	.js .work-col {
		transform: translate3d(0, var(--parallax, 0px), 0);
		will-change: transform;
	}
}

@media (prefers-reduced-motion: reduce) {
	.js .work-col { transform: none; }
}

.film-tile { display: block; text-decoration: none; color: inherit; }

.film-tile__viewport {
	position: relative;
	aspect-ratio: 16 / 9;
	background: #000;
	overflow: hidden;
}

.film-tile__video,
.film-tile__poster {
	position: absolute; inset: 0;
	width: 100%; height: 100%;
	object-fit: cover; display: block;
}

/* The play badge covers the whole frame until the film is running, so the
   target is the picture rather than a small circle in the middle of it. */
.film-tile__play {
	position: absolute; inset: 0;
	display: flex; align-items: center; justify-content: center;
	width: 100%; height: 100%; padding: 0;
	background: rgba(0, 20, 11, .28);
	border: 0; cursor: pointer;
	color: var(--ink-dk);
	transition: background 180ms var(--ease-out), opacity 180ms var(--ease-out);
}
.film-tile__play svg { width: 62px; height: 62px; }
.film-tile__play:hover { background: rgba(0, 20, 11, .14); }
.film-tile__play:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }
.film-tile.is-playing .film-tile__play { opacity: 0; pointer-events: none; }
/* No JavaScript means no play badge, so the native controls stand in. */
html:not(.js) .film-tile__play { display: none; }

.film-tile__body { margin-top: var(--s5); }

.film-tile__title {
	font-family: var(--display); font-weight: 800;
	font-variation-settings: "wdth" 108;
	font-size: clamp(22px, 2.4vw, 32px); line-height: 1.1;
	letter-spacing: -.02em; color: var(--fg); margin: 0;
}
.film-tile__summary {
	color: var(--body); font-size: var(--t-small);
	margin: var(--s3) 0 0; max-width: 46ch;
}
.film-tile__meta { margin: var(--s3) 0 0; }

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

/* ---------------------------------------------------------------
 * The showreel overlay
 * ------------------------------------------------------------- */

/* The video overlay. Deliberately plain: position:fixed, a class, and an
   [hidden] attribute. It was a native <dialog> with showModal(), which did not
   open at all on the client's iPhone 13 and SE in Safari or Firefox. Nothing
   here needs a top layer, a ::backdrop or any recent platform feature. */
.reel[hidden] { display: none; }

.reel {
	position: fixed; inset: 0; z-index: 2000;
	/* Scrollable, because on a phone held sideways the film plus the close
	   button can be taller than the screen and there was no way to reach the
	   button. Centring is done with margin:auto on the child rather than
	   align-items:center, which clips the TOP of anything that overflows and
	   would have put the button out of reach even with scrolling on. */
	display: flex;
	overflow-y: auto;
	padding: var(--s5);
	/* Opaque. At 96% the page behind stayed legible through it, which made
	   the overlay look like a tint rather than a player. */
	background: #000A05;
	opacity: 0;
	transition: opacity 220ms var(--ease-out);
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
}
.reel.is-open { opacity: 1; }

/* Nothing behind the overlay should scroll while it is open. */
html.is-reel-open, html.is-reel-open body { overflow: hidden; }

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

.reel__inner {
	position: relative;
	width: min(98vw, 2600px);
	max-width: 100%;
	margin: auto;
}

.reel__video {
	display: block; width: 100%; height: auto;
	max-height: calc(100vh - 110px); background: #000;
}

.reel__close {
	position: absolute; top: -46px; right: 0;
	display: inline-flex; align-items: center; justify-content: center;
	width: 44px; height: 44px; padding: 0;
	background: transparent; border: 0; cursor: pointer;
	color: var(--ink-dk);
}
.reel__close svg { width: 22px; height: 22px; }
.reel__close:hover { color: var(--accent); }
.reel__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

@media (max-width: 700px) {
	.reel { padding: 0; }
	.reel__inner { width: 100vw; }
	/* No room above the film on a phone, so the close sits over it. The
	   44px box is the minimum comfortable tap target. */
	.reel__close {
		top: auto; bottom: calc(100% + 14px); right: 14px;
	}
}

/* A phone held sideways. There is no room above the film for the close
   button, so it moves onto the film's own corner, clear of the notch. */
@media (max-height: 460px) {
	.reel { padding: 0; }
	.reel__inner { width: 100vw; }
	.reel__video { max-height: 100vh; }
	.reel__close {
		top: calc(8px + env(safe-area-inset-top, 0px));
		right: calc(8px + env(safe-area-inset-right, 0px));
		bottom: auto;
		background: rgba(0, 10, 5, .6);
		border-radius: 50%;
	}
}

/* ---------------------------------------------------------------
 * Treatment tags
 *
 * The named treatments the deck lists under each offer. Outlined only,
 * and square: nothing on this site has a rounded corner.
 * ------------------------------------------------------------- */

.pill-row {
	display: flex; flex-wrap: wrap; gap: 6px;
	list-style: none; padding: 0; margin: var(--s4) 0 0; max-width: none;
}
.pill {
	font-family: var(--serif); font-weight: 500;
	font-size: 12px; line-height: 1.2;
	color: var(--mute);
	border: 1px solid var(--rule);
	border-radius: 0;
	padding: 5px 10px 6px;
	margin: 0;
	white-space: nowrap;
	/* Named properties with an explicit duration, so the hover eases in
	   rather than snapping. */
	transition: color 220ms var(--ease-out), border-color 220ms var(--ease-out);
}

/* The row is inside a link, so the tags pick up the row's hover. */
.service-row:hover .pill { border-color: var(--local-accent, var(--accent)); color: var(--fg); }

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

/* A section head centred over centred content. */
.sec-head--centre .sec-head__row { justify-content: center; }
.sec-head--centre { text-align: center; }

/* A picture band running the full width of the window. */
.strip { margin: 0; overflow: hidden; }
.strip img {
	width: 100%; height: 100%; display: block;
	object-fit: cover;
	object-position: center center;
}

.strip--full {
	width: 100%;
	height: clamp(170px, 16vw, 240px);
	/* Above the mark in the band below, which is absolutely positioned and
	   otherwise paints over the bottom of the picture. */
	position: relative;
	z-index: 2;
}

/* Headline, lead, body: a hierarchy rather than two matching columns. */
.about-head__title { font-size: clamp(30px, 5vw, 68px); }

.about-head__lead { margin-top: var(--s8); max-width: min(46ch, 100%); }

/* Both paragraphs step in from the headline by the same amount, so they line
   up with each other and with the hero and call to action elsewhere. */
@media (min-width: 900px) {
	.about-head__lead { margin-left: min(7vw, 100px); }
}

.about-head__body {
	font-family: var(--serif); font-size: var(--t-copy); line-height: 1.6;
	color: var(--body); margin: var(--s6) 0 0; max-width: min(56ch, 100%);
}
@media (min-width: 900px) {
	.about-head__body { margin-left: min(7vw, 100px); }
}

/* ---------------------------------------------------------------
 * The About page opening
 * ------------------------------------------------------------- */

/* The mark runs through this band and the credentials under it, so only the
   sideways axis is clipped. */
.about-head {
	overflow-x: clip; overflow-y: visible;
	/* Not --band-pad here: the picture above, the headline, the rule and the
	   counters all sit on the same 96px rhythm. */
	padding-top: var(--s9);
	padding-bottom: var(--s9);
}

.about-head__vision {
	margin-top: var(--s7);
	max-width: min(62ch, 100%);
}
@media (min-width: 900px) {
	.about-head__vision { margin-left: min(7vw, 100px); }
}

/* ---------------------------------------------------------------
 * Global reach page
 *
 * The map is the hero, centred and full width rather than sharing the row
 * with copy as it does on the homepage.
 * ------------------------------------------------------------- */

/* Undo the homepage treatment, where the map bleeds off the right edge to
   make room for the text beside it. Here it is the only thing in the band. */
.reach-hero .world {
	width: 100%;
	margin-left: 0;
	margin-right: 0;
	max-width: min(1180px, 100%);
}
.reach-hero__map { display: flex; justify-content: center; margin-top: var(--s9); }

/* The line that closes the section. Display face and accent, centred, so it
   reads as a statement under the map rather than a caption on it. p carries
   the reading measure by default, which would not centre cleanly here. */
.reach-hero__line {
	font-family: var(--display); font-weight: 900;
	font-variation-settings: "wdth" 108;
	font-size: clamp(22px, 2.4vw, 34px); line-height: 1.25;
	letter-spacing: -.015em;
	color: var(--local-accent-ink, var(--accent-ink));
	text-align: center;
	/* Three lines at desktop sizes. 780px is where it drops from four to
	   three and 1080px is where it drops to two, so this sits between
	   them with room on both sides for font-render variation. */
	max-width: min(840px, 100%);
	margin: var(--s9) auto 0;
	text-wrap: balance;
}

.continents {
	list-style: none; padding: 0; margin: 0;
	/* Lists inherit the reading measure, which squeezed six columns into
	   626px and broke the names mid-word. */
	max-width: none;
	display: grid; gap: var(--s7) var(--s6);
	grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 700px)  { .continents { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .continents { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--s7); } }

.continents__item { margin: 0; }

.continents__name {
	font-family: var(--display); font-weight: 800;
	font-variation-settings: "wdth" 108;
	font-size: clamp(20px, 1.9vw, 27px); line-height: 1.1;
	letter-spacing: -.015em;
	color: var(--local-accent-ink, var(--accent-ink));
	margin: 0;
}

/* ---------------------------------------------------------------
 * The work page header
 *
 * Mirrors the services header: headline to the right, mark to the left.
 * ------------------------------------------------------------- */

.work-head__inner { display: grid; gap: var(--s6); justify-items: center; text-align: center; }

/* The mark is free to run past the band vertically so the whole shape can be
   seen on the way down, with the films passing over it. Clipping only the
   horizontal axis keeps it from ever adding a scrollbar. */
.work-head {
	overflow-x: clip;
	overflow-y: visible;
}

.work-head__note {
	font-family: var(--serif); font-size: var(--t-copy);
	color: var(--body); margin: 0; max-width: min(44ch, 100%);
}

/* The cycling phrases at headline scale. The slot is still a single grid
   cell, so the longest phrase sets the width and nothing below it moves. */
.capline--display {
	display: block;
	color: var(--fg);
	/* .capline sets the body face for the small one in the hero, and it wins
	   over .t-display by source order, so the display face is restated here. */
	font-family: var(--display);
	font-weight: 800;
	font-variation-settings: "wdth" 110;
	/* Its own scale, not --t-display: this has to hold the longest phrase on
	   one line at every width, and --t-display bottoms out too large. */
	font-size: clamp(22px, 6.2vw, 100px);
	line-height: 1;
	letter-spacing: -.025em;
	text-shadow: none;
	margin: 0;
}
.capline--display .capline__slot { display: inline-grid; vertical-align: baseline; }
.capline--display .capline__item { white-space: nowrap; }

/* At headline scale the 520ms wipe reads as a flick. It has a 2.5s slot
   before the next phrase, so there is room to take its time. */
.js .capline--display .capline__item.is-on {
	animation: capline-in 1100ms var(--ease-out) forwards;
}

/* ---------------------------------------------------------------
 * Jump links
 *
 * Not a filter: every service is on this page, so these only move the
 * reader down to one of them.
 * ------------------------------------------------------------- */

/* Narrower than the lead default, so it reads as a caption to the headline
   rather than a column of its own. */
.page-lead--inset { max-width: min(600px, 100%); }

/* And steps in from the headline, the same move as the hero and the call to
   action. Scales with the viewport rather than sitting at a flat 100px. */
@media (min-width: 900px) {
	.page-lead--inset { margin-left: min(7vw, 100px); }
}

/* The rail sticks under the header. Its own band, so the sticky ancestor is
   the page rather than a section that would scroll it away. */
.jumps-rail {
	position: sticky; top: 0; z-index: 60;
	background: var(--green-800);
	padding: var(--s5) var(--s5);
	transition: padding 200ms var(--ease-out);
}

/* Plain links, stuck or not. This is navigation, not a row of calls to
   action, so it never wears a box. */
.jumps {
	display: flex; flex-wrap: wrap; justify-content: center;
	gap: var(--s4) var(--s7);
	max-width: var(--wrap); margin: 0 auto;
}
.jumps a {
	font-family: var(--serif); font-size: var(--t-small);
	color: var(--body-dk); text-decoration: none;
	padding: 4px 0 5px;
	transition: color 160ms var(--ease-out);
}
.jumps a:hover { color: var(--accent); }
.jumps a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Stuck, it only closes up and lifts its ground off the page behind it. */
.jumps-rail.is-stuck {
	padding-top: var(--s3); padding-bottom: var(--s3);
}

/* On a phone these three labels wrapped onto three rows, 185px of rail under a
   112px header: 35% of the screen given over permanently to navigation the
   reader has already used. One row that scrolls sideways instead, which is the
   ordinary way to carry more links than fit and costs about a sixth of that. */
@media (max-width: 899px) {
	.jumps-rail { padding: var(--s3) 0; }
	.jumps {
		flex-wrap: nowrap; justify-content: flex-start;
		gap: var(--s6);
		overflow-x: auto;
		scrollbar-width: none;
		padding: 0 var(--s5);
		/* So the last item can reach the middle rather than jamming at the edge. */
		scroll-padding-inline: var(--s5);
	}
	.jumps::-webkit-scrollbar { display: none; }
	.jumps a { white-space: nowrap; flex: none; }
	.jumps-rail.is-stuck { padding-top: var(--s2); padding-bottom: var(--s2); }
}

@media (prefers-reduced-motion: reduce) {
	.jumps-rail, .jumps a { transition: none; }
}

/* The header is fixed, so an anchor has to stop clear of it. */
.band[id],
.film-tile[id] { scroll-margin-top: var(--s9); }

/* ---------------------------------------------------------------
 * Filters on the work archive
 * ------------------------------------------------------------- */

.filters { display: flex; flex-wrap: wrap; gap: var(--s3); margin-bottom: var(--s8); }
.filters a {
	font-family: var(--serif); font-size: 10.5px; text-decoration: none;
	border: 1px solid var(--rule); color: var(--mute);
	padding: 8px 14px; border-radius: 2px;
}
.filters a:hover { border-color: var(--local-accent); color: var(--local-accent); }
.filters a[aria-current] { border-color: var(--local-accent); color: var(--bg); background: var(--local-accent); }

/* ---------------------------------------------------------------
 * Single film
 * ------------------------------------------------------------- */

.film-player { position: relative; aspect-ratio: 16 / 9; background: #000; }
.film-player iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.film-spec {
	display: grid; gap: 1px; background: var(--rule-soft);
	border: 1px solid var(--rule-soft); margin: var(--s8) 0;
}
@media (min-width: 640px) { .film-spec { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } }
.film-spec__cell { background: var(--surface); padding: var(--s5); }
.film-spec__k {
	display: block; font-family: var(--serif); font-size: 9.5px; color: var(--mute); margin-bottom: var(--s2);
}
.film-spec__v { display: block; font-family: var(--serif); font-size: 15px; color: var(--fg); font-variant-numeric: tabular-nums; }

.credits { display: grid; gap: 0; border-top: 1px solid var(--rule); margin-top: var(--s7); }
.credits__row {
	display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4);
	padding: var(--s3) 0; border-bottom: 1px solid var(--rule-soft);
	font-family: var(--serif); font-size: 12.5px;
}
.credits__role { color: var(--mute); font-size: 10.5px; align-self: center; }
.credits__person { color: var(--fg); }

/* Team */

/* The brand green, not --local-accent: on the light ground that resolves to
   the deep accent, which reads brown in gold and rust in coral. */


.site-header__actions { display: flex; align-items: center; gap: var(--s3); flex: none; }

/* Where both are on show, they are a pair, so they are the same width as well
   as the same height. A grid with equal auto columns sizes every column to the
   widest item's content, which makes "Menu" match "Contact" without either
   being given a hand-measured width. */
@media (max-width: 899px) {
	.site-header__actions {
		display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
		justify-content: end;
	}
	.site-header__actions > * { justify-content: center; text-align: center; }
}
/* One declaration for both header controls. The nav toggle is listed here
   rather than sizing itself, so Contact and Menu are identical by construction
   instead of by two sets of numbers that happen to agree. */
.btn--sm,
.nav-toggle { padding: 7px 19px 9px; font-size: 14.5px; }
@media (max-width: 560px) {
	.site-header__actions .btn--sm,
	.nav-toggle { padding: 6px 15px 8px; font-size: 14px; }
}

/* Term links in labels read as labels, not as body links. */
.t-label a { color: inherit; text-decoration: none; }
.t-label a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* ---------------------------------------------------------------
 * The lockup. One SVG, coloured entirely by CSS, so the same file
 * serves bone on the brand green and green on bone.
 * ------------------------------------------------------------- */

.logo-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.logo { display: block; height: auto; color: currentColor; }
/* Enlarged on the client's 16/09/26 note 14, now that it stands alone. */
.logo--footer { width: 188px; color: var(--ink-dk); }

/* ---------------------------------------------------------------
 * Watching the reel.
 *
 * On a mouse the whole hero is the trigger and the reticle says so, which is
 * why there is no button competing with the headline. This control is the
 * same action for anyone on a keyboard or a touchscreen, so the affordance is
 * never mouse-only.
 * ------------------------------------------------------------- */

.hero--watchable { cursor: pointer; }

/* Carries .btn .btn--primary, so everything about its shape, type and colour
   comes from the button component; there is nothing to restate here. */

/* A pointer gets the reticle label instead, so the control only needs to be
   visible when there is no pointer to carry the message. */
@media (pointer: fine) {
	.hero__watch {
		position: absolute; width: 1px; height: 1px;
		overflow: hidden; clip-path: inset(50%); white-space: nowrap;
	}
	.hero__watch:focus-visible {
		position: static; width: auto; height: auto;
		overflow: visible; clip-path: none;
	}
}

/* The Watch reel label carries the accent as a ground, so it stays legible
   over whatever frame happens to be under the cursor. */
@media (max-width: 700px) {
	.cta__heading {
		white-space: normal; text-wrap: balance;
		max-width: min(16ch, 100%);
		font-size: clamp(39px, 6vw, 76px);
	}
}

@media (max-width: 700px) {
	/* Same reason as the display classes in base.css: these ratios are set for
	   type two to three times this size. */
	.cta__heading       { line-height: 1.12; }
	.service-row__title { line-height: 1.2; }
	.credentials__name  { line-height: 1.26; }
}

.reticle__label {
	background: var(--accent);
	color: var(--green-950);
	padding: 6px 12px 7px;
	border-radius: 2px;
}

/* ---------------------------------------------------------------
 * The mark as ambient texture.
 *
 * A single oversized monogram sitting behind a band, masked rather than
 * drawn so its colour comes from the same tokens as everything else. It is
 * deliberately close to the ground colour: this is texture, not content, and
 * it must never compete with the type in front of it.
 * ------------------------------------------------------------- */

.band-mark {
	position: absolute;
	top: 50%;
	right: -18%;
	width: min(150%, 1500px);
	height: auto;
	pointer-events: none;
	z-index: 0;

	/* Transform only, so the drift costs nothing to paint. */
	transform: translate3d(0, calc(-50% + var(--parallax, 0px)), 0);
	will-change: transform;
}

/* The stroke travels the monogram's centreline inside a clip of the real
   outline, so the shape draws itself in the exact logo silhouette. */
.band-mark__trace {
	stroke: var(--mark-tint);
	stroke-dasharray: var(--trace-len, 1200);
	stroke-dashoffset: var(--draw, 0);
}
.band-mark__dot {
	fill: var(--mark-tint);
	opacity: var(--draw-dot, 1);
}

/* On a phone the mark was reading as a small logo sitting in the band rather
   than as the texture behind it. The cause is that it is sized by width and
   then clamped to the band, so its HEIGHT follows the band's width: at 1440
   that gives a mark 103-132% of the band's height, and at 390 only 18%. Sized
   generously here so the proportion of band it covers is closer to the desktop
   one; the band clips it, as it does on a wide screen. */
@media (max-width: 899px) {
	.band-mark { width: min(340%, 1400px); max-width: none; }
}

/* Asked for on 18/09/26: the mark on the team page should fill more of the
   empty ground beside the portraits. It is sized by width and clipped by the
   band, so a larger width simply shows more of the monogram. */
.band-mark--xl { width: min(210%, 2100px); max-width: none; }

.band-mark--left { right: auto; left: -18%; }
.band-mark--centre { right: auto; left: 50%; transform: translate3d(-50%, calc(-50% + var(--parallax, 0px)), 0); }

/* Content sits above the texture. */
.band:has(.band-mark):not(.work-head):not(.about-head) { overflow: hidden; }
.band > .wrap { position: relative; z-index: 1; }

@media (prefers-reduced-motion: reduce) {
	.band-mark { transform: translate3d(0, -50%, 0); }
	.band-mark__trace { stroke-dashoffset: 0; }
	.band-mark__dot { opacity: 1; }
}

/* ---------------------------------------------------------------
 * The equation.
 *
 * Three terms on the left, a single pivot in the middle, the answer on the
 * right. On a narrow screen it collapses to one column and the equals simply
 * becomes the next line, which is how you would write it out by hand.
 * ------------------------------------------------------------- */

.equation { display: grid; gap: var(--s6); align-items: center; }

@media (min-width: 900px) {
	.equation {
		grid-template-columns: auto minmax(var(--s8), 1fr) auto;
		gap: 0;
	}
	.equation__equals { justify-self: center; }
}

.equation__terms { display: grid; gap: var(--s4); justify-items: start; }

.equation__term {
	font-family: var(--display); font-weight: 600;
	font-variation-settings: "wdth" 104;
	font-size: clamp(20px, 2.3vw, 30px); line-height: 1.15;
	letter-spacing: -.015em;
	color: var(--fg); margin: 0; max-width: 22ch;
}

/* The operators are structure, not reading matter, so they sit back. */
.equation__op,
.equation__equals {
	font-family: var(--display); font-weight: 500;
	color: var(--fg); opacity: .5; line-height: 1;
	display: block;
}
.equation__op { font-size: clamp(20px, 2.2vw, 28px); }
.equation__equals { font-size: clamp(38px, 6vw, 84px); opacity: .45; }

.equation__answer {
	font-family: var(--display); font-weight: 800;
	font-variation-settings: "wdth" 108;
	font-size: clamp(28px, 4.4vw, 58px); line-height: 1.03;
	letter-spacing: -.025em;
	color: var(--fg); margin: 0; max-width: none;
}

.equation__note {
	font-family: var(--serif); font-size: var(--t-copy);
	line-height: 1.5; color: var(--fg); opacity: .78;
	margin: var(--s5) 0 0; max-width: 32ch;
}

/* ---------------------------------------------------------------
 * The bridging image.
 *
 * Sits across the join between the counters and the equation, half on each
 * ground. It lives in the lower section and is pulled up by its own half
 * height plus that section's top padding, so the split lands exactly on the
 * colour change whatever the padding resolves to.
 *
 * It cannot live in the section above: that one clips its overflow to contain
 * the ambient mark.
 * ------------------------------------------------------------- */

/* The row is what straddles the join. It is pulled up by the next section's
   own top padding plus half the tall centre image, so the split lands exactly
   on the colour change. The side images are shorter but share the same centre
   line, so they straddle it evenly too. */
.straddle-row {
	position: relative;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: var(--s5);
	width: min(94%, 1360px);
	margin: calc(-1 * (var(--band-pad) + var(--straddle-h) / 2)) auto var(--band-pad);
}

.straddle {
	position: relative;
	margin: 0;
	height: var(--straddle-h);
	overflow: hidden;
	flex: 0 1 auto;
	min-width: 0;
}

.straddle--main { width: min(58%, 820px); }

/* Smaller either side, and shorter, so the centre stays the subject. */
.straddle--side {
	width: min(21%, 300px);
	height: calc(var(--straddle-h) * 0.66);
}

.straddle img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* On a narrow screen the centre image carries the join on its own. Three
   abreast becomes three slivers, and stacking them breaks the straddle:
   the pull-up is half of one image, not half of a column of three. */
@media (max-width: 899px) {
	.straddle-row { width: 100%; }
	.straddle--main { width: 100%; }
	.straddle--side { display: none; }
}

/* ---------------------------------------------------------------
 * Global reach.
 *
 * The landmasses are a mask, so the map takes the brand green from the tokens
 * rather than being a flat image. The routes above it draw themselves in the
 * same gesture as the logo.
 * ------------------------------------------------------------- */

/* The map bleeds past the wrap, so the band clips rather than scrolls. */
.band--reach {
	overflow-x: clip; --band-pad: clamp(64px, 8vw, 120px);
	/* Sampled from the supplied animation, whose own ground is #04331A rather
	   than the theme's green-950. A mismatch shows as a seam along the video. */
	--bg: #04331A;
}

/* The credits band carries its own padding: at the shared 176px it towered
   over its own content once the rows were distributed. */
/* The credits band is beige now, so the broadcaster names read green. Its own
   padding too: at the shared 176px it towered over its own content. */
.band--credits {
	--band-pad: clamp(56px, 6.5vw, 104px);
	--bg:        var(--bone-2);
	--fg:        var(--ink-lt);
	--body:      var(--body-lt);
	--mute:      var(--mute-lt);
	--rule:      var(--bone-line);
	--rule-soft: #D2C9B4;
	--surface:   var(--bone-3);
	--mark-tint: var(--bone-3);
	--local-accent:     var(--accent);
	/* The "Broadcast credits" heading reads from this. Green rather than the
	   deep gold, so the heading and the names it introduces are one colour and
	   the hierarchy comes from size alone. */
	--local-accent-ink: var(--ink-lt);
}

.reach-split { display: grid; gap: var(--s8); align-items: center; }

@media (min-width: 900px) {
	/* The map spans the whole row and the copy sits on top of its western
	   ocean, which is the only way to give the map real size without
	   squeezing the text into a column too narrow to read. */
	.reach-split {
		grid-template-columns: minmax(0, 0.72fr) minmax(0, 1.28fr);
		gap: 0;
	}
	.reach-split__body {
		grid-column: 1; grid-row: 1;
		position: relative; z-index: 1;
	}
	.reach-split .world {
		grid-column: 1 / -1; grid-row: 1;
		/* width:auto matters. With width:100% the box ignores these margins and
		   the eastern edge of the map runs off screen. */
		width: auto;
		margin-left: 19.5%;
		/* Half the row minus half the viewport is exactly the distance from the
		   wrap's right edge to the screen's, whatever the gutters happen to be. */
		margin-right: calc(50% - 50vw);
	}
}

.reach-split__actions { margin-top: var(--s7); }
.reach-split__body p { color: var(--body); font-size: var(--t-copy); line-height: 1.55; max-width: 34ch; }

/* The client's supplied animation, in place of the SVG map this theme used to
   draw. It carries its own ground, so the band matches it above. */
.world { position: relative; margin: 0 auto; width: 100%; max-width: min(1180px, 100%); }
.world__video {
	display: block; width: 100%; height: auto;
	aspect-ratio: 16 / 9;
	background: #04331A;
}

/* ---------------------------------------------------------------
 * The reach sequence.
 *
 * One ordered run once the row comes into view: the map wipes in, the
 * words follow, the pins land, and only then are the routes traced out
 * and broken into dashes. Driven off a single .is-in so the order can
 * never come apart.
 * ------------------------------------------------------------- */

/* The map itself no longer wipes: the supplied animation does its own opening,
   and running an iris over the top of it fought the video's first second. The
   words below still follow it in. */

/* Negative top and bottom insets, as everywhere else: these headlines run a
   line-height below 1, so a box-edge clip would cut the descenders off. */
.js .fx-world .reach-split__title .line > b {
	display: block; font-weight: inherit;
	clip-path: inset(-0.4em 100% -0.4em 0);
}
.js .fx-world.is-in .reach-split__title .line > b {
	animation: world-headline 760ms var(--ease-out) 320ms forwards;
}

.js .fx-world .reach-split__body p,
.js .fx-world .reach-split__actions { opacity: 0; transform: translateY(14px); }

.js .fx-world.is-in .reach-split__body p {
	animation: world-rise 700ms var(--ease-out) 560ms forwards;
}
.js .fx-world.is-in .reach-split__actions {
	animation: world-rise 700ms var(--ease-out) 700ms forwards;
}

/* The pin and route animations went with the SVG: the supplied video draws
   its own. Only the two that still drive the words beside it remain. */
@keyframes world-rise { to { opacity: 1; transform: none; } }
@keyframes world-headline { to { clip-path: inset(-0.4em 0 -0.4em 0); } }

@media (prefers-reduced-motion: reduce) {
	.js .fx-world .reach-split__title .line > b,
	.js .fx-world.is-in .reach-split__title .line > b { clip-path: none; animation: none; }
	.js .fx-world .reach-split__body p,
	.js .fx-world .reach-split__actions,
	.js .fx-world.is-in .reach-split__body p,
	.js .fx-world.is-in .reach-split__actions { opacity: 1; transform: none; animation: none; }
}
