/* =============================================================================
 *
 * SCOPE CHANGE 2026-08-30: every selector in this file used to be pinned
 * to body.page-id-5983 (the Alaska feature). Kedyn asked for the same
 * editorial treatment on /ten-years-later/, so the pin is now the
 * .pm-essay body class, added by kedynsierra_theme_essay_body_class()
 * (functions.php) for the page IDs in kedynsierra_theme_essay_page_ids().
 * Making a page an essay = adding its ID to that one list. Page-specific
 * rules below (the aurora :has(#aurora) grid, the 768x1024 snapshot cap,
 * the sponsor-band override) key on markup that exists on only one page,
 * so sharing the class cannot activate them elsewhere.
 *
 * (This note used to be wrapped in a comment of its own, nested inside this
 * one, and closed right here. CSS comments do not nest, so that inner close
 * terminated the WHOLE docblock at line 12, and everything below it -
 * including the prose that quotes rules like
 * `#wrapper { padding-top: 172px }` - was parsed as live CSS. The parser's
 * error recovery then swallowed the `html body.pm-essay { ... }` block that
 * defines --pm-scrim / --pm-hero-focus-x / --pm-hero-focus-y, so the hero lost
 * its scrim and its crop on BOTH essay pages: white title on a bright
 * photograph, and background-position stuck at 0% 0%. Keep this file to ONE
 * top comment.)
 *
 * hero-essay.css - the opening screen of "Thirty Days in the Tongass" (post 5983).
 *
 * WHY THIS FILE EXISTS
 * Kedyn: "this is not a cinematic entrance to article". He is right, and the
 * cause is geometry, not decoration. Three separate defects stack up:
 *
 *   1. screen.css:252  `#wrapper { padding-top: 172px }`. #page_caption is
 *      INSIDE #wrapper (header.php:406 opens it, page.php:124 emits the
 *      caption), so this put a white band ABOVE the photograph - measured at
 *      126px on the live page at scrollY 0 (94px of it padding, 32px the
 *      logged-in admin bar).
 *
 *      CORRECTION, and read this before touching screen.css: that 172px is
 *      NOT stale. The header is 176px tall at scroll-top and shrinks to 78px
 *      once .scroll is added - both measured. 172px is sized for the
 *      EXPANDED header, and it is right for every other page on the site.
 *      An earlier reading of this file called it "dead space above every
 *      page"; that was measured while the page was already scrolled, and it
 *      was wrong. Changing it globally would make the expanded header
 *      overlap the content of ~40 pages.
 *   2. screen.css:3288 `#page_caption.hasbg { max-height: 750px }` - the frame
 *      cannot fill a 1203px viewport, so there is white page BELOW it too.
 *   3. screen.css:474  `h1..h7 { text-transform: uppercase }` - centred
 *      uppercase display type over a photograph is the stock hero of every
 *      wedding/hotel/Squarespace template in existence. Making that same
 *      composition bigger produces a bigger template, not an opening.
 *
 * So: fill the screen, crop on a chosen focal point rather than dead centre,
 * and set the title in mixed-case Fraunces - a magazine title page, not a
 * banner. One settle, one rule, one dateline, one sienna line running out of
 * the bottom of the frame. Nothing loops. Nothing is drawn on top of the
 * picture.
 *
 * THE HEIGHT MATH IS SELF-CORRECTING - this is the important part.
 * The hero's top edge is #wrapper's padding-top (T) and its height is
 * (100svh - T - chrome - peek). Its bottom edge therefore lands at
 * (100svh - chrome - peek) NO MATTER WHAT T IS. An error in the header
 * measurement can only move the TOP edge, and because the header is opaque
 * white and painted above it (position:fixed, z-index 9), a T that is too
 * SMALL is invisible - the bar simply covers the top of the photograph.
 *
 * That is exactly what happens here, deliberately: T is 78px (the SCROLLED
 * header) against a 176px expanded header, so the bar overlaps the top 66px
 * of the frame at scroll-top. Verified on the live page - the photograph
 * appears to begin flush under the nav, because the bar is opaque and the
 * image is cover-cropped, and the hero's bottom edge lands identically
 * either way. Biasing low is safe; biasing HIGH would show a white sliver.
 *
 * SCOPING - constraint 1. templates/template-header.php is shared by six
 * templates. Nothing in this file is scoped to that partial, or to any markup;
 * every selector below begins `html body.pm-essay`, and the enqueue in
 * functions.php is gated on is_page(5983). Either gate alone is sufficient.
 * Note also (verified) that page 5983 does NOT render
 * templates/template-header.php at all - page.php carries its own inline copy
 * of the #page_caption block at page.php:124. Anyone who "scopes the shared
 * partial" edits a file this page never loads and sees no change.
 *
 * FAIL-SAFE INVARIANT - constraint 4. No base rule in this file contains
 * opacity:0, visibility:hidden, or a zeroed transform. Those values exist ONLY
 * inside @keyframes `from` blocks, and every animation uses `backwards` -
 * never `forwards`, never `both`. Delete every animation declaration here and
 * the entrance still renders complete, just instantly. That is exactly what
 * the reduced-motion block at the foot of this file does, with
 * `animation: none` and nothing else. Grep before editing: if you ever need
 * `opacity: 0` in a base rule, you have broken the invariant.
 *
 * NO JAVASCRIPT - constraint 5. There is no script, so Jetpack Boost's
 * Concatenate JS and Defer Non-Essential JS have nothing to reorder and there
 * is no execution-order assumption to violate.
 *
 * NO NEW OVERFLOW/TRANSFORM ANCESTOR ABOVE THE ANCHOR BAR - constraint 7.
 * The only overflow this file introduces is on #page_caption itself, which is
 * a SIBLING of .ppb_wrapper, not an ancestor. `clip` is used rather than
 * `hidden` specifically because `hidden` makes a scroll container. Today
 * .kcb-anchor-bar is position:fixed and appended to document.body
 * (kcb-anchor-bar.js), so it is unreachable from here either way - but if it
 * is ever relocated, `clip` keeps that safe. Do not move it inside
 * #page_caption.
 *
 * CASCADE TRAPS, all line-checked against the live files:
 *   grid.css:763  #page_caption h1 { font-size: 22px !important }   (<=767)
 *   grid.css:519  h1.withtopbar { margin-top: 160px !important }    (768-960)
 *   grid.css:1122 #page_caption.hasbg #bg_regular { height: 200px } (<=767)
 *   grid.css:1116 #page_caption.hasbg { height: 200px }             (<=767)
 *   grid.css:500  #page_caption.hasbg { height: 500px }             (768-960)
 *   grid.css:321  #page_caption h1 { font-size: 26px }              (768-960)
 *   grid.css:205  #page_caption h1 { font-size: 36px }              (960-1099)
 *   grid.css:299  body.admin-bar #wrapper { padding-top: 112px }    (<=960)
 *   Kirki inline  h1 { font-size: 48px; font-weight: 300 }          (0,0,1)
 * Only the first three need !important. Everything else loses on specificity.
 * Selectors here lead with `html` purely to buy one element of specificity so
 * that grid.css's `body.admin-bar #wrapper` (1,1,1) is beaten outright rather
 * than tied and resolved by source order - the enqueue order is correct today,
 * but a tie that happens to work is not a design.
 * ============================================================================= */

html body.pm-essay {
	/* Header height. Biased LOW on purpose - see "self-correcting" above. */
	--pm-hero-top: 78px;

	/* Fixed viewport chrome ABOVE the document. 0 for the public; the WP admin
	   bar overlays the viewport for Kedyn and must come out of the height or
	   the bottom of the frame falls past the fold on his review pass. */
	--pm-hero-chrome: 0px;

	/* A deliberate sliver of the essay left visible under the fold. This is the
	   invitation downward, and it is why there is no bouncing chevron and no
	   looping scroll pulse anywhere in this file: a hard dark photograph edge
	   with light page beneath it is unambiguous evidence that the article
	   continues, and it cannot be mistaken for a loading indicator. */
	--pm-hero-peek: 84px;

	/* The crop. #bg_regular is a CSS background - no srcset, no <picture>, no
	   art direction - so background-position is the ONLY crop control that
	   exists. KED_1042.jpg is 1920x1280 (3:2); cover-cropping that into a
	   ~1.2:1 desktop box discards roughly 200px per side. Leaving it at
	   50% 50% is how the kayak leaves the photograph. These are STARTING
	   values, not answers - see the verification notes. */
	--pm-hero-focus-x: 50%;
	--pm-hero-focus-y: 42%;

	/* The one knob. Set to 1 and the photograph's motion is gone, with nothing
	   else in the design disturbed. */
	--pm-hero-settle: 1.04;

	/* Scrim base, as an r,g,b triple so alpha can vary per stop. Warm near-black
	   rather than pure #000 - it sits under a photograph, not a UI. */
	--pm-scrim: 12, 10, 9;
}

/* Desktop admin bar is 32px and overlays the viewport. */
html body.admin-bar.page-id-5983 { --pm-hero-chrome: 32px; }

/* -----------------------------------------------------------------------------
 * DEFECT 1: the 172px reservation, overridden for THIS PAGE ONLY (it is
 * correct everywhere else - see the correction at the top of this file).
 * This lifts everything below the hero by ~94px as well, which is intended -
 * it is the same white band, just measured from the other side. It does NOT
 * disturb jump-link landings: blocks.css's `scroll-margin-top: 160px` is
 * measured against the FIXED header, which this rule does not move.
 * -------------------------------------------------------------------------- */
html body.pm-essay #wrapper {
	padding-top: var(--pm-hero-top);
}

/* -----------------------------------------------------------------------------
 * DEFECT 2: the frame.
 * -------------------------------------------------------------------------- */
html body.pm-essay #page_caption.hasbg {
	/* Two-line fallback rather than @supports: a browser without svh keeps the
	   vh line, and there is only one place per breakpoint to forget. */
	/* Was a full-viewport cinematic entrance (100svh minus chrome/peek).
	   Rescaled 2026-08-30 on Kedyn's call after seeing it published on a
	   large screen: "too overwhelming and doesn't match flow of site" -
	   every other page opens with a bounded #page_caption band, and at
	   2000px the full-screen version was a wall. 58vh lands near the EU
	   expedition page's header scale on common desktops while keeping
	   the photographic opening; the clamp floor keeps short laptop
	   windows from crushing the title, the ceiling stops tall monitors
	   from rebuilding the wall. The <=900px rules below already cap by
	   105vw on phones and are untouched. */
	height: clamp(430px, 58vh, 680px);
	min-height: 0;
	max-height: none;          /* kills screen.css's 750px cap */
	margin: 0;
	padding: 0;                /* grid.css:767 adds 30/40px padding at <=767 */
	overflow: hidden;          /* fallback for engines without `clip` */
	overflow: clip;            /* NOT hidden - hidden makes a scroll container */
	background-color: #0c0a09; /* any gap letterboxes to black, never white */
}

/* The photograph. Opacity is NEVER touched anywhere in this file - it is the
   LCP element and it must be fully painted in the first frame (constraint 4).
   The only animation on it is a transform, which is compositor-only and does
   not gate the paint that records LCP. */
html body.pm-essay #page_caption.hasbg #bg_regular {
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;              /* beats grid.css:1122's 200px at <=767 */
	min-height: 100%;
	background-position: var(--pm-hero-focus-x) var(--pm-hero-focus-y);
	/* Written out in full: screen.css:3325 already puts translate3d(0,0,0) on
	   this element, and a bare scale() in the keyframe would silently drop it. */
	transform: translate3d(0, 0, 0);
	animation: pmTongassSettle 1400ms var(--ease-signature, cubic-bezier(.22, 1, .36, 1)) 0ms 1 backwards;
}

@keyframes pmTongassSettle {
	from { transform: translate3d(0, 0, 0) scale(var(--pm-hero-settle, 1.04)); }
}

/* The scrim, re-pointed from screen.css's percentage ramp to PIXEL distances
   from the bottom edge. A percentage ramp tuned against a 750px band puts its
   darkest zone somewhere else once the box is ~1040px tall; the type sits a
   fixed distance off the bottom, so the scrim should too. */
html body.pm-essay #page_caption.hasbg::after {
	background-image: linear-gradient(
		to top,
		rgba(var(--pm-scrim), .72) 0,
		rgba(var(--pm-scrim), .50) 120px,
		rgba(var(--pm-scrim), .20) 260px,
		rgba(var(--pm-scrim), 0) 420px
	);
}

/* -----------------------------------------------------------------------------
 * The line leaving the frame.
 * A single sienna hairline running down out of the bottom edge of the
 * photograph. It is the only colour in the entrance, it needs no markup, and
 * it does not move once it has been drawn. --color-accent-dark (#c48f6a), not
 * --color-accent (#92573b): reset.css:52 documents the former as "the SAME
 * sienna lifted for dark grounds", and the core value reads muddy against the
 * deepened scrim. Fades to rgba(196,143,106,0) rather than `transparent` to
 * avoid a grey midpoint in the gradient.
 * Centred with `left: calc(50% - .5px)` rather than translateX(-50%) so the
 * transform channel is free for the animation - no positioning value has to be
 * carried inside the keyframe, which is where that class of bug lives.
 * -------------------------------------------------------------------------- */
html body.pm-essay #page_caption.hasbg::before {
	content: "";
	position: absolute;
	z-index: 4;                /* above the z-index:2 scrim */
	left: calc(50% - .5px);
	bottom: 0;
	width: 1px;
	height: 72px;
	background-image: linear-gradient(
		to bottom,
		rgba(196, 143, 106, 0) 0,
		var(--color-accent-dark, #c48f6a) 55%,
		var(--color-accent-dark, #c48f6a) 100%
	);
	transform: scaleY(1);
	transform-origin: top center;
	animation: pmTongassLine 600ms var(--ease-signature, cubic-bezier(.22, 1, .36, 1)) 900ms 1 backwards;
}

@keyframes pmTongassLine {
	from { transform: scaleY(0); }
}

/* -----------------------------------------------------------------------------
 * DEFECT 3: the type. This is the half that stops it reading as theme furniture.
 * Kept CENTRED, not set lower-left: Kedyn has already overruled a left-aligned
 * treatment on this site once ("everything is aligned left", commit a6c2cc2),
 * and blocks.css centres this page's entire prose column. A left-anchored hero
 * title would set up an axis the following 21,000px contradicts. Centred
 * mixed-case Fraunces over a full-bleed frame is a magazine title page; centred
 * UPPERCASE Fraunces is the template. The case is the whole difference.
 * -------------------------------------------------------------------------- */
html body.pm-essay #page_caption.hasbg .page_title_wrapper {
	bottom: clamp(112px, 10.5vh, 152px);   /* clears the 72px sienna line */
	width: 100%;
	padding-inline: clamp(24px, 6vw, 90px);
	box-sizing: border-box;
}

html body.pm-essay #page_caption.hasbg h1 {
	font-family: var(--font-family-heading, Fraunces, Georgia, serif);
	font-size: clamp(2.6rem, 6vw, 5.25rem);   /* 41.6px -> 84px */
	font-weight: 300;
	line-height: 1.02;
	text-transform: none;                     /* screen.css:474, the template tell */
	letter-spacing: -0.01em;                  /* overrides --heading-letter-spacing: 1px */
	color: var(--color-white, #fff);
	max-width: 17ch;
	margin: 0 auto;
	text-wrap: balance;
	/* Two shadows: a tight one for edge definition against busy foliage, and a
	   very wide, very soft one that behaves like a local scrim so the type
	   survives a bright sky without darkening the whole frame. */
	text-shadow: 0 1px 2px rgba(0, 0, 0, .30), 0 2px 46px rgba(0, 0, 0, .34);
	opacity: 1;
	transform: none;
	animation: pmTongassRise 620ms var(--ease-signature, cubic-bezier(.22, 1, .36, 1)) 260ms 1 backwards;
}

@keyframes pmTongassRise {
	from { opacity: 0; transform: translate3d(0, 14px, 0); }
}

/* Only fires if page_menu_transparent is ever enabled on this page, which it is
   not today. Guarded anyway because grid.css:519 uses !important and would
   otherwise drop a 160px top margin into a bottom-anchored title block. */
html body.pm-essay #page_caption.hasbg h1.withtopbar {
	margin-top: 0 !important;
}

/* -----------------------------------------------------------------------------
 * The dateline. Real DOM text from the existing "Page Tagline (Optional)"
 * postmeta (kedynsierra-ui-ux/includes/post-meta/fields.php:431), rendered by
 * page.php:132 through wp_kses_post(). It is selectable, searchable and read
 * aloud - nothing in this entrance is CSS `content` except three purely
 * geometric marks.
 *
 * NO CONTENT IS SHIPPED FOR IT. Coordinates and a date span for a real
 * expedition belong to Kedyn's log, not to me; inventing them on a working
 * photographer's own essay would be worse than leaving it empty. If the field
 * stays empty the div does not render, the title simply stands alone, and no
 * orphan rule appears - the hairline lives on .page_tagline::before, so it
 * cannot exist without its line of text.
 *
 * Uppercase is correct HERE and wrong on the h1: at 11px with wide tracking it
 * is a caption convention, not a display gesture.
 * -------------------------------------------------------------------------- */
html body.pm-essay #page_caption.hasbg .page_tagline {
	position: relative;
	display: inline-block;
	margin: 22px 0 0;
	padding: 26px 0 0;
	border: 0;
	font-family: var(--font-family-body, 'Work Sans', Arial, sans-serif);
	font-size: 11px;
	font-weight: 500;
	line-height: 1.6;
	letter-spacing: .28em;
	text-transform: uppercase;
	/* Coordinates and date spans set with proportional figures look accidental. */
	font-variant-numeric: tabular-nums;
	color: rgba(255, 255, 255, .86);
	text-shadow: 0 1px 12px rgba(0, 0, 0, .5);
	opacity: 1;
	transform: none;
	animation: pmTongassRise 620ms var(--ease-signature, cubic-bezier(.22, 1, .36, 1)) 520ms 1 backwards;
}

html body.pm-essay #page_caption.hasbg .page_tagline::before {
	content: "";
	position: absolute;
	top: 0;
	left: calc(50% - 22px);
	width: 44px;
	height: 1px;
	background: rgba(255, 255, 255, .5);
	transform: scaleX(1);
	transform-origin: center;
	animation: pmTongassRule 520ms var(--ease-signature, cubic-bezier(.22, 1, .36, 1)) 440ms 1 backwards;
}

@keyframes pmTongassRule {
	from { transform: scaleX(0); }
}

/* -----------------------------------------------------------------------------
 * TABLET PORTRAIT AND PHONE (<= 900px)
 * The full-screen ambition and a 3:2 frame are genuinely incompatible on a
 * phone, and this is a photography site, so the frame wins: the box is capped
 * at --pm-hero-frame-cap rather than running to the full viewport height.
 *
 * THE TRADE, in numbers, because this is a judgement call and not a fact.
 * #bg_regular is background-size:cover, so a taller box shows LESS of a
 * landscape photograph. On a 414x896 phone, against the 3:2 source:
 *
 *     cap      box aspect   width of the frame shown   box height (% of screen)
 *     130vw      1 : 1.30            51%                538px  (60%)
 *     118vw      1 : 1.18            56%                489px  (55%)
 *     105vw      1 : 1.05            63%                435px  (49%)   <- shipped
 *     100vw      1 : 1.00            67%                414px  (46%)
 *      90vw      1 : 0.90            74%                373px  (42%)
 *      67vw      3 : 2 (whole)      100%                277px  (31%)
 *
 * 105vw is where a near-square editorial crop still shows about two thirds of
 * the frame while filling half the screen. Showing the WHOLE picture (67vw)
 * turns the opening into a postcard on the device most visitors use, which
 * defeats the entrance; a tall 130vw crop keeps the drama but throws away half
 * the photograph, which defeats the photograph. This is one number, and it is
 * Kedyn's to move - hand him the table, not an argument.
 *
 * The peek is kept at this width: it is the ONLY downward signal here, since
 * the sienna line is suppressed below.
 * -------------------------------------------------------------------------- */
@media only screen and (max-width: 900px) {
	html body.pm-essay {
		--pm-hero-focus-x: 47%;
		--pm-hero-peek: 64px;
		--pm-hero-frame-cap: 105vw;
	}

	html body.pm-essay #page_caption.hasbg {
		height: min(calc(100vh - var(--pm-hero-top) - var(--pm-hero-chrome) - var(--pm-hero-peek)), var(--pm-hero-frame-cap, 105vw));
		height: min(calc(100svh - var(--pm-hero-top) - var(--pm-hero-chrome) - var(--pm-hero-peek)), var(--pm-hero-frame-cap, 105vw));
		min-height: 400px;
	}

	/* Suppressed rather than shrunk: at this width the peek already carries the
	   whole downward affordance, and a 1px accent line under a title on a phone
	   is noise. */
	html body.pm-essay #page_caption.hasbg::before { display: none; }

	html body.pm-essay #page_caption.hasbg .page_title_wrapper {
		bottom: 34px;
		padding-inline: 24px;
	}

	/* !important is required and only here: grid.css:763 sets
	   `#page_caption h1 { font-size: 22px !important }` at <=767, and
	   specificity cannot beat !important. */
	html body.pm-essay #page_caption.hasbg h1 {
		/* Raised 2026-08-29 after measuring the page at real phone widths: at
		   8vw the title rendered 29.6-32.4px on 320-405px screens while the
		   section H2s below it sit at a flat 34px - the article title was the
		   SMALLEST display type on the page, on the devices most readers use.
		   9.5vw puts it at 36-41px across that range, comfortably above the H2
		   and without touching the 105vw frame cap above, which is a deliberate
		   documented composition choice rather than a defect. */
		font-size: clamp(2.25rem, 9.5vw, 3rem) !important;
		line-height: 1.06 !important;
		max-width: 15ch;
	}

	html body.pm-essay #page_caption.hasbg .page_tagline {
		margin-top: 16px;
		padding-top: 20px;
		letter-spacing: .22em;
	}

	html body.pm-essay #page_caption.hasbg::after {
		background-image: linear-gradient(
			to top,
			rgba(var(--pm-scrim), .74) 0,
			rgba(var(--pm-scrim), .44) 96px,
			rgba(var(--pm-scrim), 0) 260px
		);
	}
}

/* The WP admin bar is 46px below 783px, not 32px - and it is position:absolute
   there, so it scrolls away. Subtracting it costs 46px of hero height after the
   first scroll but guarantees nothing is cut off on load, which is the right
   trade for a review pass. Written at the SAME (0,2,1) specificity as the base
   admin-bar rule above and placed after it, so it actually wins - a
   `body.pm-essay` (0,1,1) override here would silently lose. */
@media only screen and (max-width: 782px) {
	html body.admin-bar.page-id-5983 { --pm-hero-chrome: 46px; }
}

/* -----------------------------------------------------------------------------
 * SHORT LANDSCAPE PHONES
 * min-height would otherwise make the hero taller than the whole viewport.
 * The dateline is dropped rather than crushed; the title stays.
 * -------------------------------------------------------------------------- */
@media only screen and (orientation: landscape) and (max-height: 560px) {
	html body.pm-essay {
		--pm-hero-peek: 40px;
	}

	html body.pm-essay #page_caption.hasbg {
		height: calc(100vh - var(--pm-hero-top) - var(--pm-hero-chrome) - var(--pm-hero-peek));
		height: calc(100svh - var(--pm-hero-top) - var(--pm-hero-chrome) - var(--pm-hero-peek));
		min-height: 0;
		max-height: none;
	}

	html body.pm-essay #page_caption.hasbg .page_tagline,
	html body.pm-essay #page_caption.hasbg::before { display: none; }

	html body.pm-essay #page_caption.hasbg h1 {
		font-size: clamp(1.5rem, 4.4vw, 2.125rem) !important;
		line-height: 1.08 !important;
	}

	html body.pm-essay #page_caption.hasbg .page_title_wrapper { bottom: 24px; }

	html body.pm-essay #page_caption.hasbg::after {
		background-image: linear-gradient(
			to top,
			rgba(var(--pm-scrim), .66) 0,
			rgba(var(--pm-scrim), 0) 180px
		);
	}
}

/* -----------------------------------------------------------------------------
 * REDUCED MOTION - constraint 3.
 * `animation: none` and nothing else, on all five animated selectors. No
 * compensating opacity, no transform reset, no !important. That is sufficient
 * BECAUSE of the fail-safe invariant at the top of this file: with the
 * animations removed every element falls back to its base rule, and every base
 * rule is already the finished state - photograph at scale 1 with the focal
 * crop, title and dateline opaque and in position, rule at scaleX(1), sienna
 * line at scaleY(1).
 *
 * The LAYOUT is deliberately not reverted. Full-viewport height, the focal
 * crop, the pixel-depth scrim, the mixed-case type and the peek are the design,
 * not the motion. A reduced-motion visitor gets the same opening, held still.
 * -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
	html body.pm-essay #page_caption.hasbg #bg_regular,
	html body.pm-essay #page_caption.hasbg::before,
	html body.pm-essay #page_caption.hasbg h1,
	html body.pm-essay #page_caption.hasbg .page_tagline,
	html body.pm-essay #page_caption.hasbg .page_tagline::before {
		animation: none;
	}
}


/* ===========================================================================
 * LAYOUT SYSTEM — added 2026-08-29, same page, same gate as everything above.
 *
 * Kedyn: "look at layout of what we have currently and clean it up /
 * reorganize, structure, center everything".
 *
 * CENTRING WAS NOT THE FAULT. Every block already sat on the same axis, and
 * the uniform -15px left/right difference is the scrollbar gutter, which is
 * correct. What read as "not centred" was that content appeared at SIX
 * unrelated widths, so nothing lined up with anything:
 *
 *    640px  prose and headings          the reading measure
 *    668px  pull-quotes                 a box-model leak (see below)
 *    768px  one image                   the source file is only 768px wide
 *   1100px  the gallery-slider          an arbitrary cap
 *   1245px  maps, galleries, lightboxes the container
 *   2180px  one full-bleed band         intentional
 *
 * ...over SEVEN vertical gaps with no scale: 0, 16, 41, 42, 48, 54, 55px.
 * The 41/42 and 54/55 pairs are one intended value drifting, because
 * blocks.css spaces headings in em (.kcb-heading{margin:1.6em 0 .5em}) and
 * three font sizes then yield three different gaps. Spacing was a side effect
 * of type size rather than a decision.
 *
 * THE SYSTEM: three widths, three gaps.
 *   M  = 640px   measure  - prose, headings, quotes, the small image
 *   2M = 1245px  frame    - photographs and apparatus (the column itself)
 *   bleed        untouched
 *   16 / 48 / 96 - and 16 is already the paragraph gap, and is M/40.
 *
 * -------------------------------------------------------------------------
 * MECHANISM: SPACE-BEFORE ONLY. This is the part that is easy to get wrong.
 *
 * The obvious approach - symmetric margins, let them collapse, every gap is
 * max(a,b) - DOES NOT WORK HERE, and it fails silently. Measured live: a
 * heading's bottom margin is contained inside its own .kcb-style-wrap rather
 * than collapsing through it, so adjacent gaps SUM instead of collapsing.
 * With symmetric margins the page rendered paragraph->H2 at 112 (16+96),
 * bleed->H2 at 192 (96+96), and produced nine distinct gaps - worse than the
 * seven it replaced.
 *
 * So: margin-bottom is pinned to 0 on every block and ALL spacing is
 * margin-top. Each gap is then exactly one declared value, never a sum.
 * Context (what a block FOLLOWS) is handled with :has() on the wrap.
 *
 * Two further rules that are load-bearing:
 *   - Margins go on `.kcb-style-wrap > *`, NEVER on `.kcb-style-wrap`. All 83
 *     wraps carry an inline `style="margin:0px...;padding:0px..."` from the
 *     builder, and inline beats author CSS.
 *   - LONGHANDS ONLY (margin-top/margin-bottom). The `margin` shorthand would
 *     reset margin-left/right, and that is what makes the bleed band bleed.
 *
 * Space sits ABOVE a heading and is tight below it: a head belongs to the
 * text it introduces, not to the section it follows.
 *
 * Without :has() (very old Safari) the context rules simply do not apply and
 * every gap falls back to 48 with H2 at 96 - uniform and readable, never
 * broken. Verified supported in this browser.
 * ======================================================================== */

html body.pm-essay {
	--pm-line: 16px;
	--pm-block: 48px;
	--pm-section: 96px;
}

/* --- WIDTHS ------------------------------------------------------------ */

/*
 * The pull-quote was never a different width. It was 640 + 24px padding + 4px
 * rule on a CONTENT-BOX element = 668. Centred at 668, its box hung 14px LEFT
 * of the text column while its own text sat 14px RIGHT of it, so neither edge
 * aligned - which is exactly why it read as a mistake rather than a choice.
 * border-box puts the outer edge back on the measure and lets the rule hang
 * inside it, which is what a quotation should do anyway.
 */
html body.pm-essay .kcb-style-wrap > .kcb-pull-quote {
	box-sizing: border-box;
}

/*
 * The 768px image is NOT a design decision - the source file is 768px wide
 * (naturalWidth measured) and was rendering at intrinsic size. Two of the
 * three layout proposals wanted it promoted to the 1245px frame; that is a
 * 62% upscale of an already-undersized asset and it would go soft. It joins
 * the measure instead, where it is downscaled and sharp. To make it a plate,
 * re-export it first.
 *
 * #page_content_wrapper is in the selector for specificity, not decoration:
 * screen.css's `#page_content_wrapper img { max-width:100% }` is (1,0,1) and
 * an ID beats any number of classes. Without the ID here this rule loses and
 * the image silently stays at 768.
 */
html body.pm-essay #page_content_wrapper .kcb-style-wrap > .kcb-image {
	/* min(), not the bare token: replacing the theme's max-width:100%
	   with a fixed 640px made the image 640 wide inside a 339px column on a
	   414px phone - clipped, and invisible as a scrollbar only because main
	   carries overflow-x:clip. Caught in a 414px test frame. */
	max-width: min(var(--measure-reading, 640px), 100%);
	height: auto;
	margin-left: auto;
	margin-right: auto;
}

/* 1100 among 1245s is the same near-miss as 668 among 640s. The slider's own
   frames are 1200px natural, so joining the frame tier costs a 3.7% upscale. */
html body.pm-essay .slider_wrapper.kcb-gallery-slider {
	max-width: 100%;
}

/* --- RHYTHM ------------------------------------------------------------ */

/* Everything is a block, one step apart, and carries no space after it. */
html body.pm-essay .kcb-style-wrap > * {
	margin-top: var(--pm-block);
	margin-bottom: 0;
}

/* A section opens with the long rest. */
html body.pm-essay .kcb-style-wrap > h2.kcb-heading {
	margin-top: var(--pm-section);
}

/* Whatever follows a heading belongs to it - prose or plate alike. */
html body.pm-essay .kcb-style-wrap:has(> .kcb-heading) + .kcb-style-wrap > * {
	margin-top: var(--pm-line);
}

/* Prose runs tight against prose. */
html body.pm-essay .kcb-style-wrap:has(> .kcb-paragraph) + .kcb-style-wrap > .kcb-paragraph {
	margin-top: var(--pm-line);
}

/* The one full-bleed band gets the section step. */
html body.pm-essay .kcb-style-wrap--full-bleed > * {
	margin-top: var(--pm-section);
}

/*
 * The first block must not push itself away from the hero - the 84px peek
 * under the photograph is already doing that job.
 */
html body.pm-essay .sidebar_content > .kcb-style-wrap:first-child > * {
	margin-top: 0;
}

/* ...and the last needs air before the footer, the one place a bottom
   margin is correct. */
html body.pm-essay .sidebar_content > .kcb-style-wrap:last-child > * {
	margin-bottom: var(--pm-section);
}

/* --- MOBILE ------------------------------------------------------------ */

@media only screen and (max-width: 767px) {
	html body.pm-essay {
		--pm-block: 32px;
		--pm-section: 64px;
	}
}

/* === PAGE-5983 CLEANUP PASS (2026-08-29) ================================ */

/* --- The map legend as a caption -----------------------------------------
 * The paragraph immediately after the trip map ("Everything on this line
 * is water we paddled...") reads as the map's legend, so it is dressed as
 * a caption: small, muted, centered under the chart. Position-keyed on the
 * adjacent-sibling relationship, which the canvas cleanup this same day
 * guarantees (three empty leftover paragraphs between map and legend were
 * deleted). The #-free :has() still outranks the generic spacing rule on
 * class count alone. */
html body.pm-essay .kcb-style-wrap:has(> .kcb-trip-map) + .kcb-style-wrap > .kcb-paragraph {
	max-width: min(720px, 100%);
	margin-top: var(--pm-line);
	margin-inline: auto;
	font-size: 14px;
	line-height: 1.65;
	opacity: 0.72;
	text-align: center;
}

/* --- The scholarship snapshot --------------------------------------------
 * thumb_IMG_0965_1024.jpg (Daniel Fox visit) is a 768x1024 phone photo
 * rendering at its full 768px - a snapshot at plate size. Keyed on its
 * exact width/height attributes ON PURPOSE: the only other centered
 * lightbox image is a landscape plate that must stay full width, and CSS
 * cannot select by orientation. If Kedyn re-exports this photo larger the
 * attributes change and the cap silently lifts - which fails VISIBLE
 * (image renders big again) rather than silently capping a new plate. */
html body.pm-essay #page_content_wrapper .kcb-style-wrap > .kcb-lightbox-image img[width="768"][height="1024"] {
	max-width: min(480px, 100%);
	height: auto;
}

/* --- The film ------------------------------------------------------------
 * The video-embed player sits between the reading measure and the frame -
 * wide enough to feel like the finale, not so wide it dwarfs the text. */
html body.pm-essay #page_content_wrapper .kcb-style-wrap > .kcb-video-embed {
	max-width: min(900px, 100%);
	margin-inline: auto;
}

/* --- The aurora sequence band --------------------------------------------
 * The row holding #aurora is Kedyn's 7-frame night timelapse: H2, intro,
 * then image/timestamp pairs (01:03 -> 01:54), the EXIF line and a pulled
 * quote - 18 flat children he had set to a 4-column auto-flow, which
 * scattered timestamps away from their frames and clipped images at the
 * bleed edge. This keeps his content and order untouched and re-lays the
 * same children as: title, intro, a 3-frame row, a 4-frame row (each frame
 * with its own timestamp directly beneath), then EXIF + quote as a footer.
 *
 * Placement is nth-child by design: the sequence lives in ONE row block,
 * and CSS is the presentation layer for it. If a frame is added or removed
 * in the canvas the grid degrades to auto-flow rows (ugly but complete),
 * and this block needs re-tuning - the selector map below is the single
 * place to do it.
 *
 * Images keep their NATIVE aspect ratios, bottom-aligned on a shared
 * baseline - cropping seven night-sky pans to force equal heights would be
 * editing the photographs, not presenting them (the same principle
 * portfolio-grid.css documents for filmstrip's no-filter decision). */
html body.pm-essay .kcb-row:has(#aurora) .kcb-style-wrap > * {
	margin-top: 0;
}

html body.pm-essay .kcb-row:has(#aurora) img {
	display: block;
	width: 100%;
	height: auto;
}

/* Group rhythm lives on the wrap CHILDREN, not the wraps: the canvas
 * writes inline margin:0 on every wrap, which would beat any stylesheet
 * margin on the wraps themselves. */
html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(2) > *,
html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(2n+3):not(:nth-child(n+16)) > *,
html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(17) > *,
html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(18) > * {
	margin-top: var(--pm-block);
}

/* Timestamps: children 4,6,8,...,16 - tight under their frame, set like
 * the anchor-nav's small caps. */
html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(2n+4):not(:nth-child(18)) > * {
	margin-top: 8px;
	/* Something upstream sets these paragraphs right-aligned - a timestamp
	   belongs under its frame's left edge. */
	text-align: left;
	font-size: 12px;
	letter-spacing: 0.2em;
	opacity: 0.65;
	font-variant-numeric: tabular-nums;
}

/* EXIF footnote (child 17). */
html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(17) > * {
	font-size: 13px;
	line-height: 1.6;
	opacity: 0.7;
}

@media (min-width: 768px) {
	html body.pm-essay .kcb-row:has(#aurora) {
		/* blocks.css caps a full-bleed wrap's row at the 1245px frame
		   (.kcb-style-wrap--full-bleed > .kcb-row { max-width: 1245px }).
		   The padding-inline below performs that same centering itself, so
		   with BOTH active the insets stacked: at a 2000px screen the row
		   was 1245 wide AND carried 377px side padding, squeezing all
		   eighteen children into a 490px column right of center (the
		   published-day break Kedyn screenshotted). Invisible on any
		   viewport narrower than 1245px - which every earlier verification
		   pass was - because there the cap does nothing. */
		max-width: none;
		display: grid;
		grid-template-columns: repeat(12, 1fr);
		column-gap: var(--pm-line);
		row-gap: 0;
		box-sizing: border-box;
		padding-block: var(--pm-section);
		/* Hold the sequence to the 1245px frame inside the full-bleed dark
		   band - the bleed is the backdrop, not the content width. */
		padding-inline: max(var(--pm-block), calc((100% - var(--measure-frame, 1245px)) / 2));
	}

	/* Title + intro. */
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(1) { grid-row: 1; grid-column: 1 / -1; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(2) { grid-row: 2; grid-column: 1 / -1; justify-self: center; width: 100%; max-width: min(var(--measure-reading, 640px), 100%); }

	/* First strip: three frames on a shared bottom baseline. */
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(3) { grid-row: 3; grid-column: 1 / 5; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(5) { grid-row: 3; grid-column: 5 / 9; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(7) { grid-row: 3; grid-column: 9 / 13; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(4) { grid-row: 4; grid-column: 1 / 5; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(6) { grid-row: 4; grid-column: 5 / 9; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(8) { grid-row: 4; grid-column: 9 / 13; }

	/* Second strip: four frames. */
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(9) { grid-row: 5; grid-column: 1 / 4; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(11) { grid-row: 5; grid-column: 4 / 7; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(13) { grid-row: 5; grid-column: 7 / 10; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(15) { grid-row: 5; grid-column: 10 / 13; align-self: end; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(10) { grid-row: 6; grid-column: 1 / 4; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(12) { grid-row: 6; grid-column: 4 / 7; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(14) { grid-row: 6; grid-column: 7 / 10; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(16) { grid-row: 6; grid-column: 10 / 13; }

	/* Footer: EXIF left, the NOLS quote right. */
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(17) { grid-row: 7; grid-column: 1 / 6; align-self: start; }
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:nth-child(18) { grid-row: 7; grid-column: 6 / 13; align-self: start; }
}

/* --- The sponsor wall ----------------------------------------------------
 * logo-showcase.css paints the band - and the "Sponsored by" heading wrap
 * above it - in the warm limestone surface (#edebe7). That was designed
 * for the homepage, where it is one of several alternating bands. Here it
 * lands as an inset grey rectangle with white gutters either side of it,
 * in the middle of an article whose whole visual system is white measure
 * plus FULL-BLEED dark bands - so it reads as a stray box rather than a
 * designed surface (Kedyn, 2026-08-29: "the grey background here doesn't
 * work").
 *
 * UPDATE 2026-08-29 (same day): the fill was subsequently dropped for
 * EVERY page in logo-showcase.css - on the homepage it was erasing the
 * boundary between the last logo band and the footer, which paints the
 * same token. So the background half of this rule is now redundant and
 * has been removed; what remains is the padding, which is still this
 * page's business: the 40px insets existed to hold the logos off the grey
 * edge, and spacing here comes from the space-before rhythm at the top of
 * this file instead. */
html body.pm-essay .logo-showcase,
html body.pm-essay .kcb-style-wrap:has(+ .kcb-style-wrap > .logo-showcase) {
	padding-top: 0;
}

html body.pm-essay .logo-showcase {
	padding-bottom: 0;
}

/* The aurora band on a phone -------------------------------------------
 * The band is full-bleed, and the desktop grid above (which carries the
 * inline padding) starts at 768px - so below that the heading, intro,
 * timestamps, EXIF line and quote all ran to the screen edge and slightly
 * past it (measured in a 390px frame: paragraph left edge at -7px and
 * 390px wide, against 30px/315px for a normal body paragraph).
 *
 * Pads the CHILDREN, not the wraps: the canvas writes an inline
 * padding:0px 0px 0px 0px on every .kcb-style-wrap, which no stylesheet
 * rule can outrank. :not(:has(img)) keeps the seven photographs
 * full-bleed - that is the point of the band - so only the type insets. */
@media only screen and (max-width: 767px) {
	html body.pm-essay .kcb-row:has(#aurora) > .kcb-style-wrap:not(:has(img)) > * {
		box-sizing: border-box;
		padding-left: 24px;
		padding-right: 24px;
	}
}

/* The page-scoped button tap-target rule that used to sit here is GONE.
 * It was written before the same 32px button was found on the homepage
 * CTA and the contact form's Submit, at which point fixing it per-page
 * stopped making sense - reset.css now retunes --button-font-size and
 * --button-padding for phones sitewide, which covers this page's button
 * too. Kept as a note so the page-scoped version is not reintroduced. */
