/**
 * Background — newo25
 *
 * Geometric nets that fold into solids as they drift up the page margins, and
 * two parallel streams of binary digits. Decorative only: the layer is
 * aria-hidden, holds nothing focusable, and is printed by
 * newo25_background_layer() in functions.php.
 *
 * Three rules this file keeps (previewed and agreed 2026-10-27):
 *  1. Nothing is drawn over text. Each gutter is computed from the 720px
 *     content column minus 48px of clear space, and clips what it holds, so
 *     a shape cannot reach the column even mid-rotation. Below 1100px there is
 *     no room beside the text and nothing is drawn.
 *  2. A visitor can stop it (WCAG 2.2.2): the header's pause button, kept in
 *     localStorage by assets/js/background.js.
 *  3. Reduced motion means no motion; high contrast, reduced transparency and
 *     print mean no layer.
 */

:root {
	--nw-col: 720px;   /* theme.json contentSize */
	--nw-clear: 48px;
	--nw-gw: max(0px, calc((100vw - var(--nw-col)) / 2 - var(--nw-clear)));
	--nw-teal: var(--wp--preset--color--accent-1, #0F7173);
	--nw-navy: var(--wp--preset--color--accent-3, #272643);

	/* One fold-and-unfold, one full turn, one climb up the window, one sway. */
	--nw-fold-dur: 84s;
	--nw-spin-dur: 150s;
	--nw-rise-dur: 170s;
	--nw-sway-dur: 38s;
}

/* Registered so keyframes can animate them; every face reads them. */
@property --nw-p    { syntax: "<number>"; inherits: true; initial-value: 0; }
@property --nw-spin { syntax: "<angle>";  inherits: true; initial-value: 0deg; }

/* ── Stacking ──
   The layer sits behind the page. Header and footer are opaque, so a shape
   that drifts behind them is covered, never drawn over their text. Full-width
   bands already carry their own background. */
.wp-site-blocks { position: relative; z-index: 1; }
.wp-site-blocks > header.wp-block-template-part,
.wp-site-blocks > footer.wp-block-template-part {
	position: relative;
	z-index: 2;
	background-color: var(--wp--preset--color--base, #FFFFFF);
}

/* The grey full-width bands (home: What I Do, Work) let the layer show
   through (2026-10-27). Their grey is repainted as the ink colour at 3.5%,
   which over the white page is exactly the #F7F7F8 they had: (26,26,46) at
   0.035 over 255 gives 247/247/248. The text inside stays in the content
   column, where the layer never reaches. */
main .alignfull.has-accent-5-background-color {
	background-color: rgb(26 26 46 / 0.035) !important;
}

/* ── The layer ── */

.nw-bg { display: none; }
@media (min-width: 1100px) {
	.nw-bg { display: block; }
}

.nw-gutter {
	position: fixed; top: 0; bottom: 0; z-index: 0;
	width: var(--nw-gw);
	overflow: hidden;
	pointer-events: none;
}
.nw-gutter--l { left: 0; }
.nw-gutter--r { right: 0; }

/* Home page: everything on the left, nothing on the right (2026-10-27, after
   seeing it live). The right gutter's shapes move into the left gutter beside
   the streams, drifting in its inner half so they do not sit on the digits;
   the left gutter's own small cube is hidden, so the count stays three. */
.home .nw-not-home { display: none; }
.home .nw-gutter--r { right: auto; left: 0; }
.home .nw-gutter--r .nw-anchor { left: 62%; }

/* ── Shapes ──
   .nw-anchor rises from below the window to above it (linear, very slow)
   .nw-sway   drifts side to side inside the gutter
   .nw-solid  folds and turns in 3D                                        */
.nw-anchor {
	--s: min(calc(var(--nw-gw) / 5), 96px);
	position: absolute; left: 50%; top: 0;
	width: var(--s); height: var(--s);
	margin-left: calc(var(--s) / -2);
	animation: nw-rise var(--nw-rise-dur) linear infinite;
	animation-delay: var(--rise-delay, 0s);
}
.nw-sway {
	position: absolute; inset: 0;
	perspective: 900px;
	animation: nw-sway var(--nw-sway-dur) ease-in-out infinite alternate;
	animation-delay: var(--sway-delay, 0s);
}
/* From fully below the window to fully above it: 3.2 faces covers an unfolded net. */
@keyframes nw-rise {
	from { transform: translateY(calc(100vh + var(--s) * 3.2)); }
	to   { transform: translateY(calc(var(--s) * -3.2)); }
}
@keyframes nw-sway {
	from { transform: translateX(calc(var(--nw-gw) * -0.2)); }
	to   { transform: translateX(calc(var(--nw-gw) * 0.2)); }
}

.nw-solid {
	position: absolute; inset: 0;
	transform-style: preserve-3d;
	transform: rotateX(var(--tilt, 58deg)) rotateZ(var(--nw-spin));
	animation:
		nw-fold var(--nw-fold-dur) ease-in-out infinite,
		nw-spin var(--nw-spin-dur) linear infinite;
	animation-delay: var(--delay, 0s), var(--delay, 0s);
	will-change: transform;
}
/* Flat → folds into a solid → holds → unfolds → rests flat. */
@keyframes nw-fold {
	0%, 12%   { --nw-p: 0; }
	44%, 62%  { --nw-p: 1; }
	94%, 100% { --nw-p: 0; }
}
@keyframes nw-spin { to { --nw-spin: 360deg; } }

/* A face: squares draw with a border, triangles with an SVG polygon. */
.nw-solid i {
	position: absolute; display: block;
	transform-style: preserve-3d;
	box-sizing: border-box;
}
.nw-sq {
	border: 1.25px solid var(--stroke, var(--nw-teal));
	background: color-mix(in srgb, var(--stroke, var(--nw-teal)) 6%, transparent);
}
.nw-tri svg { display: block; width: 100%; height: 100%; overflow: visible; }
.nw-tri polygon {
	fill: color-mix(in srgb, var(--stroke, var(--nw-teal)) 6%, transparent);
	stroke: var(--stroke, var(--nw-teal));
	stroke-width: 1.25;
	vector-effect: non-scaling-stroke;
	stroke-linejoin: round;
}

/* Cube: a cross-shaped net. Side faces hinge on the base and fold 90°;
   the lid hinges on the north face. */
.nw-cube i { width: var(--s); height: var(--s); }
.nw-cube .base { left: 0; top: 0; }
.nw-cube .n   { left: 0; top: calc(var(--s) * -1); transform-origin: 50% 100%; transform: rotateX(calc(var(--nw-p) * -90deg)); }
.nw-cube .lid { left: -1.25px; top: calc(var(--s) * -1 - 1.25px); transform-origin: 50% 100%; transform: rotateX(calc(var(--nw-p) * -90deg)); }
.nw-cube .s   { left: 0; top: var(--s); transform-origin: 50% 0; transform: rotateX(calc(var(--nw-p) * 90deg)); }
.nw-cube .e   { left: var(--s); top: 0; transform-origin: 0 50%; transform: rotateY(calc(var(--nw-p) * -90deg)); }
.nw-cube .w   { left: calc(var(--s) * -1); top: 0; transform-origin: 100% 50%; transform: rotateY(calc(var(--nw-p) * 90deg)); }

/* Square pyramid with equilateral sides: each triangle folds 125.26°
   (180° minus the 54.74° between a side and the base), and the apexes meet. */
.nw-pyramid { --h: calc(var(--s) * 0.8660254); }
.nw-pyramid .base { left: 0; top: 0; width: var(--s); height: var(--s); }
.nw-pyramid .n { left: 0; top: calc(var(--h) * -1); width: var(--s); height: var(--h); transform-origin: 50% 100%; transform: rotateX(calc(var(--nw-p) * -125.26deg)); }
.nw-pyramid .s { left: 0; top: var(--s); width: var(--s); height: var(--h); transform-origin: 50% 0; transform: rotateX(calc(var(--nw-p) * 125.26deg)); }
.nw-pyramid .e { left: var(--s); top: 0; width: var(--h); height: var(--s); transform-origin: 0 50%; transform: rotateY(calc(var(--nw-p) * -125.26deg)); }
.nw-pyramid .w { left: calc(var(--h) * -1); top: 0; width: var(--h); height: var(--s); transform-origin: 100% 50%; transform: rotateY(calc(var(--nw-p) * 125.26deg)); }

/* Triangular prism: three rectangles, the outer two folding 120° (an
   equilateral prism's faces meet at 60°), and a triangle at each end folding 90°. */
.nw-prism { --w: calc(var(--s) * 0.78); --l: calc(var(--s) * 1.5); --h: calc(var(--w) * 0.8660254); }
.nw-prism .base { left: calc((var(--s) - var(--w)) / 2); top: calc((var(--s) - var(--l)) / 2); width: var(--w); height: var(--l); }
.nw-prism .e { left: calc(var(--w) - 1.25px); top: -1.25px; width: var(--w); height: var(--l); transform-origin: 0 50%; transform: rotateY(calc(var(--nw-p) * -120deg)); }
.nw-prism .w { left: calc(var(--w) * -1 - 1.25px); top: -1.25px; width: var(--w); height: var(--l); transform-origin: 100% 50%; transform: rotateY(calc(var(--nw-p) * 120deg)); }
.nw-prism .n { left: -1.25px; top: calc(var(--h) * -1 - 1.25px); width: var(--w); height: var(--h); transform-origin: 50% 100%; transform: rotateX(calc(var(--nw-p) * -90deg)); }
.nw-prism .s { left: -1.25px; top: calc(var(--l) - 1.25px); width: var(--w); height: var(--h); transform-origin: 50% 0; transform: rotateX(calc(var(--nw-p) * 90deg)); }

/* ── Binary streams ──
   Two close parallel columns on one plane, turned in 3D so the pair recedes
   toward the text column, the digits falling. The digits are generated
   content: no find-in-page match, nothing to copy, nothing indexed. */
.nw-streams {
	position: absolute; top: -8%; height: 116%;
	left: 12%; width: 17.5ch;   /* two 8-digit columns and a gap */
	perspective: 520px; perspective-origin: 50% 40%;
	font: 500 12px/1.3 var(--wp--preset--font-family--fira-code, ui-monospace), ui-monospace, "SFMono-Regular", Menlo, monospace;
	/* Soft edges only: the digits cross the whole window (they faded out after
	   78% of it until 2026-10-27, and the message vanished too soon). The
	   column overhangs the window by 8% at each end, so 7% here is a thin
	   fade at the window's own edges. */
	-webkit-mask-image: linear-gradient(transparent, #000 7%, #000 93%, transparent);
	        mask-image: linear-gradient(transparent, #000 7%, #000 93%, transparent);
}
.nw-streams__plane {
	height: 100%;
	display: flex; justify-content: space-between;
	overflow: hidden;
	transform-style: preserve-3d;
	transform: rotateY(52deg) rotateX(6deg);
	opacity: .5;
}
.nw-stream {
	width: 8ch; text-align: left;
	color: var(--stroke, var(--nw-teal));
	animation: nw-fall var(--fall-dur, 70s) linear infinite;
	animation-delay: var(--delay, 0s);
}
/* The digits spell two messages (2026-10-27), in 8-bit ASCII: the first
   stream "code loves new0", the second the home page's tagline, "web
   engineer / software architect / edge & devops" (" / " for its "·", which
   is two bytes in UTF-8 and would not decode row by row; --lines and the
   duration are set per stream). Originally: "code loves new0" in 8-bit ASCII,
   one byte per row, so the whole message (15 rows and a blank row before it
   repeats, ~300px) is on screen at once. It was one bit per row first, 135
   rows, and no screen could show it whole. Two copies back to back; moving
   down by exactly one copy loops seamlessly. The distance is a row count,
   not -50%: a trailing blank row may not be laid out, which would make the
   halves unequal and the loop jump. */
.nw-stream { --lines: 16; }
/* Ten copies, so the column is always overfilled: one copy is ~300px, and
   with two (until 2026-10-27) the digits filled only the top ~600px of a
   column that is 116% of the window — the lower half of the screen stayed
   empty. Ten covers windows up to ~2,300px tall; the loop still moves by
   exactly one copy. */
.nw-stream::before {
	content: var(--bits) var(--bits) var(--bits) var(--bits) var(--bits)
		var(--bits) var(--bits) var(--bits) var(--bits) var(--bits);
	white-space: pre;
}
@keyframes nw-fall {
	from { transform: translateY(calc(var(--lines) * -1.3em)); }  /* one row = the line height, 1.3em */
	to   { transform: translateY(0); }
}

.nw-stream--a {
	--fall-dur: 16s;
	--bits: "01100011\A 01101111\A 01100100\A 01100101\A 00100000\A 01101100\A 01101111\A 01110110\A 01100101\A 01110011\A 00100000\A 01101110\A 01100101\A 01110111\A 00110000\A \A ";
}
.nw-stream--b {
	--fall-dur: 62s; --delay: -9s; --stroke: var(--nw-navy); --lines: 50;
	--bits: "01110111\A 01100101\A 01100010\A 00100000\A 01100101\A 01101110\A 01100111\A 01101001\A 01101110\A 01100101\A 01100101\A 01110010\A 00100000\A 00101111\A 00100000\A 01110011\A 01101111\A 01100110\A 01110100\A 01110111\A 01100001\A 01110010\A 01100101\A 00100000\A 01100001\A 01110010\A 01100011\A 01101000\A 01101001\A 01110100\A 01100101\A 01100011\A 01110100\A 00100000\A 00101111\A 00100000\A 01100101\A 01100100\A 01100111\A 01100101\A 00100000\A 00100110\A 00100000\A 01100100\A 01100101\A 01110110\A 01101111\A 01110000\A 01110011\A \A ";
}

/* ── The pause button (header) ──
   Hidden wherever nothing moves. At least 32px tall (WCAG 2.5.8 asks 24). */
.nw-motion {
	display: none;
	align-items: center; gap: 0.5em;
	min-height: 32px;
	padding: 4px 12px 4px 10px;
	border: 1px solid color-mix(in srgb, currentColor 22%, transparent);
	border-radius: 999px;
	background: var(--wp--preset--color--base, #FFFFFF);
	color: var(--wp--preset--color--contrast, #1A1A2E);
	font: inherit;
	font-size: var(--wp--preset--font-size--small, 0.875rem);
	font-weight: 600;
	line-height: 1;
	white-space: nowrap;
	cursor: pointer;
}
.nw-motion:hover { border-color: var(--nw-teal); }
.nw-motion:focus-visible { outline: 2px solid var(--nw-teal); outline-offset: 3px; }
.nw-motion svg { width: 12px; height: 12px; fill: currentColor; flex: none; }
.nw-motion .nw-motion__play { display: none; }
:root[data-nw-motion="paused"] .nw-motion .nw-motion__pause { display: none; }
:root[data-nw-motion="paused"] .nw-motion .nw-motion__play { display: block; }
@media (min-width: 1100px) {
	.nw-motion { display: inline-flex; }
}

/* ── Accessibility ── */

/* Paused by the visitor: everything stops where it is. */
:root[data-nw-motion="paused"] :is(.nw-anchor, .nw-sway, .nw-solid, .nw-stream) {
	animation-play-state: paused;
}

/* Reduced motion: nothing moves. Shapes rest folded, at fixed places and
   angles; the streams stand still. Nothing to pause, so no button. */
@media (prefers-reduced-motion: reduce) {
	.nw-anchor { animation: none; transform: translateY(var(--rest)); }
	.nw-sway { animation: none; }
	.nw-solid { animation: none; --nw-p: 1; --nw-spin: 28deg; }
	.nw-stream { animation: none; transform: translateY(-25%); }
	.nw-motion { display: none !important; }
}

/* High-contrast modes, more contrast, less transparency, print: no layer. */
@media (forced-colors: active), (prefers-contrast: more), (prefers-reduced-transparency: reduce), print {
	.nw-bg, .nw-motion { display: none !important; }
}
