/* ---------------------------------------------------------------
 * Team grid, About
 * ------------------------------------------------------------- */

/* ---------------------------------------------------------------
 * The people
 *
 * One person per row: a large portrait on one side, everything else on the
 * other, swapping sides down the page. The same rhythm as the services
 * sections, so the page never reads as a grid of cards.
 * ------------------------------------------------------------- */

/* 18/09/26: more air between the people, and between each photograph and its
   copy, so the page reads as a series of profiles rather than a list. */
.people { display: grid; gap: clamp(var(--s9), 9vw, 160px); }

/* Top aligned, not centred. Centred, each photograph sat against the middle of
   its own bio, so with bios of different lengths the tops drifted by anything
   from 215px above the name to 44px below it and no two rows started level. */
.person { display: grid; gap: var(--s7); align-items: start; }

@media (min-width: 860px) {
	/* Fixed tracks, not fractions. With fractions the text column is wider
	   than the text, so the leftover space lands on a different side in each
	   row and the two gaps read as unequal. */
	.person {
		grid-template-columns: 320px minmax(0, 600px);
		gap: clamp(var(--s9), 8vw, 150px);
		justify-content: center;
	}
	/* Flip the row rather than reordering the markup, so the reading order
	   stays portrait then name whichever side the picture is on. */
	.person--right { direction: rtl; }
	.person--right > * { direction: ltr; }
}

/* Capped as well as proportioned, so it stays a portrait rather than
   becoming a poster on a wide screen. justify-self: start resolves
   against the row's own direction, so it hugs the outer edge on both. */
.person__portrait {
	margin: 0; aspect-ratio: 4 / 5; overflow: hidden;
	background: var(--surface);
	width: 100%;
}
.person__portrait img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Opens from a different edge on each side, towards the copy it belongs to. */
/* Closed until the portrait arrives, then one wipe from the edge nearest the
   copy it belongs to. Deliberately not scroll linked: scrubbed against scroll
   position it sat part open whenever the reader stopped mid-page, which reads
   as a half-loaded image rather than as an effect.

   Only under .js, so without scripting the portrait is simply visible. */
/* The clip goes on the IMAGE, never on the figure. Clipping the observed
   element to zero width makes its intersectionRatio 0, so the observer that
   should reveal it never fires and it hides itself for good. The figure stays
   unclipped and watchable; the image inside it is what wipes. */
/* Driven by --p, which the script sets from the portrait's scroll position:
   0 closed, 1 open. It falls back to 0, closed, so the portrait is not shown
   finished for a frame and then snapped shut the moment the script first runs.
   That costs a dependency: everything here is under .js, and motion.js carries
   a failsafe that opens them if anything goes wrong, which is the same bargain
   every other reveal on the site makes. Opens from the edge nearest the copy
   it belongs to. */
.js .person--left  .person__portrait img { clip-path: inset(0 calc((1 - var(--p, 0)) * 100%) 0 0); }
.js .person--right .person__portrait img { clip-path: inset(0 0 0 calc((1 - var(--p, 0)) * 100%)); }

@media (prefers-reduced-motion: reduce) {
	.js .person--left  .person__portrait img,
	.js .person--right .person__portrait img { clip-path: none; }
}

.person__name {
	font-family: var(--display); font-weight: 800;
	font-variation-settings: "wdth" 108;
	font-size: clamp(30px, 3.4vw, 48px); line-height: 1.12;
	letter-spacing: -.02em; color: var(--fg); margin: 0;
}
.person__role {
	font-family: var(--serif); font-size: var(--t-small);
	/* The gap below is wider than the one above, so the role reads as
	   belonging to the name rather than floating between the two. */
	color: var(--green-brand); margin: var(--s3) 0 var(--s7);
}
.person__bio { color: var(--body); }
.person__bio p { font-size: var(--t-body); line-height: 1.65; margin: 0 0 var(--s5); }
.person__bio p:last-child { margin-bottom: 0; }
.person .pill-row { margin-top: var(--s6); }

.chip-list { display: flex; flex-wrap: wrap; gap: var(--s2); list-style: none; padding: 0; margin: var(--s5) 0 0; max-width: none; }
.chip-list .chip {
	font-family: var(--serif); font-size: 10px;
	border: 1px solid var(--rule); color: var(--mute);
	padding: 5px 9px; border-radius: 0; margin: 0;
}

/* Approach list */

.approach { border-top: 1px solid var(--rule); }
.approach__row {
	display: grid; gap: var(--s3);
	padding: var(--s7) 0; border-bottom: 1px solid var(--rule);
}
@media (min-width: 860px) {
	.approach__row { grid-template-columns: 62px 1fr 1fr; gap: var(--s6); align-items: baseline; }
}
.approach__n { font-family: var(--serif); font-size: 12px; color: var(--local-accent-ink); }
.approach__k {
	font-family: var(--display); font-weight: 700;
	font-size: clamp(20px, 2.4vw, 28px); line-height: 1.14;
	letter-spacing: -.015em; color: var(--fg); margin: 0;
}
.approach__v { color: var(--body); margin: 0; font-size: var(--t-small); line-height: 1.62; }

/* ---------------------------------------------------------------
 * Services index
 * ------------------------------------------------------------- */

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

/* Larger than the shared section size, per the client's 16/09/26 note 11:
   these three are the page, so they lead it. */
.offer__body h2 { font-size: clamp(43px, 6.2vw, 88px); line-height: 1.02; margin: 0 0 var(--s7); }
@media (max-width: 700px) {
	.offer__body h2 { line-height: 1.12; }
	.person__name   { line-height: 1.2; }
}

/* The service paragraphs step down from the shared lead size. The hero's own
   lead keeps the full size, so the top of the page still leads. */
.offer .t-lead { font-size: clamp(18px, 2vw, 22px); }
@media (min-width: 900px) {
	.offer { grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.6fr); gap: var(--s10); }

	/* The body copy indents from its headline, so each section reads as a
	   title with an argument set under it rather than one flush slab. */
	.offer__body .t-lead,
	.offer__body .entry { margin-left: var(--offer-indent, 100px); }

	/* Alternate sections put the pictures on the other side. */
	.offer--right { direction: rtl; }
	.offer--right > * { direction: ltr; }

	/* Body copy is left-aligned, so the slack left by a ragged right edge
	   always falls on the right. Where the pictures are on the right that
	   slack IS the gap, and the section breathes: 128px of grid gap measured
	   as ~300px of daylight. Mirrored, the text butts straight up against the
	   pictures and the identical gap measured as 128px, which is why only
	   this section looked tight. Padding the inner edge puts comparable
	   daylight on the side facing the pictures either way. */
	.offer--right .offer__body { padding-left: var(--s9); }

	/* The collage runs to the window edge, and stops there.
	   NOT calc(50% - 50vw): that resolves against this grid column rather than
	   the wrap, so it overshot by ~400px and cut the frames off. The distance
	   from the wrap's content edge to the window is its gutter, plus half of
	   whatever the window has spare once the wrap has hit its max width. */
	.offer .shots  { margin-right: calc(-1 * (var(--s8) + max(0px, (100vw - var(--wrap)) / 2))); }
	.offer--right .shots { margin-right: 0; margin-left: calc(-1 * (var(--s8) + max(0px, (100vw - var(--wrap)) / 2))); }
}

/* ---------------------------------------------------------------
 * The picture cluster
 *
 * Three frames from the deck, staggered rather than stacked: a large one
 * anchoring the group and two smaller ones overlapping it. Built on a grid
 * so the overlaps hold at any width without absolute positioning.
 * ------------------------------------------------------------- */

.shots { display: grid; gap: var(--s4); }

/* Two tall frames and two short ones, placed so the pair reads as a diagonal:
   the first reaches above its neighbour, the last drops below its own.
   The short frames are pinned to the far side of their row, top-right and
   bottom-left, so the space each leaves is on the same diagonal as the tall
   frames and reads as part of the arrangement. Varying the ratios freely, with
   every frame pinned to the top of its row, is what made this look accidental
   before: the leftover space had no relationship to anything. */
@media (min-width: 900px) {
	.shots {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--s3);
	}
	.shots__item--1 { grid-column: 1; grid-row: 1; aspect-ratio: 3 / 4; }
	.shots__item--2 { grid-column: 2; grid-row: 1; aspect-ratio: 4 / 3; align-self: end; }
	.shots__item--3 { grid-column: 1; grid-row: 2; aspect-ratio: 4 / 3; align-self: start; }
	.shots__item--4 { grid-column: 2; grid-row: 2; aspect-ratio: 3 / 4; }

	/* Three keeps the same diagonal: the third runs the full width underneath. */
	.shots--3 .shots__item--3 { grid-column: 1 / -1; aspect-ratio: 21 / 9; align-self: start; }
	/* Two sit side by side, one tall and one short, still offset. */
	.shots--2 .shots__item--2 { align-self: end; }
	/* One fills the width. */
	.shots--1 .shots__item--1 { grid-column: 1 / -1; aspect-ratio: 16 / 9; }
}

.shots__item { position: relative; margin: 0; overflow: hidden; }
.shots__item img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Each frame opens from a different edge, as the bridging row does. */
/* Driven by --p, the same arrival mechanism the team portraits use, so the
   collage opens when you reach it instead of tracking the scroll bar. Each
   frame still wipes from its own edge, which is what keeps the group from
   reading as one block. */
.js .shots__item--1 { clip-path: inset(0 calc((1 - var(--p, 0)) * 100%) 0 0); }
.js .shots__item--2 { clip-path: inset(calc((1 - var(--p, 0)) * 100%) 0 0 0); }
.js .shots__item--3 { clip-path: inset(0 0 0 calc((1 - var(--p, 0)) * 100%)); }
.js .shots__item--4 { clip-path: inset(calc((1 - var(--p, 0)) * 100%) 0 0 0); }

@media (prefers-reduced-motion: reduce) {
	.js .shots__item--1,
	.js .shots__item--2,
	.js .shots__item--3,
	.js .shots__item--4 { clip-path: none; --p: 1; }
}

/* ---------------------------------------------------------------
 * Treatment subsections
 *
 * Each named treatment from the deck, with its own picture and copy,
 * sitting under the service it belongs to.
 * ------------------------------------------------------------- */

.subs {
	display: grid; gap: var(--s8);
	/* Matched, so the rule sits centred in its own gap rather than being
	   pushed down towards the subsections. */
	margin-top: var(--s8);
	padding-top: var(--s8);
	border-top: 1px solid var(--rule);
}
@media (min-width: 760px)  { .subs { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s9) var(--s10); } }
@media (min-width: 1100px) { .subs { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s9) var(--s10); } }

.sub { margin: 0; }
.sub__shot { margin: 0; aspect-ratio: 4 / 3; overflow: hidden; }
.sub__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }

.sub__title {
	font-family: var(--display); font-weight: 700;
	font-variation-settings: "wdth" 108;
	font-size: clamp(22px, 2.2vw, 30px); line-height: 1.15;
	letter-spacing: -.015em; color: var(--fg); margin: 0;
}
/* Only after a picture, so a subsection without one still starts flush. */
.sub__shot + .sub__title { margin-top: 3rem; }
.sub__text {
	font-family: var(--serif); font-size: var(--t-small); line-height: 1.6;
	color: var(--body); margin: var(--s3) 0 0;
}

/* Like the cluster above them, the subsections open from different edges
   rather than all wiping the same way. Cycles every three, so a row of two
   or a row of three never has two neighbours moving together. */
.js .sub__shot.fx-reveal--block,
.js .sub:nth-child(3n + 1) .sub__shot.fx-reveal--block {
	clip-path: inset(0 calc((1 - var(--reveal, 1)) * 100%) 0 0);
}
.js .sub:nth-child(3n + 2) .sub__shot.fx-reveal--block {
	clip-path: inset(calc((1 - var(--reveal, 1)) * 100%) 0 0 0);
}
.js .sub:nth-child(3n + 3) .sub__shot.fx-reveal--block {
	clip-path: inset(0 0 0 calc((1 - var(--reveal, 1)) * 100%));
}

@media (prefers-reduced-motion: reduce) {
	.js .sub__shot.fx-reveal--block,
	.js .sub:nth-child(3n + 1) .sub__shot.fx-reveal--block,
	.js .sub:nth-child(3n + 2) .sub__shot.fx-reveal--block,
	.js .sub:nth-child(3n + 3) .sub__shot.fx-reveal--block { clip-path: none; --reveal: 1; }
}

.offer__n { font-family: var(--serif); font-size: 12px; color: var(--local-accent-ink); }
.offer .entry p { font-size: var(--t-small); line-height: 1.65; color: var(--body); }

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

/* The mark needs room to read as texture, and the band is short, so the
   padding is set here rather than left to --band-pad. */
.contact-head {
	padding-top: var(--s9);
	padding-bottom: var(--s9);
}

/* One line at the top end, stepping down on narrow screens rather than
   wrapping. Capped against the viewport so it never drives overflow. */
.contact-head__title { font-size: clamp(40px, 9.2vw, 118px); }

.contact-head__lead { margin: var(--s7) 0 0; max-width: min(54ch, 100%); }

/* Stepped in from the headline, the same way the team page's lead is, and
   only where there is room: below 900px the indent would eat the measure. */
@media (min-width: 900px) {
	.contact-head__lead { margin-left: min(7vw, 100px); }
}

.contact { display: grid; gap: var(--s9); align-items: start; }
@media (min-width: 900px) { .contact { grid-template-columns: 1.5fr 1fr; gap: var(--s10); } }

/* Every run of text on this page sits at the standard paragraph size. The
   page had drifted to seven different sizes between 10.5px and 25px, which
   read as arbitrary rather than as hierarchy. Hierarchy here comes from the
   headings and from space, not from shrinking the body copy. */
.contact p,
.contact li,
.contact address,
.contact__note,
.contact__address,
.contact__list li,
.notice-panel p,
.notice-panel li { font-size: var(--t-body); line-height: 1.62; }

/* The section headings. The display face, as .t-minor uses it: Archivo 700,
   sentence case, the system's slightly negative tracking, no caps and no
   letter-spacing. Bigger than the site-wide .t-label because here they are
   the only thing organising a long column. Scoped, so the label on the film
   pages keeps its own treatment. The accent colour carries over. */
.contact .t-label {
	font-family: var(--display); font-weight: 700;
	font-variation-settings: "wdth" 108;
	font-size: 27px; line-height: 1.2; letter-spacing: -.015em;
}

.form { display: grid; gap: var(--s5); max-width: 640px; }
.form__row { display: grid; gap: var(--s2); }
.form__grid { display: grid; gap: var(--s5); }
@media (min-width: 620px) { .form__grid { grid-template-columns: 1fr 1fr; } }

/* 600 is the top of the Literata range loaded in the header; 700 would only
   be synthesised. */
.form__label {
	font-family: var(--serif); font-size: var(--t-body); font-weight: 600;
	line-height: 1.4; color: var(--fg);
}

/* Boxed fields: a rule on all four sides, so an empty field still reads as
   somewhere to type. The focus ring is drawn with an inset shadow rather
   than a width change, so nothing shifts when you tab through. */
/* A touch of the foreground mixed into the hairline. --rule is tuned for
   dividers, which sit under text; a field border has to hold an empty box on
   its own, so it needs more weight. Mixed rather than hard-coded, so it
   follows whichever ground and palette the form lands on. */
.form__control {
	font-family: var(--serif); font-size: var(--t-body); line-height: 1.5;
	color: var(--fg); background: var(--bg);
	border: 1px solid var(--rule);
	/* Declared second so a browser without color-mix drops this line and keeps
	   the plain --rule above, rather than falling back to currentColor. */
	border-color: color-mix(in srgb, var(--rule) 68%, var(--fg));
	border-radius: 0; padding: 13px 15px; width: 100%;
	transition: border-color var(--d-fast) ease, box-shadow var(--d-fast) ease;
}
.form__control::placeholder { color: var(--mute); }
.form__control:hover { border-color: var(--mute); }
.form__control:focus {
	outline: none; border-color: var(--local-accent-ink, var(--accent-ink));
	box-shadow: inset 0 0 0 1px var(--local-accent-ink, var(--accent-ink));
}
.form__control:focus-visible { outline: none; }
textarea.form__control { resize: vertical; min-height: 160px; }

/* appearance:none strips the native arrow, so the chevron is drawn back in.
   Padded right so a long option never runs under it. */
select.form__control {
	appearance: none; cursor: pointer; padding-right: 44px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' fill='none' stroke='%236B7264' stroke-width='1.6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 16px center;
}

.form__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form .btn { justify-self: start; }

.notice-panel {
	border-left: 3px solid var(--local-accent);
	background: var(--surface); padding: var(--s5);
	margin-bottom: var(--s7);
}
.notice-panel--bad { border-left-color: #B3311B; }
.notice-panel p, .notice-panel li { margin-bottom: 0; }

.contact__list { list-style: none; padding: 0; margin: var(--s4) 0 0; }
.contact__list li { margin-bottom: var(--s3); }
.contact__list a { text-decoration: none; color: var(--fg); }
.contact__list a:hover { color: var(--local-accent); text-decoration: underline; text-underline-offset: 4px; }

/* The LinkedIn button is inside this list, so the link colour above would win
   over anything set in components.css, which loads earlier. Stated here so the
   white wordmark actually stays white. */
.contact__list a.linkedin-btn,
.contact__list a.linkedin-btn:hover { color: #fff; text-decoration: none; }
.contact__address { font-style: normal; font-family: var(--serif); color: var(--body); margin-top: var(--s4); }
.contact__note { color: var(--body); margin-top: var(--s4); }

/* ---------------------------------------------------------------
 * Reach map
 * ------------------------------------------------------------- */

.reach { margin: 0; }
.reach__svg { width: 100%; height: auto; overflow: visible; }

.reach__route {
	fill: none;
	stroke: var(--local-accent);
	stroke-width: 1.1;
	stroke-linecap: round;
	opacity: .75;
	stroke-dasharray: 340;
	stroke-dashoffset: 340;
}
.js .fx-routes.is-in .reach__route {
	animation: reach-draw 1100ms var(--ease-trace) forwards;
	animation-delay: calc(var(--i) * 90ms);
}
@keyframes reach-draw { to { stroke-dashoffset: 0; } }

.reach__pin { fill: var(--fg); }
.reach__hub.is-home .reach__pin { fill: var(--local-accent); }
.reach__label {
	font-family: var(--serif); font-size: 5.4px; fill: var(--mute);
}
.reach__hub.is-home .reach__label { fill: var(--local-accent-ink); }

.js .fx-routes .reach__hub { opacity: 0; }
.js .fx-routes.is-in .reach__hub {
	animation: reach-pin 420ms var(--ease-out) forwards;
	animation-delay: calc(600ms + var(--i) * 55ms);
}
@keyframes reach-pin { to { opacity: 1; } }

.reach__caption {
	font-family: var(--serif); font-size: 11px; color: var(--mute); margin-top: var(--s6);
}

/* Regions */

.region-grid { display: grid; gap: 1px; background: var(--rule-soft); border: 1px solid var(--rule-soft); }
@media (min-width: 640px) { .region-grid { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } }
.region-card {
	background: var(--surface); padding: var(--s6) var(--s5);
	text-decoration: none; display: block;
}
.region-card:hover { background: var(--surface-2); }
.region-card__name {
	font-family: var(--display); font-weight: 700; font-size: clamp(19px, 2vw, 24px);
	color: var(--fg); margin: 0 0 var(--s2); letter-spacing: -.01em;
}
.region-card__count { font-family: var(--serif); font-size: 11px; color: var(--mute); }

/* ---------------------------------------------------------------
 * Prose from the editor
 * ------------------------------------------------------------- */

.entry p { color: var(--body); }
.entry h2 {
	font-family: var(--display); font-weight: 700;
	font-size: clamp(24px, 3vw, 34px); line-height: 1.1;
	color: var(--fg); margin: var(--s8) 0 var(--s4); letter-spacing: -.02em;
}
.entry h3 {
	font-family: var(--display); font-weight: 700; font-size: 20px;
	color: var(--fg); margin: var(--s7) 0 var(--s3);
}
.entry a { color: var(--local-accent); }

@media (prefers-reduced-motion: reduce) {
	.js .fx-routes .reach__route,
	.js .fx-routes.is-in .reach__route { stroke-dashoffset: 0; animation: none; }
	.js .fx-routes .reach__hub,
	.js .fx-routes.is-in .reach__hub { opacity: 1; animation: none; }
}
