/* ---------------------------------------------------------------------------
 * Kristoffer Canimo 2026 — galleries, cards, buttons, lightbox.
 * ------------------------------------------------------------------------ */

/* ---------------------------------------------------------------------------
 * The article grid.
 *
 * Prose runs at a narrow measure; photographs break out past it. Three named
 * columns — text, wide, full — so any block can pick its own width without
 * anyone needing to wrap it in a container.
 * ------------------------------------------------------------------------ */

.kc-article {
	--measure: 38rem;
	--breakout: clamp(1rem, 7vw, 14rem);
	/* The gap between frames, used both across a row and between rows, so the
	   grid reads as one mesh rather than rows floating apart. */
	--photo-gap: clamp(var(--sp-3), 1.6vw, var(--sp-5));

	display: grid;
	grid-template-columns:
		[full-start] minmax(var(--gutter), 1fr)
		[wide-start] minmax(0, var(--breakout))
		[text-start] min(100% - (var(--gutter) * 2), var(--measure)) [text-end]
		minmax(0, var(--breakout)) [wide-end]
		minmax(var(--gutter), 1fr) [full-end];
	--row-gap: clamp(2.5rem, 6vw, 6rem);
	row-gap: var(--row-gap);
}

.kc-article > * { grid-column: text; }
.kc-article > .kc-wide { grid-column: wide; }
.kc-article > .kc-full { grid-column: full; }

/* The dissolved rows below mean these blocks are children of this grid in the
   box tree but DOM descendants of a `display: contents` wrapper — and CSS
   combinators read the DOM, so `>` never sees them. They have to be named. */
.kc-article .kc-prose,
.kc-article .kc-heading,
.kc-article .kc-figure,
.kc-article .kc-embed,
.kc-article .kc-space,
.kc-article .kc-rule { grid-column: text; }

/* ---------------------------------------------------------------------------
 * Gutenberg blocks on the same grid.
 *
 * Nothing here is bespoke: these are the blocks WordPress ships. Wide and Full
 * in the editor's toolbar map straight onto the grid's own columns, so the
 * width control Kris sees is the one the layout actually uses.
 * ------------------------------------------------------------------------ */

.kc-article > :where(p, h2, h3, h4, ul, ol, blockquote, figure, .wp-block-image, .wp-block-quote, .wp-block-embed) {
	grid-column: text;
	margin-block: 0;
}

.kc-article > .alignwide { grid-column: wide; }
.kc-article > .alignfull { grid-column: full; }

/* Every block is its own grid row, so consecutive text would otherwise be
   pushed apart by the gap meant for photographs. Pull it back to a normal
   paragraph rhythm, and leave the big gap for anything next to a picture. */
.kc-article > :where(p, h2, h3, h4, ul, ol, blockquote)
	+ :where(p, h2, h3, h4, ul, ol, blockquote) {
	margin-top: calc(var(--sp-4) - var(--row-gap));
}
.kc-article > p + p { margin-top: calc(.89em - var(--row-gap)); }

.kc-article > :where(p, ul, ol, blockquote) + :where(h2, h3) {
	margin-top: calc(var(--sp-7) - var(--row-gap));
}

.kc-article > ul,
.kc-article > ol { padding-left: 1.25em; }

.kc-article > blockquote {
	margin: 0;
	padding-left: var(--sp-5);
	border-left: 2px solid var(--rule-strong);
	color: var(--ink-soft);
}

.kc-article > figure { margin: 0; }
.kc-article .wp-block-image { position: relative; }
.kc-article .wp-block-image img { width: 100%; height: auto; display: block; }
.kc-article > figure > figcaption {
	font-family: var(--font-text);
	margin-top: var(--sp-3);
	font-size: var(--step--2);
	line-height: 1.5;
	color: var(--ink-faint);
	max-width: 34rem;
}

/* A paragraph beside a photograph, the native way. The editor's width
   control picks the size:
     none  — the wide measure, a little outside the text column
     Wide  — gutter to gutter, as wide as the photo grids
     Full  — edge to edge; a text column on an outer edge keeps the gutter */
.kc-article > .wp-block-columns {
	grid-column: wide;
	display: grid;
	grid-template-columns: repeat(var(--kc-cols, 2), minmax(0, 1fr));
	gap: clamp(var(--sp-5), 4vw, var(--sp-8));
	align-items: start;
}
.kc-article > .wp-block-columns.alignwide {
	grid-column: full;
	padding-inline: calc(var(--gutter) + env(safe-area-inset-left)) calc(var(--gutter) + env(safe-area-inset-right));
}
.kc-article > .wp-block-columns.alignfull { grid-column: full; padding-inline: 0; }
.kc-article > .wp-block-columns.alignfull > .wp-block-column:first-child:not(:has(figure)) { padding-left: calc(var(--gutter) + env(safe-area-inset-left)); }
.kc-article > .wp-block-columns.alignfull > .wp-block-column:last-child:not(:has(figure)) { padding-right: calc(var(--gutter) + env(safe-area-inset-right)); }
@media (max-width: 781px) {
	.kc-article > .wp-block-columns.alignfull > .wp-block-column:not(:has(figure)) { padding-inline: calc(var(--gutter) + env(safe-area-inset-left)) calc(var(--gutter) + env(safe-area-inset-right)); }
}
.kc-article > .wp-block-columns:has(> :nth-child(3)) { --kc-cols: 3; }
@media (max-width: 781px) {
	.kc-article > .wp-block-columns { --kc-cols: 1 !important; }
}

/* A thin rule over every run of text, as on theperformancelab.ca: over a text
   column it sits level with the top of the photographs beside it, and the
   words start a short step below. */
/* Drawn inside the column's content box, so a Full-width text column's
   gutter padding does not drag the rule out to the screen edge. */
.kc-article .wp-block-column:has(> :is(p, h2, h3, h4):first-child)::before {
	content: "";
	display: block;
	border-top: 1px solid var(--ink);
	margin-bottom: 1.1rem;
}
.kc-article > p:not(p + p):not(:empty) {
	border-top: 1px solid var(--ink);
	padding-top: 1.1rem;
}

/* Text starts level with the top of the photographs beside it. Core's own
   stylesheet forces align-items: normal !important, so this has to be
   !important too; the editor's vertical-alignment control still wins. */
.kc-article > .wp-block-columns:not([class*="are-vertically-aligned"]) { align-items: start !important; }

/* A line box carries half its leading above the capitals, which left the
   first line of text sitting visibly lower than the photograph's top edge.
   Trim it so the capitals start level with the picture. */
.kc-article .wp-block-column > :is(p, h2, h3, h4):first-child { text-box: trim-start cap alphabetic; }
@supports not (text-box: trim-start cap alphabetic) {
	.kc-article .wp-block-column > :is(p, h2, h3, h4):first-child { margin-top: -.52em; }
}

/* A figure's browser default is a 40px margin each side, which only
   .kc-article > figure was clearing — so an image inside a column sat 80px
   narrower than the column it was in. */
.kc-article .wp-block-column figure { margin: 0; }
.kc-article .wp-block-column > * { margin-block: 0; }
.kc-article .wp-block-column > * + * { margin-top: var(--sp-4); }

/* Inside a column nothing takes the article's line names: those columns do not
   exist there, and an unknown name spawns an implicit track. */
.kc-article .wp-block-column .kc-gallery__item,
.kc-article .wp-block-column .kc-gallery__row,
.kc-article .wp-block-column .kc-prose {
	grid-column: auto;
	padding-inline: 0;
}
.kc-article .wp-block-column .kc-gallery__row { grid-template-columns: 1fr; }

/* --- gallery --------------------------------------------------------- */

/* A trip post is stored as [vc_row][vc_column][vc_gallery], and none of that
   scaffolding means anything here. Dissolving all of it — rows, columns and
   the gallery box — makes every figure a direct child of the article grid, so
   each one can choose its own measure. Without this the figures are laid out
   by the builder's 12-column row instead. */
.kc-article .kc-row,
.kc-article .kc-row__inner,
.kc-article .kc-col,
.kc-article .kc-gallery { display: contents; }

/* Unless the row genuinely has more than one column — that is a deliberate
   layout, such as a paragraph set beside a photograph, so it keeps its grid. */
.kc-article .kc-row:has(.kc-col + .kc-col) {
	display: block;
	grid-column: wide;
}

.kc-article .kc-row:has(.kc-col + .kc-col) > .kc-row__inner {
	display: grid;
	grid-template-columns: repeat(12, minmax(0, 1fr));
	gap: clamp(var(--sp-5), 3vw, var(--sp-7));
	width: 100%;
	align-items: center;
}

.kc-article .kc-row:has(.kc-col + .kc-col) .kc-col { display: block; }

/* Inside such a column everything simply fills it; the article's line names do
   not apply there. */
.kc-article .kc-row:has(.kc-col + .kc-col) .kc-gallery__item,
.kc-article .kc-row:has(.kc-col + .kc-col) .kc-gallery__row,
.kc-article .kc-row:has(.kc-col + .kc-col) .kc-prose,
.kc-article .kc-row:has(.kc-col + .kc-col) .kc-heading,
.kc-article .kc-row:has(.kc-col + .kc-col) .kc-figure {
	grid-column: auto;
	padding-inline: 0;
}

.kc-gallery__item {
	position: relative;
	margin: 0;
	grid-column: text;
}

/* The width ladder: the more photographs share a row, the wider that row runs.
   A single horizontal frame is the one that can carry the whole measure. */
.kc-gallery__item--full { grid-column: full; }
.kc-gallery__item--tall { grid-column: text; }

.kc-gallery__row {
	display: grid;
	gap: var(--photo-gap);
	align-items: start;
}

/* Rows that are not separated by anything else close up to the same gap they
   use across their columns. A paragraph between them starts a new gallery
   block, so those rows are not siblings and keep the full row gap. */
.kc-gallery > * + * { margin-top: calc(var(--photo-gap) - var(--row-gap)); }

.kc-gallery__row--2 {
	grid-column: wide;
	grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Three across needs the room, or each frame ends up narrower than the text. */
.kc-gallery__row--3 {
	grid-column: full;
	padding-inline: var(--gutter);
	grid-template-columns: repeat(3, minmax(0, 1fr));
}

/* Inside a row the figures are ordinary grid items. Without this they keep the
   `text`/`full` line names meant for the article grid, which do not exist here,
   and each one spawns an implicit track of its own. */
.kc-gallery__row > .kc-gallery__item { grid-column: auto; }

/* Over a full-bleed frame the photograph's edge is the screen's, so the
   caption lines up with the page gutter instead of the frame's own inset. */
.kc-article .kc-gallery__item--full figcaption,
.kc-gallery__item--full figcaption { --cap-inset: var(--gutter); }

@media (max-width: 640px) {
	.kc-gallery__row,
	.kc-gallery__row--3 {
		grid-column: text;
		padding-inline: 0;
		grid-template-columns: 1fr;
		gap: clamp(2.5rem, 6vw, 6rem);
	}
	/* Stacked, there are no columns to match, so let the frames breathe. */
	.kc-gallery > * + * { margin-top: 0; }
}

.kc-gallery__link {
	display: block;
	overflow: hidden;
	background: var(--bg-sunk);
	cursor: zoom-in;
}

.kc-gallery__img {
	width: 100%;
	aspect-ratio: var(--kc-ar, auto);
	object-fit: cover;
	transition: opacity var(--speed) var(--ease);
}

/* A photograph is the work, not a control, so a focus ring lands *on* it —
   and a browser will put one there long after the click that caused it, since
   closing the viewer restores focus to the photograph and Chrome counts any
   restored focus as keyboard focus once the keyboard has been used at all.
   The photograph answers by dimming instead.

   Keyboard only. Hovering changes nothing: the cursor is already an answer to
   "can I click this", and a photograph should look the same whether or not a
   pointer happens to be over it. */
.kc-gallery__link:focus-visible {
	box-shadow: none;
	border-radius: 0;
}

.kc-gallery__link:focus-visible .kc-gallery__img { opacity: .68; }

/* Captions ride on the photograph rather than sitting under it, set straight
   on the frame with nothing behind them. */
.kc-article .kc-gallery figcaption,
.kc-gallery figcaption,
.kc-article .wp-block-image > figcaption {
	font-family: var(--font-text);
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	margin: 0;
	--cap-inset: var(--sp-4);
	padding: var(--sp-4) var(--cap-inset) var(--sp-3);
	/* Stop the words at a readable measure on a full-bleed frame. */
	padding-inline-end: max(var(--cap-inset), 100% - 34rem);
	font-size: var(--step--2);
	line-height: 1.5;
	color: var(--photo-ink);
	text-wrap: pretty;
	/* The whole frame is the lightbox trigger; the caption must not eat it. */
	pointer-events: none;
}

.kc-article .kc-gallery figcaption a,
.kc-gallery figcaption a,
.kc-article .wp-block-image > figcaption a {
	color: inherit;
	pointer-events: auto;
}

/* --- figures --------------------------------------------------------- */

.kc-figure { margin: 0 0 var(--sp-4); }
.kc-figure--center { margin-inline: auto; text-align: center; }
.kc-figure--round .kc-figure__img { border-radius: 50%; aspect-ratio: 1; object-fit: cover; }
.kc-figure__img { border-radius: var(--radius-sm); }
.kc-figure figcaption {
	margin-top: var(--sp-2);
	font-size: var(--step--1);
	color: var(--ink-faint);
}

/* --- headings from the shim ------------------------------------------ */

.kc-heading { margin: 0 0 var(--sp-3); }
.kc-heading:last-child { margin-bottom: 0; }
.kc-heading--caps {
	font-size: var(--step--1);
	letter-spacing: .12em;
	text-transform: uppercase;
	color: var(--ink-faint);
	font-weight: 600;
}
p.kc-heading, div.kc-heading {
	font-size: var(--step-0);
	font-weight: 400;
	line-height: 1.6;
	letter-spacing: normal;
	color: var(--ink-soft);
}

.kc-embed { margin-block: var(--sp-5); }
.kc-embed iframe { max-width: 100%; border: 0; }

/* --- index of trips --------------------------------------------------- */

/* Edge to edge, photographs touching: four across on a desktop, two on a
   tablet, one on a phone. */
.kc-index {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 0;
}

@media (max-width: 1023px) {
	.kc-index { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 599px) {
	.kc-index { grid-template-columns: minmax(0, 1fr); }
}

/* The builder page wraps the index in a padded, boxed row; let it out. */
.kc-row:has(.kc-index) { padding-block: 0; }
.kc-row:has(.kc-index) > .kc-row__inner { width: 100%; gap: 0; }
.kc-col:has(> .kc-index) { text-align: left; }
.kc-page--builder .kc-row:has(> .kc-row__inner > .kc-col:only-child:empty) { display: none; }
/* ...and let the last row of photographs meet the footer. */
.kc-content:has(.kc-index) { padding-bottom: 0; }
.kc-main:has(.kc-index) + .kc-footer { margin-top: 0; }

.kc-card__link {
	display: block;
	text-decoration: none;
	color: #fff;
}

/* Everything sits on the photograph, like the cover it opens: no scrim, the
   type carries its own shadow. */
.kc-card__frame {
	position: relative;
	display: block;
	aspect-ratio: 4 / 3;
	overflow: hidden;
	background: var(--bg-sunk);
	--kc-card-pad: clamp(.875rem, 1.25vw, 1.375rem);
}

.kc-card__img {
	position: absolute;
	inset: 0;
	width: 100%; height: 100%;
	object-fit: cover;
}

.kc-card__date,
.kc-card__meta {
	font-family: var(--font-mono);
	font-size: .625rem; /* 10px, every width */
	font-weight: 500;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: rgb(255 255 255 / .88);
	text-shadow: 0 1px 7px rgb(0 0 0 / .55);
}

.kc-card__date {
	position: absolute;
	top: var(--kc-card-pad);
	right: var(--kc-card-pad);
	line-height: 1;
}

.kc-card__body {
	position: absolute;
	left: var(--kc-card-pad);
	right: var(--kc-card-pad);
	bottom: var(--kc-card-pad);
	display: block;
}

.kc-card__title {
	display: block;
	font-family: var(--font-display);
	font-weight: 400;
	font-size: clamp(1.375rem, 1rem + 1vw, 2rem);
	line-height: 1.02;
	letter-spacing: -.012em;
	text-shadow: 0 1px 10px rgb(0 0 0 / .45);
}

.kc-card__meta {
	display: block;
	margin-top: clamp(.375rem, .6vw, .625rem);
}

.kc-card__line {
	display: block;
	line-height: 1.55;
	/* Long camera lists wrap rather than lose their tail to an ellipsis. */
	text-wrap: pretty;
}

.kc-card__sep { margin: 0 .34em; opacity: .55; }

.kc-card--ink .kc-card__link { color: var(--ink); }
.kc-card--ink .kc-card__date,
.kc-card--ink .kc-card__meta {
	color: rgb(19 18 16 / .82);
	text-shadow: 0 1px 7px rgb(255 255 255 / .65);
}
.kc-card--ink .kc-card__title { text-shadow: 0 1px 10px rgb(255 255 255 / .55); }

/* The front page's arrow, bottom right, on hover only. The body stops short of
   it at all times so nothing rewraps when it appears. */
.kc-card__body { right: calc(var(--kc-card-pad) * 2 + var(--kc-arrow)); }
.kc-card__go { display: none; }

@media (hover: hover) and (pointer: fine) {
	.kc-card__go {
		display: block;
		position: absolute;
		right: var(--kc-card-pad);
		bottom: var(--kc-card-pad);
		width: var(--kc-arrow);
		height: auto;
		opacity: 0;
		transform: translateX(calc(var(--kc-arrow) * -.35));
		transition: opacity 240ms var(--ease), transform 380ms var(--ease);
		filter: drop-shadow(0 1px 4px rgb(0 0 0 / .6));
		pointer-events: none;
	}
	.kc-card__link:hover .kc-card__go,
	.kc-card__link:focus-visible .kc-card__go { opacity: 1; transform: none; }
	.kc-card--ink .kc-card__go { filter: drop-shadow(0 1px 4px rgb(255 255 255 / .7)); }
}

@media (hover: none), (pointer: coarse) {
	.kc-card__body { right: var(--kc-card-pad); }
}

/* No zoom, no fade: keyboard focus underlines the title and nothing moves. */
.kc-card__link:focus-visible {
	box-shadow: none;
	border-radius: 0;
}
.kc-card__link:focus-visible .kc-card__title { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .12em; }

/* --- story nav -------------------------------------------------------- */

.kc-storynav {
	display: flex;
	flex-wrap: wrap;
	gap: var(--sp-4);
	justify-content: space-between;
	padding-block: var(--sp-6);
	border-top: 1px solid var(--rule);
}

.kc-storynav__link { display: grid; gap: var(--sp-1); text-decoration: none; max-width: 22rem; }
.kc-storynav__link--next { text-align: right; margin-left: auto; }
.kc-storynav__label {
	font-family: var(--font-display);
	font-size: var(--step--1);
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--ink-faint);
}
.kc-storynav__title { font-family: var(--font-display); font-weight: 400; font-size: var(--step-1); }

/* --- buttons ---------------------------------------------------------- */

.kc-actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }

.kc-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 2.75rem;
	padding: var(--sp-2) var(--sp-5);
	border-radius: 0;
	background: var(--accent);
	color: var(--accent-ink);
	text-decoration: none;
	font-size: var(--step--1);
	font-weight: 500;
	letter-spacing: .04em;
	border: 1px solid transparent;
	transition: filter var(--speed) var(--ease);
}
.kc-btn:hover { filter: brightness(1.08); }

.kc-btn--quiet {
	background: transparent;
	color: var(--ink);
	border-color: var(--rule-strong);
}

/* --- pagination -------------------------------------------------------- */

.kc-pagination { padding-block: var(--sp-6); }
.kc-pagination .nav-links { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.kc-pagination a, .kc-pagination .current {
	display: inline-grid;
	place-items: center;
	min-width: 2.5rem; min-height: 2.5rem;
	padding-inline: var(--sp-2);
	border-radius: var(--radius-sm);
	border: 1px solid var(--rule);
	text-decoration: none;
}
.kc-pagination .current { background: var(--bg-sunk); font-weight: 560; }

/* --- lightbox ---------------------------------------------------------- */

/* --- lightbox ---------------------------------------------------------
   Modelled on a photo viewer rather than a web modal: the photograph grows
   out of the frame that was clicked, the chrome is bare glyphs that fade
   away while you look, and a drag follows the finger. */

.kc-lightbox {
	border: 0;
	padding: 0;
	max-width: 100vw;
	max-height: 100dvh;
	width: 100vw;
	height: 100dvh;
	/* The scrim lives on the dialog, not on ::backdrop, so a drag can fade it
	   with the photograph and let the page show through underneath. */
	background: var(--scrim-photo);
	color: var(--ink-inverse);
	overflow: hidden;
	grid-template-areas: "stage";
}

/* An author `display` would beat the UA's `dialog:not([open]) { display: none }`
   — author origin wins over UA whatever the specificity — and the viewer would
   sit open on top of every page. Only ever set it on the open state. */
.kc-lightbox[open] { display: grid; }

/* Blurring the page behind it, rather than only darkening it, is what makes
   the viewer feel like a layer above the article instead of a panel drawn on
   top of it. Static — never animated in or out; that is the one blur
   transition reduced-motion users are told to be spared. */
.kc-lightbox::backdrop {
	background: transparent;
	backdrop-filter: blur(24px) saturate(120%);
	-webkit-backdrop-filter: blur(24px) saturate(120%);
}

.kc-lightbox__stage {
	grid-area: stage;
	position: relative;
	width: 100%;
	height: 100%;
	/* Kept tight. The photograph is height-limited at almost every window
	   shape, so these two values — not the card's width cap — are what decide
	   how big it gets. The bottom inset is now only the count's line: moving
	   the caption onto the photograph is what freed it up. */
	padding: clamp(1rem, 2.4vw, 2.25rem);
	padding-bottom: clamp(2.25rem, 4.5vh, 3rem);
}

/* A size container, so a card can be sized from the space it has rather than
   from the viewport — the stage is inset by its padding and the chrome. */
.kc-lightbox__stack {
	position: relative;
	width: 100%;
	height: 100%;
	container-type: size;
	display: grid;
	place-items: center;
}

/* Each card is the shape of its own photograph, letterboxed into the stage:
   the width it wants, or the width its height allows, whichever is smaller.
   Cards keep their own shapes, which is what makes the stack read as prints
   rather than as a carousel of identical slides. */
.kc-lightbox__card {
	position: absolute;
	margin: 0;
	/* Width is held back to 82% because the fan needs the room either side.
	   Height is not contested the same way — every card behind is scaled below
	   the front one — so it runs to 94%. */
	width: min(82cqw, calc(94cqh * var(--kc-ar, 1)));
	aspect-ratio: var(--kc-ar, 1);
	overflow: hidden;
	border-radius: 4px;
	backface-visibility: hidden;
	transition:
		transform 460ms var(--ease),
		opacity 320ms var(--ease-frame),
		filter 460ms var(--ease-frame);
	will-change: transform;
}

.kc-lightbox__card[hidden] { display: none; }

/* The one you are looking at, then two behind it fanned out either side.

   Depth is carried three ways, because on a near-black ground with dark
   photographs no single one of them is enough: the cards behind protrude far
   enough to be read as separate objects, they are dimmed so the front one is
   plainly the subject, and each casts a shadow. The shadow does nothing
   against the backdrop — black on black — but where one card overlaps another
   it darkens the photograph underneath, which is exactly where the separation
   has to be legible. */
.kc-lightbox__card {
	box-shadow: 0 26px 70px -24px rgb(0 0 0 / .95), 0 3px 14px -4px rgb(0 0 0 / .8);
}

.kc-lightbox__card[data-depth="0"] {
	z-index: 3;
	transform: translate3d(var(--kc-drag-x, 0px), 0, 0) rotate(var(--kc-drag-r, 0deg));
}

/* Far enough out to be plainly two more photographs, close enough that they
   stay a backdrop to the one in front rather than competing with it. */
.kc-lightbox__card[data-depth="1"] {
	z-index: 2;
	transform: translate3d(11.5%, 3%, 0) rotate(4.6deg) scale(.94);
	filter: brightness(.7);
}

.kc-lightbox__card[data-depth="2"] {
	z-index: 1;
	transform: translate3d(-11.5%, 6.5%, 0) rotate(-5.8deg) scale(.875);
	filter: brightness(.5);
}

/* Parked at the back: laid out and already carrying its photograph, but not
   visible. It exists so the bottom of the stack is never empty while the top
   card is in flight — with only three cards the third photograph appeared on
   the left a beat late, after the throw had finished. */
.kc-lightbox__card[data-depth="3"] {
	z-index: 0;
	opacity: 0;
	pointer-events: none;
	transform: translate3d(-11.5%, 6.5%, 0) rotate(-5.8deg) scale(.875);
	filter: brightness(.5);
}

/* Recycling a card to the back has to happen with no transition, or it flies
   across the screen on its way there. */
.kc-lightbox__card.is-snapping { transition: none; }

/* A card on its way out keeps the top of the pile while it goes, even though
   it has already been given its place at the back. */
.kc-lightbox__card.is-flying { z-index: 9; }

/* A photograph being dealt back on top from the left, following the hand. It
   has to clear everything, including a card still in flight. */
.kc-lightbox__card.is-returning {
	z-index: 10;
	transition: none;
	filter: none;
}

.kc-lightbox__card.is-dragging {
	transition: opacity 320ms var(--ease-frame);
	cursor: grabbing;
}

/* Dragging a card must not start the browser's own image drag. That hands the
   pointer to the drag-and-drop machinery, which shows a ghost of the
   photograph and swallows every pointermove — so the card sat still while the
   cursor carried a copy of it around. */
.kc-lightbox__card {
	user-select: none;
	-webkit-user-select: none;
}

.kc-lightbox__img {
	-webkit-user-drag: none;
	user-drag: none;
}

.kc-lightbox__card[data-depth="0"] { cursor: grab; }
.kc-lightbox[data-idle="true"] .kc-lightbox__card[data-depth="0"] { cursor: none; }

.kc-lightbox__img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	/* The card is already the photograph's shape, so cover and contain agree
	   and neither can crop it. */
	object-fit: cover;
	border-radius: inherit;
}

/* The thumbnail is already decoded in the article, so it paints on the same
   frame the dialog opens and holds the composition while the original
   downloads.

   No blur on it: upscaled from the tile size it is soft enough on its own, and
   a blur would bleed past the card's edge as a halo. */
.kc-lightbox__img--thumb { filter: none; }

.kc-lightbox__img--full {
	opacity: 0;
	transition: opacity 280ms var(--ease-frame);
}
.kc-lightbox__img--full[data-ready="true"] { opacity: 1; }

/* --- chrome ----------------------------------------------------------- */

.kc-lightbox__chrome {
	grid-area: stage;
	position: relative;
	pointer-events: none;
}
.kc-lightbox__chrome > * { pointer-events: auto; }

/* On a fine pointer the controls step aside while you look at the photograph
   and come back the moment the pointer moves — the same bargain a full-screen
   video player makes. Never on touch: there is no hover there to bring them
   back with. */
.kc-lightbox[data-idle="true"] :is(.kc-lightbox__close, .kc-lightbox__nav) { opacity: 0; }
.kc-lightbox[data-idle="true"] { cursor: none; }

/* The caption and the counter are not chrome — they are what the photograph
   is and where you are in the essay — so they stay while the buttons go. */

/* Bare glyphs with a shadow, no plate. A translucent disc has to be tested
   against every photograph it might sit on; a stroke with a shadow under it
   does not. Same call as the hero's arrow. */
.kc-lightbox__close,
.kc-lightbox__nav {
	position: absolute;
	z-index: 2;
	display: grid;
	place-items: center;
	/* 44px of target, ~24px of glyph. */
	width: 2.75rem;
	height: 2.75rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	color: #fff;
	cursor: pointer;
	opacity: .78;
	transition: opacity var(--speed) var(--ease-frame), transform var(--speed) var(--ease-frame);
}

.kc-lightbox__close svg,
.kc-lightbox__nav svg {
	width: 1.5rem;
	height: 1.5rem;
	filter: drop-shadow(0 1px 5px rgb(0 0 0 / .7));
}

.kc-lightbox__close:hover,
.kc-lightbox__nav:hover { opacity: 1; }
.kc-lightbox__nav--prev:hover { transform: translateX(-3px); }
.kc-lightbox__nav--next:hover { transform: translateX(3px); }

/* Dimmed at the ends, not removed. A control that disappears makes the frame
   you are looking at feel broken; one that dims tells you where you are. */
/* These sit on a photograph over a near-black ground, where the page's ink
   halo cannot be seen at all. */
.kc-lightbox__close:focus-visible,
.kc-lightbox__nav:focus-visible {
	outline: none;
	box-shadow: var(--focus-ring-paper);
	opacity: 1;
}

/* WordPress core's own lightbox ships `outline: -webkit-focus-ring-color auto`,
   which is the blue ring by another name. The theme renders galleries itself so
   nothing here should ever be inside one, but the rule loads with the block
   library and would fire if anything ever were. */
/* `body` only to outrank it: the selectors are otherwise identical and the
   block library's stylesheet loads after the theme's. */
body .wp-lightbox-container button:focus-visible {
	outline: none;
	box-shadow: var(--focus-ring-paper);
}

.kc-lightbox__nav[disabled] {
	opacity: .22;
	cursor: default;
	transform: none;
}

.kc-lightbox__close { top: var(--sp-4); right: var(--sp-4); }
.kc-lightbox__nav { top: 50%; margin-top: -1.375rem; }
.kc-lightbox__nav--prev { left: var(--sp-4); }
.kc-lightbox__nav--next { right: var(--sp-4); }

@supports (padding: max(0px)) {
	.kc-lightbox__close { top: max(var(--sp-4), env(safe-area-inset-top)); }
	.kc-lightbox__nav--prev { left: max(var(--sp-4), env(safe-area-inset-left)); }
	.kc-lightbox__nav--next { right: max(var(--sp-4), env(safe-area-inset-right)); }
}

/* --- what the photograph is, and where you are ------------------------- */

/* The caption rides on the photograph, bottom left, the same way it does in
   the article — white, nothing behind it. It lives inside the card, so the
   card's overflow clips it, it travels with the card when one is thrown, and
   the space under the stack goes back to the picture instead of being held
   open for a line of text that might run to two. */
.kc-lightbox__caption {
	font-family: var(--font-text);
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	margin: 0;
	padding: var(--sp-6) var(--sp-4) var(--sp-3);
	/* Stop the words at a readable measure on a wide frame. */
	padding-inline-end: max(var(--sp-4), 100% - 34rem);
	font-size: var(--step--2);
	line-height: 1.5;
	color: var(--photo-ink);
	text-wrap: pretty;
	pointer-events: none;
	transition: opacity 260ms var(--ease-frame);
}

.kc-lightbox__caption:empty { display: none; }

/* Only the photograph in front says what it is. */
.kc-lightbox__card:not([data-depth="0"]) .kc-lightbox__caption { opacity: 0; }

/* How far into the essay you are, out of the way in the corner. The viewer
   runs the length of the whole article, so without this there is no way to
   tell. */
.kc-lightbox__bar {
	position: absolute;
	inset-inline: 0;
	bottom: 0;
	display: flex;
	justify-content: flex-end;
	padding-bottom: max(var(--sp-4), env(safe-area-inset-bottom));
	padding-inline: max(var(--sp-5), env(safe-area-inset-left)) max(var(--sp-5), env(safe-area-inset-right));
}

.kc-lightbox__count {
	margin: 0;
	font-size: var(--step--2);
	font-variant-numeric: tabular-nums;
	letter-spacing: .08em;
	color: color-mix(in oklab, var(--ink-inverse) 46%, transparent);
}

.kc-lightbox__count:empty { display: none; }

/* On a phone the photograph gets the screen. The insets come right in, the
   arrows sit on the picture rather than asking for room beside it, and the fan
   is allowed to run off the edges — the dialog clips it, and a stack running
   past the edge still reads as a stack. Only the front card is guaranteed to
   fit whole, which is the one that matters. */
@media (max-width: 700px) {
	.kc-lightbox__stage {
		padding: clamp(.5rem, 2vw, 1rem);
		padding-bottom: clamp(2rem, 4vh, 2.5rem);
	}

	.kc-lightbox__card { width: min(96cqw, calc(92cqh * var(--kc-ar, 1))); }

	.kc-lightbox__card[data-depth="1"] {
		transform: translate3d(6%, 2.5%, 0) rotate(3.4deg) scale(.945);
	}
	.kc-lightbox__card[data-depth="2"],
	.kc-lightbox__card[data-depth="3"] {
		transform: translate3d(-6%, 5%, 0) rotate(-4.4deg) scale(.895);
	}

	.kc-lightbox__bar { padding-inline: var(--sp-3); }
}

/* Touch: there is no hover to bring hidden chrome back with, so nothing hides,
   and a drag moves the photograph itself.

   The arrows stay even though a swipe does the same thing — a gesture should
   never be the only way to reach something. Pulled tight to the edges so they
   sit beside the photograph rather than on it. */
@media (hover: none) {
	.kc-lightbox[data-idle="true"] :is(.kc-lightbox__close, .kc-lightbox__nav) { opacity: .78; }
	.kc-lightbox[data-idle="true"] { cursor: auto; }
	.kc-lightbox__nav--prev { left: 0; }
	.kc-lightbox__nav--next { right: 0; }
	/* pinch-zoom, not none: the drag needs the pans, but a reader should still
	   be able to pinch into a photograph to look closer. */
	.kc-lightbox__card { touch-action: pinch-zoom; }
}

body.kc-lightbox-open { overflow: hidden; }

/* ---------------------------------------------------------------------------
 * Trip tiles — the front page's primary content.
 * ------------------------------------------------------------------------ */

.kc-trip__link {
	display: grid;
	gap: var(--sp-4);
	text-decoration: none;
	color: inherit;
}

.kc-trip__frame {
	display: block;
	aspect-ratio: 3 / 2;
	overflow: hidden;
	background: var(--bg-sunk);
}

.kc-trip__img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	transition: transform 700ms var(--ease), filter 700ms var(--ease);
}

@media (hover: hover) {
	.kc-trip__link:hover .kc-trip__img { transform: scale(1.02); }
}

.kc-trip__body { display: grid; gap: var(--sp-2); }

.kc-trip__title {
	font-family: var(--font-display);
	font-weight: 400;
	font-size: var(--step-2);
	line-height: 1.12;
	letter-spacing: -.005em;
}

.kc-trip__meta {
	font-size: var(--step--1);
	color: var(--ink-faint);
}

/* --- legacy content, set as a quiet document --------------------------- */

.kc-content--front .kc-heading--caps {
	color: var(--ink-faint);
}

/* The testimonial portraits and service icons are decorative at this size. */
.kc-content--front .kc-figure img {
	max-width: 5rem;
	filter: grayscale(1);
}
.kc-content--front .kc-figure--round img { max-width: 4.5rem; }

/* The old hero galleries only ever held client logos; the masthead has them. */
.kc-content--front .kc-gallery { display: none; }

/* ---------------------------------------------------------------------------
 * Neutralising the old palette that is baked into the saved content.
 *
 * Uncode wrote per-element inline colours (browns, and a near-black used on
 * the agency list) straight into post_content. Left alone they are invisible
 * against a monochrome page, so the document inherits its colour instead.
 * ------------------------------------------------------------------------ */

.kc-content [style*="color"],
.kc-content [style*="colour"] {
	color: inherit !important;
	background-color: transparent !important;
}

.kc-content font[color] { color: inherit !important; }

/* Builder columns narrower than a readable measure get promoted. Four prose
   columns across a 1240px shell is 290px, which is too tight to read. */
.kc-content--front .kc-col--1,
.kc-content--front .kc-col--2,
.kc-content--front .kc-col--3 { grid-column: span 4; }

@media (max-width: 1100px) {
	.kc-content--front .kc-col--1,
	.kc-content--front .kc-col--2,
	.kc-content--front .kc-col--3,
	.kc-content--front .kc-col--4,
	.kc-content--front .kc-col--5 { grid-column: span 6; }
}

/* Nested builder rows: three columns inside a quarter-width column is 98px of
   measure. Each column is its own container, so a nested grid stacks on the
   width it actually has rather than on the viewport's. */
.kc-col { container-type: inline-size; }

@container (max-width: 34rem) {
	.kc-row__inner > .kc-col { grid-column: 1 / -1; }
}

/* ---------------------------------------------------------------------------
 * Contact form (Contact Form 7)
 * ------------------------------------------------------------------------ */

.wpcf7 { max-width: 34rem; }

.wpcf7 p { margin: 0 0 var(--sp-4); }

.wpcf7 :where(input[type="text"], input[type="email"], input[type="tel"], input[type="url"], textarea) {
	width: 100%;
	font: inherit;
	color: var(--ink);
	background: transparent;
	border: 0;
	border-bottom: 1px solid var(--rule-strong);
	border-radius: 0;
	padding: var(--sp-3) 0;
	transition: border-color var(--speed) var(--ease);
	appearance: none;
}

.wpcf7 textarea { min-height: 8rem; resize: vertical; }

.wpcf7 :where(input, textarea)::placeholder { color: var(--ink-faint); opacity: 1; }

.wpcf7 :where(input, textarea):focus {
	outline: 0;
	border-bottom-color: var(--ink);
}
.wpcf7 :where(input, textarea):focus-visible {
	outline: none;
	box-shadow: var(--focus-ring);
}

.wpcf7 input[type="submit"] {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 2.75rem;
	padding: var(--sp-2) var(--sp-6);
	background: var(--accent);
	color: var(--accent-ink);
	border: 1px solid transparent;
	border-radius: 0;
	font-size: var(--step--1);
	font-weight: 500;
	letter-spacing: .04em;
	cursor: pointer;
	transition: filter var(--speed) var(--ease);
	width: auto;
}
.wpcf7 input[type="submit"]:hover { filter: brightness(1.12); }

.wpcf7-not-valid-tip { color: var(--ink); font-size: var(--step--1); }
.wpcf7 .wpcf7-response-output {
	border: 1px solid var(--rule-strong);
	padding: var(--sp-3) var(--sp-4);
	margin: var(--sp-4) 0 0;
	font-size: var(--step--1);
}
.wpcf7 .g-recaptcha, .wpcf7 .grecaptcha-badge { margin-block: var(--sp-4); }
