/*
Theme Name: Umbrella Site
Theme URI: 
Author: 
Description: A dark, moody block theme for a band, solo productions, visual work and news. Built for self-hosted WordPress.
Version: 5.69
Requires at least: 6.5
Requires PHP: 7.4
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: umbrella-site
*/

/* ---- Background artwork -----------------------------------------------
   The artwork stays still while the page scrolls over it. That used to be
   `background-attachment: fixed` on <body>, which has two real costs:

   1. It forces the browser to repaint the whole background image on every
      scroll frame instead of just translating a layer. On a `cover`-sized
      image that is expensive, and it is the most likely explanation for the
      scroll-time CPU load seen in Activity Monitor.
   2. iOS Safari does not honour it. There, `cover` gets computed against the
      full document height rather than the viewport, so the artwork renders
      heavily zoomed and cropped.

   A fixed-position pseudo-element produces the same visual result, gets its
	   own compositor layer, and behaves identically on iOS. The image set below
	   upgrades pixel density while retaining the same `cover` and anchor points. ---- */
/* ---- The canvas is black, and html is what has to carry it.

   Until 4.42 only `body` was black. A body background is propagated to the
   canvas while the root element has none, and that propagation is what painted
   the area outside the document. Firefox on iOS leaves a band at the foot of
   the homepage that the propagated colour does not reach, and the band came
   out white, which is the canvas default: Home is `height: 100svh` with its
   overflow clipped, so its document stops above the bottom of that browser's
   visible web view. Painting html itself covers the canvas whatever the
   document height, on every page and in every browser. Reported 18 September.

   Body then has to give its own background up. Propagation only happens while
   the root has no background, so the moment html is black, `body`'s black
   stops being the canvas and becomes an ordinary background on the body box,
   which paints OVER the `z-index: -1` artwork layer below it and hides the
   artwork completely. Measured in the browser on 18 September: html black with
   body left black gives four black pages. So every body background in this
   theme is transparent and the black under all of it comes from html.

   `html body` rather than `body`, because theme.json emits
   `body { background-color: ... }` into the global stylesheet; two elements
   beat one wherever the two rules land in the head. ---- */
html {
	background-color: var(--wp--preset--color--background);
}

html body {
	background-color: transparent;
}

body::before {
	content: "";
	position: fixed;
	inset: 0;
	z-index: -1;
	background-image: url('assets/img/bg-interior.jpg');
	background-image: -webkit-image-set(
		url('assets/img/bg-interior.webp') 1x,
		url('assets/img/bg-interior-2x.webp') 2x
	);
	background-image: image-set(
		url('assets/img/bg-interior.webp') 1x,
		url('assets/img/bg-interior-2x.webp') 2x
	);
	background-size: cover;
	background-position: center top;
	background-repeat: no-repeat;
	pointer-events: none;
}

body.home::before {
	background-image: url('assets/img/bg-home.jpg');
	background-image: -webkit-image-set(
		url('assets/img/bg-home.webp') 1x,
		url('assets/img/bg-home-2x.webp') 2x
	);
	background-image: image-set(
		url('assets/img/bg-home.webp') 1x,
		url('assets/img/bg-home-2x.webp') 2x
	);
	background-position: center center;
}

/* The interior artwork includes a narrow solid-black strip along its top
   edge. Oversizing its fixed layer slightly crops that strip without changing
   the source image. The Visual page was excluded from this until 4.40, when
   it stopped painting itself black and started showing the same artwork as
   Audio and About. It needs the same crop for the same reason. */
body:not(.home)::before {
	inset: -2rem 0;
}

/* ---- Wordmark: white like the rest of the text since 4.59. It was grey
       (#737373), matching the original draft, and hard to read over the
       artwork. ---- */
.site-wordmark,
.site-wordmark a {
	color: var(--wp--preset--color--foreground);
}

/* ---- Homepage (5.10, at Reuben's request): the same top bar as every other
       page, and nothing in the middle, so the picture is left whole. Until
       5.09 Home had its own hero part with the three links centred in the
       window's height, where they sat on the picture's horizontal mirror axis,
       its brightest band (measured on the 4K poster, 2 October). The main is
       empty but for a hidden heading. ---- */

/* ---- Single-screen page: Home never scrolls. Audio, Updates and About
       scroll normally; Visual manages its own scrolling.

       ABOUT CAME OFF THIS RULE AT 4.17, when the contact form moved onto it.
       This rule clips overflow rather than scrolling it, so a page taller than
       the viewport loses everything past the fold with no scrollbar to say so.
       That is exactly what removed Contact from the list when the form was
       built, and About now carries the same form under a lede and a heading.
       Left clipped, the Send button was unreachable on a laptop and the reason
       would have looked like nothing at all. Do not add it back.

       Home clips its overflow, so the height has to be the space actually
       available. `100vh` on a phone is the viewport with the browser chrome
       retracted, which is taller than what you can see on first load, so the
       nav on the homepage was being pushed under the address bar with no way
       to reach it. `100svh` is the small viewport height (chrome visible) and
       is the correct unit here. The `100vh` line above each one stays as the
       fallback for browsers that don't know `svh`. ---- */
body.home:not(.blog) {
	height: 100vh;
	height: 100svh;
	overflow: hidden;
}

/* The WordPress admin bar (logged-in views only) offsets the page by 32px */
body.admin-bar.home:not(.blog) {
	height: calc(100vh - 32px);
	height: calc(100svh - 32px);
}

@media screen and (max-width: 782px) {
	body.admin-bar.home:not(.blog) {
		height: calc(100vh - 46px);
		height: calc(100svh - 46px);
	}
}

/* Footer is intentionally empty for now — the wordmark that used to sit here
   read as a stray link mid-page on sparse pages. Kept as a template part so
   there's somewhere to put a copyright line or socials later. */
.site-footer:empty {
	display: none;
}

/* ---- Audio player over the background stop (4.85) ---------------------
   Settled with Reuben on 1 October 2026 in the private artifact "Audio Player
   Draft" (palette C, buttons auto, seek bar, height 1.16, loop 180%, lift
   40%, titles 5, gap 0, shade 0.55). The player is a box centred in the
   window and kept inside its middle two thirds, a little taller than wide,
   over the background's Audio stop.

   Colours. Every stop is copper highlights on indigo glass round a black
   middle, each a little different. player.js sets two colours measured from
   the stop on screen, --ub-warm (copper) and --ub-cool (indigo), from
   data/audio-stop-colours.json; everything below derives from them, and they
   fade over 0.8 s between stops (registered with @property so they can).
   The defaults are the median of all 25 stops. Buttons and times are lighter
   than the bar so they read as the things to press.

   Shapes. The seek bar and the volume are long soft rectangles, ends rounded
   to a point and a slight swell to the middle, feathered with a small blur.
   The petal, leaves and loop pair are those of 4.69. Sizes are in cqw of the
   player box (its own container), so the whole player scales with the window.
   Below 700 px wide or 540 px tall the stage is ordinary page flow. ---- */
@property --ub-warm {
	syntax: "<color>";
	inherits: true;
	initial-value: #B26E4A;
}

@property --ub-cool {
	syntax: "<color>";
	inherits: true;
	initial-value: #46528C;
}

/* `main` in the selector outranks the constrained layout's max-width and
   the block gap's margin, which would otherwise shrink the fixed stage. */
main .ub-player-stage {
	position: fixed;
	inset: 0;
	z-index: 1;
	max-width: none;
	margin: 0;
	display: grid;
	place-items: center;
	container-type: size;
	pointer-events: none;
}

.ub-player {
	--ub-warm: #B26E4A;
	--ub-cool: #46528C;
	--ub-ratio: 1.16;
	--ub-pw: min(66.667cqw, calc(66.667cqh / var(--ub-ratio)));
	--ub-text: #F1E9E4;
	--ub-text-lit: color-mix(in srgb, var(--ub-warm) 65%, #FFE6D0);
	--ub-glow: color-mix(in srgb, var(--ub-cool) 85%, transparent);
	--ub-base: color-mix(in srgb, var(--ub-cool) 55%, transparent);
	--ub-fill-a: color-mix(in srgb, var(--ub-warm) 55%, #000);
	--ub-fill-c: color-mix(in srgb, var(--ub-warm) 65%, #FFE6D0);
	--ub-loop: color-mix(in srgb, var(--ub-warm) 45%, #FFE6D0); /* 5.13, was #C85E69: the loop follows the stop */
	--ub-btn: color-mix(in srgb, var(--ub-warm) 78%, #FFE6D0);
	--ub-btn-lit: #F4BC94;
	--ub-btn-rim: rgba(170, 176, 240, 0.45);
	--ub-btn-glow: color-mix(in srgb, var(--ub-cool) 90%, transparent);
	--ub-btn-base: rgba(140, 90, 62, 0.45);
	--ub-time: color-mix(in srgb, var(--ub-warm) 58%, #FFEBDA);
	--ub-bar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 10' preserveAspectRatio='none'%3E%3Cpath d='M0 5C.15 3.75.7 2.8 1.7 2.7Q50 1.7 98.3 2.7C99.3 2.8 99.85 3.75 100 5C99.85 6.25 99.3 7.2 98.3 7.3Q50 8.3 1.7 7.3C.7 7.2.15 6.25 0 5Z'/%3E%3C/svg%3E");
	--ub-vbar: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 10' preserveAspectRatio='none'%3E%3Cpath d='M0 5C2.5 2.9 6 2.4 12 2.35Q50 1.6 88 2.35C94 2.4 97.5 2.9 100 5C97.5 7.1 94 7.6 88 7.65Q50 8.4 12 7.65C6 7.6 2.5 7.1 0 5Z'/%3E%3C/svg%3E");
	--ub-vbar-v: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 100' preserveAspectRatio='none'%3E%3Cpath d='M 5 0 C 2.9 2.5 2.4 6 2.35 12 Q 1.6 50 2.35 88 C 2.4 94 2.9 97.5 5 100 C 7.1 97.5 7.6 94 7.65 88 Q 8.4 50 7.65 12 C 7.6 6 7.1 2.5 5 0Z'/%3E%3C/svg%3E");
	position: relative;
	box-sizing: border-box;
	display: grid;
	align-content: center;
	width: var(--ub-pw);
	height: calc(var(--ub-pw) * var(--ub-ratio));
	padding: calc(var(--ub-pw) * 0.04) 0 calc(var(--ub-pw) * 0.05);
	color: var(--ub-text);
	container-type: inline-size;
	pointer-events: auto;
	transition: --ub-warm 0.8s ease, --ub-cool 0.8s ease;
}

/* The player's centre on the artwork's centre (5.02, 5.24). The Audio stop is
   a mirror image whose axes meet at (1376, 461.5) of the 3840 x 2160 source;
   since 5.24 the background puts that point at the window's centre, so the
   player only evens its own padding (0.04 top, 0.05 bottom): the extra 0.005
   of the box width lands the content's middle there, not the box's. */
main .ub-player-stage > .ub-player {
	translate: 0 calc(var(--ub-pw) * 0.005);
}

/* A soft dark pool behind the box, so the titles stay readable on the two
   stops with a lit middle (21 and 24 of the draft). */
.ub-player::before {
	content: "";
	position: absolute;
	inset: -12% -22%;
	z-index: -1;
	background: radial-gradient(closest-side, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0.33) 62%, transparent);
	pointer-events: none;
}

.ub-player__empty {
	margin-top: 2rem;
	text-align: center;
}

/* Visually hidden, still read out. The play and loop labels live in these so
   the icons beside them survive player.js rewriting the words. */
.ub-player__play-label,
.ub-player__loop-label,
.ub-player__track-number,
.ub-player__volume-label {
	position: absolute !important;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* ---- Folders and tracks (4.87) -------------------------------------
   Folders down the left, the open folder's tracks on the right. The soft
   vertical bar that stood between them (4.87) was removed in 4.93 at
   Reuben's request; its column and gaps stay, so nothing else moves.
   Folder names are set like the site nav, small capitals in Work Sans, so
   they read as places rather than songs; the open one is lit, and the one
   holding the chosen track keeps a copper mark while another is open. Both
   columns start at the top, as a tree does. */
.ub-player__browser {
	display: grid;
	grid-template-columns: 28cqw 1cqw minmax(0, 1fr);
	column-gap: 4cqw;
	align-items: start;
	padding-inline: 4cqw;
}

.ub-player__folders {
	grid-column: 1;
	grid-row: 1;
	margin: 0;
	padding: 0;
	list-style: none;
	/* 4.97, Reuben: a little closer to the centre line (3cqw); 5.00, a
	   little more again (6cqw). Moved, not re-laid, so the tracks column
	   stays where it is. */
	transform: translateX(6cqw);
}

.ub-player__folder {
	position: relative;
	display: block;
	width: 100%;
	padding: 2.2cqw 0;
	background: none;
	border: 0;
	color: var(--ub-text);
	font-family: var(--wp--preset--font-family--nav);
	font-size: max(2.4cqw, 0.7rem);
	line-height: 1.45;
	letter-spacing: 0.14em;
	text-align: left;
	text-transform: uppercase;
	text-shadow: 0 0 10px rgba(0, 0, 0, 0.85);
	opacity: 0.6;
	cursor: pointer;
	transition: color 0.25s, opacity 0.25s;
}

.ub-player__folder:hover {
	opacity: 0.9;
}

.ub-player__folder[aria-expanded="true"] {
	color: var(--ub-text-lit);
	opacity: 1;
}

/* 4.95: no mark on the folder of the chosen track (4.87 to 4.94: a copper
   petal at its left edge); player.js still sets data-has-current. */

.ub-player__tracks {
	grid-column: 3;
	grid-row: 1;
	min-width: 0;
	margin: 0;
	padding: 0;
	/* 4.97, Reuben: the s of "As the World Changes" ends where the seek bar
	   ends (bar end 86.75cqw from the player's left, the title's end
	   84.8cqw before). Titles are 5cqw, so this holds while the player is at
	   least 352 px wide; below that their 1.1rem floor makes them longer. */
	padding-inline-start: 1.95cqw;
	list-style: none;
}

.ub-player__tracks[hidden] {
	display: none;
}

.ub-player__track-button {
	display: block;
	/* 5.67: the credits are placed against the row (below). */
	position: relative;
	width: 100%;
	padding: 2.2cqw 0;
	background: none;
	border: 0;
	color: inherit;
	font-family: inherit;
	font-size: max(5cqw, 1.1rem);
	line-height: 1.2;
	text-align: left;
	text-shadow: 0 0 10px rgba(0, 0, 0, 0.85);
	cursor: pointer;
	transition: color 0.25s;
}

.ub-player__track-button:hover,
.ub-player__track-button[aria-current="true"] {
	color: var(--ub-text-lit);
}

.ub-player__track-title {
	position: relative;
	display: inline-block;
	flex: none;
}

/* 5.63 (Reuben): the credits, in the lit colour and smaller, for the
   chosen track. 5.65: under the title for every track, on one line that
   runs as far as it runs (no wrapping; it may pass the column's edge).
   5.67 (Reuben): where the stipple streak was, in the row's bottom padding,
   out of the flow, so the list is its 5.62 height (rows 46 px at
   1512 x 777) and a choice moves nothing. 2.8cqw up from the row's foot
   (0.6cqw into the title's line box, under its descenders) keeps the last
   track's line clear of the seek bar (measured in Chrome).
   visibility rather than display, so the hidden ones are not read out
   (nor, until typed, the rest of the one shown). */
.ub-player__track-credits {
	position: absolute;
	left: 0;
	top: calc(100% - 2.8cqw);
	color: var(--ub-text-lit);
	font-size: max(2.4cqw, 0.7rem);
	line-height: 1;
	white-space: nowrap;
	visibility: hidden;
	pointer-events: none;
}

.ub-player__track-button[aria-current="true"] .ub-player__track-credits {
	visibility: visible;
}

/* They type themselves out (player.js, typeCredits): the letters not yet
   typed hold their place, unseen. */
.ub-player__track-button[aria-current="true"] .ub-player__track-credits-rest {
	visibility: hidden;
}

/* Until 5.66 the playing track rested on a streak of warm stipple under
   its title (assets/img/stipple-warm.png, still in the theme); 5.67
   (Reuben) the credits take its place. */

/* ---- Controls: the bar, and one row under it, close against it ---- */
/* 4.95: the row is three quarters of its 4.94 width, so the bar is that much
   shorter and the times, at its ends, come in with it; the buttons stay in
   the middle. */
.ub-player__controls {
	display: grid;
	width: calc((100% - 2cqw) * 0.75);
	margin: 0 auto;
	grid-template-columns: minmax(0, 1fr) auto auto auto minmax(0, 1fr);
	grid-template-areas:
		"bar bar  bar  bar bar"
		"cur loop play vol dur";
	align-items: center;
	column-gap: 5.6cqw;
	row-gap: 0.32cqw;
}

.ub-player__time {
	color: var(--ub-time);
	font-size: max(4cqw, 0.9rem); /* 4.95, was max(3.5cqw, 0.8rem) */
	line-height: 1;
	letter-spacing: 0.02em;
	font-variant-numeric: tabular-nums;
	text-shadow: 0 0 8px #000;
}

.ub-player__time[data-current] {
	grid-area: cur;
	justify-self: start;
}

.ub-player__time[data-duration] {
	grid-area: dur;
	justify-self: end;
}

/* Play (5.09): a piece of the background video, 384 x 448 of the 4K frame,
   drawn by player.js through the canvas with its black made transparent;
   the still under it is the resting frame keyed the same way, and shows
   until the canvas has drawn (data-play-live), or on its own when nothing
   can draw. Both are 10cqw tall like the row (the pause pair's height,
   4.90), 8.6cqw wide for the file's shape; the button's box was 6.88cqw.
   An oval fade on both (opaque to 55% of the way out, gone at the border)
   so the blue, when it sweeps through, arrives as a soft patch and not a
   box; the pool and the picture show through round the shape. The glow is
   the drop-shadow of what is drawn, as before. The <video> that feeds the
   canvas is in the button too, kept a pixel and invisible rather than
   display: none, which stops Safari decoding it. */
.ub-player__play {
	grid-area: play;
	display: grid;
	justify-content: center;
	width: 8.6cqw;
	padding: 0;
	background: none;
	border: 0;
	color: var(--ub-btn);
	line-height: 0;
	cursor: pointer;
	filter: drop-shadow(0 0 6px var(--ub-btn-glow));
	transition: color 0.2s, filter 0.2s;
}

.ub-player__play:hover {
	color: var(--ub-btn-lit);
	filter: drop-shadow(0 0 10px var(--ub-btn-glow));
}

.ub-player__play-still,
.ub-player__play-canvas {
	grid-area: 1 / 1;
	display: block;
	width: 8.6cqw;
	height: 10cqw;
	-webkit-mask-image: radial-gradient(closest-side, #000 55%, transparent 100%);
	mask-image: radial-gradient(closest-side, #000 55%, transparent 100%);
	pointer-events: none;
}

.ub-player__play[data-play-live] .ub-player__play-still,
.ub-player__play:not([data-play-live]) .ub-player__play-canvas {
	visibility: hidden;
}

/* 5.29 (Reuben): until music first plays (player.js puts ub-audio-played on
   <html>), Play is the copper bowl on its side, as on Play background and
   Send (#7E4A24), 8cqw tall, 80% of the piece's 10cqw box (the piece's
   shape fills about 90% of its file); then the piece as above. The same
   for Play audio's icon (.ub-music__cup). 5.31 (Reuben): the bowl in the
   stop's copper (--ub-warm, from data/audio-stop-colours.json; player.js
   paints it on the player, and on <html> for Play audio and Play
   background), no longer the fixed #7E4A24. */
.ub-player__play-cup,
.ub-music__cup {
	grid-area: 1 / 1;
	place-self: center;
	display: block;
	overflow: visible;
	fill: var(--ub-warm);
	pointer-events: none;
}

.ub-player__play-cup {
	width: calc(8cqw * 218 / 477);
	height: 8cqw;
}

html:not(.ub-audio-played) .ub-player__play-still,
html:not(.ub-audio-played) .ub-player__play-canvas,
html:not(.ub-audio-played) .ub-music__still,
html:not(.ub-audio-played) .ub-music__canvas,
html.ub-audio-played .ub-player__play-cup,
html.ub-audio-played .ub-music__cup {
	visibility: hidden;
}

.ub-player__play-video {
	position: absolute;
	width: 1px;
	height: 1px;
	opacity: 0;
	pointer-events: none;
}

/* The bowl (4.90), traced from the homepage artwork and defined once in the
   pattern. It drew Play and Pause until 5.09 and the loop until 5.07;
   since 5.29 it draws Play's sideways bowl before music first plays. */
.ub-player__defs {
	position: absolute;
	width: 0;
	height: 0;
	overflow: hidden;
}

/* The seek bar (4.95: 2.9cqw tall, was 2.4; ends rounded off, the tip's
   tangent near upright instead of about 48 degrees). Base and lit layers are the same bar, cut out by a mask; the
   fill (whose width player.js sets) clips a full width copy of the lit bar at
   the playhead, so the bar lights from the left without being squashed. Its
   ::after widens the hit area to a 44 px target without changing the shape. */
.ub-player__progress {
	grid-area: bar;
	position: relative;
	height: 2.9cqw;
	min-width: 0;
	cursor: pointer;
	container-type: inline-size;
	touch-action: none;
	filter: drop-shadow(0 0 6px var(--ub-glow));
	/* One em is 1cqw of the player for the brace (5.13): inside this box
	   cqw would be the bar's own width. Nothing here sets text. */
	font-size: 1cqw;
}

.ub-player__progress::after {
	content: "";
	position: absolute;
	inset: -16px 0;
}

.ub-player__base,
.ub-player__fill-shape {
	position: absolute;
	left: 0;
	top: 0;
	width: 100cqw;
	height: 100%;
	-webkit-mask: var(--ub-bar) center / 100% 100% no-repeat;
	mask: var(--ub-bar) center / 100% 100% no-repeat;
	filter: blur(0.6px);
}

.ub-player__base {
	background: var(--ub-base);
}

.ub-player__fill-shape {
	background: linear-gradient(to right, var(--ub-fill-a) 0%, var(--ub-warm) 70%, var(--ub-fill-c) 100%);
}

.ub-player__progress-fill {
	position: absolute;
	left: 0;
	top: 0;
	bottom: 0;
	width: 0;
	overflow: hidden;
}

/* Practice loop on the bar (5.13): the stretch from A to B lit and a brace
   over it, both placed by --loop-from and --loop-to, which player.js sets on
   the progress element (left and width are still written, but the band
   spans the whole bar and lights only that part). While only A is set both
   run from A to the playhead, the loop the next press would make, fainter,
   and the brace breathes. The colour is the stop's warm towards its light
   (--ub-loop), so it changes with the stop like the rest of the player;
   4.69 to 5.11 it was a fixed coral. */
.ub-player__loop {
	position: absolute;
	left: 0 !important;
	top: 0;
	width: 100cqw !important;
	height: 100%;
	z-index: 2;
	background: linear-gradient(to right,
		transparent var(--loop-from, 0%),
		var(--ub-loop) var(--loop-from, 0%) var(--loop-to, 0%),
		transparent var(--loop-to, 0%));
	-webkit-mask: var(--ub-bar) center / 100% 100% no-repeat;
	mask: var(--ub-bar) center / 100% 100% no-repeat;
	filter: blur(0.6px);
	opacity: 0.45;
	mix-blend-mode: screen;
	pointer-events: none;
}

.ub-player[data-loop="start"] .ub-player__loop {
	opacity: 0.2;
}

.ub-player__loop[hidden],
.ub-player__loop[hidden] + .ub-player__brace {
	display: none;
}

/* The brace, after Ableton's loop brace: a flat bar over the loop with a
   short foot down to the seek bar at each end, its inner edge on the slant.
   Sized in em (1cqw of the player, set on the progress element): the bar
   0.9em thick, the feet 0.9em wide and 0.7em deep, reaching the seek bar's
   top edge at 0.72em from the top of the 2.9em box. Never narrower than
   2.2em, so a loop of a few seconds still shows both feet. The bar's glow (the progress element's
   drop-shadow) falls on it too. */
.ub-player__brace {
	position: absolute;
	z-index: 3;
	left: var(--loop-from, 0%);
	bottom: calc(100% - 0.72em);
	width: calc(var(--loop-to, 0%) - var(--loop-from, 0%));
	min-width: 2.2em;
	height: 1.6em;
	background: linear-gradient(to bottom,
		color-mix(in srgb, var(--ub-loop) 75%, #fff) 0,
		var(--ub-loop) 0.9em,
		color-mix(in srgb, var(--ub-loop) 80%, transparent) 100%);
	-webkit-clip-path: polygon(0 0, 100% 0, 100% 100%, calc(100% - 0.12em) 100%, calc(100% - 0.9em) 0.9em, 0.9em 0.9em, 0.12em 100%, 0 100%);
	clip-path: polygon(0 0, 100% 0, 100% 100%, calc(100% - 0.12em) 100%, calc(100% - 0.9em) 0.9em, 0.9em 0.9em, 0.12em 100%, 0 100%);
	pointer-events: none;
}

.ub-player[data-loop="start"] .ub-player__brace {
	opacity: 0.5;
}

@media (prefers-reduced-motion: no-preference) {
	.ub-player[data-loop="start"] .ub-player__brace {
		animation: ub-loop-wait 1.4s ease-in-out infinite alternate;
	}
}

@keyframes ub-loop-wait {
	from { opacity: 0.3; }
	to { opacity: 0.7; }
}

/* Loop (5.13, Reuben): one word for the state, drawn here from the
   player's data-loop: loop, point a once A is set, looping once B is. The
   word is generated content with empty alt text, so the button's name stays
   the hidden label, which says what the next press does (browsers without
   the alt syntax read the word as well). It sits on the button's right
   edge, the side next to Play, and runs left, so it can change length
   without moving the row: the button keeps the 8.52 by 9.72cqw box the icon
   had (Pause's width, 4.97). Set like the folders, dim at rest as they are,
   lit in the loop's colour once a point is set; the focus ring goes round
   the word. 5.07 to 5.11: the two crescents of the Audio stop on a canvas;
   4.95 to 5.06: three bowls chasing round a circle. */
.ub-player__loop-button {
	grid-area: loop;
	justify-self: end;
	position: relative;
	width: 8.52cqw;
	height: 9.72cqw;
	padding: 0;
	background: none;
	border: 0;
	color: var(--ub-text);
	font-family: var(--wp--preset--font-family--nav);
	font-size: max(2.4cqw, 0.7rem);
	line-height: 1;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	text-shadow: 0 0 10px rgba(0, 0, 0, 0.85);
	cursor: pointer;
}

.ub-player__loop-button::before {
	content: "loop";
	content: "loop" / "";
	position: absolute;
	top: 50%;
	right: -0.14em; /* the tracking after the last letter */
	transform: translateY(-50%);
	white-space: nowrap;
	opacity: 0.6;
	transition: color 0.25s, opacity 0.25s;
}

.ub-player__loop-button:hover::before {
	opacity: 0.85;
}

.ub-player[data-loop="start"] .ub-player__loop-button::before {
	content: "point a";
	content: "point a" / "";
	color: var(--ub-loop);
	opacity: 1;
}

.ub-player[data-loop="active"] .ub-player__loop-button::before {
	content: "looping";
	content: "looping" / "";
	color: var(--ub-loop);
	opacity: 1;
}

.ub-player .ub-player__loop-button:focus-visible {
	outline: none;
}

.ub-player .ub-player__loop-button:focus-visible::before {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 3px;
}

/* Volume, which is also the mute control: a small soft bar lit to the
   level, no thumb. The level is announced by the slider itself. */
.ub-player__volume {
	grid-area: vol;
	justify-self: start;
	position: relative;
	display: flex;
	align-items: center;
	width: 12cqw;
	cursor: pointer;
	filter: drop-shadow(0 0 5px var(--ub-btn-glow));
}

.ub-player__volume-input {
	--ub-volume: 100%;
	-webkit-appearance: none;
	appearance: none;
	width: 100%;
	height: 3cqw;
	margin: 0;
	background: none;
	cursor: pointer;
	-webkit-mask: var(--ub-vbar) center / 100% 2.1cqw no-repeat;
	mask: var(--ub-vbar) center / 100% 2.1cqw no-repeat;
	filter: blur(0.4px);
}

.ub-player__volume-input::-webkit-slider-runnable-track {
	height: 3cqw;
	background: linear-gradient(to right, var(--ub-btn) 0 var(--ub-volume), var(--ub-btn-base) var(--ub-volume) 100%);
}

.ub-player__volume-input::-moz-range-track {
	height: 3cqw;
	background: var(--ub-btn-base);
}

.ub-player__volume-input::-moz-range-progress {
	height: 3cqw;
	background: var(--ub-btn);
}

.ub-player__volume-input::-webkit-slider-thumb {
	-webkit-appearance: none;
	width: 1px;
	height: 1px;
	opacity: 0;
}

.ub-player__volume-input::-moz-range-thumb {
	width: 1px;
	height: 1px;
	border: 0;
	opacity: 0;
}

/* The mask would cut a focus ring off, so the bar glows instead. */
.ub-player__volume-input:focus-visible {
	outline: none;
}

.ub-player__volume:focus-within {
	filter: drop-shadow(0 0 4px var(--wp--preset--color--foreground));
}

/* Mute, shown in place of the slider only where the platform will not let a
   page set the volume (iOS and iPadOS). */
.ub-player__mute-button {
	grid-area: vol;
	justify-self: start;
	padding: 0.4rem 0;
	background: none;
	border: none;
	color: var(--ub-time);
	font-family: var(--wp--preset--font-family--nav);
	font-size: 0.7rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	opacity: 0.7;
	cursor: pointer;
}

.ub-player__mute-button:hover,
.ub-player__mute-button:focus-visible {
	opacity: 1;
}

.ub-player__mute-button[data-muted="true"] {
	opacity: 1;
	color: var(--ub-btn-lit);
}

/* Both rules above set display, which beats the browser's own [hidden] rule,
   so the attribute player.js toggles has to be honoured explicitly. */
.ub-player__volume[hidden],
.ub-player__mute-button[hidden] {
	display: none;
}

/* Failure states. Previously the player had no way to report a problem, so a
   missing file looked identical to a broken script. */
.ub-player__status {
	margin: 1rem 0 0;
	font-family: var(--wp--preset--font-family--nav);
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	text-align: center;
	color: #D98A72;
}

/* The status paragraph is a live region and stays in the document rather than
   being toggled with `hidden`, or the message is announced to nobody: content
   inserted into a hidden region is generally not read out. Empty, it has no
   height, so dropping the margin here is all it takes to cost no space.
   Finding 28. */
.ub-player__status:empty {
	margin: 0;
}

.ub-player__status--admin {
	color: var(--wp--preset--color--accent);
}

/* Small or short windows: two thirds would be too small to read, so the
   player joins the page flow under the header at up to 26rem wide. */
@media (max-width: 700px), (max-height: 540px) {
	main .ub-player-stage {
		position: static;
		display: block;
		container-type: normal;
		pointer-events: auto;
	}

	.ub-player {
		--ub-pw: min(100%, 26rem);
		height: auto;
		margin: 0 auto;
	}

	/* In the page flow the player follows the header, not the artwork. */
	main .ub-player-stage > .ub-player {
		translate: none;
	}

	/* The pool keeps its 22% overhang where it fits and otherwise stops at the
	   window's edge: with the player the full width of the column the overhang
	   stuck out past both sides, and the right side scrolled (441 px of page at
	   390, 356 at 320, up to about 597 wide). Fades to transparent at the edge,
	   so no cut line. */
	.ub-player::before {
		inset-inline: max(-22%, calc((100% - 100vw) / 2));
	}
}

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

/* The divider under page titles was removed at Reuben's request — the
   original WordPress.com design had none. This replaces the vertical space
   it used to occupy so the heading doesn't collide with the content. */
.wp-block-post-title {
	margin-bottom: 3rem;
}

/* Header sits directly on the artwork — no panel, no blur. Legibility comes
   from a text shadow instead, which keeps the image continuous behind it. */
.site-header-bar {
	position: sticky;
	top: 0;
	z-index: 50;
	background-color: transparent;
}

.site-header-bar .wp-block-site-title a,
.site-header-bar .site-nav__link {
	text-shadow: 0 1px 10px rgba(0, 0, 0, 0.95), 0 0 4px rgba(0, 0, 0, 0.8);
}

/* A site-wide guard: WordPress applies a block-gap top margin to any element
   preceded by a sibling, and nothing should ever be pushed away from the top
   of a page. (It was added for the retired Visual1 gallery shell.) */
.wp-site-blocks > *:first-child {
	margin-top: 0 !important;
}

/* Visitors whose system asks for less motion get no smooth scrolling. The
   Visual page's snapping has its own rules further down. */
@media (prefers-reduced-motion: reduce) {
	html {
		scroll-behavior: auto;
	}
}

/* ---- Site navigation --------------------------------------------------
   Hand-written markup since 4.8, replacing core/navigation and the 64KB of
   CSS and JavaScript that block put on every page. Every value below was read
   off the block's own computed styles at 1440x900 immediately before the swap,
   so the rendered header is unchanged: an 18.4px link box with 4.8/10.4px
   padding and a 40.87px height, holding a 15.2px uppercase label at 1.824px
   tracking.

   Three declarations are here only because the block's stylesheet used to
   supply them: the font family, `display: block` (without which the vertical
   padding would not occupy space) and `text-decoration: none`. Colour is
   deliberately not set. theme.json already paints links white and hovers them
   amber, which is what the block resolved to anyway. ---- */
.site-nav {
	font-family: var(--wp--preset--font-family--nav);
}

.site-nav__list {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	list-style: none;
	margin: 0;
	padding: 0;
}

/* Padding is on every link so the mark appearing never shifts the layout.

   The current page is marked by the pair of shapes from the homepage artwork,
   the arch above and the bowl below with the word in the dark band between
   them, as the pairs face each other in the picture (4.65, replacing the 1px
   box). Both were traced from bg-home-2x.webp (arch at 13,39 and bowl at
   13,560, each 474x214 with its companion sliver) and are filled with the
   highlight preset, #7E4A24, which matches the median colour of those shapes (#7D471E).
   They are pseudo-elements with no pointer events, so the link box, its tap
   target and its measured size are unchanged. */
.site-nav__link {
	display: block;
	text-decoration: none;
	padding: 0.3rem 0.65rem;
	border-radius: 2px;
}

.site-nav__item.is-current-page {
	position: relative;
}

.site-nav__item.is-current-page::before,
.site-nav__item.is-current-page::after {
	content: "";
	position: absolute;
	left: 50%;
	width: 84%;
	height: 13px;
	transform: translateX(-50%);
	background-repeat: no-repeat;
	background-size: 100% 100%;
	filter: drop-shadow(0 0 5px rgba(126, 74, 36, 0.7));
	pointer-events: none;
}

.site-nav__item.is-current-page::before {
	top: -5px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 474 214'%3E%3Cpath d='M91 209C88 212 78 213 98 213C118 213 162 209 208 209C254 209 348 213 377 213C406 213 385 212 384 210C382 208 380 204 370 200C359 195 343 187 322 182C301 177 264 171 242 170C220 169 207 171 192 173C177 175 164 179 151 182C138 186 126 190 116 194C106 199 94 206 91 209ZM3 182C3 188 6 195 10 199C14 202 6 209 27 205C49 200 108 179 141 172C174 165 203 163 224 161C245 160 251 161 268 162C285 164 298 163 327 171C356 178 420 200 442 204C465 209 457 204 462 200C467 196 471 189 471 183C471 177 468 171 464 165C460 158 469 165 447 143C424 121 357 58 330 35C303 13 299 15 284 9C269 3 255 0 240 0C225 0 207 3 192 8C176 14 173 11 146 33C118 55 50 121 27 142C5 164 14 157 10 164C5 170 3 176 3 182Z' fill='%237E4A24'/%3E%3C/svg%3E");
}

.site-nav__item.is-current-page::after {
	bottom: -5px;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 474 214'%3E%3Cpath d='M3 30C2 36 4 40 8 47C12 53 6 49 28 71C51 93 117 156 143 179C170 201 174 199 190 204C205 210 222 213 238 213C254 213 269 210 284 204C299 198 302 201 330 178C358 155 427 89 450 67C473 45 463 52 466 46C470 40 471 36 471 31C470 26 467 18 463 15C459 11 467 4 446 9C426 13 371 33 339 40C306 47 273 50 251 52C229 54 222 53 205 51C188 49 177 50 148 43C119 36 55 13 32 9C9 4 16 10 11 13C6 17 3 24 3 30ZM91 4C92 6 92 8 102 12C111 17 134 26 149 31C164 36 177 38 193 40C209 42 223 45 245 43C267 41 303 36 324 31C345 26 362 17 372 13C382 8 383 5 384 3C384 1 396 -0 377 0C358 0 316 4 269 4C222 4 128 0 98 0C68 -0 90 2 91 4Z' fill='%237E4A24'/%3E%3C/svg%3E");
}

/* Forced colours drop background images, so the box comes back there. */
@media (forced-colors: active) {
	.site-nav__item.is-current-page > .site-nav__link {
		outline: 1px solid LinkText;
	}

	.ub-contact__submit {
		border: 1px solid ButtonText;
	}
}

.site-nav__label {
	letter-spacing: 0.12em;
	text-transform: uppercase;
	font-size: 0.95rem;
}

/* In the header bar the nav is a flex item of a space-between row, so it
   shrink-wraps and sits against the right edge. 1.5rem was the block gap the
   navigation block carried there. */
.site-header-bar .site-nav__list {
	justify-content: flex-end;
	gap: 1.5rem;
}

@media screen and (max-width: 600px) {
	.site-header-bar .site-nav__list {
		gap: 0.15rem;
	}

	.site-header-bar .site-nav__link {
		padding-right: 0.35rem;
		padding-left: 0.35rem;
	}

	.site-header-bar .site-nav__label {
		font-size: 0.8rem;
		letter-spacing: 0.06em;
	}
}

.site-wordmark {
	letter-spacing: 0.2em;
	text-transform: uppercase;
}

.page-divider {
	width: 64px;
	height: 1px;
	background-color: var(--wp--preset--color--accent);
	border: none;
	margin: 1.5rem auto;
}

.entry-list-item {
	border-top: 1px solid var(--wp--preset--color--accent);
	padding-top: 1.5rem;
	padding-bottom: 1.5rem;
}

.entry-list-item:last-child {
	border-bottom: 1px solid var(--wp--preset--color--accent);
}

.entry-list-item a {
	text-decoration: none;
}

.entry-list-item .entry-meta {
	font-family: var(--wp--preset--font-family--nav);
	letter-spacing: 0.08em;
	text-transform: uppercase;
	font-size: 0.75rem;
	opacity: 0.65;
}

/* ---- Contact form ----------------------------------------------------
   Fields are transparent with a single hairline underline, so the artwork
   stays visible through the form rather than sitting behind a panel. The
   underline picks up the amber accent on focus — the only colour change,
   which is what makes focus obvious without a browser default outline ring
   fighting the design.

   Note: the page carrying this form must not be one of the clipped 100vh
   pages. With three fields plus a heading it can exceed a short viewport, and
   the clipped rule hides overflow rather than scrolling it, which would make
   the Send button unreachable on laptops with no visible cause. That took
   Contact off the list when the form was built and took About off it at 4.17,
   when the form moved there. */

.ub-contact {
	margin-top: 2.5rem;
}

.ub-contact__notice {
	margin: 0 0 2rem;
	padding: 0.75rem 1rem;
	border: 1px solid var(--wp--preset--color--accent);
	font-size: 1rem;
}

.ub-contact__notice--error {
	border-color: #B3543C;
}

/* ---- Fields (4.95) -----------------------------------------------------
   No line and no box: the field is the black page under its label. With
   focus the label lights in the stop's colour, the lit title colour of the
   Audio player (copper mixed toward cream), fading in 0.35 s; on an error it
   takes the error colour. player.js sets --ub-warm and --ub-cool on the form
   from data/audio-stop-colours.json (about_frames, measured from each About
   stop by tests/stop-colours.py) and they fade over 0.8 s between stops; the
   defaults are the player's (the median of the Audio stops). 4.89 to 4.94:
   the player's soft bar under each field; before 4.89 a 1 px white line. */
.ub-contact {
	--ub-warm: #B26E4A;
	--ub-cool: #46528C;
	--ub-label-lit: color-mix(in srgb, var(--ub-warm) 65%, #FFE6D0);
	transition: --ub-warm 0.8s ease, --ub-cool 0.8s ease;
}

.ub-contact__field {
	display: flex;
	flex-direction: column;
	margin: 0 0 1.75rem;
}

.ub-contact__field:focus-within > .ub-contact__label {
	color: var(--ub-label-lit);
}

.ub-contact__field:has(> .has-error) > .ub-contact__label {
	color: #D98A72;
}

.ub-contact__label {
	display: block;
	margin-bottom: 0.5rem;
	font-family: var(--wp--preset--font-family--nav);
	font-size: 0.7rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--wp--preset--color--foreground);
	transition: color 0.35s ease;
}

.ub-contact__input {
	display: block;
	box-sizing: border-box;
	width: 100%;
	padding: 0.5rem 0;
	background: none;
	border: 0;
	border-radius: 0;
	color: var(--wp--preset--color--foreground);
	font-family: var(--wp--preset--font-family--body);
	font-size: var(--wp--preset--font-size--medium);
	line-height: 1.6;
}

.ub-contact__input:focus {
	outline: none;
}

/* Chrome and Safari paint autofilled fields pale blue or yellow; a black
   inset shadow covers it so the box stays invisible (4.95). */
.ub-contact__input:-webkit-autofill,
.ub-contact__input:-webkit-autofill:hover,
.ub-contact__input:-webkit-autofill:focus {
	-webkit-text-fill-color: var(--wp--preset--color--foreground);
	-webkit-box-shadow: 0 0 0 1000px #000 inset;
	caret-color: var(--wp--preset--color--foreground);
	transition: background-color 9999s 0s;
}

.ub-contact__textarea {
	resize: vertical;
	min-height: 8rem;
}

.ub-contact__error {
	order: 2;
	display: block;
	margin-top: 0.4rem;
	font-family: var(--wp--preset--font-family--nav);
	font-size: 0.75rem;
	letter-spacing: 0.04em;
	color: #D98A72;
}

.ub-contact__actions {
	margin: 2rem 0 0;
	text-align: center; /* 5.27: Send and the note in the middle */
}

/* Matches the audio player's play button, so the two interactive elements
   on the site read as the same control. */
/* R29. 5.66 (Reuben): the third line of About's opening lines, under the
   credit and set as it is (the lede's face at 1rem, centred); until 5.65 a
   small Work Sans note under Send. */
.ub-contact__privacy {
	max-width: 640px;
	margin: 0 auto;
	font-family: var(--wp--preset--font-family--body);
	font-size: 1rem;
	line-height: 1.4;
	text-align: center;
	color: var(--wp--preset--color--foreground);
}

/* 5.27 (Reuben): Send in the middle, with the bowl on its side after the
   word as on Play background, in the copper and glow of the arch and bowl
   that framed it from 4.93 to 5.26 (#7E4A24). 5.28 (Reuben): set like Play
   background, the button 1.25rem with the word in the dock's type (see
   .ub-bg-play__words) and the icon 1.1em, 22 px. 44 px tall for a finger. */
.ub-contact__submit {
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 0.55rem;
	min-height: 44px;
	padding: 0.25rem 0.5rem;
	background: none;
	border: 0;
	color: inherit;
	font-family: var(--wp--preset--font-family--nav);
	font-size: 1.25rem;
	line-height: 1;
	cursor: pointer;
}

.ub-contact__submit-icon {
	display: block;
	flex: none;
	width: calc(1.1em * 218 / 477);
	height: 1.1em;
	overflow: visible;
	fill: #7E4A24;
	filter: drop-shadow(0 0 5px rgba(126, 74, 36, 0.7));
}

.ub-contact__submit:hover,
.ub-contact__submit:focus-visible {
	color: var(--wp--preset--color--accent);
}

/* Honeypot. Kept out of the layout without display:none — some bots skip
   fields that are display:none, but will happily fill this one. */
.ub-contact__trap {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

/* ---- About lede ------------------------------------------------------
   Measured against the original WordPress.com About page (3024px-wide
   Retina capture, so device px ÷ 2 = CSS px):

     body copy      cap height 32dev, x-height 21dev, line 1 width 1237dev
     → Cormorant Garamond at 49dev = 24.5px CSS
     baseline pitch 67.5dev = 33.75px CSS → line-height 1.38

   The 640px max-width is not arbitrary. At 24.5px this string breaks after
   "mixing," only while the column sits between 616px and 657px; 640px is
   the middle of that window, so the original three-line break is reproduced
   without hard line breaks, and it still reflows normally on narrow screens. */

.about-lede {
	max-width: 640px;
	margin-left: auto;
	margin-right: auto;
	font-family: var(--wp--preset--font-family--body);
	font-size: 1.53rem;
	line-height: 1.38;
}

/* 5.64 (Reuben, 10 October): the credit line under the lede, centred, in
   the lede's face at about two thirds of its size. */
.about-credit {
	max-width: 640px;
	margin-left: auto;
	margin-right: auto;
	font-family: var(--wp--preset--font-family--body);
	font-size: 1rem;
	line-height: 1.4;
}

body .about-credit {
	margin-top: 0.4rem;
}

/* ---- Focus the shell moves (4.95) --------------------------------------
   After an in-page move site-shell.js focuses the new page's h1, or its main
   (Home has no h1, and its main is the whole window), with tabindex -1, so a
   screen reader starts there. Chrome draws its focus ring on that after a key
   press (Esc from the overview), framing Home in blue; Safari does not. The
   target is not a control, so it shows no ring. */
.wp-site-blocks[tabindex="-1"]:focus,
.wp-site-blocks main[tabindex="-1"]:focus,
.wp-site-blocks main h1[tabindex="-1"]:focus {
	outline: none;
}

/* ---- Keyboard focus --------------------------------------------------
   Everything interactive here is transparent on black: the player buttons
   have no fill, the progress bar is a hairline, and the nav links are plain
   text. The browser default focus ring is either invisible or nearly so
   against the artwork, which leaves keyboard users with no idea where they
   are. Amber matches the current-page nav box, so focus reads as part of the
   design rather than a browser artefact. */
.ub-player__track-button:focus-visible,
.ub-player__folder:focus-visible,
.ub-player__play:focus-visible,
.ub-player__loop-button:focus-visible,
.ub-player__mute-button:focus-visible,
.ub-player__progress:focus-visible,
.ub-contact__submit:focus-visible,
.site-nav__link:focus-visible,
.ub-bg-play:focus-visible,
.ub-music:focus-visible,
.site-wordmark a:focus-visible {
	outline: 2px solid var(--wp--preset--color--accent);
	outline-offset: 3px;
}

/* ---- Updates feed ----------------------------------------------------
   Font, size, line height and colour are deliberately identical to
   `.about-lede`, so the two text pages read in the same voice. What differs
   is alignment and width: `.about-lede` is a centred 640px statement, while
   this is a list of dated lines that gets scanned rather than read, so it
   runs left aligned across the full content column. */
.ub-updates {
	margin-top: 1rem;
}

/*
 * Timestamp column, then the message. Grid rather than a float or an inline
 * span, so every message starts on the same x position however long the
 * timestamp renders, and a message that wraps stays inside its own column
 * instead of running back under the date.
 *
 * `max-content` sizes the first column to the widest timestamp on the page and
 * then stops, so the gap does not grow with the longest message.
 */
.ub-updates__item {
	display: grid;
	grid-template-columns: max-content 1fr;
	column-gap: 1.4rem;
	align-items: baseline;
	margin: 0 0 1.1rem;
	font-family: var(--wp--preset--font-family--body);
	font-size: 1.53rem;
	line-height: 1.38;
	color: var(--wp--preset--color--foreground);
}

/*
 * Tabular figures so the digits sit in a true column. Cormorant Garamond
 * defaults to proportional numerals, which makes a stack of dates visibly
 * ragged even when the grid column is exact.
 */
.ub-updates__when {
	font-variant-numeric: tabular-nums;
	font-feature-settings: "tnum" 1;
	white-space: nowrap;
}

.ub-updates__item:last-child {
	margin-bottom: 0;
}

/* ---- Off-site links in the top bar -------------------------------------

   The header bar is a flex row justified space-between, so it can only ever
   have two children: the left cluster and the nav. The left cluster,
   `.site-header-left`, held the wordmark and the off-site links; since 4.67
   it holds only the links (the wordmark home link was removed at Reuben's
   request), so they sit against the left edge where the wordmark was.

   That wrapper is a `layout: default` group rather than a flex one, so the
   block editor emits no layout classes of its own and the arrangement is
   decided here. WordPress still applies a block-gap top margin to any element
   preceded by a sibling, which in a flex row would push the links down
   relative to the wordmark rather than apart from it, so it is zeroed.

   Type matches the site nav exactly: same font family, 0.95rem, uppercase,
   0.12em tracking, same 0.3rem/0.65rem padding box. Colour is left to
   theme.json, which paints links white and hovers them amber. ---- */
.site-header-left {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	/* 1.1rem between columns while the wordmark was here; 0 since the home
	   mark took its place (4.82), so the four marks keep one spacing. */
	gap: 0.35rem 0;
}

.site-header-left > * {
	margin-top: 0 !important;
	margin-bottom: 0 !important;
}

.site-social__list {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	list-style: none;
	margin: 0;
	padding: 0;
	font-family: var(--wp--preset--font-family--nav);
}

/* Each item is a flex box (5.03). As a plain list item the inline-flex link
   sat on the baseline of a 31.28px line box, so the item was 34.8px tall with
   the link at its top and every social icon's centre 2.5px above the home
   mark's and the nav's (measured in Chrome on Local at 1512 wide). With no
   line box the link is the item's whole height and the row centres it. */
.site-social__item {
	display: flex;
}

.site-social__link {
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	padding: 0.3rem 0.65rem;
	border-radius: 2px;
	text-decoration: none;
}

/* The word is off the screen at every width since 4.17, and still in the
   accessible name. Same clip technique as `.ub-visually-hidden`, written out
   here rather than reached for by adding that class in the markup, because
   `.site-social__label` is the hook the phone rules already used and one
   selector is easier to find than a class split across two files.

   It used to show above 600px and hide below it, which is why the type
   properties were here. They are gone with the pixels: nothing renders this
   text, so letter-spacing and uppercase described a box that no longer
   exists. */
.site-social__label {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Sized in em against the link's inherited body type rather than a fixed pixel
   value, so the marks scale with the rest of the header. flex-none was there
   to stop a narrow row shrinking the logo instead of the word; with the words
   gone it now just refuses to squash the mark itself. */
.site-social__icon {
	display: block;
	flex: none;
	width: 1.1em;
	height: 1.1em;
}

/* ---- Home link (4.82) ---------------------------------------------------
   The arch and bowl pair at the left end of the bar, before the off-site
   links (markup in umbrella_site_home_link_markup). Same 0.3rem/0.65rem box
   as those links so the row keeps one rhythm. The mark is a little larger
   than the 1.1em social icons because the pair is two thin shapes with the
   dark band between them. Since 5.03 it is painted in currentColor, so it
   takes the white theme.json gives links and hovers amber with the social
   icons; until then it was #7E4A24 with a warm glow and brightened on
   hover. ---- */
.site-home {
	display: inline-flex;
	align-items: center;
	padding: 0.3rem 0.65rem;
	border-radius: 2px;
	text-decoration: none;
}

.site-home__icon {
	display: block;
	flex: none;
	width: 1.35em;
	height: 1.35em;
}

/* ---- The dock (5.20) ----------------------------------------------------
   Bottom left of the window on every page but Visual: Play background
   (background.js, 5.11 and 5.14). Fixed and outside the swapped content,
   so it stays through page changes. The words start in line with the
   header's 1.5rem side padding (theme.json) less the button's own 0.5rem.
   Reuben moved it here from the centre on 2 October: centred, it met the
   privacy note under the form on About. A holder of its own so more can
   join it; only the buttons take clicks. */
.ub-dock {
	position: fixed;
	left: max(1rem, env(safe-area-inset-left));
	bottom: max(10px, env(safe-area-inset-bottom));
	z-index: 10;
	display: flex;
	align-items: center;
	gap: 0.75rem;
	pointer-events: none;
}

.ub-dock > * {
	pointer-events: auto;
}

/* ---- The music control (5.20) -------------------------------------------
   Bottom right of the window on every page, Audio and Visual included
   (Reuben, 2 October), the icon's right edge in line with the header's
   1.5rem padding less the button's 0.5rem. Under 360 px wide it would meet
   Play background (152 px from the left, itself 180 px wide with the phone
   words, measured in Chrome), so there it sits a row higher. On Visual the
   keys help (.gallery-key-help) is lifted above it.
   "Play audio" or "Pause audio" and the Audio player's own Play button, the
   copper shape cut from the background video (5.09), still while nothing
   plays and moving while the music does: the keyed still until the canvas
   has drawn (data-play-live), and alone with reduced motion or Save-Data.
   Set like Play background (the shared rules below), 2.5rem tall with the
   file's 384 by 448 shape and the player's oval fade, in the warm glow the
   nav's bowl had (4.97 to 5.19, now gone). */
/* 5.31 (Reuben): the words as close to the icon as Play background's are
   to its bowl, the button's 0.55rem gap to what is seen. The piece's shape
   starts 58 of the file's 384 px in (measured on play-loop.webp through the
   oval fade), 0.32rem of the box, so the box is pulled left by that much,
   and the bowl before first play sits at that same left edge rather than in
   the middle of the box, so the words do not move when the piece takes over.
   The glow in the stop's copper. */
.ub-music__icon {
	display: grid;
	flex: none;
	width: calc(2.5rem * 384 / 448);
	height: 2.5rem;
	margin-left: -0.32rem;
	filter: drop-shadow(0 0 6px color-mix(in srgb, var(--ub-warm) 80%, transparent));
}

.ub-music__still,
.ub-music__canvas {
	grid-area: 1 / 1;
	display: block;
	width: 100%;
	height: 100%;
	-webkit-mask-image: radial-gradient(closest-side, #000 55%, transparent 100%);
	mask-image: radial-gradient(closest-side, #000 55%, transparent 100%);
	pointer-events: none;
}

.ub-music[data-play-live] .ub-music__still,
.ub-music:not([data-play-live]) .ub-music__canvas {
	visibility: hidden;
}

/* 5.29: Play audio's bowl the size of Play background's, 1.1em of the
   1.25rem button, 22 px, centred in the 40 px box the piece takes. */
.ub-music__cup {
	justify-self: start;
	margin-left: 0.32rem;
	width: calc(1.1em * 218 / 477);
	height: 1.1em;
}

/* ---- The background's play control (5.11, 5.14) ------------------------
   background.js makes it once the manifest has a play entry: "Play
   background" and the bowl turned right, "Pause background" and the two half
   bowls while it plays. In the dock at the bottom left (5.20; centred
   before), on every page but Visual (hidden there, and with reduced motion or
   Save-Data). The words are set like the nav's labels (Work Sans, 0.95rem, 0.12em
   tracking, capitals, the header's shadow) and both are kept in one grid
   cell, right aligned against the bowl, so the box keeps the longer one's
   width and the bowl does not shift when the words change. White like the
   social icons; the bowl 1.375rem tall (1.1em of the 1.25rem button), from
   assets/img/bowl.svg#bowl. The box is 44 px tall for a finger. */
.ub-bg-play,
.ub-music {
	position: relative;
	display: flex;
	align-items: center;
	gap: 0.55rem;
	box-sizing: border-box;
	height: 44px;
	margin: 0;
	padding: 0 0.5rem;
	border: 0;
	border-radius: 2px;
	background: none;
	color: #fff;
	font-family: var(--wp--preset--font-family--nav);
	font-size: 1.25rem;
	line-height: 1;
	white-space: nowrap;
	opacity: 0.8;
	cursor: pointer;
	transition: opacity 0.15s ease;
}

.ub-bg-play[hidden],
.ub-music[hidden] {
	display: none;
}

.ub-bg-play:hover,
.ub-bg-play:focus-visible,
.ub-music:hover,
.ub-music:focus-visible {
	opacity: 1;
}

.ub-bg-play__words,
.ub-music__words,
.ub-contact__submit-word {
	display: grid;
	justify-items: end;
	font-size: 0.95rem;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	text-shadow: 0 1px 10px rgba(0, 0, 0, 0.95), 0 0 4px rgba(0, 0, 0, 0.8);
}

.ub-bg-play__word,
.ub-music__word {
	grid-area: 1 / 1;
}

.ub-bg-play[data-playing="true"] .ub-bg-play__word--play,
.ub-bg-play:not([data-playing="true"]) .ub-bg-play__word--pause,
.ub-music[data-playing="true"] .ub-music__word--play,
.ub-music:not([data-playing="true"]) .ub-music__word--pause {
	visibility: hidden;
}

.ub-bg-play__icon {
	display: block;
	flex: none;
	height: 1.1em;
	overflow: visible;
	filter: drop-shadow(0 0 5px color-mix(in srgb, var(--ub-warm) 70%, transparent)); /* 5.28: Send's glow; 5.31: in the stop's copper */
}

.ub-bg-play__icon--play {
	width: calc(1.1em * 218 / 477);
}

.ub-bg-play__icon--pause {
	width: calc(1.1em * 328 / 477);
}

/* 5.28 (Reuben): the bowl in Send's copper, Play and Pause alike.
   5.31 (Reuben): in the stop's copper instead, as the player's Play.
   player.js sets --ub-warm and --ub-cool on <html> from the last stop the
   background landed on (the Audio tile's table on sheet A, the About tile's
   on sheet B); before any stop, the registered default (the median of all
   stops). They fade over 0.8 s like the player's. */
.ub-bg-play__icon use {
	fill: var(--ub-warm);
}

html {
	transition: --ub-warm 0.8s ease, --ub-cool 0.8s ease;
}

/* 5.28 (Reuben): the words of Play background and Play audio turn amber
   (the accent, as Send) under the pointer, and with keyboard focus. */
.ub-bg-play:focus-visible .ub-bg-play__words,
.ub-music:focus-visible .ub-music__words {
	color: var(--wp--preset--color--accent);
}

@media (hover: hover) {
	.ub-bg-play:hover .ub-bg-play__words,
	.ub-music:hover .ub-music__words {
		color: var(--wp--preset--color--accent);
	}
}

.ub-bg-play[data-playing="true"] .ub-bg-play__icon--play,
.ub-bg-play:not([data-playing="true"]) .ub-bg-play__icon--pause {
	display: none;
}

/* After the shared rules, which make both relative. */
.ub-music {
	position: fixed;
	right: max(1rem, env(safe-area-inset-right));
	bottom: max(10px, env(safe-area-inset-bottom));
	z-index: 10;
}

@media screen and (max-width: 359px) {
	.ub-music {
		bottom: calc(max(10px, env(safe-area-inset-bottom)) + 44px);
	}
}

@media screen and (max-width: 600px) {
	.ub-bg-play__words,
	.ub-music__words,
	.ub-contact__submit-word {
		font-size: 0.8rem;
		letter-spacing: 0.06em;
	}
}

/* ---- Visually hidden ---------------------------------------------------

   Text that is not on screen but is still in the accessible name. Not
   `display: none` and not `visibility: hidden`, either of which would take it
   out of the accessibility tree along with the pixels, which is the opposite
   of the point.

   Used for the "(opens in a new tab)" note on the off-site links, and for the
   words YouTube and Instagram themselves once the header narrows. ---- */
.ub-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* Audio and About carry their h1 for screen readers only (4.84, Reuben: the
   nav already marks the page). The title stays first in main, so the
   constrained layout would give the next block its top gap; take it off so
   the content starts at the top of main. */
main > .wp-block-post-title.ub-visually-hidden + * {
	margin-block-start: 0;
}

/* ---- Off-site links on a narrow screen ---------------------------------

   Two more links in a bar that already wraps cost the phone header a whole
   extra row: it measured 209.01px before they were added and 250.04px after,
   at 375 wide. Hiding the words below 600px gave that back. Since 4.17 the
   words are hidden everywhere, so what is left here is the touch target.

   The padding grows rather than shrinks. An icon on its own is about 17px, and
   a 17px tap target on a phone is a miss waiting to happen, so the box is held
   at 44px square, which is the usual floor for a touch target. ---- */
@media screen and (max-width: 600px) {
	/* The three-link navigation fits on one row, but the complete 157px bar
	   still covered nearly a fifth of a 375x812 screen while scrolling. */
	.site-header-bar {
		position: static;
	}

	/* Keep the display face prominent without carrying its full desktop scale
	   into a 327px phone column. */
	h1 {
		font-size: 4rem;
	}

	/* The desktop lede reproduces the original 24.5px reference. At phone
	   width, 20px gives the copy room to breathe and keeps the same voice. */
	.about-lede {
		font-size: 1.25rem;
	}

	.about-credit,
	.ub-contact__privacy {
		font-size: 0.9375rem;
	}

	/* Grow the touchable controls without changing the desktop player. The
	   seek bar's target is its own ::after (4.85). */
	.ub-player__loop-button,
	.ub-player__mute-button,
	.ub-player__volume-input {
		box-sizing: border-box;
		min-height: 44px;
	}

	.site-social__link {
		/* border-box, or the 44px floor lands on the content and the padding
		   is added on top of it: measured at 53.59px square before this line,
		   which is 9.59px of header height bought for nothing. Width and
		   padding are set with the home link below (33px wide since 4.82). */
		box-sizing: border-box;
		justify-content: center;
		min-height: 44px;
		gap: 0;
	}

	.site-social__icon {
		width: 1.35em;
		height: 1.35em;
	}

	/* The home link (4.82) made four marks on the left, and at 44px square
	   each the row came to 384px against 342px at 390 wide, so the nav
	   wrapped to a second row again. Measured headless with the theme's
	   fonts: the nav is 178.8px and the gap between the halves 12px, which
	   leaves 136px at 375 wide for the four marks. So below 600px each mark
	   is 33px wide (4px to spare at 375, for font rendering) and still 44px
	   tall: above the 24px floor of WCAG 2.2 (2.5.8), below the 44px Apple
	   suggests in width only. */
	.site-social__link,
	.site-home {
		min-width: 33px;
		padding: 0.2rem;
	}

	.site-home {
		box-sizing: border-box;
		justify-content: center;
		min-height: 44px;
	}

	.site-home__icon {
		width: 1.4em;
		height: 1.4em;
	}
}

/* Hover-only brightening leaves these controls permanently dim on touch
   screens. They remain subtle on pointer devices and become fully legible
   where hover is unavailable. */
@media (hover: none) {
	.ub-player__volume-label,
	.ub-player__loop-button,
	.ub-player__mute-button {
		opacity: 1;
	}
}

/* Both galleries sit on the black canvas. The old slug stays during a failed
   or incomplete page migration so existing links remain usable. */
body.page-slug-visual::before,
body.page-slug-visual2::before,
body.page-slug-visuals2::before {
	background-image: none;
}

/* ---- Stills sheet gallery (R41) ----------------------------------------

   The component ships with its own demo colours and is not edited here: both
   copies of its files have to stay byte-identical, so the site's colours are
   mapped onto it from outside.

   Two classes in the selector, not one. The wrapper carries both
   `stills-sheet` and `wp-block-umbrella-stills-sheet`, and the component sets
   its own tokens on `.stills-sheet`. A single class would tie on specificity
   and leave the winner to whichever stylesheet the browser saw last, which is
   not something this theme should depend on.

   The values were sampled from the background artwork on 17 September:
   the dark tones are pure black, and the lightest one percent of the interior
   averages #BAAB9A. The focus cue uses the same light tone.             */
.stills-sheet.wp-block-umbrella-stills-sheet {
	--sheet-bg: var(--wp--preset--color--background, #000000);
	--sheet-ink: #baab9a;
	--sheet-accent: #baab9a;
}

/* Snap each contact sheet into place while browsing the overview. The stage
   pauses snapping so closing a still can restore the original position. */
html:has(body:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2)):not(.visual2-viewer-open) {
	scroll-snap-type: y mandatory;
}

:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .stills-sheet.wp-block-umbrella-stills-sheet {
	scroll-snap-align: start;
	scroll-snap-stop: always;
	min-height: 100vh;
	min-height: 100svh !important;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	margin-top: 0 !important;
	margin-bottom: 0 !important;
}

/* The row markers (0:00, 1:00) hang in the margin to the left of the sheet,
   at `right: calc(100% + 0.65rem)`. The page's own 1.5rem gutter is not wide
   enough to hold them: measured in Chromium, a marker is 26.4px wide and its
   left edge lands 12.8px off screen at 375px and at every width below about
   1150px, which is where the column stops being narrower than the viewport.
   So the sheet is inset by 3rem, the same gutter the component's own demo
   leaves for them, and it gives up that width rather than overflowing the
   column to the right. Measured, not guessed: see VERIFICATION.md. */
:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .stills-sheet.wp-block-umbrella-stills-sheet {
	width: min(calc(100% - 5rem), 125svh);
	margin-left: 5rem;
}

/* Air above and below each sheet, so two galleries on one page do not read as
   one grid. */
:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .stills-sheet.wp-block-umbrella-stills-sheet + .stills-sheet.wp-block-umbrella-stills-sheet {
	margin-top: 0;
}

/* The no-JavaScript fallback built by render.php since 4.59: the same
   curated sheet, six across, each tile cut from its atlas. It only exists in
   a browser that is not running scripts. */
/* A browser that is not running scripts shows <noscript> inline, and inside
   the wrapper's flex row that collapses the grid to nothing. With scripts
   running the browser hides <noscript> whatever this says. */
.stills-sheet > noscript {
	display: block;
	width: 100%;
}

.stills-sheet__noscript {
	display: grid;
	grid-template-columns: repeat(6, 1fr);
	gap: 2px;
	width: 100%;
}

.stills-sheet__noscript a {
	display: block;
}

.stills-sheet__noscript span {
	display: block;
	aspect-ratio: var(--stills-noscript-aspect, 1);
	background-repeat: no-repeat;
}

.gallery-number-index {
	position: fixed;
	z-index: 45;
	left: .45rem;
	top: 50%;
	transform: translateY(-50%);
	max-height: min(70svh, 580px);
	width: 3.2rem;
	overflow-y: auto;
	overscroll-behavior: contain;
	scrollbar-width: none;
	display: flex;
	flex-direction: column;
	background: rgb(0 0 0 / 72%);
}

.gallery-number-index button {
	border: 0;
	background: transparent;
	color: #9d9185;
	font: 600 .67rem/1.5 monospace;
	letter-spacing: .1em;
	padding: .18rem .3rem;
	cursor: pointer;
}

.gallery-number-index button[aria-current="true"],
.gallery-number-index button:hover,
.gallery-number-index button:focus-visible {
	color: #fff;
	background: #4a4036;
}

/* 4.92: the number Left selects, and Up and Down move, is grey. */
.gallery-number-index button.is-selected {
	color: #fff;
	background: #5c5c5c;
	outline: none;
}

/* 4.95: while a number is selected the index widens and each number shows a
   small picture of its gallery's first still to its right, the height of the
   number's line, 4:3, for seeking by colour (gallery-navigation.js). */
.gallery-number-index button {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: .35rem;
}

.gallery-number-index.is-choosing button {
	justify-content: flex-start;
}

.gallery-number-index__thumb {
	display: none;
	position: relative;
	flex: none;
	height: 1.5em;
	aspect-ratio: 4 / 3;
	overflow: hidden;
}

.gallery-number-index__thumb > span {
	position: absolute;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	background-repeat: no-repeat;
	image-rendering: pixelated;
}

.gallery-number-index.is-choosing {
	width: auto;
}

.gallery-number-index.is-choosing .gallery-number-index__thumb {
	display: block;
}

.gallery-key-help {
	position: fixed;
	z-index: 46;
	right: .8rem;
	/* 5.20: above Play audio, which is bottom right on every page. */
	bottom: calc(max(10px, env(safe-area-inset-bottom)) + 44px + .35rem);
	max-width: calc(100vw - 5.5rem);
	padding: .35rem .55rem;
	background: rgb(0 0 0 / 78%);
	color: #ddd4c9;
	font: 11px/1.35 system-ui, sans-serif;
	text-align: right;
	pointer-events: none;
}

.gallery-key-help[hidden] { display: none; }

@media (max-width: 600px) {
	.gallery-key-help { font-size: 10px; max-width: 14rem; }
	:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .stills-sheet.wp-block-umbrella-stills-sheet {
		width: min(calc(100% - 4rem), 125svh);
		margin-left: 4rem;
	}
}

/* ---- All galleries at a glance (4.88) -------------------------------
   Left arrow on the Visual page (no still open) puts this in place of the
   galleries: one numbered thumbnail per gallery, its first still, eight
   across where a gallery has six, laid out like a sheet (same column, near
   black plate, a hairline gutter). A thumbnail, Enter or the index on the
   left goes to that gallery; Esc goes back. Built in gallery-navigation.js.
   The three knobs: columns, the cell's shape (width over height) and how a
   still sits in it (cover trims the long side, contain shows all of it). */
.gallery-overview-hidden {
	display: none !important;
}

.gallery-overview {
	min-height: 100vh;
	min-height: 100svh;
	display: flex;
	align-items: center;
	box-sizing: border-box;
	padding-block: 1rem;
	scroll-snap-align: start;
}

.gallery-overview__grid {
	--gallery-overview-columns: 8;
	--gallery-overview-aspect: 1.3333;
	--gallery-overview-fit: cover;
	display: grid;
	grid-template-columns: repeat(var(--gallery-overview-columns), minmax(0, 1fr));
	gap: 3px;
	/* The galleries' width. Their 5rem left margin loses to WordPress's
	   constrained layout (margin auto !important), so they sit centred in
	   the 1100px column, measured 40px each side at 1440 wide; this matches
	   where they are, not the rule. */
	width: min(calc(100% - 5rem), 125svh);
	margin-inline: auto;
	background: #0b0b0c;
}

.gallery-overview__cell {
	appearance: none;
	position: relative;
	display: block;
	aspect-ratio: var(--gallery-overview-aspect);
	overflow: hidden;
	margin: 0;
	border: 0;
	border-radius: 0;
	padding: 0;
	background: #0b0b0c;
	cursor: pointer;
}

.gallery-overview__thumb {
	position: absolute;
	left: 50%;
	top: 50%;
	width: 100%;
	height: 100%;
	transform: translate(-50%, -50%);
	background-repeat: no-repeat;
	image-rendering: pixelated;
	pointer-events: none;
}

.gallery-overview__number {
	position: absolute;
	left: 0;
	top: 0;
	padding: .1rem .3rem;
	background: rgb(0 0 0 / 72%);
	color: #9d9185;
	font: 600 .67rem/1.5 monospace;
	letter-spacing: .1em;
	pointer-events: none;
}

.gallery-overview__cell[aria-current="true"] .gallery-overview__number,
.gallery-overview__cell:hover .gallery-overview__number,
.gallery-overview__cell:focus-visible .gallery-overview__number {
	color: #fff;
	background: #4a4036;
}

/* 4.95: no bar under the focused cell; its lit number is the cue. */
.gallery-overview__cell:focus-visible {
	outline: none;
}

/* 4.95: no bar under a focused thumbnail on the site either (the component's
   inset bottom line, kept in the curator); the index number marks the
   gallery and a still opens straight from the index. */
.wp-site-blocks .stills-sheet__cell--frame:focus-visible {
	box-shadow: none;
}

/* 4.95: reached with Tab (html.ub-tabbing, set by gallery-navigation.js), a
   thumbnail shows a blinking upright line near its left edge, like a text
   caret; steady with reduced motion. Keys and clicks show nothing. */
@keyframes ub-caret-blink {
	50% { opacity: 0; }
}

.ub-tabbing .wp-site-blocks .stills-sheet__cell--frame:focus-visible::after {
	content: "";
	position: absolute;
	left: 8%;
	top: 22%;
	bottom: 22%;
	width: 2px;
	background: #e8e2d6;
	box-shadow: 0 0 3px rgb(0 0 0 / 70%);
	animation: ub-caret-blink 1.06s steps(1) infinite;
	pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
	.ub-tabbing .wp-site-blocks .stills-sheet__cell--frame:focus-visible::after {
		animation: none;
	}
}

@media (max-width: 600px) {
	.gallery-overview__grid {
		width: min(calc(100% - 4rem), 125svh);
	}
}

/* The template part is a separate wrapper around the visible header bar.
   Keep that wrapper at the top while the Visual page scrolls; sticking only
   the inner bar would stop at the bottom of its short parent. */
body:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .wp-site-blocks > .wp-block-template-part:first-child {
	position: sticky;
	top: 0;
	z-index: 50;
}

body:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .wp-site-blocks > .wp-block-template-part:first-child .site-header-bar {
	position: static;
}

body.admin-bar:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .wp-site-blocks > .wp-block-template-part:first-child {
	top: 32px;
}

@media screen and (max-width: 782px) {
	body.admin-bar:is(.page-slug-visual, .page-slug-visual2, .page-slug-visuals2) .wp-site-blocks > .wp-block-template-part:first-child {
		top: 46px;
	}
}

/* ---- Phone header on one row (4.64) ------------------------------------
   Measured at 390x844 on live 4.63: 342px of row, and the wordmark (29px),
   its 18px gap, the three 44px icon targets (132px), the 24px gap between
   the halves and the three links (179px) came to 382px, so the links wrapped
   onto a second row. Below 600px the wordmark goes and the gap between the
   halves is 12px: 132 + 12 + 179 = 323px, which fits a 375px phone (327px of
   row). The homepage has its own hero header and is not affected. Since
   4.67 the header has no wordmark at any width, so only the gap rule is
   left. ---- */
@media screen and (max-width: 600px) {
	.site-header-bar.wp-block-group {
		gap: 0.75rem;
	}
}

/* ---- Phones in portrait on the Visual page (4.64) ----------------------
   The galleries are made to be seen landscape. iPhone Safari cannot lock
   the orientation, so a phone held upright gets this prompt in place of the
   galleries (made in gallery-navigation.js); the header stays above it so
   the other pages are still a tap away. Over an open still it covers the
   viewer too. ---- */
.visual-rotate-prompt {
	display: none;
}

@media (orientation: portrait) and (pointer: coarse) and (max-width: 600px) {
	.visual-rotate-prompt {
		position: fixed;
		z-index: 40;
		inset: 0;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		gap: 1.25rem;
		padding: 2rem;
		background: #000;
		color: #baab9a;
		font-family: var(--wp--preset--font-family--nav);
		font-size: 0.8rem;
		letter-spacing: 0.12em;
		text-transform: uppercase;
		text-align: center;
	}

	html.visual2-viewer-open .visual-rotate-prompt {
		z-index: 2147483002;
	}

	.visual-rotate-prompt__icon {
		width: 22px;
		height: 38px;
		border: 1.5px solid currentColor;
		border-radius: 4px;
		animation: visual-rotate-hint 2.4s ease-in-out infinite;
	}

	.gallery-number-index,
	.gallery-key-help {
		display: none;
	}
}

@keyframes visual-rotate-hint {
	0%, 30% { transform: rotate(0deg); }
	60%, 100% { transform: rotate(-90deg); }
}

@media (prefers-reduced-motion: reduce) {
	.visual-rotate-prompt__icon {
		animation: none;
		transform: rotate(-90deg);
	}
}

/* Forced colours would drop the bars' fills and leave nothing to see, so
   they keep their own colours. */
@media (forced-colors: active) {
	.ub-player__progress,
	.ub-player__volume-input,
	.ub-player__play-still,
	.ub-player__play-canvas,
	.ub-music__still,
	.ub-music__canvas,
	.ub-player__brace {
		forced-color-adjust: none;
	}
}

/* ---- The transition background (4.80) ---------------------------------
   background.js plays the 2m30 video between pages behind everything, and
   it replaces the still artwork wherever its media is on the server
   (functions.php adds .ub-has-bg to <body> then). The layer is printed first
   in <body>, outside .wp-site-blocks, so site-shell.js never swaps it.

   Geometry: the video covers the viewport at 16:9 (box W x H at ox, oy).
   background.js sets the stage's transform for every frame of a move and
   writes the viewport it measured into --ub-vw and --ub-vh. Before it runs,
   and without JavaScript, the rules below give each page its first picture:
   Audio and About zoomed 2.5x into their tiles, Visual under black.
   Since 5.06 one layer, the full 3840 x 2160 frame, in that box. */
body.ub-has-bg::before,
body.ub-has-bg.home::before {
	background-image: none;
}

.ub-bg {
	--ub-vw: 100vw;
	--ub-vh: 100vh;
	--ub-W: max(var(--ub-vw), calc(var(--ub-vh) * 16 / 9));
	--ub-H: calc(var(--ub-W) * 9 / 16);
	--ub-ox: calc((var(--ub-vw) - var(--ub-W)) / 2);
	--ub-oy: calc((var(--ub-vh) - var(--ub-H)) / 2);
	position: fixed;
	inset: 0;
	z-index: -1;
	overflow: hidden;
	background: #000;
	pointer-events: none;
}

.ub-bg__stage {
	position: absolute;
	inset: 0;
	transform-origin: 0 0;
	will-change: transform;
}

/* 5.08: the picture fills the 16:9 frame box itself (W x H at ox, oy, the
   box background.js and the continuation canvases measure from), not the
   window. Sized to the window with object-fit: cover, it was cut to the
   window's shape: on a window wider than 16:9 the top and bottom of the
   frame (160 source px each at 1512 x 724) were hidden, a black band with a
   straight edge above Audio and About and below row 2; on a narrower window
   the sides were hidden, a gap before the continuation. */
.ub-bg__poster,
.ub-bg__stage > .ub-bg__video {
	position: absolute;
	left: var(--ub-ox);
	top: var(--ub-oy);
	width: var(--ub-W);
	height: var(--ub-H);
	object-fit: fill;
	opacity: 0;
}

.ub-bg .is-on {
	opacity: 1;
}

/* 5.18: the two canvases that crossfade each new picture from the one before
   while the play files slow down (background.js, "frame blending"); the same
   box as the video, over it, opacity set by the script. */
.ub-bg__stage > .ub-bg__blend {
	position: absolute;
	left: var(--ub-ox);
	top: var(--ub-oy);
	width: var(--ub-W);
	height: var(--ub-H);
	z-index: 1;
	opacity: 0;
	pointer-events: none;
}

.ub-bg__black {
	position: absolute;
	inset: 0;
	background: #000;
	opacity: 0;
}

body.page-slug-visual .ub-bg__black {
	opacity: 1;
}

/* 5.61: Play's black (background.js), over the video, the grain layer and
   Visual's black, under the page: the last second of each pass rises into
   it, it holds 2 s, and Play rises from it over 0.18 s. Opacity set by the
   script on every picture the video presents. */
.ub-bg__fade {
	position: absolute;
	inset: 0;
	background: #000;
	opacity: 0;
	pointer-events: none;
}

body.page-slug-audio .ub-bg__stage {
	--ub-u: 0.358333;
}

body.page-slug-about .ub-bg__stage {
	--ub-u: 0.641146;
}

body:is(.page-slug-audio, .page-slug-about) .ub-bg__stage {
	transform: translate(
		calc(var(--ub-vw) / 2 - (var(--ub-ox) + var(--ub-W) * var(--ub-u)) * 2.5),
		calc(var(--ub-vh) / 2 - (var(--ub-oy) + var(--ub-H) * 0.213657) * 2.5)
	) scale(2.5);
}

/* 5.04: the picture past the frame's left and right edges, for the squares
   at the ends of the rows and the loop between them. background.js draws it
   (EXT there is the 0.42 here). */
.ub-bg__ext {
	position: absolute;
	top: var(--ub-oy);
	width: calc(var(--ub-W) * 0.42);
	height: var(--ub-H);
}

.ub-bg__ext--l {
	left: calc(var(--ub-ox) - var(--ub-W) * 0.42);
}

.ub-bg__ext--r {
	left: calc(var(--ub-ox) + var(--ub-W));
}

/* Home: a click on the picture zooms into the square under it (site-shell.js). */
html.ub-squares body.home {
	cursor: zoom-in;
}

/* ---- The persistent page (4.80) ----------------------------------------
   site-shell.js fades the content out and the next page's in, over the
   same 180 ms as the prototype's text. */
.ub-fade {
	transition: opacity 0.18s linear;
}

.ub-fade.ub-out {
	opacity: 0;
}

/* 5.47 (Reuben): the new page's content fades in over 0.45 s, eased out,
   so it ends about as the camera settles (1.2 s after the click); the old
   content still goes in 0.18 s. Not the fade to and from Visual, which
   follows the background's own progress. */
.ub-fade.ub-in {
	transition-duration: 0.45s;
	transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1);
}

.ub-announce {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}

/* ---- About on one screen (4.83) ----------------------------------------
   Reuben, 1 October: the About page fits the window and does not scroll;
   the message box scrolls inside itself instead.

   The page is a column exactly the window's height: the header, then <main>,
   which takes the rest and passes its spare height down to the message box.
   The box's field has a flex basis of 0 (since 5.26; before, the box had
   `height: 0`) and grows from a floor of two lines, so its rows attribute
   no longer sets the page height. Nothing is clipped. When the
   window is too short even for that floor (under about 745 px), <main> keeps
   its content height (a flex item's automatic minimum), the column runs past
   the window and the page scrolls as it did before, so Send cannot end up out
   of reach (the trap described above the contact form rules).

   The footer is empty, but its block gap would push the column 24 px past
   the window, so the gap goes.

   Phones, 600 px and narrower, keep the scrolling page: the form does not fit
   one phone screen, and the keyboard takes half of it once a field has focus.
   ---- */
@media (min-width: 601px) {
	/* 5.64 (Reuben, 10 October): the page centred on the window. The middle
	   between the top of the lede and the foot of the privacy note (since
	   5.66 the foot of Send's word and bowl) sits on
	   the window's middle, whatever the header's height: the column is a
	   grid and <main> spans all of it, under the header (whose bar stays on
	   top, z-index 50, and keeps its clicks), with the same padding above
	   and below. Before, <main> started under the header, so the content sat
	   38 px low at 1512 x 777 (lede top 188, note foot 112 px off the
	   bottom). A min-height rather than a height (a grid with a fixed height
	   would not grow the row <main> spans), so a window too short for the
	   form still scrolls. */
	body.page-slug-about .wp-site-blocks {
		display: grid;
		grid-template-columns: minmax(0, 1fr);
		grid-template-rows: auto 1fr auto;
		min-height: 100vh;
		min-height: 100svh;
	}

	body.page-slug-about .wp-site-blocks > .wp-block-template-part:first-child {
		grid-row: 1;
		grid-column: 1;
	}

	body.page-slug-about .wp-site-blocks > footer {
		grid-row: 3;
		grid-column: 1;
	}

	body.admin-bar.page-slug-about .wp-site-blocks {
		min-height: calc(100vh - 32px);
		min-height: calc(100svh - 32px);
	}

	body.page-slug-about .wp-site-blocks > * {
		flex: none;
	}

	body.page-slug-about .wp-site-blocks > footer {
		margin-block-start: 0;
	}

	/* 5.26: 5.5rem below the form (was 2rem), so Send and its note sit
	   above the row of Play background and Play audio; 5.30 (Reuben): 7rem,
	   Send and the note 24 px higher again. */
	/* 5.64: 8.5rem above the lede and below the note, from the window's
	   edges (was 3rem or 1.25rem under the header, 7rem below). At 1512 x
	   777 the message box keeps its height with the credit line added.
	   Under 720 px tall both shrink together, to 7rem at 672 px, so the
	   page stays centred while the message box is on its floor; shorter
	   than about 667 px it scrolls. */
	body.page-slug-about main.wp-block-group {
		grid-row: 1 / -1;
		grid-column: 1;
		margin-block: 0;
		display: flex;
		flex-direction: column;
		--ub-about-edge: clamp(7rem, 50vh - 14rem, 8.5rem);
		padding-top: var(--ub-about-edge) !important;
		/* 5.66: Send is the last thing on the page; its button is 44 px
		   for a finger, the word and bowl (22 px) in its middle, so the
		   visible foot is 11 px above the button's. */
		padding-bottom: calc(var(--ub-about-edge) - 11px) !important;
	}

	body.page-slug-about .ub-contact,
	body.page-slug-about .ub-contact__form {
		flex: 1 1 auto;
		display: flex;
		flex-direction: column;
	}

	/* 5.27: the form keeps the 34rem it had when the wrapped privacy note
	   set its width (main's auto margins make it shrink to its content). */
	body.page-slug-about .ub-contact {
		width: 34rem;
		max-width: 100%;
	}

	/* 5.26 (Reuben): typing starts to the right of each field's title, not
	   under it, which gives the height back to the message box. A 6rem
	   column holds the longest title (YOUR EMAIL, 84 px in Chrome); the
	   title's top sits 0.906rem below the field's, which puts its baseline on
	   the typed text's first baseline (measured 14.5 px in Chrome; grid
	   baseline alignment does not reach into a textarea). Errors go under the
	   typing. The message field takes the spare height (flex basis 0) and
	   never less than the box's floor, so a short window still scrolls. */
	body.page-slug-about .ub-contact__field {
		display: grid;
		grid-template-columns: 6rem minmax(0, 1fr);
		column-gap: 1rem;
		align-items: start;
	}

	body.page-slug-about .ub-contact__label {
		margin: 0.906rem 0 0;
	}

	body.page-slug-about .ub-contact__error {
		grid-column: 2;
	}

	body.page-slug-about .ub-contact__field:has(.ub-contact__textarea) {
		flex: 1 1 0;
		min-height: 4.75rem;
		grid-template-rows: minmax(0, 1fr) auto;
	}

	/* 5.25 (Reuben, for now, to review once Audio grows): the lede 89 px
	   lower, its top level with Audio's first line (222 px at 1512 x 728,
	   was 133) and clear of the bright band at the top of the picture. The
	   message box gives up the height (176 to 87 px at 728 tall). */
	/* 5.30 (Reuben): one line (34 px) higher, the fields left where they
	   were by the same 34 px as padding under it (padding, since a margin
	   there collapses into the block gap below). */
	/* 5.64: the lede starts at main's padding; the 34 px under it now
	   sits under the credit line, 0.4rem below the lede. */
	body.page-slug-about .about-lede {
		margin-top: 0;
		padding-bottom: 0;
	}

	body.page-slug-about .about-credit {
		margin-top: 0.4rem;
		padding-bottom: 0;
	}

	body.page-slug-about .ub-contact__textarea {
		align-self: stretch;
		height: 100%;
		min-height: 4.75rem;
		resize: none;
		overflow-y: auto;
		overscroll-behavior: contain;
	}
}

@media screen and (min-width: 601px) and (max-width: 782px) {
	body.admin-bar.page-slug-about .wp-site-blocks {
		min-height: calc(100vh - 46px);
		min-height: calc(100svh - 46px);
	}
}

/* Windows 960 px tall or less: the gaps in the form close up so the message
   box keeps three lines or more down to about 770 px. */
@media (max-height: 960px) and (min-width: 601px) {

	body.page-slug-about .ub-contact__field {
		margin-bottom: 0.875rem;
	}

	body.page-slug-about .ub-contact__actions {
		margin-top: 1rem;
	}

}

/* ---- About's opening lines and the gap to the form (5.66) ---------------
   Reuben, 10 October: the privacy note is the third line under the lede
   and the credit, 0.4rem under the credit as the credit is under the lede;
   the gap from there to YOUR EMAIL closes from 82 px (5.64 at 1512 x 777)
   to 2rem, 1.25rem on phones. The empty paragraph left in the page's
   content after the lede (zero high, but its 24 px block gap pushed the
   form down) is not shown; the content is not changed. */
body.page-slug-about .wp-block-post-content > p:empty {
	display: none;
}

body.page-slug-about .ub-contact {
	margin-top: 0.4rem;
}

body.page-slug-about .ub-contact__form {
	margin-top: 2rem;
}

/* ---- About on one phone screen (5.66) -----------------------------------
   Reuben, 10 October: on a phone About fits the box above the controls
   (.wp-site-blocks since 5.56) and the message box scrolls inside itself
   rather than the page, as on the desktop since 4.83, because the moves to
   other pages start from the page's scroll. The page is a column the box's
   height: the header, then <main>, whose spare height goes to the message
   box (flex basis 0, never under 4.75rem, three lines). The field titles
   sit beside the typing as on the desktop (5.26), which gives 81 px back at
   390 wide (typing from 6rem plus 1rem in, 230 px wide at 390). A box too
   short even for the floor (an iPhone SE in Safari) still scrolls, a
   little. Not centred top to bottom: there is no room under the header for
   the mirror of the space it takes. */
@media screen and (max-width: 600px) {
	body.page-slug-about .wp-site-blocks {
		display: flex;
		flex-direction: column;
	}

	body.page-slug-about .wp-site-blocks > * {
		flex: none;
	}

	body.page-slug-about .wp-site-blocks > footer {
		margin-block-start: 0;
	}

	body.page-slug-about main.wp-block-group {
		flex: 1 1 auto;
		display: flex;
		flex-direction: column;
		margin-block-start: 0;
		padding-top: 0.75rem !important;
		padding-bottom: 0.75rem !important;
	}

	body.page-slug-about .about-lede {
		margin-top: 0;
	}

	body.page-slug-about .ub-contact,
	body.page-slug-about .ub-contact__form {
		flex: 1 1 auto;
		display: flex;
		flex-direction: column;
	}

	body.page-slug-about .ub-contact__form {
		margin-top: 1.25rem;
	}

	body.page-slug-about .ub-contact__field {
		display: grid;
		grid-template-columns: 6rem minmax(0, 1fr);
		column-gap: 1rem;
		align-items: start;
		margin-bottom: 0.875rem;
	}

	body.page-slug-about .ub-contact__label {
		margin: 0.906rem 0 0;
	}

	body.page-slug-about .ub-contact__error {
		grid-column: 2;
	}

	body.page-slug-about .ub-contact__field:has(.ub-contact__textarea) {
		flex: 1 1 0;
		min-height: 4.75rem;
		grid-template-rows: minmax(0, 1fr) auto;
	}

	body.page-slug-about .ub-contact__textarea {
		align-self: stretch;
		height: 100%;
		min-height: 4.75rem;
		resize: none;
		overflow-y: auto;
		overscroll-behavior: contain;
	}

	body.page-slug-about .ub-contact__actions {
		margin-top: 1rem;
	}
}

/* Windows 820 px tall or less: until 5.64 the title sat where Audio's
   does (main's top padding 1.25rem). Since 5.64 About is centred on the
   window at every height, so nothing changes here. */

/* ---- The entry screen (5.36) ------------------------------------------
   A first visit with no background chosen: functions.php puts
   html.ub-entry-on in <head> before anything paints, background-entry.js
   fills .ub-entry with the three choices. Black over everything, the
   poster hidden under it. The choices in the site's type (Work Sans
   capitals, letter spaced, as Play background), the copper bowl after the
   word as the marker: on the suggested choice at rest, on the one under
   the pointer or with focus. One line under each. After a choice the
   others go, the line gives way to a bar in the stop's copper, and the
   screen fades over 0.6 s. */
.ub-entry {
	display: none;
}

html.ub-entry-on .ub-entry,
.ub-entry.is-leaving {
	position: fixed;
	inset: 0;
	z-index: 100000;
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	padding: 2rem max(1.5rem, env(safe-area-inset-right)) 2rem max(1.5rem, env(safe-area-inset-left));
	background: #000;
	color: #fff;
	font-family: var(--wp--preset--font-family--nav);
	opacity: 1;
	transition: opacity 0.6s ease;
}

.ub-entry.is-leaving {
	opacity: 0;
	pointer-events: none;
}

html.ub-entry-on {
	overflow: hidden;
}

html.ub-entry-on .ub-bg__poster {
	visibility: hidden;
}

.ub-entry__list {
	display: grid;
	gap: 2rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.ub-entry__item {
	display: grid;
	justify-items: start;
	gap: 0.15rem;
	transition: opacity 0.4s ease, visibility 0s linear 0s;
}

.ub-entry.is-chosen .ub-entry__item:not(.is-chosen) {
	opacity: 0;
	visibility: hidden;
	transition: opacity 0.4s ease, visibility 0s linear 0.4s;
}

.ub-entry__choice {
	display: flex;
	align-items: center;
	gap: 0.55rem;
	box-sizing: border-box;
	min-height: 44px;
	margin: 0 0 0 -0.5rem;
	padding: 0 0.5rem;
	border: 0;
	border-radius: 2px;
	background: none;
	color: #fff;
	font: inherit;
	font-size: 1.5rem;
	line-height: 1;
	letter-spacing: 0.16em;
	text-transform: uppercase;
	cursor: pointer;
	opacity: 0.85;
	transition: opacity 0.15s ease;
}

.ub-entry__choice:hover,
.ub-entry__choice:focus {
	opacity: 1;
	outline: none;
}

.ub-entry__choice:focus-visible .ub-entry__word {
	color: var(--wp--preset--color--accent);
}

@media (hover: hover) {
	.ub-entry__choice:hover .ub-entry__word {
		color: var(--wp--preset--color--accent);
	}
}

.ub-entry__bowl {
	display: block;
	flex: none;
	width: calc(1.1em * 218 / 477);
	height: 1.1em;
	overflow: visible;
	opacity: 0;
	filter: drop-shadow(0 0 5px color-mix(in srgb, var(--ub-warm) 70%, transparent));
	transition: opacity 0.2s ease;
}

.ub-entry__bowl use {
	fill: var(--ub-warm);
}

/* The marker: on the suggested choice until the pointer or focus is on
   another, then there. */
.ub-entry__item.is-suggested .ub-entry__bowl,
.ub-entry__choice:hover .ub-entry__bowl,
.ub-entry__choice:focus .ub-entry__bowl,
.ub-entry__item.is-chosen .ub-entry__bowl {
	opacity: 1;
}

.ub-entry__list:has(.ub-entry__choice:hover) .ub-entry__item:not(:hover) .ub-entry__bowl,
.ub-entry__list:has(.ub-entry__choice:focus) .ub-entry__item.is-suggested:not(:focus-within) .ub-entry__bowl {
	opacity: 0;
}

.ub-entry__line {
	max-width: 62ch;
	margin: 0;
	color: rgba(255, 255, 255, 0.62);
	font-size: 0.85rem;
	line-height: 1.4;
	letter-spacing: 0.04em;
}

.ub-entry__item.is-chosen .ub-entry__line {
	display: none;
}

.ub-entry__bar {
	box-sizing: border-box;
	width: 100%;
	min-width: 12rem;
	height: 2px;
	margin-top: 0.6rem;
	overflow: hidden;
	background: rgba(255, 255, 255, 0.18);
}

.ub-entry__bar[hidden] {
	display: none;
}

.ub-entry__fill {
	display: block;
	width: 100%;
	height: 100%;
	background: var(--ub-warm);
	box-shadow: 0 0 6px color-mix(in srgb, var(--ub-warm) 80%, transparent);
	transform: scaleX(0);
	transform-origin: 0 50%;
}

@media screen and (max-width: 600px) {
	.ub-entry__choice {
		font-size: 1.25rem;
	}

	.ub-entry__line {
		font-size: 0.8rem;
	}
}

/* ---- The visuals choice (5.36; 5.55 on every page) ---------------------
   "Visuals: 4K · 1080p · No visuals", the current one lit, in the dock's row
   after Play visuals, on every page but Visual (background-entry.js hides
   it there). Each option is a button: a press keeps that choice and
   reloads the same page in it. Smaller and dimmer than Play visuals.
   Phones: a row above the dock, which Play visuals and Play audio fill. */
.ub-bg-choice {
	order: 2;
	display: flex;
	align-items: center;
	gap: 0.3em;
	box-sizing: border-box;
	height: 44px;
	margin: 0;
	padding: 0 0.5rem;
	border-radius: 2px;
	color: #fff;
	font-family: var(--wp--preset--font-family--nav);
	font-size: 0.72rem;
	line-height: 1;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	white-space: nowrap;
	text-shadow: 0 1px 10px rgba(0, 0, 0, 0.95), 0 0 4px rgba(0, 0, 0, 0.8);
	opacity: 0.7;
	transition: opacity 0.15s ease;
}

.ub-bg-choice[hidden] {
	display: none;
}

.ub-bg-choice:hover,
.ub-bg-choice:focus-within {
	opacity: 1;
}

.ub-bg-choice__opt {
	appearance: none;
	display: flex;
	align-items: center;
	height: 44px;
	margin: 0;
	padding: 0 0.15em;
	border: 0;
	background: none;
	color: inherit;
	font: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
	text-shadow: inherit;
	cursor: pointer;
}

.ub-bg-choice__opt.is-current {
	cursor: default;
}

.ub-bg-choice__opt:not(.is-current):focus-visible {
	color: var(--wp--preset--color--accent);
	opacity: 1;
}

@media (hover: hover) {
	.ub-bg-choice__opt:not(.is-current):hover {
		color: var(--wp--preset--color--accent);
		opacity: 1;
	}
}

.ub-bg-choice__opt,
.ub-bg-choice__dot {
	opacity: 0.5;
}

.ub-bg-choice__opt.is-current {
	opacity: 1;
}

@media screen and (max-width: 600px) {
	.ub-bg-choice {
		position: fixed;
		left: max(1rem, env(safe-area-inset-left));
		bottom: calc(max(10px, env(safe-area-inset-bottom)) + 44px); /* 5.56: 44, clear of the dock's row (40 overlapped it by 4 px) */
		font-size: 0.66rem;
		letter-spacing: 0.08em;
	}

	/* Room for that row under About's last line when scrolled to the end
	   (the privacy note met it by 6 px at 375 x 812). */
	body.page-slug-about main.wp-block-group {
		padding-bottom: calc(64px + 44px) !important;
	}
}

/* 5.55: under 360 px Play audio sits a row higher (above), so the
   choice row goes one more row up. */
@media screen and (max-width: 359px) {
	.ub-bg-choice {
		bottom: calc(max(10px, env(safe-area-inset-bottom)) + 88px);
	}
}

/* ---- Phones: the page stops above the controls (5.56) -------------------
   Reuben, 8 October: on a phone the page's text slid under the rows at the
   foot of the screen (Play visuals, Play audio, the visuals choice) while
   it scrolled. Now every page but Visual scrolls inside .wp-site-blocks,
   which ends above those rows (--ub-band: the dock's 10 px and 44 px, the
   choice row's 44 px above it, 2 px clear), so nothing passes under them.
   The background, the dock and the player's stage stay fixed to the
   window. site-shell.js reads and sets this box's scroll (scroller()).
   Visual keeps the window's scroll: its galleries and keys use it. */
@media screen and (max-width: 600px) {
	:root {
		--ub-band: calc(max(10px, env(safe-area-inset-bottom)) + 90px);
	}

	body:not(.page-slug-visual):not(.page-slug-visual2):not(.page-slug-visuals2) .wp-site-blocks {
		position: fixed;
		top: 0;
		right: 0;
		bottom: var(--ub-band);
		left: 0;
		overflow-x: hidden;
		overflow-y: auto;
		overscroll-behavior-y: contain;
		-webkit-overflow-scrolling: touch;
	}

	/* The room 5.36 left under About's privacy note for the row is now
	   below the box; the page ends with the usual space. */
	body.page-slug-about main.wp-block-group {
		padding-bottom: 1.5rem !important;
	}
}

@media screen and (max-width: 359px) {
	:root {
		--ub-band: calc(max(10px, env(safe-area-inset-bottom)) + 134px);
	}
}

/* "1080p": the p stays lower case in the capitals. */
.ub-lower {
	text-transform: none;
}

/* ---- Load visuals (5.37) ----------------------------------------------
   Where the play files go through Media Source the control first reads
   LOAD VISUALS; pressed, the words' box becomes a bar filling towards the
   bowl (--ub-load, 0 to 1, from background.js) while the forward play file
   is fetched; then PLAY BACKGROUND as before. All three words share the
   grid cell, so the box keeps the longest one's width. */
.ub-bg-play__bar {
	grid-area: 1 / 1;
	align-self: center;
	justify-self: stretch;
	height: 2px;
	overflow: hidden;
	background: rgba(255, 255, 255, 0.25);
	visibility: hidden;
}

.ub-bg-play__fill {
	display: block;
	height: 100%;
	background: #fff;
	box-shadow: 0 0 6px rgba(255, 255, 255, 0.6);
	transform: scaleX(var(--ub-load, 0));
	transform-origin: 0 50%;
}

.ub-bg-play[data-state="loading"] .ub-bg-play__bar {
	visibility: visible;
}

.ub-bg-play[data-state="load"] .ub-bg-play__word--play,
.ub-bg-play[data-state="loading"] .ub-bg-play__word,
.ub-bg-play:not([data-state="load"]) .ub-bg-play__word--load {
	visibility: hidden;
}

.ub-bg-play[data-state="loading"] {
	cursor: progress;
}

/* ---- The grain layer (5.38) -------------------------------------------
   background-grain.js draws the clean 1080p picture with its grain on one
   window-sized WebGL canvas between the stage and the black (so Visual's
   black still covers it); clear outside the frame. Shown once it has drawn. */
.ub-bg__grain {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	opacity: 0;
	pointer-events: none;
}

