/*
 * site.css
 * Implementazione di docs/design.md. I token sono gli stessi, con gli stessi
 * nomi: se un valore cambia, cambia prima nel design system.
 * Nessun build step: questo file si serve così com'è.
 */

@import url('/assets/fonts/fonts.css');

/* ---------- tokens ---------- */
:root {
	--color-surface: #000000;
	--color-surface-raised: #191919;
	--color-surface-inverse: #FEFCF6;
	--color-on-surface: #FEFCF6;
	--color-on-surface-muted: #989794;
	--color-on-surface-subtle: #666562;
	--color-on-surface-inverse: #000000;
	--color-on-surface-inverse-muted: #5C5B58;
	--color-on-surface-inverse-faint: #BDBBB5;
	--color-accent: #006FED;
	--color-border: #333230;
	--color-border-inverse: #CBCAC5;

	--font-display: "Darker Grotesque", Georgia, serif;
	--font-text: "Darker Grotesque", Georgia, serif;

	--display-xl: 500 88px/0.95 var(--font-display);
	--display-l: 500 64px/1 var(--font-display);
	--display-m: 500 44px/1.05 var(--font-display);
	--display-s: 500 32px/1.1 var(--font-display);
	/* One family for the whole site. Darker Grotesque carries a much lower
	   x-height than a text face, so every size here is raised by roughly a
	   third against the equivalent in a neutral sans, and the leading is
	   tightened to match: at the old numbers the same words read a size
	   smaller than they are. */
	--body-l: 500 24px/1.45 var(--font-text);
	--body: 500 21px/1.5 var(--font-text);
	--body-s: 500 18px/1.45 var(--font-text);
	--body-strong: 600 24px/1.4 var(--font-text);
	--label: 600 15px/1.2 var(--font-text);
	--nav: 500 20px/1.2 var(--font-text);

	--ls-display-xl: -0.02em;
	--ls-display-l: -0.02em;
	--ls-display-m: -0.015em;
	--ls-display-s: -0.01em;
	--ls-label: 0.08em;

	--rounded-none: 0px;
	--rounded-sm: 2px;
	--rounded-pill: 9999px;

	--space-3xs: 4px;
	--space-2xs: 8px;
	--space-xs: 12px;
	--space-sm: 16px;
	--space-md: 24px;
	--space-lg: 40px;
	--space-xl: 64px;
	--space-2xl: 96px;
	--space-3xl: 144px;

	--header-h: 80px;
	--reveal-rise: 18px;
	--reveal-duration: 1100ms;
	--reveal-stagger: 140ms;
	/* Distributed rather than front loaded: an expo curve spends its whole
	   budget in the first third, so a longer one reads as a pause, not as slower. */
	--reveal-ease: cubic-bezier(0.33, 1, 0.68, 1);

	--container: 1920px;
	--measure: 34em;
}

/* ---------- base ---------- */
*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
	margin: 0;
	background: var(--color-surface);
	color: var(--color-on-surface);
	font: var(--body);
	/* Grayscale smoothing thins light glyphs on a dark ground: it is why body is 500. */
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, p, ul, ol { margin: 0; }
h1, h2, h3 { text-wrap: balance; }
ul, ol { padding: 0; list-style: none; }
a { color: inherit; }
/* Video sits here beside img on purpose. Both carry width and height
   attributes, which the browser reads as a style; without height auto the
   declared height wins over any aspect ratio, and the element keeps the pixel
   height of the file whatever width it is given. */
img, video { max-width: 100%; height: auto; display: block; }

:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }

.skip {
	position: absolute; left: var(--space-md); top: -100px;
	background: var(--color-surface-inverse); color: var(--color-on-surface-inverse);
	padding: var(--space-xs) var(--space-md); border-radius: var(--rounded-pill);
	font: var(--nav); z-index: 20;
}
.skip:focus { top: var(--space-md); }

/* ---------- reveal ----------
   The hidden state applies only while JS is running (html.js), and it carries
   its own way out: if reveal.js never marks the document ready, the keyframe
   below reveals the text after 3s with no script involved. */
.js .reveal {
	opacity: 0; transform: translateY(var(--reveal-rise));
	animation: reveal-failsafe 300ms linear 3s forwards;
}
.js.reveal-ready .reveal { animation: none; }
.js .reveal.is-in {
	opacity: 1; transform: none; animation: none;
	transition: opacity var(--reveal-duration) var(--reveal-ease),
	            transform var(--reveal-duration) var(--reveal-ease);
}
/* A second way in, for the band under the hero: the element resolves out of a
   blur instead of rising. Nothing moves, so a strip that sits on a hairline
   does not appear to slide against it, and the pair arrives one after the
   other rather than together. */
.js .reveal-blur { transform: none; filter: blur(8px); }
.js .reveal-blur.is-in { filter: none; transition: opacity var(--reveal-duration) var(--reveal-ease), filter var(--reveal-duration) var(--reveal-ease); }
.js .reveal-2.is-in { transition-delay: var(--reveal-stagger); }
.js .reveal-3.is-in { transition-delay: calc(var(--reveal-stagger) * 2); }
/* the band under the hero arrives after the three lines above it, on the
   same load: it belongs to the first screen, not to what is scrolled to */
.js .reveal-4.is-in { transition-delay: calc(var(--reveal-stagger) * 3); }
.js .reveal-5.is-in { transition-delay: calc(var(--reveal-stagger) * 4); }
@keyframes reveal-failsafe { to { opacity: 1; transform: none; filter: none; } }
@media (prefers-reduced-motion: reduce) {
	.js .reveal, .js .reveal.is-in { opacity: 1; transform: none; filter: none; animation: none; transition: none; }
}

/* ---------- type ---------- */
.display-xl { font: var(--display-xl); letter-spacing: var(--ls-display-xl); }
.display-l  { font: var(--display-l);  letter-spacing: var(--ls-display-l); }
.display-m  { font: var(--display-m);  letter-spacing: var(--ls-display-m); }
.display-s  { font: var(--display-s);  letter-spacing: var(--ls-display-s); }
.body-l { font: var(--body-l); }
.body-s { font: var(--body-s); }
.body-strong { font: var(--body-strong); }
.muted { color: var(--color-on-surface-muted); }
.measure { max-width: var(--measure); }

.label {
	display: inline-flex; align-items: center; gap: var(--space-2xs);
	font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase;
	color: var(--color-on-surface-muted);
}
.label::before { content: ""; width: 10px; height: 10px; background: var(--color-accent); flex: none; }

/* ---------- layout ---------- */
.container {
	max-width: var(--container);
	margin: 0 auto;
	padding-left: var(--space-lg);
	padding-right: var(--space-lg);
}

.section { padding-top: var(--space-2xl); padding-bottom: var(--space-2xl); }
.section + .section { border-top: 1px solid var(--color-border); }
/* Rhythm. Seven sections of the same density read as one long block, whatever
   their height. A tight section next to an airy one is what gives the page a
   pulse, so the modifiers exist to be used sparingly and on purpose. */
.section--tight { padding-top: var(--space-xl); padding-bottom: var(--space-xl); }
.section--tight .row { padding: var(--space-md) 0; }

/* editorial two-column: title column against a wider content column */
.split { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: var(--space-xl); align-items: start; }
/* Content that opens under the hairline, at the same distance from the label
   as the first row of a list. Every section starts on the same line. */
/* A section's label is not separated from what it introduces. It is already
   set apart by size, colour and its accent square, and a rule under it reads
   as a lid closing the label off from the sentence it belongs to. Space does
   the work instead, and it is the same lg everywhere: whatever a section opens
   with, a headline or a row of cards, it starts at the same distance. The dividers between one row and the next stay: those do
   separate two things. */
.ruled { margin-top: var(--space-lg); }
.stack > * + * { margin-top: var(--space-md); }

/* ---------- header ----------
   Sticky, so the one action stays within reach at any depth of the page.
   Opaque: the page scrolls under it, nothing shows through. */
.site-header {
	position: sticky; top: 0; z-index: 10;
	background: var(--color-surface);
	border-bottom: 1px solid var(--color-border);
	transition: transform 240ms ease;
}
/* set by assets/js/nav-scroll.js: away on the way down, back on the way up */
.site-header.is-hidden { transform: translateY(-100%); }
@media (prefers-reduced-motion: reduce) { .site-header { transition: none; } }
/* an anchored section must clear the header instead of hiding behind it */
section[id] { scroll-margin-top: calc(var(--header-h) + var(--space-md)); }
.site-header .container { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); height: var(--header-h); }
.brand { font: var(--nav); text-decoration: none; white-space: nowrap; }
.site-nav { display: flex; align-items: center; gap: var(--space-lg); }
.site-nav .btn { white-space: nowrap; }
.nav-link { font: var(--nav); text-decoration: none; padding: var(--space-2xs) 0; white-space: nowrap; }
.nav-link:hover { color: var(--color-accent); }
/* the page you are on: underlined, not coloured, so accent keeps meaning hover */
.nav-link[aria-current="page"] { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 6px; }

/* ---------- burger, phone only ----------
   Built by assets/js/nav-menu.js, which moves the two page links out of the
   bar and into a panel under it. The action never follows them: it is the one
   thing the site asks for and it stays in sight. The panel hangs from the
   header, so it travels with it, and the bars cross into a close sign on the
   same press that opens it, which is feedback on a control and not an arrival.
   Nothing here exists without the script, and without the script the links are
   simply in the bar, which is where they were. */
.nav-toggle {
	appearance: none; -webkit-appearance: none; background: none; border: 0;
	padding: 0; cursor: pointer; color: inherit;
	width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
	/* the bars sit on the page's right margin, like the name on the left */
	margin-right: -10px;
}
.nav-toggle-bars { position: relative; display: block; width: 20px; height: 2px; background: currentColor; }
.nav-toggle-bars::before, .nav-toggle-bars::after {
	content: ""; position: absolute; left: 0; width: 20px; height: 2px; background: currentColor;
}
.nav-toggle-bars::before { top: -6px; }
.nav-toggle-bars::after { top: 6px; }
.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
	transition: transform 200ms var(--reveal-ease), background-color 120ms ease;
}
.nav-toggle[aria-expanded="true"] .nav-toggle-bars { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars::after { transform: translateY(-6px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
	.nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after { transition: none; }
}
.site-nav-panel {
	position: absolute; top: 100%; left: 0; right: 0;
	background: var(--color-surface);
	border-bottom: 1px solid var(--color-border);
	padding: var(--space-md) var(--space-md) var(--space-lg);
	display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-md);
}
/* display on the rule above outranks the browser's own display none for the
   hidden attribute, so the panel has to be told again */
.site-nav-panel[hidden] { display: none; }
/* full size in the panel: the steps down to 18 and 16px exist to fit three
   items in one strip, and in here there is nothing to fit */
.site-nav-panel .nav-link { font: var(--nav); padding: 0; }

/* ---------- buttons ----------
   The accent grows from the point where the cursor crossed the edge. The radius
   is a registered property so it can be transitioned; where @property is missing
   the value snaps and the button simply changes colour, which is what it did
   before. assets/js/button-fill.js only records the entry point. */
@property --btn-r {
	syntax: '<length-percentage>';
	inherits: false;
	initial-value: 0%;
}

.btn {
	display: inline-block; border: 1px solid transparent; cursor: pointer;
	font: var(--nav); text-decoration: none; text-align: center;
	border-radius: var(--rounded-pill); padding: 16px 32px;
	transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
}
.btn-primary {
	background-color: var(--color-surface-inverse);
	/* The soft edge is a multiple of the radius, not a fixed distance: at rest the
	   radius is zero and so is the feather, which is what keeps a blue halo from
	   sitting on an untouched button. */
	background-image: radial-gradient(circle at var(--btn-x, 50%) var(--btn-y, 50%),
		var(--color-accent) 0 var(--btn-r), transparent calc(var(--btn-r) * 1.35));
	color: var(--color-on-surface-inverse);
	transition: --btn-r 420ms cubic-bezier(0.33, 1, 0.68, 1),
	            /* the text flips just after the edge has swept past it, which for a
	               centred label is a little under half the travel */
	            color 120ms ease 70ms,
	            border-color 120ms ease;
}
.btn-primary:hover, .btn-primary:focus-visible {
	--btn-r: 100%;
	color: var(--color-on-surface);
}
.btn-ghost { background: transparent; color: var(--color-on-surface); border-color: var(--color-border); }
.btn-ghost:hover { border-color: var(--color-on-surface); }
.btn-small { padding: 10px 20px; }
/* No spreading fill: the button changes colour and that is all. This has to
   come after the rules above, or source order hands them the win. */
@media (prefers-reduced-motion: reduce) {
	.btn-primary { background-image: none; transition: background-color 120ms ease, color 120ms ease; }
	.btn-primary:hover, .btn-primary:focus-visible { background-color: var(--color-accent); }
}

/* ---------- hero ---------- */
.hero {
	position: relative; overflow: hidden;
	/* The hero stops short of the fold on purpose: the top of the next section
	   stays visible, which says "there is more" without a second call to action. */
	min-height: calc(100vh - var(--header-h) - 160px);
	min-height: calc(100svh - var(--header-h) - 160px);
	display: flex; align-items: center;
	/* fluid padding so a short laptop viewport still leaves the peek intact */
	padding-top: clamp(var(--space-lg), 8vh, var(--space-2xl));
	padding-bottom: clamp(var(--space-lg), 8vh, var(--space-2xl));
}
.hero > .container { position: relative; z-index: 1; width: 100%; }
/* interactive ground: a canvas of small squares, see assets/js/hero-field.js */
.hero-field { position: absolute; inset: 0; width: 100%; height: 100%; display: block; pointer-events: none; }
/* phase 2: a reel takes the same slot. Keep it muted, looped, and behind the text. */
.hero-media { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.55; }
/* The ground has to end without an edge, or the hero reads as a panel sitting on
   the page instead of the top of it. Masking beats fading a drawn colour: it
   works whatever fills the slot, the canvas today and a reel later. */
.hero-field, .hero-media {
	-webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 96%);
	mask-image: linear-gradient(to bottom, #000 0%, #000 58%, transparent 96%);
}
.hero h1 { max-width: 12em; }
.hero .lede { margin-top: var(--space-lg); max-width: var(--measure); }
.hero .cta { margin-top: var(--space-lg); }
.hero .questions { margin-top: var(--space-md); max-width: var(--measure); }

/* ---------- editorial rows (offers, cases, situations) ---------- */
.rows { margin-top: var(--space-lg); }
.row { border-top: 1px solid var(--color-border); padding: var(--space-lg) 0; display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); gap: var(--space-xl); }
.rows .row:first-child { border-top: none; padding-top: 0; }
.row:last-child { border-bottom: 1px solid var(--color-border); }
.row h3 { font: var(--display-m); letter-spacing: var(--ls-display-m); }
.row h3 a { text-decoration: none; }
.row h3 a:hover { color: var(--color-accent); }
.row p { max-width: var(--measure); color: var(--color-on-surface-muted); font: var(--body-l); }

/* ---------- disclosure ----------
   Built by assets/js/disclosure.js on phones only: the title of a situation
   becomes the control that opens its sentence. It keeps the title's type and
   adds the one round sign the site uses for opening things, a plus that loses
   its vertical bar once open. Nothing rotates or slides. */
.disclosure {
	display: flex; align-items: center; justify-content: space-between; gap: var(--space-md);
	width: 100%; padding: 0; border: 0; background: none;
	font: inherit; letter-spacing: inherit; color: inherit; text-align: left; cursor: pointer;
}
.disclosure:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 4px; }
.disclosure-sign { position: relative; flex: none; width: 40px; height: 40px; border: 1px solid var(--color-border); border-radius: var(--rounded-pill); }
.disclosure-sign::before, .disclosure-sign::after {
	content: ""; position: absolute; left: 50%; top: 50%; background: var(--color-on-surface);
	transform: translate(-50%, -50%); transition: opacity 160ms ease;
}
.disclosure-sign::before { width: 14px; height: 1px; }
.disclosure-sign::after { width: 1px; height: 14px; }
.disclosure[aria-expanded="true"] .disclosure-sign::after { opacity: 0; }
.disclosure:hover .disclosure-sign { border-color: var(--color-on-surface); }
@media (prefers-reduced-motion: reduce) { .disclosure-sign::after { transition: none; } }
/* duration and pricing basis of a format, on their own line above the description */
/* The length of an engagement belongs to its name, not to a line under it: a
   buyer reads the two titles to compare them, and the weeks are half of what
   is being compared. A hairline pill, label type, muted, never filled, so it
   reads as a piece of the title rather than as a second button on the page. */
.tag {
	display: inline-flex; align-items: center; white-space: nowrap;
	vertical-align: middle; margin-left: var(--space-xs);
	padding: 2px var(--space-sm); border: 1px solid var(--color-border);
	border-radius: var(--rounded-pill);
	font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase;
	color: var(--color-on-surface-muted);
}
.row .proof { color: var(--color-accent); font: var(--body-l); margin-top: var(--space-md); }
.row .more { display: inline-block; margin-top: var(--space-md); font: var(--nav); text-decoration: none; border-bottom: 1px solid var(--color-border); padding-bottom: 2px; }
.row .more:hover { color: var(--color-accent); border-color: var(--color-accent); }

.threshold { margin-top: var(--space-lg); font: var(--body-l); }

/* ---------- bridge ---------- */
/* The one pause on the page: a whole screen of cream holding a single
   sentence. Its job is the emptiness, not the sentence. The section is two
   and a half screens tall and the sentence stays pinned while the page moves
   underneath, so the scroll reads it out word by word (see
   assets/js/highlight-text.js). This is the one place motion follows the
   scroll position: it is the section's whole reason to exist. */
.bridge {
	background: var(--color-surface-inverse); color: var(--color-on-surface-inverse);
	min-height: 240vh;
	min-height: 240svh;
}
.bridge > .container {
	position: sticky; top: 0;
	min-height: 100vh;
	min-height: 100svh;
	display: flex; align-items: center;
	padding-top: var(--space-2xl); padding-bottom: var(--space-2xl);
}
.bridge p { font: var(--display-xl); letter-spacing: var(--ls-display-xl); max-width: 14em; }
/* Set by the script. Until it runs, and if it never does, the sentence is
   plain on-surface-inverse: the muted tone exists only on split words, so no
   state here can leave a word unreadable. */
.bridge p.is-split .hl-w { display: inline-block; color: var(--color-on-surface-inverse-faint); will-change: color; }
@media (prefers-reduced-motion: reduce) {
	/* nothing to read out: one screen, no pin, full ink */
	.bridge { min-height: 720px; min-height: min(100svh, 900px); }
	.bridge > .container { position: static; }
	.bridge p.is-split .hl-w { color: inherit; }
}

/* ---------- plan ----------
   Three steps in a row, joined by a hairline. The line is the whole point:
   it says the three are one sequence, not three things on offer. The boxes
   are as tall as the longest of the three and no taller: a square was a shape
   imposed on the text rather than taken from it, and it left the short steps
   mostly empty. Grid stretches the row on its own, so the height is simply
   whatever the fullest step needs. */
.plan {
	counter-reset: step;
	margin-top: var(--space-xl);
	display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
	gap: var(--space-lg);
}
.plan li {
	counter-increment: step; position: relative;
	border: 1px solid var(--color-border);
	padding: clamp(var(--space-md), 3vw, var(--space-lg));
	display: flex; flex-direction: column; gap: var(--space-md);
}
.plan li::before {
	content: counter(step, decimal-leading-zero);
	font: var(--label); letter-spacing: var(--ls-label); color: var(--color-accent);
}
.plan li:not(:last-child)::after {
	content: ""; position: absolute;
	top: calc(clamp(var(--space-md), 3vw, var(--space-lg)) + 8px);
	left: 100%; width: var(--space-lg); height: 1px; background: var(--color-border);
}
.plan p { font: var(--body-l); }

/* ---------- case accordion ---------- */
/* ---------- case cards ----------
   Three cards side by side, one per decision. Each is the cover of the case
   with the client's mark and the outcome over it; the facts wait underneath
   and rise over the image on hover or focus, which is the whole interaction.
   The card is not a card in the shadowed sense: a hairline, a picture, and a
   panel that slides. Nothing floats. */
.cases { margin-top: var(--space-lg); display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-md); }

.case-card {
	position: relative; overflow: hidden;
	aspect-ratio: 479 / 560;
	border: 1px solid var(--color-border);
	background: var(--color-surface-raised);
	transition: border-color 300ms var(--reveal-ease);
}
.case-card:hover, .case-card:focus-within { border-color: var(--color-on-surface); }

.case-card-image {
	position: absolute; inset: 0; width: 100%; height: 100%;
	object-fit: cover;
	transition: transform 300ms var(--reveal-ease), opacity 300ms var(--reveal-ease);
}
/* A flat veil, not a gradient: the mark and the title sit on a picture that was
   not composed for text, and an even wash is the one thing that makes them
   legible without inventing a light source. At 55% the lightest cover still
   clears 4.5:1 behind the title; at 45% it measured 3.3:1, which passes for
   large text but leaves nothing for a cover lighter than today's. */
.case-card::before {
	content: ""; position: absolute; inset: 0; z-index: 1;
	background: rgba(0, 0, 0, 0.55);
	transition: opacity 300ms var(--reveal-ease);
}
.case-card:hover .case-card-image, .case-card:focus-within .case-card-image { transform: scale(1.03); }

/* Title and panel are one column, so the panel can never reach the title
   however many lines it takes: it starts where the title ends and slides
   within what is left. The title keeps its place and turns to ink as the
   ground behind it becomes cream, which is what makes the two read as one
   sheet rising. */
.case-card-stack {
	position: absolute; inset: 0; z-index: 3;
	display: flex; flex-direction: column;
	pointer-events: none;
}
/* The title is never positioned, on any screen: the link's ::after has to
   find the card as its nearest positioned ancestor, or the whole-card target
   shrinks to the title. */
.case-card-title {
	flex: none; padding: var(--space-md);
	font: var(--display-s); letter-spacing: var(--ls-display-s);
	transition: color 300ms var(--reveal-ease), background-color 300ms var(--reveal-ease);
}
.case-card:hover .case-card-title, .case-card:focus-within .case-card-title { background: var(--color-surface-inverse); }
.case-card-title a { color: var(--color-on-surface); text-decoration: none; }
.case-card:hover .case-card-title a, .case-card:focus-within .case-card-title a { color: var(--color-on-surface-inverse); }

/* the link covers the card, so the whole surface is the target while the
   accessible name stays the title alone */
.case-card-link { pointer-events: auto; }
.case-card-link::after { content: ""; position: absolute; inset: 0; z-index: 5; }
.case-card-link:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 3px; }
/* the mark is the only place the client is named, since the title states the
   outcome instead. It is the name set in our own type rather than a borrowed
   logo: three marks at three different weights and proportions never sit on a
   picture as one line does. */
.case-mark {
	position: absolute; z-index: 2; left: var(--space-md); bottom: var(--space-md);
	font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase;
	color: var(--color-on-surface);
}

/* The panel waits just below the card and rises over the image. It carries the
   sentence and the facts, so what it reveals is information, never the way in:
   the link is on the title and works whether the panel has risen or not. */
/* The panel is the room left under the title, and it clips: the sheet inside
   travels its own full height, so at rest it is exactly out of sight however
   many lines the title took, and the two can never overlap. */
.case-card-panel { flex: 1 1 auto; overflow: hidden; display: flex; }
.case-card-sheet {
	flex: 1 1 auto; transform: translateY(100%);
	background: var(--color-surface-inverse); color: var(--color-on-surface-inverse);
	padding: var(--space-md);
	display: flex; flex-direction: column; gap: var(--space-md);
	transition: transform 300ms var(--reveal-ease);
}
.case-card:hover .case-card-sheet, .case-card:focus-within .case-card-sheet { transform: translateY(0); }
.case-card-sheet p { font: 400 var(--body-s); color: var(--color-on-surface-inverse); }
.case-card-sheet .case-facts {
	margin-top: 0; padding-top: 0; border-top: 0;
	grid-template-columns: 1fr; gap: var(--space-2xs);
}
.case-card-sheet .case-facts > div {
	display: flex; justify-content: space-between; align-items: baseline; gap: var(--space-md);
	padding: var(--space-2xs) 0;
	border-bottom: 1px solid var(--color-border-inverse);
}
.case-card-sheet .case-facts dt { font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--color-on-surface-inverse-muted); }
.case-card-sheet .case-facts dd { margin: 0; font: 400 var(--body-s); text-align: right; }

@media (prefers-reduced-motion: reduce) {
	.case-card, .case-card-image, .case-card-sheet { transition: none; }
	.case-card:hover .case-card-image, .case-card:focus-within .case-card-image { transform: none; }
}

/* ---------- about ----------
   The About page is built from the same parts as the rest of the site: the
   head of a case, the rows of the home, the list of cases. Only two things
   is its own: the portrait beside the head. The way of working is the
   same split as the argument on the home, expanded. */
/* The top of the About is one grid, so the portrait can hold its place
   while the reader scrolls. Introduction and background sit on the left,
   with the same spacing they had as two sections; the portrait sits on the
   right, spanning both, cut upright at 2 by 3 with its top edge on the line
   where the text starts. It is sticky: it stays put under the bar until the
   last line of the background reaches its bottom edge, then leaves with the
   page. A sticky element stops at the end of its container, which is why the
   background has to share the portrait's grid rather than have a section of
   its own, even though it is laid out exactly as one.
   Mirrored at export, so the gaze turns up and left, to the title, and the
   cut leaves the room in front of the face. */
.about-top { padding-top: var(--space-3xl); }
.about-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 2fr); column-gap: var(--space-xl); }
.about-intro { grid-column: 1; grid-row: 1; }
/* An invisible stand-in for the portrait, as tall as it, in the first row:
   it gives that row the portrait's height, so the background starts below
   the portrait's bottom edge with the space it had as a section of its own,
   while the portrait itself spans both rows and can travel down them. */
.about-grid::before { content: ""; grid-column: 2; grid-row: 1; aspect-ratio: 2 / 3; }
.about-background { grid-column: 1; grid-row: 2; margin-top: calc(var(--space-xl) + var(--space-2xl)); }
.about-portrait {
	grid-column: 2; grid-row: 1 / 3; align-self: start;
	margin: 0; position: sticky; top: calc(var(--header-h) + var(--space-md));
}
.about-portrait img { display: block; width: 100%; aspect-ratio: 2 / 3; object-fit: cover; object-position: 35% 50%; background: var(--color-surface-raised); }
.about-background .prose { margin-top: var(--space-lg); }
/* the last line is trimmed to its baseline, so the photograph's bottom edge meets the words, not the space under them */
.about-background .prose p:last-child { text-box: trim-end alphabetic; }
/* the case list on the About names the outcome large and the client small */
.more-case .more-case-client { flex: none; font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--color-on-surface-muted); }

/* ---------- case transition ----------
   Home to case, rebuilt from the reference in two halves that meet on one
   full-screen picture. On the home (assets/js/case-transition.js) the card's
   picture grows to fill the window in 1.5s while the words on the card fade in
   0.3s; then the browser navigates. On the case page the same picture is
   already there, full screen, and after 0.1s it moves down and in for 1.2s
   until it sits exactly on the case's hero, where it hands over to the real
   one (assets/js/case-arrival.js). This is the one arrival on the site that is longer
   than the reveal, and it is the one the reader asked for with a click. */
.case-transition {
	position: fixed; z-index: 100; overflow: hidden;
	background: var(--color-surface-raised);
	pointer-events: none;
}
.case-transition img { display: block; width: 100%; height: 100%; object-fit: cover; }
.case-transition-veil { position: absolute; inset: 0; background: rgba(0, 0, 0, 0.45); }
/* The card stays above the growing copy and gives up everything but its
   words: its own picture and veil go at once, since the copy under it is the
   same picture in the same place, and its words fade the way the reference's do. */
.case-card.is-leaving { z-index: 101; background: transparent; border-color: transparent; }
.case-card.is-leaving::before, .case-card.is-leaving .case-card-image { opacity: 0; transition: none; }
.case-card.is-leaving .case-card-title,
.case-card.is-leaving .case-mark,
.case-card.is-leaving .case-card-sheet { opacity: 0; transition: opacity 300ms var(--reveal-ease); }

/* The second half, on the case page. The inline script in the head marks the
   page only when it was reached from the home by a fresh click, so a reload,
   a step back through history or a link between cases never plays it. This
   cover holds the first paint at full screen; assets/js/case-arrival.js then
   replaces it with a copy and moves that copy onto the hero. If the script
   never runs, the cover leaves on its own after 2.5s, so the page can never
   stay hidden behind a picture. */
html.from-home::after {
	content: ""; position: fixed; inset: 0; z-index: 100;
	background: var(--color-surface-raised) var(--cover) center / cover no-repeat;
	pointer-events: none;
	animation: arrival-failsafe 300ms linear 2.5s forwards;
}
@keyframes arrival-failsafe { to { opacity: 0; visibility: hidden; } }
/* the travelling copy lives in page coordinates, so it scrolls with the hero */
.case-transition.is-arriving { position: absolute; }
@media (prefers-reduced-motion: reduce) { html.from-home::after { display: none; } }

/* ---------- case study ----------
   One structure for all three pages. The decision line under the name is the
   sentence that has to do the work: it sits at display-m and is never muted. */
.case-head { padding-top: var(--space-3xl); padding-bottom: var(--space-xl); }
/* The outcome is the page: it takes the display scale and the h1. The client
   is named above it in label type, the same trattamento the mark gets on the
   home card, because who it was for qualifies the claim rather than making it. */
.case-client, .eyebrow { font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--color-on-surface-muted); }
.case-head h1, .about-intro h1 { margin-top: var(--space-md); font: var(--display-xl); letter-spacing: var(--ls-display-xl); max-width: 16em; }
.about-intro .lede { margin-top: var(--space-lg); max-width: var(--measure); font: var(--body-l); }
/* The cover runs edge to edge: the figure drops the browser's default margin
   and the container gives up its width and its gutters here only. At 5 by 3
   a full-width picture outgrows the window on a wide screen, so it is capped
   at the window's height and cropped from the centre instead. */
.case-figure { margin: 0; }
.case-figure > .container { max-width: none; padding-left: 0; padding-right: 0; }
.case-figure img {
	display: block; width: 100%; aspect-ratio: 5 / 3; max-height: 100vh; max-height: 100svh;
	object-fit: cover; background: var(--color-surface-raised);
}

.prose { max-width: var(--measure); }
.prose p { font: var(--body-l); }
.prose p + p { margin-top: var(--space-md); }

/* the one place a claim stands alone, and only when the number is verifiable */
.proof { color: var(--color-accent); font: var(--body-l); max-width: var(--measure); }
.row .proof { margin-top: var(--space-md); }
.prose + .proof { margin-top: var(--space-xl); }

/* The facts of the engagement as a row rather than a box: what it was, when,
   and what was not included. A column is left out when the answer is not
   confirmed, because an empty column is honest and an invented one is not. */
.case-facts {
	margin-top: var(--space-xl); padding-top: var(--space-md);
	border-top: 1px solid var(--color-border);
	display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--space-lg);
}
.case-facts dt { font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--color-on-surface-muted); }
.case-facts dd { margin: var(--space-2xs) 0 0; font: var(--body); }
/* the facts sit in the head, under the decision line: a buyer reads the limits before the story */
.case-head .case-facts { margin-top: var(--space-xl); }
/* when the proof or the KPIs open a section, the section's own padding is the space above */
.case-proof:first-child, .case-kpis:first-child { margin-top: 0; }

/* Three numbers and what each one measures, the last thing before the action.
   Hairline boxes, no fill: the number is the only thing that should be loud.
   The placeholder variant is dashed, like an image slot, and leaves with it. */
.case-kpis { list-style: none; margin: var(--space-xl) 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-md); }
.kpi { border: 1px solid var(--color-border); padding: var(--space-md); display: flex; flex-direction: column; gap: var(--space-xs); min-height: 160px; justify-content: space-between; }
.kpi strong { font: var(--display-l); letter-spacing: var(--ls-display-l); }
.kpi span { font: var(--body-s); color: var(--color-on-surface-muted); }
.kpi--placeholder { border-style: dashed; background: var(--color-surface-raised); }
.kpi--placeholder strong { color: var(--color-on-surface-subtle); }

/* the strongest verifiable claim on a page, set at display size and alone */
.case-proof {
	margin-top: var(--space-2xl); max-width: 18em;
	font: var(--display-m); letter-spacing: var(--ls-display-m); color: var(--color-accent);
}

.more-cases { margin-top: var(--space-lg); border-top: 1px solid var(--color-border); }
.more-case {
	display: flex; align-items: center; justify-content: space-between; gap: var(--space-md);
	border-bottom: 1px solid var(--color-border); padding: var(--space-md) 0;
	text-decoration: none;
}
.more-case span { font: var(--display-m); letter-spacing: var(--ls-display-m); }
.more-case:hover span { color: var(--color-accent); }

/* ---------- case chapters and media ----------
   The body runs as chapters, each a label over a paragraph, and the media sit
   between them at the rhythm MetaLab uses: text never runs long before a
   screen interrupts it. Four kinds of media and no others: the cover, one
   optional video, one gallery, and pairs of screens side by side. */
.case-chapter .label + .prose { margin-top: var(--space-lg); }
.case-chapter .section-media { margin-top: var(--space-xl); }
.section-media img, .section-media video { width: 100%; display: block; aspect-ratio: 16 / 9; object-fit: cover; background: var(--color-surface-raised); }

/* Two screens side by side, the block used most on the reference. Square
   corners, same height, one gap. Stacks below 720px. */
.case-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }

/* The product video arrives by growing, the way it does on the reference:
   it starts narrow and widens to the full column as the reader scrolls it
   into place. The growth is tied to the scroll rather than to a clock, so
   the reader sets its pace, and it is the third and last thing on this site
   allowed to be: a video that simply appears at full width lands like a
   slide, while one that opens says the page is still being read.
   The frame keeps the final height from the start, so nothing below it
   jumps while the video grows inside it. Where scroll-driven animation is
   not supported, and wherever reduced motion is asked for, the video is
   simply there at full width. */
/* Full width: the media leaves the container the way the hero figure does and
   spans the viewport, which is why it is a direct child of the section and not
   of .container. The section carries no horizontal padding, so nothing has to
   be undone here. */
.section-media--full { width: 100%; }
.section-media--grow { aspect-ratio: 16 / 9; display: grid; place-items: center; }
@media (prefers-reduced-motion: no-preference) {
	@supports (animation-timeline: view()) {
		.section-media--grow video {
			width: 62%;
			animation: media-grow linear both;
			animation-timeline: view();
			animation-range: entry 15% cover 55%;
		}
		@keyframes media-grow { to { width: 100%; } }
		/* On a phone the frame is a fifth as wide, so the same fraction would
		   open from something too small to read. It starts closer to full. */
		@media (max-width: 720px) { .section-media--grow video { width: 80%; } }
	}
}
.case-pair img, .case-pair video { width: 100%; display: block; aspect-ratio: 4 / 3; object-fit: cover; background: var(--color-surface-raised); }
/* A board is not a screen. Where the pair holds one, a design system laid
   out wide, the cells take the frame it was drawn in instead of cropping a
   quarter of it away. Both cells move together: two ratios side by side
   would read as a mistake. */
.case-pair--wide img, .case-pair--wide .img-slot { aspect-ratio: 16 / 9; }
/* and upright where it holds a panel taller than it is wide */
.case-pair--tall img, .case-pair--tall video, .case-pair--tall .img-slot { aspect-ratio: 9 / 10; }

/* The gallery is a scroll-snap strip, no script. Each screen takes most of
   the width so the next one peeks in from the right, which is the whole
   invitation to scroll. The region is focusable so it can be scrolled from
   the keyboard, and the scrollbar is hidden because the peek already says
   there is more. */
.case-gallery {
	display: flex; gap: var(--space-md);
	overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: 0;
	scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.case-gallery::-webkit-scrollbar { display: none; }
/* min-width 0 is not decoration: a picture in the strip is a flex item whose
   automatic minimum is its own width, and width 100% would make that the whole
   column, so one screen would fill the strip and the peek would be gone. */
.case-gallery > * { flex: 0 0 min(720px, 82%); min-width: 0; scroll-snap-align: start; }
/* Where the pieces were drawn square, the strip takes them square and its
   cells narrow: a 16 by 10 cut would take a third of each away, and a square
   720px wide would be a screen tall. The peek from the right is kept. */
.case-gallery--square > * { flex-basis: min(520px, 74%); }
.case-gallery img, .case-gallery video { width: 100%; display: block; aspect-ratio: 16 / 10; object-fit: cover; background: var(--color-surface-raised); }
.case-gallery--square img, .case-gallery--square video, .case-gallery--square .img-slot { aspect-ratio: 1 / 1; }
.case-gallery:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 4px; }

/* ---------- affiliation band ----------
   The one borrowed name on the site, on a strip of its own directly under the
   hero, between two hairlines. It is the first thing after the claim because
   it is what makes the claim credible, and it is a strip rather than a line
   inside a section because a borrowed name qualifies the whole page, not the
   paragraph it happens to sit next to. Centred, at label size, low: this is a
   seal, not a heading, and a reader who never stops on it has lost nothing.
   The logo is admissible only because it is a single colour and that colour
   is on-surface, so it adds no hue the palette does not already have. */
.affiliation-band { border-top: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border); }
.affiliation-band > .container {
	display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
	gap: var(--space-xs) var(--space-md);
	padding-top: var(--space-md); padding-bottom: var(--space-md);
}
.affiliation-band p { font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--color-on-surface-muted); }
.affiliation-band img { width: 150px; }

/* ---------- image slots (temporary) ----------
   Dashed boxes that mark where an image is meant to go. Each carries a note
   on the intended subject. Remove this block once the real images are in. */
.img-slot {
	aspect-ratio: var(--ratio, 3 / 2);
	border: 1px dashed var(--color-border); background: var(--color-surface-raised);
	display: grid; place-items: center; text-align: center; padding: var(--space-md);
}
.img-slot span { font: var(--body-s); color: var(--color-on-surface-muted); max-width: 24em; }
.img-slot span::before { content: "Image"; display: block; font: var(--label); letter-spacing: var(--ls-label); text-transform: uppercase; color: var(--color-on-surface-subtle); margin-bottom: var(--space-2xs); }
.row .img-slot { margin-top: var(--space-md); }

/* ---------- policy ----------
   A legal page is not read from the top: it is read in pieces, by someone
   looking for one answer. So it is a sequence of labelled blocks with the same
   air between them as between sections, each holding a whole answer, and the
   measure of the text is the page's own. Nothing else is added: a page that
   says no data is taken should not need a special design to be believed. */
.policy { margin-top: var(--space-2xl); }
.policy .label { display: flex; }
.policy .label + .prose { margin-top: var(--space-lg); }
.policy .prose + .label { margin-top: var(--space-2xl); }
.policy .prose a { text-underline-offset: 3px; }

/* ---------- inverted section ----------
   One section on the page takes the cream ground, and it is the one with the
   only action on the site. A change of ground is the loudest device available
   here, louder than any size of type, so it is spent where a reader has to
   decide whether to book the call. The page then has two cream blocks and no
   more: the sentence it wants read, and the thing it wants done. Everything
   inside flips to the inverse tokens, borders included, and the button turns
   over: black fill on cream instead of cream on black, with the same accent
   arriving from where the cursor crossed the edge. The hairline that separates
   one section from the next is dropped here, since the ground already does it. */
.section--invert {
	background: var(--color-surface-inverse);
	color: var(--color-on-surface-inverse);
	border-top: none;
}
.section--invert .muted { color: var(--color-on-surface-inverse-muted); }
.section--invert .plan li { border-color: var(--color-border-inverse); }
.section--invert .plan li:not(:last-child)::after { background: var(--color-border-inverse); }
.section--invert .btn-primary {
	background-color: var(--color-surface);
	color: var(--color-on-surface);
}
/* the label is already the colour it has to become, so nothing flips under the
   blue: it only has to stay put */
.section--invert .btn-primary:hover, .section--invert .btn-primary:focus-visible { color: var(--color-on-surface); }

/* ---------- footer ----------
   The footer keeps the page's own ground. It used to invert it, which made
   sense while it was the last light block on the page; now that the call to
   action is cream, a cream footer under it would read as one block with the
   action, and the quietest part of the page would be as loud as the loudest.
   So it closes on black, set apart by a hairline and by the air above it, and
   every line steps back to 400: what is here is reference, not argument. */
.site-footer {
	background: var(--color-surface);
	color: var(--color-on-surface);
	border-top: 1px solid var(--color-border);
	padding: var(--space-2xl) 0 var(--space-lg);
}
.footer-main { display: flex; flex-wrap: wrap; gap: var(--space-lg) var(--space-xl); justify-content: space-between; align-items: start; }
.footer-brand { font: var(--display-m); letter-spacing: var(--ls-display-m); }
.footer-role { margin-top: var(--space-2xs); font: var(--body-l); font-weight: 400; color: var(--color-on-surface-muted); }
.footer-contact { display: flex; flex-direction: column; align-items: flex-start; gap: var(--space-2xs); }
.footer-contact a { font: var(--body-l); font-weight: 400; text-decoration: none; border-bottom: 1px solid var(--color-border); padding-bottom: 2px; }
.footer-contact a:hover { color: var(--color-accent); border-color: var(--color-accent); }
.footer-legal { margin-top: var(--space-2xl); padding-top: var(--space-md); border-top: 1px solid var(--color-border); display: flex; flex-wrap: wrap; gap: var(--space-xs) var(--space-lg); justify-content: space-between; }
.footer-legal p, .footer-legal a { font: var(--body-s); font-weight: 400; color: var(--color-on-surface-muted); text-decoration: none; }
.footer-legal a:hover { color: var(--color-on-surface); }

/* ---------- responsive ---------- */
@media (max-width: 1024px) {
	:root {
		--display-xl: 500 64px/0.98 var(--font-display);
		--display-l: 500 48px/1.02 var(--font-display);
		--display-m: 500 36px/1.05 var(--font-display);
	}
}

/* Three cards need room: under 1100px the card gets too short for the panel
   to hold the sentence and the facts, so the row becomes two and the third
   card takes the line below. */
@media (max-width: 1100px) {
	.cases { grid-template-columns: repeat(2, 1fr); }
}

/* Name, Work, About and the button need 382px on one line at the normal
   scale, so it keeps that scale only from 450px up, with room to spare. Below
   that the bar tightens:
   the words step down to 18px, the gaps to 12, the button slims, and it
   needs 311px. Phones up to 369px, the common 360px Android among them, take
   one more step, since 311 would leave them a single pixel. The
   side margins never move, so the name stays aligned with the page. */
@media (max-width: 449px) {
	.site-header .container { gap: var(--space-xs); }
	.site-header .site-nav { gap: var(--space-xs); }
	.site-header .brand, .site-header .nav-link, .site-header .btn-small { font-size: 18px; }
	.site-header .btn-small { padding-left: var(--space-sm); padding-right: var(--space-sm); }
}
@media (max-width: 369px) {
	.site-header .container, .site-header .site-nav { gap: var(--space-2xs); }
	.site-header .brand, .site-header .nav-link, .site-header .btn-small { font-size: 16px; }
	.site-header .btn-small { padding-left: var(--space-xs); padding-right: var(--space-xs); }
}

@media (max-width: 720px) {
	:root {
		--display-xl: 500 44px/1 var(--font-display);
		--display-l: 500 36px/1.05 var(--font-display);
		--display-m: 500 28px/1.1 var(--font-display);
		--display-s: 500 24px/1.15 var(--font-display);
		--space-2xl: 64px;
		--space-3xl: 96px;
		--header-h: 64px;
	}
	.container { padding-left: var(--space-md); padding-right: var(--space-md); }
	.split, .row { grid-template-columns: 1fr; gap: var(--space-sm); }
	.case-pair { grid-template-columns: 1fr; }
	/* on a phone: one column, the portrait between the introduction and the
	   background, and nothing sticks */
	.about-top { padding-top: var(--space-xl); }
	.about-grid { grid-template-columns: minmax(0, 1fr); row-gap: var(--space-lg); }
	.about-grid::before { display: none; }
	.about-intro { grid-row: 1; }
	.about-portrait { grid-column: 1; grid-row: 2; position: static; }
	.about-background { grid-row: 3; margin-top: var(--space-2xl); }
	/* On a phone the upright cut would be the tallest thing on the page. A file
	   cut for the phone keeps the same framing at the top and loses the lectern
	   at the bottom, at 4 by 5. */
	.about-portrait img { aspect-ratio: 4 / 5; object-position: 50% 0; }
	/* On a phone the head of a case used to fill the whole first screen, so the
	   cover, and the picture arriving from the home card, landed below the fold.
	   Less air above the title and the facts in two columns bring it back up. */
	.case-head { padding-top: var(--space-xl); padding-bottom: var(--space-lg); }
	.case-head .case-facts { grid-template-columns: 1fr 1fr; gap: var(--space-md); margin-top: var(--space-lg); }
	.case-kpis { grid-template-columns: 1fr; }
	.split { gap: var(--space-lg); }
	/* the band keeps its two lines centred, the name above the mark */
	.affiliation-band img { width: 120px; }
	/* No hover on a touch screen, so the sheet cannot wait to be asked for, and
	   the card should not stop being a card either. It keeps its frame, its
	   picture, its veil and the title over them, and the sheet simply sits
	   below the picture instead of over it. Nothing is stacked here and nothing
	   is clipped: the card takes its height from what it holds, so no text can
	   be cut off however long the title or the facts run. */
	/* Three cards stacked run about five phone screens, in the middle of the
	   page and on the way to the one action. The row turns into a strip the
	   reader swipes instead: each card takes 82% of the width so the next one
	   shows at the right edge, which is the whole invitation to scroll, and
	   snap makes the gesture land. Same mechanism as the case galleries, no
	   script, and the strip takes focus so it can be scrolled from a keyboard.
	   It bleeds into the container gutters so a card can sit flush left while
	   the next still peeks. No dots: the section label already says there are
	   three, and a second interactive element is one more than this page wants. */
	.cases {
		display: flex; gap: var(--space-md);
		overflow-x: auto; scroll-snap-type: x mandatory;
		scrollbar-width: none; -webkit-overflow-scrolling: touch;
		margin-left: calc(var(--space-md) * -1); margin-right: calc(var(--space-md) * -1);
		padding-left: var(--space-md); padding-right: var(--space-md);
		scroll-padding-left: var(--space-md);
	}
	.cases::-webkit-scrollbar { display: none; }
	.cases:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 4px; }
	.case-card { flex: 0 0 82%; scroll-snap-align: start; }
	/* a grid with two rows, the picture and the title sharing the first cell:
	   that is how the title sits on the picture here without being positioned */
	.case-card { aspect-ratio: auto; display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto 1fr; }
	/* the picture goes back into the flow and keeps its own proportion */
	.case-card-image { grid-area: 1 / 1; position: relative; inset: auto; height: auto; aspect-ratio: 4 / 3; }
	/* the veil belongs to the picture, not to the sheet under it */
	.case-card::before { inset: 0 0 auto 0; width: 100%; aspect-ratio: 4 / 3; }
	.case-card:hover .case-card-image, .case-card:focus-within .case-card-image { transform: none; }
	/* the stack stops being a layer, so the title and the sheet can be placed
	   one over the picture and one after it */
	.case-card-stack { display: contents; }
	.case-mark { top: var(--space-md); bottom: auto; }
	.case-card-title {
		grid-area: 1 / 1; align-self: start; z-index: 4;
		padding: calc(var(--space-md) * 2 + 1.2em) var(--space-md) 0;
	}
	/* a tap on a phone counts as hover and focus, and the ink title the
	   pointer state asks for would land on the dark picture: here the title
	   stays cream whatever the state */
	.case-card:hover .case-card-title, .case-card:focus-within .case-card-title { background: none; }
	.case-card:hover .case-card-title a, .case-card:focus-within .case-card-title a { color: var(--color-on-surface); }
	/* the sheet fills everything under the picture, so the strip shows three
	   cards of one colour to the bottom edge. The sentence stays at the top,
	   where it is read first, and the facts sit on the bottom edge, so across
	   the strip both line up: a card with less to say opens the gap between
	   the two, never above the sentence or below the facts */
	.case-card-panel { grid-area: 2 / 1; display: flex; flex-direction: column; overflow: visible; }
	.case-card-sheet { flex: 1 1 auto; transform: none; }
	.case-card-sheet .case-facts { margin-top: auto; }
	/* stacked: the joining hairline turns vertical */
	.plan { grid-template-columns: 1fr; gap: var(--space-md); }
	.plan li:not(:last-child)::after { top: 100%; left: var(--space-md); width: 1px; height: var(--space-md); }
	/* Work stays in the bar on a phone: it is the way to the cases, and the
	   bar has room for it down to the narrowest screens */
	.site-nav { gap: var(--space-md); }
	.btn { display: block; }
	.btn-small { display: inline-block; }
	.site-footer { padding-top: var(--space-xl); }
	.footer-main { flex-direction: column; align-items: flex-start; }
	.footer-legal { margin-top: var(--space-xl); }
}
