/* ---------- Design tokens ---------- */
:root {
	--color-text: #1a1b1f;
	--color-bg: #ffffff;
	--color-muted: #6b6b6f;
	--font-body: 'Space Grotesk', sans-serif;
	--font-display: 'Space Grotesk', sans-serif;
	--container-max: 1140px;
	--gutter: 24px;
}

/* ---------- Blur-in media ----------
   All images/video start blurred; each one sharpens independently the
   moment it finishes loading, so images near the top of the page (which
   the browser fetches first) don't wait on slower ones further down
   (see blur-media.js). Scoped under .nadpan-js-blur-media (set by an
   inline script at the very top of <head>, before anything below it
   paints) rather than blurring unconditionally, so a page never gets
   stuck permanently blurred if JS fails to load. Scoped to
   .site-main so it only touches real page content, not the wp-admin
   toolbar. */
.nadpan-js-blur-media .site-main img,
.nadpan-js-blur-media .site-main video,
.nadpan-js-blur-media .play-grid__item--video iframe {
	filter: blur(16px);
	transition: filter 0.7s ease;
}
.nadpan-js-blur-media .site-main img.is-crisp,
.nadpan-js-blur-media .site-main video.is-crisp,
.nadpan-js-blur-media .play-grid__item--video iframe.is-crisp {
	filter: blur(0);
}

/* ---------- Page transition (Barba + GSAP overlapping parallax) ----------
   The outgoing page parallax-shifts up while a dark wash fades in over
   it; the incoming page then slides up from below on top of that wash.
   Positioned outside the Barba container so it persists across
   transitions. Sits above page content but below .site-nav (z-index
   100), which stays fixed and untouched throughout. */
.transition {
	z-index: 99;
	pointer-events: none;
	position: fixed;
	inset: 0;
	overflow: clip;
}
.transition__dark {
	opacity: 0;
	background-color: #000;
	width: 100%;
	height: 100%;
	position: absolute;
	top: 0;
	left: 0;
}

/* ---------- Custom cursor (reference: julienpianetti.com) ----------
   A small circle that follows the pointer (with a bit of trailing delay
   -- see custom-cursor.js), on top of the native cursor rather than
   replacing it. mix-blend-mode: difference keeps it visible over
   anything, light or dark. Only grows into a bigger circle with a text
   label over homepage grid thumbnails -- no other hover target on the
   site gets the enlarged variant. */
.custom-cursor {
	position: fixed;
	top: 0;
	left: 0;
	z-index: 9999;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: #fff;
	mix-blend-mode: difference;
	pointer-events: none;
	opacity: 0;
	transform: translate3d(-50%, -50%, 0);
	transition: width 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), height 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), opacity 0.3s ease;
}
.nadpan-js-custom-cursor .custom-cursor {
	opacity: 1;
}
.custom-cursor--active {
	width: 110px;
	height: 110px;
}
.custom-cursor__text {
	font-family: var(--font-display);
	font-size: 13px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.5px;
	color: #000;
	opacity: 0;
	transition: opacity 0.3s ease;
}
.custom-cursor--active .custom-cursor__text {
	opacity: 1;
}

/* ---------- Reset ---------- */
* { box-sizing: border-box; }
html { -webkit-font-smoothing: antialiased; }
body {
	margin: 0;
	font-family: var(--font-body);
	font-size: 16px;
	line-height: 1.75;
	color: var(--color-text);
	background: var(--color-bg);
}
/* The outgoing/incoming pages are each individually translated
   (position: fixed + GSAP "y") during the parallax transition, which
   promotes them to their own compositing layer -- without their own
   solid background they're transparent, letting whatever's briefly
   behind them (or nothing) show through the gap the slide opens up.
   Give every page container its own solid backing instead of relying
   on body's. */
[data-barba="container"] {
	background: var(--color-bg);
}
/* Same gradient as .about-shader-bg's own CSS placeholder (see that rule
   further down), not transparent -- this used to be transparent, so the
   canvas (which lives outside this container, see header.php) could show
   through it during a transition. That turned out to be fundamentally
   fragile: the enter animation gives this container position: fixed + a
   z-index above the canvas's own for the ~1.2s slide, and getting the
   canvas to reliably out-stack this container (and whatever page is
   still leaving alongside it) through every phase of that animation
   -- entering, leaving, re-entering -- kept surfacing new edge cases
   (transparent flashes, wrong z-index ordering, the shader failing to
   reappear at all) no matter how carefully the timing was split across
   Barba's hooks. Giving this container the SAME gradient as its own
   background instead sidesteps the whole problem: it's self-sufficient
   during a transition (no dependency on the canvas behind it at all),
   and looks identical to the canvas's own placeholder, so when the real
   canvas actually takes over -- see initAfterEnterFunctions in
   page-transitions.js, which now waits until the transition has fully
   settled -- the handoff is invisible. */
[data-barba="container"][data-page-name="About"] {
	/* TEMPORARY: flat var(--color-bg) (matching the homepage/every other
	   page's background) instead of the gradient below, for a side-by-side
	   comparison. Delete this line (and un-comment the gradient) to
	   restore it. */
	background: var(--color-bg);
	/* background: radial-gradient(120% 120% at 22% 12%, #f2f2ff 0%, #ffffff 55%, #f4f4f4 100%); */
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4 { font-family: var(--font-display); font-weight: 400; margin: 0; }
p { margin: 0 0 1em; }

.container {
	max-width: var(--container-max);
	margin: 0 auto;
	padding: 0 var(--gutter);
}

.screen-reader-text {
	position: absolute;
	width: 1px; height: 1px;
	overflow: hidden;
	clip: rect(0,0,0,0);
}

/* ---------- Header / nav ---------- */
.site-nav {
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	z-index: 100;
	background: transparent;
	/* mix-blend-mode has to live here, on the element that establishes
	   the stacking context (position: fixed already does that; isolation
	   makes it explicit), not on the individual logo/link descendants --
	   a descendant's mix-blend-mode can only blend against other content
	   painted within its own ancestor's stacking context, which for those
	   elements is just .site-nav's own (transparent) background, not the
	   real page behind it. Putting it here instead treats the whole nav's
	   rendered output (all its children's actual white pixels, wherever
	   they are, ignoring the transparent gaps between them) as one
	   flattened layer and blends THAT against the page -- confirmed by
	   testing both ways: mix-blend-mode on the children alone rendered
	   the nav completely invisible over the white page. */
	isolation: isolate;
	mix-blend-mode: difference;
	/* Uniform padding: the logo is a graphic (no text leading to
	   compensate for like the old text logo needed), so the top gap
	   already matches the left gap with the same value on all sides. */
	padding: 48px;
}
.site-nav__inner {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr;
	/* Top-aligned rather than centered: the logo mark is taller than the
	   nav text (2x its font-size), so centering would hang it below the
	   text's midline instead of lining up both at the top. */
	align-items: start;
}
.site-nav__logo {
	grid-column: 1;
	justify-self: start;
}
.site-nav__logo-mark {
	display: block;
	width: auto;
	/* fill="currentColor" in the SVG picks this up. Plain white -- the
	   actual inversion comes from mix-blend-mode on .site-nav itself (see
	   above), not from anything set here. This replaces the old JS-driven
	   luminance-sampling class swap (nav-invert.js, now removed): a white
	   nav blended with difference reads as black over a white backdrop,
	   white over black, and auto-inverts over any other color/photo too,
	   without needing to sample pixels or special-case the Play page's
	   solid black background. */
	color: #fff;
	/* Twice the nav link text's font-size (24px); the max-width: 900px /
	   360px tiers below rescale this to match as the link text resizes. */
	height: 60px;
}
/* .site-nav__links wraps the two nav items (About, Play); "contents"
   promotes them to direct grid items of .site-nav__inner so each can be
   positioned independently -- About centered on the screen, Play at
   the right margin -- regardless of the logo's width. */
.site-nav__links { display: contents; }
.site-nav__links > *:nth-child(1) { grid-column: 2; justify-self: center; }
.site-nav__links > *:nth-child(2) { grid-column: 3; justify-self: end; }
.site-nav__links li { list-style: none; margin: 0; padding: 0; }
.site-nav__link,
.site-nav__links a {
	font-family: var(--font-display);
	font-size: 24px;
	font-weight: 400;
	letter-spacing: -0.1px;
	color: #fff;
}

/* Underline-on-hover, shared by nav links and footer links: a hidden
   line grows in from the left over the text on hover. */
.site-nav__link,
.site-nav__links a,
.site-footer__item {
	position: relative;
}
.site-nav__link::before,
.site-nav__links a::before,
.site-footer__item::before {
	content: "";
	position: absolute;
	bottom: -0.0625em;
	left: 0;
	width: 100%;
	height: 0.0625em;
	background-color: currentColor;
	transition: transform 0.735s cubic-bezier(0.625, 0.05, 0, 1);
	transform-origin: right;
	transform: scaleX(0) rotate(0.001deg);
}
@media (hover: hover) and (pointer: fine) {
	.site-nav__link:hover::before,
	.site-nav__links a:hover::before,
	.site-footer__item:hover::before {
		transform-origin: left;
		transform: scaleX(1) rotate(0.001deg);
	}
}

/* ---------- Site main ----------
   .site-nav is now position: fixed (taken out of flow) to match the
   reference's persistent, transparent nav; this top padding reserves
   the space it would otherwise occupy so page content isn't hidden
   underneath it. */
.site-main {
	/* Clears the logo (60px tall + 20px top padding, with a small buffer
	   below it) -- keep in sync with .site-nav__logo-mark's height and
	   .case-header-image's matching negative margin below. */
	padding-top: 89px;
}
@media (max-width: 900px) {
	/* Logo shrunk by 42.5px on mobile (85px -> 42.5px), so this clears by
	   42.5px less too, keeping the same buffer below it. */
	.site-main { padding-top: 48px; }
}

/* ---------- Hero ---------- */
.hero {
	padding: 134px 0;
}
.hero__heading {
	font-size: 60px;
	line-height: 72px;
	font-weight: 400;
}
/* Hidden the instant the document starts parsing (see header.php's inline
   pre-paint script), not just once text-reveal.js gets around to masking
   it -- text-reveal.js is a footer script, so without this the raw,
   unsplit heading text paints fully visible for a moment before that
   script even runs, then vanishes into its mask, then reveals -- a
   flash-then-hide-then-reveal instead of a clean hide-then-reveal.
   Un-hidden explicitly once the page is actually ready to show it: from
   runPageOnceAnimation on first load, and from initBeforeEnterFunctions on
   any later Barba navigation back to the homepage (see
   page-transitions.js) -- scoped to the JS-added class so a visitor with
   JS disabled still sees the heading normally instead of it staying
   hidden forever. */
.nadpan-js-hero-reveal .hero__heading {
	opacity: 0;
}

/* ---------- Works grid ----------
   A 6-column grid; each card spans 2, 3, 4 or 6 columns (curated per
   case study via the "Homepage Grid Width" field), all sharing one
   fixed row height, images cropped to fill with object-fit: cover --
   matching the original site's manually curated masonry grid. */
.works { padding: 25px 0 100px; }
.works-container {
	margin: 0 auto;
	padding: 0 48px;
}
.works-grid {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 40px 20px;
}
.work-card--span-2 { grid-column: span 6; }
.work-card--span-3 { grid-column: span 6; }
.work-card--span-4 { grid-column: span 6; }
.work-card--span-6 { grid-column: span 6; }
@media (min-width: 700px) {
	.work-card--span-2 { grid-column: span 2; }
	.work-card--span-3 { grid-column: span 3; }
	.work-card--span-4 { grid-column: span 4; }
	.work-card--span-6 { grid-column: span 6; }
}
.work-card__image {
	display: block;
	width: 100%;
	height: min(32vw, 460px);
	background-color: #f2f2f2;
	margin-bottom: 14px;
	overflow: hidden;
}
@media (max-width: 699px) {
	.work-card__image { height: 80vw; }
	.work-card__title,
	.work-card__subtitle { font-size: 18px; line-height: 21.6px; }
}
.work-card__image img,
.work-card__image video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
/* Same specificity as .nadpan-js-blur-media .site-main img's own
   transition (2 classes + tag) and later in the cascade, so this wins
   and replaces it -- shorthand "transition" fully overrides rather than
   merging, so the blur-in filter transition has to be repeated here
   alongside transform, or hovering a still-blurring image would snap
   its filter instantly instead of continuing to fade smoothly. */
.works-grid .work-card__image img,
.works-grid .work-card__image video {
	/* No grey/hidden state and no separate entrance transition any more --
	   grid items just render straight into their blurred state (the
	   default .nadpan-js-blur-media look, same as everywhere else on the
	   site) the moment they're in the DOM. The "reveal" is entirely the
	   blur-to-crisp handoff below, which grid-reveal.js now holds off on
	   until the item has scrolled into view (see that file).
	   Same easing curve as dnco.com's grid hover zoom for the transform,
	   slightly slower (0.6s vs their 0.3s) for an even smoother, less
	   hurried feel. */
	transition: filter 0.6s ease, transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.work-card__image:hover img,
.work-card__image:hover video {
	transform: scale(1.05);
}
.work-card__meta { text-align: left; }
.work-card__title {
	display: block;
	font-family: var(--font-display);
	font-size: 24px;
	font-weight: 400;
	line-height: 28.8px;
	letter-spacing: -0.1px;
	color: var(--color-text);
}
.work-card__subtitle {
	font-family: var(--font-display);
	font-size: 24px;
	font-weight: 400;
	line-height: 28.8px;
	letter-spacing: -0.1px;
	color: #929292;
	margin-top: 0;
}

/* ---------- Case study: header image ---------- */
.case-header-image {
	line-height: 0;
	/* Cancels out .site-main's padding-top (reserved for the fixed nav)
	   just for this element, pulling it up to the true top of the page
	   so the nav floats directly over the header image instead of a
	   white gap above it. */
	margin-top: -89px;
}
@media (max-width: 900px) {
	.case-header-image { margin-top: -48px; }
}
.case-header-image img {
	width: 100%;
	height: 85vh;
	object-fit: cover;
}
@media (max-width: 900px) {
	.case-header-image img { height: auto; aspect-ratio: 1 / 1; }
}

/* ---------- Case study: overview grid ----------
   Title/subtitle sit in their own full-width row above; the overview
   and services columns share the row below, split evenly. */
.case-overview { padding: 50px 0 60px; }
.case-overview__grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	/* Row gap is the space between the discipline line (end of the title
	   row) and "Project Overview" (start of the row below). */
	gap: 50px 50px;
}
.case-overview__title { grid-column: 1 / 4; }
.case-overview__body { grid-column: 1 / 3; }
.case-overview__services { grid-column: 3 / 5; }
.heading-jumbo {
	font-size: 70px;
	line-height: 84px;
	font-weight: 400;
	margin-bottom: 0;
}
/* Discipline line under the project name (e.g. "Branding"). */
.case-overview__title .paragraph-light {
	color: var(--color-muted); /* rgb(107, 107, 111) */
}
.paragraph-light {
	font-family: var(--font-display);
	font-size: 18px;
	line-height: 1.4;
}
.case-overview__label {
	font-family: var(--font-display);
	font-size: 14px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.5px;
	color: var(--color-text);
	margin-bottom: 12px;
}
.case-overview__body .paragraph-light { margin-bottom: 16px; }
.case-overview__client { color: var(--color-muted); }
.case-overview__services {
	display: flex;
	flex-direction: column;
}
.case-overview__service {
	font-size: 18px;
	line-height: 28px;
	padding-bottom: 0;
	margin-bottom: 5px;
	color: var(--color-muted); /* rgb(107, 107, 111) */
	border-bottom: 1px solid var(--color-muted); /* rgb(107, 107, 111) */
}

/* ---------- Case study: body content blocks (Gutenberg output) ----------
   .case-body__inner spans the full viewport width (no container). Each
   block is either full-bleed (edge to edge) or "contained" at 65% width,
   centered -- matching the original site's two treatments exactly. */
.case-body { padding-bottom: 60px; }
.case-body__inner > * {
	margin: 60px auto 0;
	width: 65%;
}
.case-body__inner > *:first-child { margin-top: 0; }
.case-body__inner > .alignfull { width: 100%; }

.case-body__inner img { width: 100%; height: auto; display: block; }
.case-body__inner video { width: 100%; height: auto; display: block; }
.case-body__inner .wp-block-embed iframe {
	width: 100%;
	aspect-ratio: 16 / 9;
	height: auto;
}

/* Vimeo "background" embeds (autoplay+loop+muted "cover" videos) scale
   their video to fill the iframe's own aspect ratio; an iframe sized
   to match our 16:9 box exactly can end up letterboxed and left-
   anchored instead of centered when the source video isn't 16:9.
   Oversizing the iframe relative to the box and centering it with a
   transform guarantees full, evenly-cropped coverage instead -- the
   iframe crops the same way a CSS background-image: cover would. */
.wp-block-embed--bg-video { container-type: size; }
.wp-block-embed--bg-video iframe {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 177.78cqh;
	height: 100cqh;
	min-width: 100cqw;
	min-height: 56.25cqw;
	aspect-ratio: auto;
	transform: translate(-50%, -50%);
}

/* Two square items (image/video) side by side, no gap -- 90vw wide.
   Native core/columns block: each item is a real, editable wp:image or
   wp:video block inside a .wp-block-column -- no extra wrapper div. */
.case-body__inner > .case-square-pair {
	width: 90vw;
	display: flex;
	gap: 0;
}
.case-square-pair > .wp-block-column {
	flex: 1 1 50%;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	margin: 0;
}
.case-square-pair .wp-block-image,
.case-square-pair .wp-block-video,
.case-square-pair figure {
	margin: 0;
	width: 100%;
	height: 100%;
}
.case-square-pair img,
.case-square-pair video {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}

/* 4-column grid of square photos -- 80vw wide.
   Native core/gallery block: each photo is a real, editable wp:image
   block -- addable/removable/reorderable via the Gallery UI. */
.case-body__inner > .case-photo-grid {
	width: 80vw;
	gap: 16px !important;
}
/* WordPress's own ".columns-4" width rule only targets the legacy
   gallery markup (":not(.has-nested-images)"); the modern
   "has-nested-images" format we use relies on a per-instance inline
   <style> the JS editor normally generates, which hand-authored block
   markup doesn't have -- so the 4-per-row width is set here instead. */
.case-photo-grid.wp-block-gallery > .wp-block-image {
	flex: 0 0 calc(25% - 12px);
	width: calc(25% - 12px);
	margin: 0;
}
.case-photo-grid img {
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: cover;
	display: block;
}
@media (max-width: 1024px) {
	/* "Contained" (65%-width) blocks get cramped on a phone- or
	   tablet-sized viewport (1024px covers iPad landscape too) where
	   there's no room left for the desktop's full-bleed vs. contained
	   distinction -- go full width instead. */
	.case-body__inner > * { width: 100%; }
	.case-body__inner > .case-square-pair,
	.case-body__inner > .case-photo-grid {
		width: 100%;
	}
	.case-square-pair { flex-wrap: wrap; }
	.case-photo-grid.wp-block-gallery > .wp-block-image {
		flex-basis: calc(50% - 8px);
		width: calc(50% - 8px);
	}
}
@media (max-width: 600px) {
	/* One column per row at true phone widths, for both media grids. */
	.case-square-pair > .wp-block-column { flex: 1 1 100%; }
	.case-photo-grid.wp-block-gallery > .wp-block-image {
		flex-basis: 100%;
		width: 100%;
	}
}

/* Scroll-reveal: JS adds .reveal-on-scroll, then .is-visible once in view */
.reveal-on-scroll {
	opacity: 0;
	transform: translateY(28px);
	transition: opacity 0.7s ease, transform 0.7s ease;
}
.reveal-on-scroll.is-visible {
	opacity: 1;
	transform: translateY(0);
}

/* ---------- Case study: next project ---------- */
.case-next {
	padding: 80px 0 100px;
	text-align: center;
	margin-top: 60px;
	background: var(--color-bg);
	color: var(--color-text);
}
.case-next__label {
	font-family: var(--font-display);
	font-size: 12px;
	font-weight: 700;
	letter-spacing: 1px;
	text-transform: uppercase;
	color: var(--color-muted);
	margin-bottom: 16px;
	display: block;
}
.case-next__title {
	font-size: 56px;
	line-height: 1.15;
	display: inline-block;
	color: var(--color-text);
}
.case-next__subtitle {
	font-family: var(--font-display);
	font-size: 18px;
	color: var(--color-muted);
	margin-top: 4px;
}
@media (max-width: 900px) {
	.case-next__title { font-size: 36px; }
}

/* ---------- Footer ----------
   Mirrors the nav: same text style, and the same left / centre / right
   placement (LinkedIn under the logo's position, email centred like
   "About", phone at the right margin like "Play"). */
.site-footer { padding: 40px 0 48px; }
.site-footer__inner {
	display: grid;
	grid-template-columns: 1fr 1fr 1fr;
	align-items: center;
	padding: 0 48px;
}
.site-footer__item {
	font-family: var(--font-display);
	font-size: 24px;
	font-weight: 400;
	letter-spacing: -0.1px;
	color: var(--color-text);
}
.site-footer__item:nth-child(1) { grid-column: 1; justify-self: start; }
.site-footer__item:nth-child(2) { grid-column: 2; justify-self: center; }
.site-footer__item:nth-child(3) { grid-column: 3; justify-self: end; }
@media (max-width: 900px) {
	.site-footer__inner {
		grid-template-columns: 1fr;
		gap: 16px;
	}
	.site-footer__item:nth-child(1),
	.site-footer__item:nth-child(2),
	.site-footer__item:nth-child(3) {
		grid-column: 1;
		justify-self: center;
	}
}


/* ---------- About page ----------
   Bolder, bigger-type editorial style (reference: mouthwash.studio/studio).
   The "Minted" shader (see about-shader-bg.js) is a fixed, full-viewport
   canvas behind the whole page -- content (.about-cv) and .site-footer
   both need position: relative + a higher z-index to stay above it, since
   an unpositioned element always loses to a positioned one in paint order
   regardless of DOM order or actual overlap (learned this the hard way:
   an earlier position: sticky + overflow: clip version of this rule
   fought the same issue by clipping the canvas's paint area instead of
   fixing the stacking order -- that only worked because it kept the
   canvas geometrically out of .site-footer's screen area entirely, which
   stopped being an option once the shader needed to show through the
   footer too). .site-nav doesn't need this treatment: it's already
   z-index: 100 for unrelated reasons (staying above the Barba page
   transition overlay), which also puts it above this. */
/* Hidden by default (opacity, not display -- see below for why) since
   the canvas element itself now lives in header.php and is present on
   every page (see the comment above it there), not just swapped in/out
   with the About page's content. about-shader-bg.js adds/removes
   .is-active in lockstep with actually starting/destroying the shader --
   it's only ever toggled once a transition has fully settled though (see
   initAfterEnterFunctions/beforeLeave in page-transitions.js), never
   while GSAP still has the About container's position/z-index actively
   mid-manipulation, so this canvas never has to out-stack anything
   temporarily positioned above it the way it once needed to. */
.about-shader-bg {
	position: fixed;
	inset: 0;
	/* canvas is a replaced element -- inset: 0 alone positions it at the
	   top-left corner without stretching it (replaced elements fall back
	   to their intrinsic size, 300x150, when width/height aren't set in
	   CSS, even with all four inset sides specified). Explicit
	   width/height are required to actually fill the viewport. */
	width: 100vw;
	/* 100dvh alone is unreliable here -- Chromium doesn't always
	   recompute it on a plain window resize (confirmed by measuring
	   getBoundingClientRect() before/after resizing: the canvas stayed
	   frozen at the pre-resize height while window.innerHeight had
	   already updated), leaving a gap of plain white body background
	   visible below the canvas. --about-shader-vh (set in
	   about-shader-bg.js from window.innerHeight, kept live via a
	   resize/visualViewport listener) is a JS-driven fallback that
	   doesn't have that staleness problem; dvh only still matters for
	   the very first paint before that script runs. */
	height: 100dvh;
	height: var( --about-shader-vh, 100dvh );
	z-index: 0;
	pointer-events: none;
	/* display: none/block can't be transitioned, so visibility is opacity
	   -- not display -- letting .is-active fade in slowly instead of
	   popping in instantly. Still fully inert on every other page
	   regardless (pointer-events: none above, plus there's nothing to
	   see: no bitmap has ever been drawn into it there). */
	opacity: 0;
	/* Static stand-in for the shader (roughly its own palette: paper
	   white, the LiquidMetal lightColor, a faint grey) -- a <canvas> with
	   nothing drawn to it yet is fully transparent, so this shows through
	   from the moment .is-active fades the canvas in until the real
	   WebGPU frame (which is fully opaque, starting from its own
	   SolidColor base layer) paints over it and hides it completely.
	   Identical to [data-page-name="About"]'s own background (above) by
	   design, so the handoff from "container's static background" to
	   "canvas fading in over it" is invisible. Covers both the initial
	   load and every Barba re-entry -- see the canvas.width reset in
	   about-shader-bg.js's destroy(), which is what makes this show again
	   on reload rather than a stale last frame. */
	background: radial-gradient(120% 120% at 22% 12%, #f2f2ff 0%, #ffffff 55%, #f4f4f4 100%);
	/* Same blur-in treatment as .nadpan-js-blur-media's images/video (see
	   blur-media.js) -- the flat placeholder gradient above has no fine
	   detail of its own, so swapping it for the real shader's textured
	   frame the instant createShader() resolves still reads as an abrupt
	   pop. Starting blurred and sharpening on that same signal (see
	   about-shader-bg.js's .is-crisp toggle) turns it into a gradual
	   reveal instead, whichever of the two is actually behind the blur at
	   the time. opacity gets its own, slower duration -- the fade-in
	   itself should read as deliberate, not rushed, independent of how
	   quickly the blur-to-crisp sharpen happens once loaded. */
	filter: blur(24px);
	transition: opacity 1.4s ease, filter 0.7s ease;
}
.about-shader-bg.is-active {
	opacity: 1;
}
.about-shader-bg.is-crisp {
	filter: blur(0);
}
.about-cv {
	padding: 120px 0 140px;
	position: relative;
	z-index: 1;
}
/* Scoped via the Barba container's data-page-name (set server-side by
   nadpan_page_name(), and refreshed on every Barba swap since the whole
   container element is replaced) rather than a bare .site-footer rule,
   so the footer only gets this stacking treatment -- and only pays for
   the shader showing through it -- on the About page. */
[data-page-name="About"] .site-footer {
	position: relative;
	z-index: 1;
}
@media (max-width: 900px) {
	.about-cv { padding: 100px 0 100px; text-align: center; }
}
.about-cv .container {
	/* Overrides .container's shared 1140px max-width/24px gutter -- the
	   About page uses the site's other 48px margin (nav/works-grid/
	   footer), and its text should run much wider than the shared
	   .container allows. */
	max-width: none;
	padding: 0 48px;
	display: flex;
	flex-direction: column;
	gap: 90px;
}
/* Full-width, margin-to-margin intro (reference: mouthwash.studio/studio,
   which uses this same continuous big-type paragraph treatment) -- tight
   line-height and negative tracking on large type, but kept in the site's
   own black-on-white colour rather than copying their dark theme. */
/* Right margin doubled (48px container padding + 48px here = 96px) for
   this big intro copy specifically -- the Resume module below keeps the
   normal 48px on both sides. */
.about-cv__intro {
	padding-right: 48px;
}
@media (max-width: 900px) {
	.about-cv__intro { padding-right: 0; }
}
.about-cv__intro p {
	font-family: var(--font-display);
	font-size: 58px;
	font-weight: 500;
	line-height: 1.05;
	letter-spacing: -0.02em;
	margin: 0 0 28px;
}
.about-cv__intro p:last-child {
	font-weight: 700;
	margin-top: 12px;
}
@media (max-width: 900px) {
	.about-cv__intro p { font-size: 32px; }
}
/* Resume module -- mimics mouthwash's "Information" pattern: a caption on
   the left, small list columns filling the rest of the row. */
.about-cv__resume-module {
	display: flex;
	align-items: flex-start;
	/* Matches the column gap measured on mouthwash.studio/studio's
	   "Information" module (90px, not the 48px used elsewhere on this
	   page) -- applied uniformly now that "Resume" and the two columns
	   are flat siblings (see the flex-basis: 0 comment below for why). */
	gap: 90px;
	/* Spans the full row (margin to margin), not just the width of the
	   "Resume" label -- matches mouthwash.studio/studio's "Information"
	   heading, which sits under the same kind of full-width rule. Living
	   here (not on .about-cv__resume-label) also means every child sits
	   the same distance below it, so "Resume" and the Experience/
	   Education column headings all start at the same top edge. */
	border-top: 1px solid var(--color-text);
	padding-top: 24px;
}
/* Gutenberg wraps every group block's children in its own
   .wp-block-group__inner-container div, so the flex row above would
   otherwise only ever see ONE child (that wrapper) instead of the actual
   label/columns -- display: contents removes it from the box model so
   its children become the real flex items. */
.about-cv__resume-module > .wp-block-group__inner-container {
	display: contents;
}
.about-cv__resume-label {
	/* flex-basis: 0 makes this a pure ratio split against .resume-column's
	   flex: 1 below. "Resume", Experience and Education are flat siblings
	   here (not Resume vs. a nested two-column group) specifically so
	   this ratio is exact -- nesting a group of two flex:1 columns inside
	   a flex:2 wrapper only approximates 3x once gaps at both levels are
	   factored in. */
	flex: 3 1 0;
	font-family: var(--font-display);
	font-size: 62px;
	font-weight: 400;
	letter-spacing: -0.02em;
	/* Without this, the inherited body line-height (1.75) scales up with
	   the font-size and pads the glyphs well below the shared top edge --
	   the box tops match but the "R" visibly sits lower than "EXPERIENCE"
	   above it. Tightening this pulls the glyphs back up to the same
	   optical line as the small labels beside it. */
	line-height: 1;
	margin: 0;
}
@media (max-width: 900px) {
	.about-cv__resume-label { font-size: 40px; }
}
.resume-column { flex: 1 1 0; }
@media (max-width: 900px) {
	.about-cv__resume-module {
		flex-direction: column;
		gap: 32px;
		/* Default (flex-start, same as desktop) shrinks each child --
		   "Resume", then each .resume-column -- to its own content width
		   and left-aligns it, once column direction makes align-items the
		   horizontal axis. .about-cv's own text-align: center (mobile-
		   only, see above) already centers the text inside those boxes,
		   but a narrow shrink-wrapped box makes that centering barely
		   visible -- stretch makes every child the full row width first,
		   so the already-centered text actually centers across the whole
		   column instead of within a sliver of it. */
		align-items: stretch;
	}
	/* flex-basis is a height in column direction, not a width -- without
	   resetting it here the label's flex-basis: 0 would collapse it to
	   zero height instead. */
	.about-cv__resume-label { flex-basis: auto; }
}
.resume-column .case-overview__label { margin-bottom: 18px; }
.resume-company { margin-bottom: 18px; }
.resume-company:last-child { margin-bottom: 18px; }
.resume-company-name {
	font-family: var(--font-display);
	font-size: 16px;
	font-weight: 700;
	margin: 0;
}
.resume-company-note {
	font-family: var(--font-display);
	font-size: 16px;
	font-weight: 400;
	color: var(--color-text);
	margin: 0;
}
.resume-role {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 16px;
	/* Bottom padding matches the gap used everywhere else a divider line
	   meets the next bit of text (.resume-company's margin-bottom below,
	   and .case-overview__label's above) -- text-to-line and line-to-text
	   are now the same 18px throughout. */
	padding: 4px 0 18px;
	border-bottom: 1px solid var(--color-muted);
}
.resume-role p { margin: 0; }
.resume-role-title {
	font-family: var(--font-display);
	font-size: 16px;
}
.resume-role-date {
	font-family: var(--font-display);
	font-size: 16px;
	color: var(--color-text);
	white-space: nowrap;
}
@media (max-width: 900px) {
	/* Desktop keeps title/date pinned to opposite edges (a normal resume
	   line) -- on mobile, now that the row is full-width, that reads as
	   text-align: center's job half-undone by a flex layout still pushing
	   the two ends apart. Centering the pairing as a group here instead
	   matches the rest of the section. Placed after the base .resume-role
	   rule (not grouped with the other mobile overrides above, which sit
	   before it) so this actually wins the cascade -- same specificity,
	   later source order. */
	.resume-role { justify-content: center; }
}
.about-cv .wp-block-columns { gap: 50px; }
.about-cv h3 {
	font-size: 26px;
	margin-bottom: 24px;
}
.about-cv .cv-entry { margin-bottom: 20px; }
.about-cv .cv-entry p { margin: 0; }
.about-cv .cv-entry .cv-role { font-weight: 700; }
.about-cv .cv-entry .cv-desc,
.about-cv .cv-entry .cv-role {
	font-family: var(--font-display);
	font-size: 16px;
	line-height: 1.4;
}
.about-cv .cv-entry .cv-date {
	font-family: var(--font-display);
	font-size: 14px;
	color: var(--color-muted);
	margin-top: 4px;
}

/* ---------- Play page ----------
   Black background, unlike the rest of the (white) site -- the footer
   text/underline is manually flipped to white below (currentColor picks
   up the underline automatically). The nav needs no special-casing here:
   its mix-blend-mode: difference (see .site-nav__logo-mark/.site-nav__link
   above) already reads correctly as white-on-black on this page, the
   same way it auto-inverts everywhere else.

   The container/footer rules are scoped via [data-page-name="Play"]
   (set server-side by nadpan_page_name(), refreshed on every Barba swap
   since the whole container element is replaced) rather than
   body.page-template-page-play alone -- body's own classes are only set
   on a real page load and never update on a Barba SPA navigation, so
   relying on them left the background white whenever Play was reached
   by clicking the nav link instead of a direct/hard load. Kept alongside
   (not instead of) the body rule, which still covers body's own
   background correctly on a real page load. */
body.page-template-page-play {
	background: #000;
}
[data-barba="container"][data-page-name="Play"] {
	background: #000;
}
[data-barba="container"][data-page-name="Play"] .site-footer__item {
	color: #fff;
}
.play-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 23px 16px;
	padding: 40px 0 80px;
}
.play-grid__item {
	position: relative;
	width: 100%;
	margin: 0;
	aspect-ratio: 1 / 1;
	overflow: hidden;
	background: #1a1a1a;
}
/* width/height default to 100% (filling the box, same as before) until
   play-grid-video-fit.js sets explicit pixel dimensions once it knows the
   video's actual aspect ratio -- centered via the transform below so
   growing past 100% (to cover the box instead of Vimeo's own player
   letterboxing within it -- see that file for the full explanation)
   crops evenly from both sides rather than from one edge. */
.play-grid__item--video iframe {
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	width: 100%;
	height: 100%;
	border: 0;
}
/* Sits behind the iframe (inserted before it in the DOM -- see
   play-grid-video-fit.js) filling the gap before Vimeo's player has
   buffered and painted an actual frame of its own. inset: -16px (matching
   the blur radius) oversizes it slightly beyond the item's own
   overflow: hidden bounds, so the lighter/transparent edge filter: blur()
   otherwise leaves along a blurred element's border falls outside the
   visible crop instead of showing as a faint rim. Once Vimeo paints a
   real frame, that opaque content simply covers this -- no explicit
   "loaded" signal to time it against, which a cross-origin iframe can't
   give us anyway. */
.play-grid__item-poster {
	position: absolute;
	inset: -16px;
	background-size: cover;
	background-position: center;
	filter: blur(16px);
}
/* Transparent hit-testable layer on top of the iframe. Without this, the
   pointer is physically over a cross-document <iframe> once it's
   anywhere inside this box, and mousemove events happening inside a
   cross-origin iframe's own document never bubble out to this page's
   document -- the custom cursor (.custom-cursor, tracks the pointer via
   a document-level mousemove listener, see custom-cursor.js) would
   visibly freeze in place every time it crossed over a video instead of
   continuing to follow the pointer smoothly. These background embeds
   have no visible controls to click through to, so permanently
   capturing pointer events here has no downside. */
.play-grid__item--video::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 1;
}
.play-grid__item--image img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
@media (max-width: 900px) {
	.play-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 600px) {
	.play-grid { grid-template-columns: 1fr; padding: 24px 0 60px; }
}

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
	.hero__heading {
		font-size: 37px;
		line-height: 1.4;
		text-align: center;
	}
	.hero__heading .hero__break {
		display: none;
	}
	/* Reset the desktop grid-column spans (1/4, 1/3, 3/5): a 1fr grid
	   only has one explicit track, so those line references would
	   otherwise force extra implicit columns into existence instead of
	   actually stacking title / overview / services full-width, one
	   below the other. */
	.case-overview__grid { grid-template-columns: 1fr; }
	.case-overview__title,
	.case-overview__body,
	.case-overview__services {
		grid-column: 1;
	}
	.case-overview { text-align: center; }
	.work-card__meta { text-align: center; }
	.about-cv .wp-block-columns { flex-wrap: wrap; }
	.play-grid { grid-template-columns: 1fr; }
	.site-nav { padding: 11px; }
	/* 10% bigger than the halved size (was 17px/42.5px). */
	.site-nav__link,
	.site-nav__links a {
		font-size: 18.7px;
	}
	.site-nav__logo-mark { height: 46.75px; }
}
@media (max-width: 360px) {
	/* Narrower phones: back off to a size that still clears its column
	   comfortably even at very small widths (~320px). 10% bigger than
	   the halved size (was 32.5px/13px). */
	.site-nav__logo-mark { height: 35.75px; }
	.site-nav__link,
	.site-nav__links a {
		font-size: 14.3px;
	}
}

/* ---------- Large screens (bigger than a 16" laptop) ----------
   1728px is the default "looks like" width of a 16" MacBook Pro at its
   native Retina scaling -- beyond that (e.g. a 27"+ external monitor),
   the site stops stretching wider and locks to a fixed width, centered.
   Applied to .site-main, which wraps every page's content (hero, grid,
   case studies, about, play); .site-nav and .site-footer are siblings
   of .site-main, not children, so they're untouched and keep spanning
   the true, full viewport width. */
@media (min-width: 1729px) {
	.site-main {
		max-width: 1728px;
		margin: 0 auto;
	}
	/* These two are sized in vw (relative to the true viewport) so they
	   read as "90% / 80% of the content area" at any normal width; above
	   the cap the viewport keeps growing but .site-main doesn't, so they
	   need to switch to being relative to their now-fixed container
	   instead, or they'd overflow past .site-main's edges. */
	.case-body__inner > .case-square-pair { width: 90%; }
	.case-body__inner > .case-photo-grid { width: 80%; }
}

/* ---------- Custom Vimeo player ----------
   Replaces Vimeo's own default UI (play/pause/timeline/mute/fullscreen)
   with our own overlay for "controls" embeds (regular playback, not the
   autoplay/looping "background" ones -- those keep the existing plain
   iframe treatment, see .wp-block-embed--bg-video above). Colour
   adapted from the reference's brand orange (#FF4C24) to the site's own
   off-white/black palette instead. See vimeo-player.js. */
.vimeo-player {
	pointer-events: auto;
	color: #efeeec;
	isolation: isolate;
	background-color: #131313;
	justify-content: center;
	align-items: center;
	width: 100%;
	display: flex;
	position: relative;
	overflow: hidden;
}
.vimeo-player__iframe {
	pointer-events: none;
	width: 100%;
	height: 100%;
	position: absolute;
	border: 0;
}
.vimeo-player__before {
	padding-top: 56.25%;
}
.vimeo-player__placeholder {
	object-fit: cover;
	width: 100%;
	height: 100%;
	transition: opacity 0.3s linear;
	display: block;
	position: absolute;
}
.vimeo-player[data-vimeo-activated="true"][data-vimeo-loaded="true"] .vimeo-player__placeholder {
	opacity: 0;
}
.vimeo-player__dark {
	opacity: 0.5;
	pointer-events: none;
	background-color: #131313;
	width: 100%;
	height: 100%;
	transition: opacity 0.3s linear;
	position: absolute;
}
.vimeo-player[data-vimeo-playing="false"] .vimeo-player__dark {
	opacity: 0.33;
}
.vimeo-player[data-vimeo-activated="false"][data-vimeo-playing="false"] .vimeo-player__dark {
	opacity: 0;
}
.vimeo-player[data-vimeo-activated="true"][data-vimeo-loaded="true"] .vimeo-player__dark {
	opacity: 0;
}
.vimeo-player__play,
.vimeo-player__pause {
	cursor: pointer;
	justify-content: center;
	align-items: center;
	width: 100%;
	height: 100%;
	display: flex;
	position: absolute;
}
.vimeo-player__btn {
	-webkit-backdrop-filter: blur(1em);
	backdrop-filter: blur(1em);
	background-color: #64646433;
	border-radius: 50%;
	justify-content: center;
	align-items: center;
	width: 6em;
	height: 6em;
	transition: opacity 0.3s linear;
	display: flex;
	position: relative;
}
.vimeo-player__btn-play-svg { width: 40%; }
.vimeo-player__btn-pause-svg { width: 50%; }
.vimeo-player .vimeo-player__pause { display: none; }
.vimeo-player[data-vimeo-playing="true"] .vimeo-player__pause { display: flex; }
.vimeo-player .vimeo-player__pause .vimeo-player__btn { opacity: 0; }
.vimeo-player[data-vimeo-activated="true"][data-vimeo-playing="false"] .vimeo-player__pause .vimeo-player__btn,
.vimeo-player[data-vimeo-activated="true"][data-vimeo-hover="true"]:hover .vimeo-player__pause .vimeo-player__btn {
	opacity: 1;
}
@media (hover: none) and (pointer: coarse) {
	.vimeo-player[data-vimeo-activated="true"][data-vimeo-playing="true"] .vimeo-player__pause .vimeo-player__btn {
		opacity: 0 !important;
	}
}
.vimeo-player[data-vimeo-playing="true"] .vimeo-player__play { opacity: 0; }
.vimeo-player__loading-svg { width: 6em; }
.vimeo-player__loading {
	pointer-events: none;
	color: #efeeec;
	justify-content: center;
	align-items: center;
	width: 100%;
	height: 100%;
	transition: opacity 0.3s linear;
	display: flex;
	position: absolute;
	opacity: 0;
}
.vimeo-player[data-vimeo-playing="true"] .vimeo-player__loading { opacity: 1; }
.vimeo-player[data-vimeo-playing="true"][data-vimeo-loaded="true"] .vimeo-player__loading { opacity: 0; }
/* Always visible -- unlike the central play/pause button below, this
   bottom control bar (mute/timeline/fullscreen) doesn't hide/reveal on
   hover or playback state. */
.vimeo-player .vimeo-player__interface { opacity: 1; }
.vimeo-player .vimeo-player__interface * { pointer-events: all; }
.vimeo-player {
	--timeline-rounded-corners: 1.5em;
	--timeline-dot-height: 0.75em;
	--timeline-dot-color: #efeeec;
	--progress-bg: rgba(239, 238, 236, 0.2);
	--progress-fill-bg: #efeeec;
	--progress-height: 0.2em;
}
.vimeo-player__interface {
	pointer-events: none;
	flex-flow: column;
	justify-content: flex-end;
	align-items: stretch;
	width: 100%;
	height: 100%;
	padding: min(2em, 4vw);
	transition-property: opacity;
	transition-duration: 0.3s;
	transition-timing-function: linear;
	display: flex;
	position: absolute;
}
.vimeo-player__interface-bottom {
	grid-column-gap: 1em;
	grid-row-gap: 1em;
	justify-content: flex-start;
	align-items: center;
	display: flex;
}
.vimeo-player__timeline {
	flex-grow: 1;
	justify-content: center;
	align-items: center;
	height: 1.5em;
	display: flex;
	position: relative;
}
.vimeo-player__timeline-input {
	pointer-events: auto;
	cursor: pointer;
	-webkit-appearance: none;
	appearance: none;
	background-color: transparent;
	width: 100%;
	height: 100%;
	display: block;
	position: relative;
}
.vimeo-player__timeline-progress {
	vertical-align: top;
	-webkit-appearance: none;
	appearance: none;
	height: var(--progress-height);
	border-radius: var(--timeline-rounded-corners);
	color: var(--progress-fill-bg);
	background-color: transparent;
	border: none;
	width: 100%;
	margin: 0;
	padding: 0;
	position: absolute;
	left: 0;
	overflow: hidden;
}
.vimeo-player progress::-webkit-progress-bar {
	border-radius: var(--timeline-rounded-corners);
	background-color: var(--progress-bg);
}
.vimeo-player progress::-webkit-progress-value {
	background: var(--progress-fill-bg);
}
.vimeo-player progress::-moz-progress-bar {
	border-radius: var(--timeline-rounded-corners);
	background: var(--progress-fill-bg);
}
.vimeo-player [type="range"]::-webkit-slider-thumb { -webkit-appearance: none; }
.vimeo-player [type="range"]:focus { outline: none; }
.vimeo-player [type="range"]::-ms-track {
	width: 100%;
	cursor: pointer;
	background-color: transparent;
	border-color: transparent;
	color: transparent;
}
.vimeo-player [type="range"]::-webkit-slider-runnable-track {
	width: 100%;
	height: var(--progress-height);
	cursor: pointer;
	border-radius: var(--timeline-rounded-corners);
	background-color: transparent;
	border-color: transparent;
	color: transparent;
}
.vimeo-player [type="range"]::-webkit-slider-thumb {
	height: var(--timeline-dot-height);
	width: var(--timeline-dot-height);
	border-radius: var(--timeline-rounded-corners);
	background-color: var(--timeline-dot-color);
	cursor: pointer;
	-webkit-appearance: none;
	margin-top: calc((var(--progress-height) / 2) - (var(--timeline-dot-height) / 2));
}
.vimeo-player [type="range"]::-moz-range-track {
	width: 100%;
	height: var(--progress-height);
	cursor: pointer;
	background: var(--progress-bg);
	border: 0;
	border-radius: var(--timeline-rounded-corners);
	overflow: hidden;
	opacity: 1 !important;
}
.vimeo-player [type="range"]::-moz-range-thumb {
	border: 0;
	height: var(--timeline-dot-height);
	width: var(--timeline-dot-height);
	border-radius: var(--timeline-rounded-corners);
	background: var(--timeline-dot-color);
	cursor: pointer;
}
.vimeo-player__mute {
	cursor: pointer;
	flex-shrink: 0;
	justify-content: center;
	align-items: center;
	width: 1.5em;
	height: 1.5em;
	display: flex;
	position: relative;
}
.vimeo-player__volume-up-svg,
.vimeo-player__volume-mute-svg {
	width: 100%;
	position: absolute;
}
.vimeo-player .vimeo-player__mute svg:nth-child(2),
.vimeo-player[data-vimeo-muted="true"] .vimeo-player__mute svg:nth-child(1) {
	display: none;
}
.vimeo-player .vimeo-player__mute svg:nth-child(1),
.vimeo-player[data-vimeo-muted="true"] .vimeo-player__mute svg:nth-child(2) {
	display: block;
}
.vimeo-player__fullscreen {
	justify-content: center;
	align-items: center;
	width: 1.5em;
	height: 1.5em;
	display: flex;
	position: relative;
	cursor: pointer;
}
.vimeo-player__fullscreen-scale-svg,
.vimeo-player__fullscreen-shrink-svg {
	width: 100%;
	position: absolute;
}
.vimeo-player .vimeo-player__fullscreen svg:nth-child(2),
.vimeo-player[data-vimeo-fullscreen="true"] .vimeo-player__fullscreen svg:nth-child(1) {
	display: none;
}
.vimeo-player .vimeo-player__fullscreen svg:nth-child(1),
.vimeo-player[data-vimeo-fullscreen="true"] .vimeo-player__fullscreen svg:nth-child(2) {
	display: block;
}
.vimeo-player__duration {
	flex-shrink: 0;
	width: 2.25em;
}
.vimeo-player__duration-span {
	text-align: center;
	white-space: nowrap;
	-webkit-user-select: none;
	user-select: none;
	width: 100%;
	display: block;
}
