@charset "utf-8";
/* ============================================================================
   GIBLIN ── STUDIO
   site.css — single stylesheet, no build step, no external requests.

   Ported from the original Cargo 2 site (cargo-stylesheet.css) plus the
   measured layout model of the live site. Section index:

     01  @font-face                — Alte Haas Grotesk (display), Switzer (body)
     02  Design tokens             — :root / body custom properties
     03  Base + reset
     04  Type scale                — Cargo text styles (bodycopy, h1, h2,
                                     .title, .heading-3, .heading-4, .caption)
     05  Page shell                — .page-content, column-set / column-unit
     06  Galleries                 — gallery-grid, gallery-freeform
     07  Media                     — img/video, .video-with-preview, .embed
     08  Header + burger menu      — .site-header, .gn-* (CSS-only :target)
     09  Utilities + a11y          — .ta-*, .skip-link, focus rings
     10  Mobile (<= 768px) and the freeform reflow (<= 1080px)
     11  Reduced motion

   Sizing note: the whole design is driven by the root font size,
   html { font-size: min(0.9vw, 1.44vh) } — Cargo's --base-size. Every
   dimension that came out of the Cargo editor is therefore expressed in rem
   and scales with the viewport. Pixel values are used only where the original
   author hard-coded pixels (the nav chrome) or where a value must not scale
   (the 1000px freeform design canvas).
   ========================================================================== */


/* ============================================================================
   01  @font-face
   ========================================================================== */

/* --- Alte Haas Grotesk (headlines, .title, .heading-3) ------------------- */
@font-face {
	font-family: "Alte Haas Grotesk";
	src: url("../fonts/AlteHaasGrotesk-Regular.woff") format("woff");
	font-weight: 400;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: "Alte Haas Grotesk";
	src: url("../fonts/AlteHaasGrotesk-Bold.woff") format("woff");
	font-weight: 700;
	font-style: normal;
	font-display: swap;
}

/* --- Switzer (body copy) -------------------------------------------------
   Variable weight 100–900 in one 43 KB file. A neutral Swiss neo-grotesk,
   chosen as the replacement for the old licensed Diatype: it sits quietly
   under the Alte Haas headings instead of competing with them.
   Fontshare (Indian Type Foundry), free for personal and commercial use. */
@font-face {
	font-family: "Switzer";
	src: url("../fonts/Switzer-Variable.woff2") format("woff2-variations"),
	     url("../fonts/Switzer-Variable.woff2") format("woff2");
	font-weight: 100 900;
	font-style: normal;
	font-display: swap;
}

/* The original Cargo build loaded two other faces. Both are gone on purpose:
     - Diatype Variable (body) is a commercial Dinamo face, licensed only while
       the site was hosted on Cargo. Its files are deleted, not just
       unreferenced, so there is no way to accidentally ship it. Switzer above
       replaces it.
     - Instrument Serif (display) was dropped at the client's request.
   Alte Haas Grotesk has only weights 400 and 700, so a rule asking for 500 or
   600 renders at 400 rather than being synthetically emboldened. Switzer is
   variable across 100–900, so body copy hits its requested weight exactly. */


/* ============================================================================
   02  Design tokens
   ========================================================================== */

:root {
	/* --- Cargo swatches (verbatim from the original stylesheet) --------- */
	--swatch-1: rgba(0, 0, 0, 0.85);
	--swatch-2: rgba(0, 0, 0, 0.75);
	--swatch-3: rgba(0, 0, 0, 0.6);
	--swatch-4: rgba(0, 0, 0, 0.4);
	--swatch-5: rgba(0, 0, 0, 0.25);
	--swatch-6: #ff0000;

	/* --- Semantic colour ------------------------------------------------ */
	--ink: #000000;
	--bg: #ffffff;
	--accent: #ff0000;          /* links when active, .heading-3 */
	--rule: rgba(127, 127, 127, 0.4);

	/* --- Font stacks ----------------------------------------------------
	   Two families, three roles.
	     --font-display  h1, .title, .heading-3, the wordmark   -> Alte Haas
	     --font-serif    h2, .heading-4, the menu               -> Alte Haas
	                     (named "serif" for historical reasons — the original
	                      Cargo site had Instrument Serif here. Point this at a
	                      new family to give the big page titles their own face
	                      without touching anything else.)
	     --font-body     bodycopy, .caption                     -> Switzer
	   Fallbacks are neutral grotesks with similar proportions, so a failed
	   font load degrades quietly rather than dropping to Times. */
	--font-display: "Alte Haas Grotesk", "Helvetica Neue", Helvetica, Arial,
	                "Segoe UI", Roboto, system-ui, sans-serif;
	--font-serif: var(--font-display);
	--font-body: "Switzer", "Helvetica Neue", Helvetica, Arial, "Segoe UI",
	             Roboto, system-ui, sans-serif;

	/* --- Cargo type scale (rem, relative to --base-size) ----------------- */
	--size-body: 1.2rem;        /* bodycopy, .caption, .button-11          */
	--size-h1: 3.5rem;
	--size-h2: 6.5rem;
	--size-title: 1.75rem;      /* .title, .heading-3                      */
	--size-heading-4: 1.2rem;

	/* --- Page geometry --------------------------------------------------- */
	--page-pad: 2rem;                                    /* measured        */
	--wordmark-lh: 1.2;
	--header-h: calc(var(--size-title) * var(--wordmark-lh));  /* 2.1rem    */
	--header-pad-b: 0.5rem;
	/* Total block occupied by the fixed header: its top padding, the
	   wordmark's line box, and a hair of breathing room. The page content is
	   inset by this so it starts essentially where it did when the wordmark
	   was the first column-set in the Cargo page flow. */
	--header-block: calc(var(--page-pad) + var(--header-h) + var(--header-pad-b));

	/* --- Burger / menu (ported from the GIBLIN NAV CSS block) ------------ */
	--gn-burger-w: 28px;
	--gn-burger-h: 18px;
	/* Was a fixed 32vh from the Cargo original, which fitted six links and
	   cut off Contact when House Rules became the seventh. Sized to content
	   now, capped only by the viewport. */
	--gn-panel-h: max-content;
	--gn-dur: 200ms;
	--gn-z: 2147483000;         /* the original's absurd but intentional z  */

	/* --- Freeform canvas -------------------------------------------------- */
	--ff-canvas: 1000px;        /* the absolute design width; NOT responsive */
}


/* ============================================================================
   03  Base + reset
   ========================================================================== */

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

html {
	/* Cargo's --base-size, verified on the live site:
	   1440x900 -> 12.96px, 1920x1000 -> 14.4px */
	font-size: min(0.9vw, 1.44vh);
	background: var(--bg);
	-webkit-text-size-adjust: 100%;
	text-size-adjust: 100%;
}

body {
	margin: 0;
	background: var(--bg);
	color: var(--ink);
	font-family: var(--font-body);
	font-size: var(--size-body);
	line-height: 1.2;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* Cargo's global press feedback */
a:active,
.linked:active,
button:active { opacity: 0.7; }

img,
video {
	display: block;
	max-width: 100%;
	border: 0;
}

figure { margin: 0; }

hr {
	background: var(--rule);
	border: 0;
	height: 1px;
	display: block;
	margin: 1em 0;
}

/* Cargo list defaults */
ol { margin: 0; padding: 0 0 0 2.5em; }
ul { margin: 0; padding: 0 0 0 2em; }
ul.lineated {
	margin: 0 0 0 3em;
	padding: 0;
	list-style-type: none;
	text-indent: -3em;
}
blockquote { margin: 0; padding: 0 0 0 2em; }

sub { position: relative; vertical-align: baseline; top: 0.3em; }
sup { position: relative; vertical-align: baseline; top: -0.4em; }

.small-caps {
	font-variant: small-caps;
	text-transform: lowercase;
}


/* ============================================================================
   04  Type scale — Cargo text styles

   Every style honours the inline --font-scale custom property that the Cargo
   editor writes onto individual spans (e.g. the Latest page uses
   style="--font-scale: 1.56" on .heading-4). --font-scale multiplies the
   style's base size and inherits, exactly as it does in Cargo.
   ========================================================================== */

bodycopy {
	display: block;
	font-family: var(--font-body);
	font-size: calc(var(--size-body) * var(--font-scale, 1));
	font-weight: 500;
	font-style: normal;
	line-height: 1.2;
	letter-spacing: 0em;
	color: var(--ink);
}

/* The italic face pins slnt at -12, so it is immune to the 'slnt' 0 above;
   this rule just makes sure <i>/<em> pick the italic file. */
bodycopy i,
bodycopy em,
i, em { font-style: italic; }

bodycopy a {
	color: var(--ink);
	text-decoration: none;
}

h1 {
	margin: 0;
	font-family: var(--font-display);
	font-size: calc(var(--size-h1) * var(--font-scale, 1));
	font-weight: 700;
	font-style: normal;
	line-height: 1;
	letter-spacing: 0;
	color: var(--ink);
}
h1 a { color: var(--accent); text-decoration: none; }

h2 {
	margin: 0;
	font-family: var(--font-serif);
	font-size: calc(var(--size-h2) * var(--font-scale, 1));
	font-weight: 400;
	font-style: normal;
	line-height: 0.95;
	letter-spacing: 0.001em;
	color: var(--ink);
}
h2 a { color: var(--ink); text-decoration: none; }

/* .title — the wordmark and the service headings.
   The wordmark contains U+2500 (BOX DRAWINGS LIGHT HORIZONTAL) twice, a glyph
   Alte Haas Grotesk does not contain. The monospace tail on the stack below
   supplies it: box-drawing glyphs are exactly one advance wide in every
   monospace face, so the two rules butt together seamlessly on macOS,
   Windows and Linux instead of falling back to whatever the UA picks. */
.title {
	display: block;
	font-family: var(--font-display), "SFMono-Regular", Menlo, Consolas, monospace;
	font-size: calc(var(--size-title) * var(--font-scale, 1));
	font-weight: 700;
	font-style: normal;
	line-height: 1.2;
	letter-spacing: 0;
	color: var(--ink);
	text-decoration: none;
}
.title a { color: var(--ink); text-decoration: none; }

.heading-3 {
	display: block;
	font-family: var(--font-display);
	font-size: calc(var(--size-title) * var(--font-scale, 1));
	font-weight: 700;
	font-style: normal;
	line-height: 1.2;
	letter-spacing: 0;
	color: var(--accent);
}
.heading-3 a { color: var(--accent); text-decoration: none; }

.heading-4 {
	display: block;
	font-family: var(--font-serif);
	font-size: calc(var(--size-heading-4) * var(--font-scale, 1));
	font-weight: 400;
	font-style: normal;
	line-height: 1.2;
	letter-spacing: 0;
	color: var(--ink);
}
.heading-4 a { color: var(--ink); text-decoration: none; }

.caption {
	display: block;
	font-family: var(--font-body);
	font-size: calc(var(--size-body) * var(--font-scale, 1));
	font-weight: 500;
	font-style: normal;
	line-height: 1.2;
	letter-spacing: 0em;
	color: var(--swatch-1);
}
.caption a { color: var(--swatch-1); text-decoration: underline; }

gallery-grid .caption,
gallery-freeform .caption { margin-bottom: 2em; }

img + .caption,
video + .caption,
.embed + .caption { margin-top: 0.5em; }

/* .button-11 — Cargo button style, kept for parity with the original sheet. */
.button-11 {
	display: inline-block;
	font-family: var(--font-body);
	font-size: calc(var(--size-body) * var(--font-scale, 1));
	font-weight: 500;
	font-style: normal;
	line-height: 1.2;
	letter-spacing: 0em;
	color: var(--swatch-1);
	background: var(--bg);
	border: 0.2rem solid var(--swatch-1);
	border-radius: 0.5rem;
	padding: 0.8rem 1rem;
	filter: drop-shadow(0.3rem 0.3rem 0rem rgba(0, 0, 0, 0.85));
	will-change: filter;
}
.button-11 a,
.button-11 a.active { color: var(--swatch-1); text-decoration: none; }

/* Cargo: .page a.active { color: #ff0000 } */
a.active { color: var(--accent); }


/* ============================================================================
   05  Page shell — content box, 12-column model

   Measured: .page-content padding is 2rem, so the content box is the full
   viewport width minus 2 x 2rem. There is no max-width. The extra top padding
   clears the fixed header, which now carries the wordmark that used to be the
   first column-set in the Cargo page flow.
   ========================================================================== */

.page-content {
	padding: var(--page-pad);
	padding-top: var(--header-block);
	text-align: left;
	max-width: 100%;
}

html { scroll-padding-top: calc(var(--header-block) + 1rem); }

/* --- column-set / column-unit -------------------------------------------
   display:flex, nowrap, full width of the content box, 12-column model.
   span="N" => N/12 of the set. A missing span means full width (span 12).
   Empty column-units are spacers and must keep their width — flex-shrink is
   therefore 0 and the width is set explicitly rather than via flex-basis, so
   an empty unit does not collapse. */
column-set {
	display: flex;
	flex-wrap: nowrap;
	align-items: flex-start;
	width: 100%;
}

column-unit {
	display: block;
	/* No span attribute means "share the row equally with the other unspanned
	   units". Treating it as a full 12 columns was wrong: a column-set holding
	   two unspanned units then laid out 200% wide and pushed the page into
	   horizontal scroll. flex-basis:0 with grow:1 gives each an equal share,
	   which is what Cargo did. A single unspanned unit still fills the row. */
	flex: 1 1 0;
	min-width: 0;                /* allow long words to wrap, not overflow */
	overflow-wrap: break-word;
}

/* An explicit span is an exact fraction of the row and must not flex. */
column-unit[span] { flex: 0 0 auto; }

column-unit[span="1"]  { width: calc(100% * 1  / 12); }
column-unit[span="2"]  { width: calc(100% * 2  / 12); }
column-unit[span="3"]  { width: calc(100% * 3  / 12); }
column-unit[span="4"]  { width: calc(100% * 4  / 12); }
column-unit[span="5"]  { width: calc(100% * 5  / 12); }
column-unit[span="6"]  { width: calc(100% * 6  / 12); }
column-unit[span="7"]  { width: calc(100% * 7  / 12); }
column-unit[span="8"]  { width: calc(100% * 8  / 12); }
column-unit[span="9"]  { width: calc(100% * 9  / 12); }
column-unit[span="10"] { width: calc(100% * 10 / 12); }
column-unit[span="11"] { width: calc(100% * 11 / 12); }
column-unit[span="12"] { width: 100%; }


/* ============================================================================
   06  Galleries
   ========================================================================== */

/* --- gallery-grid --------------------------------------------------------
   columns="N" => N equal columns, each media item fills its column at its
   natural aspect ratio.
   vertical-align="center" => .va-center => align-items:center.
   --grid-span / grid-span="N" is honoured if the converter carries Cargo's
   grid-span attribute through; without it every item occupies one column.

   SPACING. Cargo stored a `gutter` on every gallery and then never applied it —
   measured on the live site, columns sat edge to edge (pitch == item width) and
   rows butted together. That was a Cargo bug rather than intent: the Services
   logo wall was authored at "8.2rem 4.1rem" and never rendered it, which is why
   the logos looked crammed. The importer now emits the authored value as
   --gap-row / --gap-col and it is honoured here. Galleries with no authored
   gutter fall back to 0.
   ========================================================================== */
gallery-grid {
	display: grid;
	grid-template-columns: repeat(var(--cols, 1), minmax(0, 1fr));
	row-gap: var(--gap-row, 0);
	column-gap: var(--gap-col, 0);
	align-items: start;
	width: 100%;
}

gallery-grid.va-center { align-items: center; }

gallery-grid > * {
	grid-column: span var(--grid-span, 1);
	min-width: 0;
}
gallery-grid > [grid-span="2"] { grid-column: span 2; }
gallery-grid > [grid-span="3"] { grid-column: span 3; }
gallery-grid > [grid-span="4"] { grid-column: span 4; }
gallery-grid > [grid-span="5"] { grid-column: span 5; }
gallery-grid > [grid-span="6"] { grid-column: span 6; }

gallery-grid > img,
gallery-grid > video,
gallery-grid > figure > img,
gallery-grid > figure > video {
	width: 100%;
	height: auto;
}

/* A logo that carries Cargo's `scale` is sized to it rather than stretched to
   fill its column: scale 20 means 200px wide, on the same 1000px basis the
   freeform canvas uses. Capped at the column width so a large value cannot
   overflow, and centred in the cell. Items with no scale still fill their
   column, which is the old behaviour. */
gallery-grid > img[style*="--scale"],
gallery-grid > video[style*="--scale"] {
	width: min(100% * var(--logo-max, 1), calc(var(--scale) * 10px));
	margin-inline: auto;
}

/* --logo-max caps every logo in a gallery to a fraction of its column, which
   both shrinks them and creates the gap between them — the whitespace is the
   unused part of each cell, so it stays even no matter how wide the logos are.
   Set it per gallery: style="--logo-max:0.55". */
gallery-grid[style*="--logo-max"] > img,
gallery-grid[style*="--logo-max"] > video {
	width: calc(100% * var(--logo-max));
	margin-inline: auto;
}

/* --- .hr-book — the House Rules, as a book -------------------------------
   One rule per spread, turned rather than scrolled. The point is that this is
   a printed object first: a book the studio posts out. So it holds a real trim
   size, has a verso and a recto, keeps a folio, and never shows more than one
   rule at a time — the constraint is what makes the typography behave.

   Spreads are stacked in the same box and only .is-on is visible, so turning a
   page is one class change and nothing reflows.
   ========================================================================== */
.hr-book {
	position: relative;
	width: 100%;
	aspect-ratio: 297 / 105;      /* a landscape spread: two ~A5 pages side by side */
	background: #f2efe9;          /* paper, not white — it should feel stock */
	color: var(--ink);
	overflow: hidden;
	user-select: none;
	box-shadow: 0 2px 40px rgba(0, 0, 0, 0.10);
}

.hr-spread {
	position: absolute;
	inset: 0;
	display: grid;
	grid-template-columns: 1fr 1fr;
	opacity: 0;
	pointer-events: none;
	transition: opacity 220ms ease;
}
.hr-spread.is-on { opacity: 1; pointer-events: auto; }

/* the gutter: a soft crease down the centre, the way a bound spread falls */
.hr-book::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 50%;
	width: 8%;
	transform: translateX(-50%);
	background: linear-gradient(to right,
		rgba(0,0,0,0) 0%, rgba(0,0,0,0.055) 45%,
		rgba(0,0,0,0.075) 50%, rgba(0,0,0,0.055) 55%, rgba(0,0,0,0) 100%);
	pointer-events: none;
	z-index: 3;
}

.hr-verso,
.hr-recto {
	display: flex;
	flex-direction: column;
	justify-content: center;
	padding: clamp(1.5rem, 4.5%, 5rem);
}
.hr-verso { align-items: flex-end;  text-align: right; }
.hr-recto { align-items: flex-start; text-align: left; }

.hr-num {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: clamp(4rem, 17vh, 16rem);
	line-height: 0.78;
	color: var(--accent);
	font-variant-numeric: tabular-nums;
}

.hr-rule {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: clamp(1.3rem, 4.4vh, 4rem);
	line-height: 1.02;
	letter-spacing: -0.015em;
	margin: 0;
	max-width: 14ch;             /* force the line breaks a typesetter would */
}

.hr-title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: clamp(2.2rem, 9vh, 8rem);
	line-height: 0.9;
	margin: 0 0 0.35em;
}
.hr-sub,
.hr-imprint {
	font-family: var(--font-body);
	font-size: clamp(0.7rem, 1.5vh, 1.1rem);
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--swatch-3);
}
.hr-colophon {
	font-family: var(--font-body);
	font-size: clamp(0.85rem, 2.2vh, 1.6rem);
	line-height: 1.3;
	margin: 0;
	max-width: 26ch;
}

/* --- turning ------------------------------------------------------------- */
.hr-nav {
	position: absolute;
	top: 0;
	bottom: 0;
	width: 22%;
	border: 0;
	background: transparent;
	color: var(--ink);
	font-size: clamp(1.5rem, 4vh, 3rem);
	line-height: 1;
	cursor: pointer;
	opacity: 0;
	transition: opacity 150ms ease;
	z-index: 4;
}
.hr-prev { left: 0;  padding-right: 60%; }
.hr-next { right: 0; padding-left: 60%; }
.hr-book:hover .hr-nav { opacity: 0.45; }
.hr-nav:hover,
.hr-nav:focus-visible { opacity: 1 !important; }
.hr-nav[disabled] { opacity: 0 !important; cursor: default; }

.hr-folio {
	position: absolute;
	bottom: clamp(0.5rem, 2.5%, 1.5rem);
	left: 50%;
	transform: translateX(-50%);
	font-family: var(--font-body);
	font-size: clamp(0.65rem, 1.4vh, 1rem);
	letter-spacing: 0.12em;
	color: var(--swatch-4);
	z-index: 4;
}

@media (max-width: 768px) {
	/* a spread is unreadable on a phone — go to single pages, portrait */
	.hr-book { aspect-ratio: 3 / 4; }
	.hr-spread { grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
	.hr-verso { align-items: flex-start; text-align: left; padding-bottom: 0; }
	.hr-book::after { display: none; }
	.hr-rule { max-width: none; }
}

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

/* --- the wordmark ---------------------------------------------------------
   The logo is G–S: two letters joined by a rule, and the rule is the device.
   Writing it as "GIBLIN ── STUDIO" with box-drawing characters was shorthand
   standing in for the real thing — those glyphs sit at whatever weight and
   length the font decides, break across lines, and get read aloud by screen
   readers as "box drawings light horizontal". So the connector is drawn: it
   scales with the type, keeps the logo's proportion, and the <a> carries the
   full name as its accessible label. */
.site-header .title {
	display: inline-flex;
	align-items: center;
	gap: 0.42em;
	white-space: nowrap;
}

.wm-rule {
	display: inline-block;
	width: 1.55em;               /* the proportion in the original mark */
	height: 0.12em;
	min-height: 1px;
	background: currentColor;
	flex: 0 0 auto;
}

/* --- .about-lede — the standing copy on About -----------------------------
   Simple: every block centred on one measure, stacked with even spacing, in
   the site's ordinary type. The House Rules below carry the design. */
.about-lede { padding: 0 0 clamp(2rem, 5vw, 5rem); }
.about-lede h2 { margin: 0; }
.about-lede > div { max-width: 62ch; margin: 0 auto 3.2em; }
.about-lede > div:first-child { max-width: none;
	/* the same breath the homepage takes after "Latest" (five <br> lines)
	   before any content starts */
	margin-bottom: 7.2rem; }
.about-lede > div:last-child { margin-bottom: 0; }

/* --- .rules-book — the House Rules panels ---------------------------------
   Design language taken from the studio's own Chivas Regal treatment, which is
   the opposite of what a "big typographic statement" instinct produces:

     - the type is SMALL against the panel. Short lines of caps, tight leading,
       parked in a corner. The colour and the image do the shouting.
     - deep saturated fields — a hard red, an electric blue — against near-black.
     - supporting copy is tiny and dense, set close to the statement.
     - images either bleed the whole panel or sit as small plates on black with
       a lot of empty space around them.
     - one device where the words are spread right across the measure so they
       read as a field of words rather than a sentence.

   Deliberately NOT a magazine spread: no crease, no folios, no page furniture.
   These are panels, sized 3:2 like the treatment's own pages.
   ========================================================================== */
.rules-book {
	width: 100%;
	--red: #c8102e;
	--blue: #1226c4;
	--ink0: #0a0a0a;
}

.rules-open { padding: clamp(2.5rem, 7vw, 7rem) 0 clamp(1.5rem, 3vw, 3rem); }
.rules-title {
	font-family: var(--font-display);
	font-weight: 700;
	font-size: clamp(1.6rem, 4.4vw, 4.4rem);
	letter-spacing: -0.02em;
	margin: 0;
}
.rules-sub {
	font-family: var(--font-body);
	font-size: clamp(0.7rem, 1vw, 0.9rem);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--swatch-3);
	margin: 1.1em 0 0;
}

/* --- the House Rules ------------------------------------------------------
   Ten rules. Photography on every one, and the type INTERACTS with it — filled
   by it, toned over it, striking it out, degrading it — instead of sitting
   beside it on a flat field. No pasted props, no drop-shadow clip-art, no
   page furniture. Type never crops off an edge.
   ========================================================================== */
@font-face { font-family:"Khand"; src:url("../fonts/Khand-Bold.woff2") format("woff2");
	font-weight:700; font-display:swap; }
@font-face { font-family:"Zodiak"; src:url("../fonts/Zodiak-Bold.woff2") format("woff2");
	font-weight:700; font-display:swap; }
@font-face { font-family:"Melodrama"; src:url("../fonts/Melodrama-Semibold.woff2") format("woff2");
	font-weight:600; font-display:swap; }
@font-face { font-family:"Clash Display"; src:url("../fonts/ClashDisplay-Variable.woff2") format("woff2-variations"),
	url("../fonts/ClashDisplay-Variable.woff2") format("woff2"); font-weight:200 700; font-display:swap; }
@font-face { font-family:"Panchang"; src:url("../fonts/Panchang-Extrabold.woff2") format("woff2");
	font-weight:800; font-display:swap; }
@font-face { font-family:"Courier Prime"; src:url("../fonts/CourierPrime-Regular.woff2") format("woff2");
	font-weight:400; font-display:swap; }
@font-face { font-family:"Eater"; src:url("../fonts/Eater-Regular.woff2") format("woff2");
	font-weight:400; font-display:swap; }
@font-face { font-family:"Boska"; src:url("../fonts/Boska-Bold.woff2") format("woff2");
	font-weight:700; font-display:swap; }
@font-face { font-family:"Tanker"; src:url("../fonts/Tanker-Regular.woff2") format("woff2");
	font-weight:400; font-display:swap; }
@font-face { font-family:"Fraunces"; src:url("../fonts/Fraunces-Latin.woff2") format("woff2");
	font-weight:300 900; font-display:swap; }
@font-face { font-family:"Bodoni Moda"; src:url("../fonts/BodoniModa-Latin.woff2") format("woff2");
	font-weight:400 700; font-display:swap; }

.rules-book { width:100%; --red:#c8102e; --blue:#1226c4; --ink0:#0f0f0f; }
.rules-open { padding: clamp(2.5rem, 7vw, 7rem) 0 clamp(1.5rem, 3vw, 3rem); }
.rules-title { font-family:var(--font-display); font-weight:700;
	font-size:clamp(1.6rem,4.4vw,4.4rem); letter-spacing:-0.02em; margin:0; }

.hr { position:relative; overflow:hidden; margin-top:clamp(1.6rem,4vw,4rem); color:#fff; }
.hr h3 { margin:0; }
.hr .note { margin:0; font-family:var(--font-body); }
.hr > img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }

/* 01 — POSTER. Colour, Tanker over the grade. ------------------------------- */
.hr-poster { aspect-ratio:16/9; }
.hr-poster > img { filter:contrast(1.05) saturate(1.12); object-position:50% 30%; }
.hr-poster::after { content:""; position:absolute; inset:0;
	background:linear-gradient(to top, rgba(0,0,0,0.72) 0%, rgba(0,0,0,0.1) 45%, rgba(0,0,0,0) 65%); }
.hr-poster h3 { position:absolute; left:5%; bottom:6%; z-index:2;
	/* The Chivas deck's masthead face is literally Times Bold set enormous —
	   pdffonts on the treatment lists Times-Bold as its display font. That
	   plainness at this scale IS the attitude, so it is reproduced exactly. */
	font-family:"Times New Roman", Times, serif; font-weight:700;
	text-transform:uppercase;
	font-size:clamp(3.2rem,10.4vw,10.4rem); line-height:0.82; letter-spacing:-0.015em; }
.hr-poster .note { position:absolute; right:5%; top:7%; z-index:2; text-align:right;
	font-family:"Times New Roman", Times, serif; font-weight:700; text-transform:uppercase;
	width:clamp(220px,24vw,400px); font-size:clamp(0.62rem,0.78vw,0.85rem);
	line-height:1.3; letter-spacing:0.08em; transform:scaleY(1.5); transform-origin:top right;
	color:rgba(255,255,255,0.9); }

/* 02 — THE WHISPER. The frame barely surfaces from black; one line. --------- */
.hr-once { aspect-ratio:21/9; background:#000; }
.hr-once > img { filter:grayscale(0.3) brightness(0.32) contrast(1.1); }
.hr-once::after { content:""; position:absolute; inset:0;
	background:radial-gradient(90% 90% at 50% 50%, rgba(0,0,0,0) 40%, rgba(0,0,0,0.75) 100%); }
.hr-once h3 { position:absolute; inset:0; display:grid; place-items:center; z-index:2;
	font-family:"Zodiak"; font-weight:700; font-size:clamp(1.6rem,3.2vw,3.2rem);
	letter-spacing:0.01em; }
.hr-once .note { position:absolute; left:50%; transform:translateX(-50%); bottom:8%;
	z-index:2; text-align:center; font-family:var(--font-display); text-transform:uppercase;
	font-size:clamp(0.58rem,0.7vw,0.78rem); letter-spacing:0.42em; line-height:2.1;
	color:rgba(255,255,255,0.42); width:auto; max-width:70ch; }

/* 03 — THE TONE. A horror one-sheet: the word itself rots. --------------- */
.hr-tone { aspect-ratio:16/9; background:#050505; }
.hr-tone > img { filter:grayscale(0.45) contrast(1.35) brightness(0.42) saturate(1.3); }
.hr-tone::after { content:""; position:absolute; inset:0;
	background:radial-gradient(80% 80% at 50% 45%, rgba(0,0,0,0) 35%, rgba(0,0,0,0.82) 100%),
	linear-gradient(to bottom, rgba(120,0,0,0.12), rgba(120,0,0,0.28)); }
.hr-tone h3 { position:absolute; inset:0; z-index:2; display:flex;
	flex-direction:column; align-items:center; justify-content:center;
	text-align:center; gap:0.4em; }
.hr-tone h3 span { display:block; }
.hr-tone .sm { font-family:"Courier Prime"; font-size:clamp(0.75rem,1.1vw,1.1rem);
	letter-spacing:0.55em; text-indent:0.55em; color:rgba(255,255,255,0.75); }
.hr-tone .huge { font-family:"Eater", cursive;
	font-size:clamp(4rem,13.5vw,13.5rem); line-height:1;
	color:#b30d0d; text-shadow:0 0 0.35em rgba(179,13,13,0.45); }
.hr-tone .note { position:absolute; right:5%; bottom:7%; z-index:2; text-align:right;
	font-family:"Courier Prime"; text-transform:uppercase; letter-spacing:0.04em;
	width:clamp(220px,22vw,360px); font-size:clamp(0.7rem,0.88vw,0.95rem);
	line-height:1.8; color:rgba(216,207,192,0.85); }

/* 04 — THE ECHO. The frame shown again and again, dragged across acid
   yellow — showing, repeated until it cannot be missed; the words barely
   bother. After the client's cascade reference. ------------------------- */
.hr-show { aspect-ratio:16/9; background-color:#f6fa00; color:#0f0f0f; }
.hr-show::before { content:""; position:absolute; inset:0;
	background-image:url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg"),
		url("../media/rules/rule-04.jpg");
	background-position:50.0% 54.0%, 45.0% 47.6%, 55.0% 60.4%, 40.0% 41.2%, 60.0% 66.8%, 35.0% 34.8%, 65.0% 73.2%, 30.0% 28.4%, 70.0% 79.6%, 25.0% 22.0%, 75.0% 86.0%, 20.0% 15.6%, 80.0% 92.4%, 15.0% 9.2%, 85.0% 98.8%, 10.0% 2.8%, 90.0% 105.2%, 5.0% -3.6%, 95.0% 111.6%, 0.0% -10.0%, 100.0% 118.0%, -5.0% -16.4%, 105.0% 124.4%, -10.0% -22.8%, 110.0% 130.8%;
	background-size:44% auto;
	background-repeat:no-repeat; }
.hr-show h3 span { display:block; }
.hr-show h3 { position:absolute; left:4.5%; bottom:7%; z-index:2; }
.hr-show .show { font-family:"Clash Display"; font-weight:600;
	font-size:clamp(3.4rem,9.5vw,9.5rem); line-height:0.85; letter-spacing:-0.01em; }
.hr-show .tell { font-family:"Clash Display"; font-weight:500;
	font-size:clamp(0.9rem,1.5vw,1.5rem); letter-spacing:0.34em; margin-top:0.8em; }
.hr-show .note { position:absolute; left:4.5%; top:7%; right:auto; z-index:2; text-align:left;
	font-family:"Clash Display"; font-weight:600; text-transform:uppercase;
	width:clamp(240px,26vw,460px); font-size:clamp(0.95rem,1.5vw,1.6rem);
	line-height:1.4; letter-spacing:0.01em; color:#f6fa00; background:#0f0f0f;
	padding:0.8em 1em; transform:rotate(-1.4deg); }

/* 05 — THE STRIP. Four frames butted hard; the words ride the seams. -------- */
.hr-strip { background:#000; }
.hr-strip .frames { display:grid; grid-template-columns:repeat(4,1fr); }
.hr-strip .frames img { width:100%; aspect-ratio:3/4; object-fit:cover; display:block; }
.hr-strip .frames img:nth-child(1) { filter:saturate(1.25); }
.hr-strip .frames img:nth-child(2) { filter:grayscale(1) contrast(1.4); object-position:15% 50%; }
.hr-strip .frames img:nth-child(3) { filter:saturate(1.3) hue-rotate(18deg); object-position:75% 50%; }
.hr-strip .frames img:nth-child(4) { filter:grayscale(1) contrast(1.6) brightness(0.85); object-position:45% 20%; }
.hr-strip h3 { position:absolute; left:4%; right:4%; top:50%; transform:translateY(-50%);
	display:flex; flex-wrap:wrap; justify-content:space-between; gap:0.5em;
	font-family:"Panchang"; font-weight:800; font-size:clamp(1.2rem,3vw,3rem);
	letter-spacing:0.03em; text-shadow:0 0 1.2em rgba(0,0,0,0.7); z-index:2; }
.hr-strip .note { position:absolute; left:50%; transform:translateX(-50%); bottom:6%;
	z-index:2; text-align:center; width:auto; max-width:44ch;
	font-size:clamp(0.95rem,1.35vw,1.45rem); line-height:1.45; color:#fefefe;
	text-shadow:0 0 4px rgba(0,0,0,0.95), 0 2px 8px rgba(0,0,0,0.8); }

/* 06 — THE GRADE. A night grade re-lit by the desk lamp in the frame: the
   script line is only light (Courier, screen-blended, half-swallowed by the
   lamp's throw); the concept line is print (Melodrama, solid, set in like a
   main-on-end title). The h3 carries no z-index/transform on purpose — no
   stacking context may isolate the script span, or its screen blend stops
   reaching the graded photograph beneath. */
.hr-strike { aspect-ratio:16/9; background:#05070c; }
.hr-strike > img { position:absolute; inset:0; width:100%; height:100%;
	object-fit:cover; filter:grayscale(1) contrast(1.24) brightness(0.6); }
.hr-strike::before { content:""; position:absolute; inset:0; z-index:1;
	background:radial-gradient(130% 130% at 42% 38%, rgba(22,32,58,0.4) 0%, rgba(8,12,24,0.82) 100%);
	mix-blend-mode:multiply; }
.hr-strike::after { content:""; position:absolute; inset:0; z-index:1;
	background:radial-gradient(48% 62% at 26% 32%, rgba(255,190,116,0.4) 0%, rgba(255,172,92,0.12) 45%, rgba(0,0,0,0) 72%);
	mix-blend-mode:screen; }
.hr-strike h3 { position:absolute; left:6.3%; right:6%; top:41%;
	display:flex; flex-direction:column; align-items:flex-start; }
.hr-strike h3 span { display:block; }
.hr-strike .script { z-index:2; font-family:"Courier Prime"; font-weight:400;
	text-transform:uppercase; font-size:clamp(0.78rem,1.05vw,1.15rem);
	letter-spacing:0.52em; color:rgba(255,208,148,0.85); mix-blend-mode:screen;
	margin:0 0 1.5em 0.15em; }
.hr-strike .concept { z-index:2; font-family:"Melodrama"; font-weight:600;
	font-size:clamp(2.9rem,8.2vw,8.2rem); line-height:0.98; letter-spacing:-0.01em;
	color:#f4eee2; text-shadow:0 0 1.2em rgba(5,8,14,0.55); }
.hr-strike .note { position:absolute; left:6.35%; bottom:8%; z-index:2;
	font-family:var(--font-display); text-transform:uppercase;
	font-size:clamp(0.6rem,0.72vw,0.8rem); letter-spacing:0.28em; line-height:2.1;
	color:rgba(233,222,203,0.55); max-width:46ch; }

/* 07 — THE IDEA. ONE, filled with the film itself. -------------------------- */
.hr-one { aspect-ratio:16/9; background:#000; }
.hr-one h3 span { display:block; }
.hr-one .one { position:absolute; left:50%; top:42%; transform:translate(-50%,-50%);
	font-family:"Khand"; font-weight:700;
	font-size:clamp(10rem,30vw,30rem); line-height:1; letter-spacing:0;
	background-image:url("../media/rules/rule-07.jpg");
	background-size:cover; background-position:50% 35%;
	-webkit-background-clip:text; background-clip:text; color:transparent; }
.hr-one .rest { position:absolute; left:50%; bottom:11%; transform:translateX(-50%);
	font-family:"Khand"; font-weight:700; font-size:clamp(1rem,2vw,2rem);
	letter-spacing:0.3em; white-space:nowrap; }
.hr-one .note { position:absolute; right:5%; top:8%; z-index:2; text-align:right;
	font-family:"Khand"; font-weight:600; text-transform:uppercase;
	width:clamp(210px,24vw,380px); font-size:clamp(0.95rem,1.45vw,1.5rem);
	line-height:1.3; letter-spacing:0.03em; color:rgba(255,255,255,0.85); }

/* 08 — THE BORE. The same photography, drained until nothing is left. ------- */
.hr-bore { aspect-ratio:21/6; background:#e9e9e7; color:#444; }
.hr-bore > img { filter:grayscale(1) contrast(0.35) brightness(1.45); opacity:0.55; }
.hr-bore h3 { position:absolute; left:5%; top:50%; transform:translateY(-50%);
	font-family:"Times New Roman", Times, serif; font-weight:400;
	font-size:clamp(1rem,1.5vw,1.5rem); }
.hr-bore .note { position:absolute; right:5%; top:50%; transform:translateY(-50%);
	font-family:"Times New Roman", Times, serif; font-style:italic;
	font-size:clamp(0.75rem,0.9vw,0.95rem); color:#8a8a86; }

/* 09 — THE AD. The promise set straight onto the photograph. ---------------- */
.hr-ad { aspect-ratio:16/9; }
.hr-ad > img { filter:saturate(1.15) contrast(1.05); }
.hr-ad::after { content:""; position:absolute; inset:0;
	background:linear-gradient(to bottom, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.05) 45%, rgba(0,0,0,0.4) 100%); }
.hr-ad h3 { position:absolute; left:0; right:0; top:10%; z-index:2; text-align:center;
	font-family:"Fraunces"; font-weight:640;
	font-variation-settings:"SOFT" 70, "WONK" 1;
	font-size:clamp(2rem,4.8vw,4.8rem); line-height:1.0; letter-spacing:-0.01em; }
.hr-ad .ast { color:#ff5a5a; }
.hr-ad .note { position:absolute; left:5%; bottom:5%; transform:none; z-index:2;
	text-align:left; font-family:"Courier Prime";
	font-size:clamp(0.58rem,0.72vw,0.78rem); letter-spacing:0.02em; line-height:1.6;
	color:rgba(255,255,255,0.6); width:auto; max-width:46ch; }

/* 10 — THE TITLE CARD. ------------------------------------------------------ */
.hr-end { aspect-ratio:16/9; background:var(--ink0); }
.hr-end > img { filter:grayscale(1) contrast(1.15) brightness(0.72); }
.hr-end::after { content:""; position:absolute; inset:0;
	background:linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 50%); }
.hr-end h3 { position:absolute; right:5%; bottom:8%; z-index:2; text-align:right;
	font-family:"Bodoni Moda"; font-weight:600; text-transform:uppercase;
	font-size:clamp(2.2rem,6vw,6rem); line-height:0.92; }
.hr-end .note { position:absolute; left:5%; top:8%; z-index:2; text-align:left;
	font-family:"Times New Roman", Times, serif; font-weight:700; text-transform:uppercase;
	width:clamp(220px,24vw,400px); font-size:clamp(0.62rem,0.78vw,0.85rem);
	line-height:1.3; letter-spacing:0.08em; transform:scaleY(1.5); transform-origin:top left;
	color:rgba(255,255,255,0.85); }

@media (max-width: 768px) {
	.hr-poster, .hr-tone, .hr-one, .hr-ad, .hr-end { aspect-ratio:4/5; }
	.hr-once { aspect-ratio:16/10; }
	.hr-show { aspect-ratio:4/5; }
	.hr-show .show { font-size:clamp(2.6rem,16vw,6rem); }
	.hr-strip .frames { grid-template-columns:repeat(2,1fr); }
	.hr-one .one { font-size:clamp(7rem,46vw,13rem); }
	.hr-one .rest { white-space:normal; text-align:center; width:88%; }
	.hr-bore { aspect-ratio:21/9; }
	.hr-bore .note { display:none; }
	.hr-tone .huge { font-size:clamp(3rem,20vw,7rem); }
	.hr-poster .note, .hr-one .note, .hr-end .note { display:none; }
}

/* --- .flicker — the Samples montage --------------------------------------
   A fast cut sequence of treatment pages. Every image is stacked in the same
   box and only one carries .is-on, so switching is a single class change with
   no layout work — that is what lets it run at ~8 frames a second without the
   page juddering.

   object-fit: contain, not cover: these are page layouts and cropping them
   would throw away the composition, which is the thing being shown. The box
   keeps a fixed aspect so pages of different proportions do not make the page
   jump as they cycle.

   The images are NOT in the HTML — assets/js/site.js builds them from
   data-count, so the markup stays small and loading can be staggered. Without
   JS the single <img> in the markup is what you get, which is a reasonable
   still fallback. */
.flicker {
	position: relative;
	width: 100%;
	aspect-ratio: var(--flicker-ar, 16 / 9);
	background: var(--bg);
	overflow: hidden;
	/* nudge the browser to give this its own layer; the images swap fast */
	contain: layout paint;
}

.flicker img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: contain;
	opacity: 0;
	/* no transition by default — a hard cut is the point */
}

.flicker img.is-on { opacity: 1; }

/* A gentle version for anyone who has asked for less motion: the cut becomes a
   slow crossfade and site.js drops the interval right down. */
@media (prefers-reduced-motion: reduce) {
	.flicker img { transition: opacity 600ms ease; }
}

.flicker-note {
	margin-top: 1em;
	color: var(--swatch-3);
}

/* --- .client-marquee — the Clients wall in motion ----------------------- */
.client-marquee {
	overflow: hidden;
	width: 100%;
	margin-block: clamp(3rem, 5vw, 5.5rem);
}

.cm-track {
	display: flex;
	align-items: center;
	width: max-content;
	animation: cm-scroll 26s linear infinite;
}

.cm-track img {
	height: clamp(26px, 3.2vw, 52px);
	width: auto;
	flex: 0 0 auto;
	margin-right: clamp(2.2rem, 4vw, 4.5rem);
}

@keyframes cm-scroll {
	to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
	.cm-track { animation: none; }
}

/* --- gallery-freeform ----------------------------------------------------
   Cargo's freeform collage. It authored on a fixed 1000px surface and never
   scaled it, which left the collage hugging the left of a wider column with
   dead space beside it. Here the canvas fills the column and holds its
   proportions via --ff-ar, with children placed in percentages: x/width
   against the 1000-unit width, y against the canvas height (a percentage
   `top` resolves against height). See section 10 for the narrow reflow. */
gallery-freeform {
	display: block;
	position: relative;
	width: 100%;
	aspect-ratio: var(--ff-ar, 1000 / 600);
	margin: 0;
}

.ff-item {
	position: absolute;
	margin: 0;
	left: var(--ff-x, 0%);
	top: var(--ff-y, 0%);
	width: var(--ff-w, auto);
	z-index: var(--ff-z, 1);
}

.ff-item > img,
.ff-item > video,
.ff-item > .embed,
.ff-item > .video-with-preview {
	width: 100%;
	height: auto;
}


/* ============================================================================
   07  Media
   ========================================================================== */

/* Breathing room between a project title and its film. The Cargo original had
   them butted together because the title and the video are separate
   column-sets with nothing between them. */
.video-with-preview { margin-top: 1.4rem; }

/* Air between a film and the next project's title. The title→own-film gap
   stays 1.4rem; project→project needs a much bigger beat. */
column-set:has(> column-unit > .video-with-preview) { margin-bottom: clamp(2.8rem, 4.5vw, 5rem); }

/* Same gap on Featured. A gallery that sits immediately after a title
   column-set gets the breathing room; the Services logo walls are separated
   from their headings by <br> elements, so they do not match this and keep
   their own spacing. */
column-set + gallery-grid { margin-top: 1.4rem; }

/* --- .video-with-preview -------------------------------------------------
   A real player (.vwp-main) with a silent looping teaser (.vwp-preview) laid
   over it. The overlay never eats the click, so the first press lands on the
   player underneath; site.js then adds .is-hidden and the teaser fades out. */
.video-with-preview {
	position: relative;
	display: block;
	width: 100%;
	background: #000;
}

.video-with-preview .vwp-main {
	display: block;
	width: 100%;
	height: auto;
}

.video-with-preview .vwp-preview {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
	z-index: 2;
	pointer-events: none;        /* lets the click hit the player */
	opacity: 1;
	background: #000;
	transition: opacity 0.2s ease;
}

.video-with-preview .vwp-preview.is-hidden {
	opacity: 0;
	visibility: hidden;          /* fully hide once faded */
}

/* --- .embed — click-to-play facade for YouTube / Vimeo -------------------
   No third-party network request happens until the button is pressed. The
   button reserves the exact aspect ratio from --ar so swapping in the iframe
   causes zero layout shift. */
.embed {
	display: block;
	position: relative;
	width: 100%;
	aspect-ratio: var(--ar, 16 / 9);
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: #000;
	color: #fff;
	font: inherit;
	overflow: hidden;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
}

.embed > img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* A self-hosted film standing in the exact same slot as an .embed facade.
   tools/ingest_videos.py emits this when a YouTube/Vimeo embed is replaced by
   a local file, so the swap causes no layout change at all: same box, same
   aspect ratio, same poster frame. preload="none" keeps the page light — the
   poster is a still image until the visitor presses play. */
.embed-video {
	display: block;
	width: 100%;
	aspect-ratio: var(--ar, 16 / 9);
	height: auto;
	margin: 0;
	background: #000;
	object-fit: cover;
}

.embed-play {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	width: clamp(44px, 12%, 96px);   /* % of the button's width */
	aspect-ratio: 1;
	border-radius: 50%;
	background: rgba(0, 0, 0, 0.55);
	display: grid;
	place-items: center;
	transition: background-color 150ms ease, transform 150ms ease;
	pointer-events: none;
}

/* Play triangle, pure CSS. Sized as a percentage of the disc so it scales
   with it at every viewport; the 7% nudge optically centres it. */
.embed-play::before {
	content: "";
	display: block;
	width: 34%;
	aspect-ratio: 1 / 1.15;
	margin-left: 7%;
	background: currentColor;
	clip-path: polygon(0 0, 100% 50%, 0 100%);
}

.embed:hover .embed-play,
.embed:focus-visible .embed-play {
	background: var(--accent);
	transform: translate(-50%, -50%) scale(1.06);
}

.embed:hover > img { opacity: 0.9; }

/* If site.js injects the player inside the button/wrapper rather than
   replacing it, keep it perfectly in the reserved box. */
.embed iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}
.embed.is-playing > img,
.embed.is-playing .embed-play { display: none; }


/* ============================================================================
   08  Header + burger menu

   Ported from the "GIBLIN NAV CSS" block at the bottom of the original
   stylesheet: a white panel that slides down from the top, right-aligned
   links, the burger swapping to a close control while open, and the "×"
   suppressed entirely. (The original set those links in Instrument Serif;
   they now use --font-serif, which resolves to Alte Haas Grotesk.)

   Two behavioural upgrades over the original, both deliberate:
     1. The original toggled display:none/block, which meant the 200ms slide
        never actually ran. Here the overlay stays laid out and is toggled
        with visibility + pointer-events while the panel is clipped by the
        overlay's overflow, so the transition is real in both directions.
     2. The original swapped the burgers with `#giblin-menu:target ~ .gn-burger`,
        which relies on the burger following the overlay in the DOM. In this
        rebuild the burgers live inside <header>, before the overlay, so the
        swap uses :has() on the root — with body.menu-open (added by site.js)
        as the fallback for engines without :has().
   ========================================================================== */

/* The wordmark was in the page flow on the Cargo original and scrolled away;
   the rebuild pins it. A solid background is therefore required, otherwise
   the black wordmark and burger become unreadable the moment artwork scrolls
   underneath. The band is exactly --header-block tall, which is also the
   .page-content top inset, so nothing is ever hidden at rest. */
.site-header {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: calc(var(--gn-z) + 1);   /* stays above the menu panel */
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--page-pad);
	padding: var(--page-pad) var(--page-pad) var(--header-pad-b);
	background: var(--bg);
}

.site-header .title {
	white-space: nowrap;
	line-height: var(--wordmark-lh);
}

/* --- Burger ------------------------------------------------------------- */
.gn-burger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 auto;
	width: var(--gn-burger-w);
	height: var(--gn-burger-h);
	color: var(--ink);
	background: transparent;
	border: 0;
	padding: 0;
	line-height: 1;
	text-decoration: none;
	cursor: pointer;
}
.gn-burger svg {
	display: block;
	width: 22px;
	height: 16px;
}

/* Default: "open" showing, "close" hidden. */
.gn-burger--open  { display: inline-flex; }
.gn-burger--close { display: none; }

/* Menu open — via :target (no JS) or via body.menu-open (site.js). */
:root:has(.gn-overlay:target) .gn-burger--open,
body.menu-open .gn-burger--open { display: none; }

:root:has(.gn-overlay:target) .gn-burger--close,
body.menu-open .gn-burger--close { display: inline-flex; }

/* --- Overlay ------------------------------------------------------------
   Sits directly under the wordmark line and occupies the top band of the
   viewport. overflow:hidden clips the panel while it is parked above. */
.gn-overlay {
	position: fixed;
	left: 0;
	right: 0;
	top: var(--header-block);
	height: auto;
	max-height: calc(100dvh - var(--header-block));
	overflow-y: auto;
	z-index: var(--gn-z);
	overflow: hidden;
	visibility: hidden;
	pointer-events: none;
	/* delay hiding until the slide-up has finished */
	transition: visibility 0s linear var(--gn-dur);
}

.gn-overlay:target,
body.menu-open .gn-overlay {
	visibility: visible;
	pointer-events: auto;
	transition-delay: 0s;
}

/* Click-anywhere-to-close. position:fixed escapes the overlay's overflow
   clip (the overlay has no transform), so this covers the whole viewport
   while sitting underneath the panel in paint order. */
.gn-backdrop {
	position: fixed;
	inset: 0;
	background: transparent;
	display: block;
}

/* --- Panel --------------------------------------------------------------- */
.gn-panel {
	/* In normal flow (not absolute) so the overlay takes its height from the
	   content — seven links, any number of links — rather than a fixed vh that
	   cut Contact off when House Rules joined the menu. */
	position: relative;
	background: var(--bg);
	color: var(--ink);
	border: 0;
	box-shadow: none;
	overflow: auto;
	padding: 1rem var(--page-pad) clamp(48px, 5vh, 96px);
	transform: translateY(-100%);
	transition: transform var(--gn-dur) ease;
}

.gn-overlay:target .gn-panel,
body.menu-open .gn-panel { transform: translateY(0); }

/* The original's "×" is hidden; the burger is the only close control. */
.gn-close { display: none !important; }

/* --- Menu list ----------------------------------------------------------
   Right-aligned so the link edges line up with the burger above them
   (the panel's right padding equals the header's). */
.gn-list {
	list-style: none;
	margin: 0;
	padding: clamp(10px, 1.6vh, 18px) 0 0;
	display: grid;
	gap: clamp(8px, 1.2vh, 14px);
	justify-items: end;
}
.gn-list li {
	margin: 0;
	padding: 0;
	justify-self: end;
}
.gn-list a {
	display: block;
	text-align: right;
	text-decoration: none;
	color: var(--ink);
	font-family: var(--font-serif);
	font-style: normal;
	font-weight: 700;
	letter-spacing: 0;
	line-height: 1.2;
	/* scales with viewport HEIGHT so six items always fit the panel */
	font-size: clamp(20px, 2.6vh, 38px);
}
.gn-list a.active { color: var(--accent); }


/* ============================================================================
   09  Utilities + accessibility
   ========================================================================== */

.ta-left   { text-align: left; }
.ta-center { text-align: center; }
.ta-right  { text-align: right; }

/* Skip link: off-screen until it takes focus. */
.skip-link {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}
.skip-link:focus {
	position: fixed;
	top: 8px;
	left: 8px;
	z-index: calc(var(--gn-z) + 2);
	width: auto;
	height: auto;
	margin: 0;
	padding: 0.75rem 1rem;
	overflow: visible;
	clip: auto;
	clip-path: none;
	background: var(--bg);
	color: var(--ink);
	font-family: var(--font-body);
	font-size: 1.2rem;
	text-decoration: none;
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}

:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 2px;
}
/* Never suppress the ring without providing one. */
:focus:not(:focus-visible) { outline: none; }


/* ============================================================================
   10  Narrow viewports

   Two breakpoints, for two different reasons:

   (a) <= 1080px — the freeform canvas reflows. It is a hard 1000px design
       canvas, so as soon as the 2rem-inset content box drops below 1000px the
       absolutely-placed items would push a horizontal scrollbar onto the whole
       page. Rather than transform:scale() the canvas — which shrinks the
       artwork to postage stamps and leaves the baked-in --ff-h wrong — the
       canvas becomes a single-column flow. That is also what the source data
       asks for: the Cargo markup carries mobile-gutter="2rem" on the freeform
       galleries, i.e. the original site stacks them with a 2rem gap on narrow
       screens. Items keep their DOM order, which in these galleries is close
       to reading order.

   (b) <= 768px — Cargo's `.mobile` body class. Column units stack full width
       and the type scale switches to Cargo's mobile sizes (bodycopy 2.35rem,
       h1 3rem, .heading-3 / .heading-4 2.35rem).
   ========================================================================== */

@media (max-width: 1080px) {
	gallery-freeform {
		width: 100%;
		height: auto;                /* overrides the baked-in --ff-h */
		display: flex;
		flex-direction: column;
		gap: 2rem;                   /* Cargo's mobile-gutter */
	}
	.ff-item {
		position: static;
		left: auto;
		top: auto;
		width: 100%;
		z-index: auto;
	}
}

@media (max-width: 768px) {
	:root {
		/* Cargo's desktop --base-size collapses to ~3px on a phone, so the
		   mobile band gets its own width-driven base. Tuned so bodycopy
		   (2.35rem) lands at ~16-18px across 320-430px devices and does not
		   run away on a 768px tablet. */
		font-size: clamp(6.4px, 2.05vw, 8.6px);


	}

	/* Cargo `.mobile` type overrides */
	bodycopy   { font-size: calc(2.35rem * var(--font-scale, 1)); }
	h1         { font-size: calc(3rem    * var(--font-scale, 1)); }
	.heading-3 { font-size: calc(2.35rem * var(--font-scale, 1)); }
	.heading-4 { font-size: calc(2.35rem * var(--font-scale, 1)); }
	.button-11 { font-size: calc(2.35rem * var(--font-scale, 1)); }

	/* Stack the 12-column grid. mobile-stack="false" opts out, as in Cargo. */
	column-set { flex-wrap: wrap; }
	column-set:not([mobile-stack="false"]) > column-unit { width: 100%; }

	/* Empty spacer units contribute nothing once stacked. */
	column-set:not([mobile-stack="false"]) > column-unit:empty { display: none; }

	.gn-list a { font-size: clamp(16px, 2.2vh, 24px); }
	.gn-panel  { padding-top: 0.5rem; }
}


/* ============================================================================
   11  Reduced motion
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	.gn-panel,
	.gn-overlay,
	.video-with-preview .vwp-preview,
	.embed-play {
		transition: none !important;
		animation: none !important;
	}

	html { scroll-behavior: auto; }

	/* Decorative autoplay: site.js pauses the looping videos and sets this
	   attribute; the rule below is the CSS half of that contract, keeping the
	   still frame visible rather than blanking the layout. */
	[data-autoplay-paused] { opacity: 1; }
}
