/*
	Homeless in Toronto — visual identity
	Portrait-first. Designed for the phone, then allowed to grow.

	The look: a Toronto block at night. Salt-stained concrete, asphalt, sodium
	amber light that turns cold LED-white after the first snow and never turns
	back. Receipts are thermal paper. Nothing here is glossy, rounded, or
	celebratory, because of what the piece is about.
*/

/* ─────────────────────────────────────────────────────────────── tokens */

:root {
	/* Surfaces */
	--ink:			#08090a;
	--ink-2:		#101113;
	--ink-3:		#191a1d;
	--hair:			#2b2d31;
	--hair-soft:	rgba(214, 206, 190, 0.14);

	/* Type */
	--paper:		#ece7dc;
	--paper-2:		#d9d3c6;
	--muted:		#8e8b85;
	--muted-2:		#65635f;

	/* Season. Sodium now, LED later — the city's own light got colder. */
	--key:			#e0a44f;
	--key-dim:		#8c6531;
	--cold:			#8fb0c4;
	--accent:		var(--key);
	--accent-dim:	var(--key-dim);

	--loss:			#c4543f;
	--gain:			#8fa27a;

	--display: "Arial Narrow", "Helvetica Neue Condensed", "Roboto Condensed",
		"Liberation Sans Narrow", "Franklin Gothic Medium", system-ui, sans-serif;
	--body: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
	--mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, "Liberation Mono", monospace;

	--pad: 16px;
	--sheet-max: 34rem;
	--dur: 0.28s;
	--ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

body[data-season="winter"] {
	--accent: var(--cold);
	--accent-dim: #4d6474;
}

/* ─────────────────────────────────────────────────────────────── reset */

*, *::before, *::after { box-sizing: border-box; }

html, body {
	margin: 0;
	padding: 0;
	min-height: 100%;
	background: var(--ink);
	color: var(--paper);
	font-family: var(--body);
	font-size: 16px;
	line-height: 1.5;
	-webkit-text-size-adjust: 100%;
	overscroll-behavior: none;
}

body {
	overflow: hidden;
	transition: background var(--dur) var(--ease);
}
body[data-phase="reckoning"] { overflow: auto; }

h1, h2, h3 { margin: 0; font-weight: 700; letter-spacing: 0.01em; }
p { margin: 0 0 0.75em; }
ul { margin: 0; padding: 0; list-style: none; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--paper); }

:focus-visible {
	outline: 2px solid var(--accent);
	outline-offset: 3px;
}

.visually-hidden {
	position: absolute !important;
	width: 1px; height: 1px;
	margin: -1px; padding: 0;
	overflow: hidden; clip-path: inset(50%);
	white-space: nowrap; border: 0;
}

.skip-link {
	position: fixed;
	top: 0; left: 50%;
	transform: translate(-50%, -110%);
	z-index: 90;
	padding: 12px 20px;
	min-height: 44px;
	background: var(--paper);
	color: var(--ink);
	font: 700 0.9rem/1.4 var(--body);
	text-decoration: none;
}
.skip-link:focus { transform: translate(-50%, 0); }

/* ─────────────────────────────────────────────────────────────── stage */

#stage {
	position: fixed;
	inset: 0;
	z-index: 0;
}
#stage canvas { display: block; width: 100%; height: 100%; }

#vignette {
	position: fixed;
	inset: 0;
	z-index: 2;
	pointer-events: none;
	/* Two stops: a wide soft fall-off, then a harder corner crush, so the
	   board sits in the frame instead of running out of it. */
	background:
		radial-gradient(128% 96% at 50% 44%, transparent 34%, rgba(4, 5, 6, 0.55) 78%, rgba(2, 3, 4, 0.86) 100%);
}

/*
	Film grain. A 64px tiled noise sprite, stepped eight times a second so it
	moves like film rather than sliding like a texture. Costs the GPU nothing —
	it is a background-position animation on one composited layer, outside WebGL
	entirely. Goes static under reduced motion via the global rule further down.
*/
#grain {
	position: fixed;
	inset: 0;
	z-index: 1;
	pointer-events: none;
	opacity: 0.05;
	mix-blend-mode: overlay;
	background-repeat: repeat;
	background-size: 64px 64px;
	will-change: background-position;
	animation: grain-shift 1s steps(1, end) infinite;
}

@keyframes grain-shift {
	0%   { background-position:   0px   0px; }
	12.5%{ background-position: -17px   9px; }
	25%  { background-position:  11px -21px; }
	37.5%{ background-position: -29px -13px; }
	50%  { background-position:  23px  17px; }
	62.5%{ background-position:  -7px  27px; }
	75%  { background-position:  31px  -5px; }
	87.5%{ background-position: -23px -31px; }
	100% { background-position:   0px   0px; }
}

/* ───────────────────────────────────────────────────────────── the intro */
/*
	The one warm image in the piece, and it is over in twelve seconds. The 3D
	layer does the street; this layer only carries the type and the skip target,
	so time-to-first-interaction is one tap regardless of what the camera is
	doing behind it.
*/
#intro {
	position: fixed;
	inset: 0;
	z-index: 55;
	pointer-events: none;
	background: #05060a;
}
#intro[hidden] { display: none; }

/*
	v0.4: the intro is four painted frames, cross-dissolved, each with its own
	slow transform. Previously it was a camera move through a dark 3D scene and it
	read as nothing happening.

	Both the fade and the Ken Burns are compositor-only properties, so a
	full-screen 2048px image moving for fourteen seconds costs the main thread
	nothing and the WebGL layer behind it is not even drawing.
*/
.intro__frames {
	position: absolute;
	inset: 0;
	overflow: hidden;
}
.intro__frame {
	position: absolute;
	inset: 0;
	background-position: center;
	background-size: cover;
	background-repeat: no-repeat;
	opacity: 0;
	will-change: opacity, transform;
	transform: scale(1.06);
}
/* The active frame fades up over a second and a half and keeps moving. */
.intro__frame[data-on="true"] { opacity: 1; }

/* A vignette, so the type at the bottom always has something to sit on. */
.intro__vig {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background:
		linear-gradient(to bottom, rgba(5, 6, 10, 0.55) 0%, rgba(5, 6, 10, 0) 26%,
			rgba(5, 6, 10, 0) 44%, rgba(5, 6, 10, 0.86) 88%, rgba(5, 6, 10, 0.96) 100%);
}

/* Under reduced motion every caption is listed at once on the held frame. */
.intro__all {
	margin: 1.1rem auto 0;
	max-width: 30rem;
	text-align: left;
	display: grid;
	gap: 0.5rem;
}
.intro__all[hidden] { display: none; }
.intro__all li {
	font: 500 0.86rem/1.45 var(--body);
	color: var(--paper-2);
	padding-left: 0.9rem;
	border-left: 2px solid var(--accent-dim);
}

.intro__type {
	position: absolute;
	left: 0; right: 0;
	bottom: 18vh;
	padding: 0 var(--pad);
	text-align: center;
	opacity: 0;
}
.intro__type[data-show="true"] { opacity: 1; transition: opacity 0.7s var(--ease); }
/* The wordmark is only for the last beat; the first three carry a line alone. */
.intro__mark[hidden] { display: none; }
.intro__mark {
	transition: opacity 0.6s var(--ease);
	/* Same retune as .title for the longer v0.5 name — see the note there. */
	font: 700 clamp(2.2rem, 14vw, 5rem)/0.9 var(--display);
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--paper);
	margin: 0 0 0.6rem;
	text-shadow: 0 2px 24px rgba(4, 5, 6, 0.85);
}
.intro__line {
	font: 400 clamp(0.95rem, 4.4vw, 1.3rem)/1.4 var(--display);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--accent);
	margin: 0;
	text-shadow: 0 2px 18px rgba(4, 5, 6, 0.9);
}

.intro__skip {
	position: absolute;
	inset: 0;
	width: 100%;
	pointer-events: auto;
	appearance: none;
	background: transparent;
	border: 0;
	cursor: pointer;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-end;
	gap: 0.25rem;
	padding: 0 0 calc(2rem + env(safe-area-inset-bottom));
	color: var(--muted);
	font: 700 0.74rem/1 var(--mono);
	letter-spacing: 0.22em;
	text-transform: uppercase;
}
.intro__skip > span:first-child {
	min-height: 44px;
	display: flex;
	align-items: center;
	padding: 0 1.2rem;
	border: 1px solid var(--hair-soft);
}
.intro__skip:hover > span:first-child { border-color: var(--accent); color: var(--accent); }
.intro__skiphint {
	font-size: 0.6rem;
	letter-spacing: 0.14em;
	color: var(--muted-2);
}

/* ────────────────────────────────────────────────────── character select */
/*
	Two large painted portraits, chosen before the board appears. This click is
	also the gesture that starts the audio context — browsers require a real one,
	and the previous build had none until the player found a toggle in the HUD.
*/
#charsel {
	z-index: 58;
	background:
		radial-gradient(120% 80% at 50% 0%, #15161b 0%, var(--ink) 62%);
}
.charsel__inner {
	max-width: 52rem;
	margin: 0 auto;
	/*
		Bottom padding clears the fixed "Need help now" button, which sits at
		z-index 90 in that corner. Without it the last lines on this screen sit
		underneath it at 360x640. Since v0.7 the last line is the sound notice
		rather than the "Choose for me" button, but the clearance is still needed —
		text half-covered by a floating control is no better than a button is.
	*/
	padding: calc(1.4rem + env(safe-area-inset-top)) var(--pad)
		calc(5rem + env(safe-area-inset-bottom));
	text-align: center;
}
.charsel__title {
	font: 700 clamp(1.7rem, 7.4vw, 3.1rem)/1.02 var(--display);
	letter-spacing: 0.02em;
	text-transform: uppercase;
	margin: 0.3rem 0 1.4rem;
}

.charsel__cards {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	gap: 0.9rem;
	/*
		Capped. Left to fill a 1920px viewport these became 780px wide, and a
		square portrait at 780px is 780px tall — which pushed the title, the
		composite note and the "Choose for me" button off the bottom of the screen
		entirely. The portraits are the hero of this screen but they are not the
		whole of it.
	*/
	max-width: 38rem;
	margin: 0 auto;
}
@media (max-width: 30rem) {
	.charsel__cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; }
}

.charcard {
	appearance: none;
	display: block;
	width: 100%;
	text-align: left;
	background: var(--ink-2);
	border: 1px solid var(--hair);
	padding: 0;
	cursor: pointer;
	color: inherit;
	overflow: hidden;
	transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.charcard:hover,
.charcard:focus-visible {
	border-color: var(--accent);
	transform: translateY(-2px);
}
.charcard__shot {
	display: block;
	width: 100%;
	/*
		Height-driven, so the card can never outgrow the screen it sits on. 32vh
		rather than 38 because at 640px tall the taller portrait pushed the
		"Choose for me" button down under the fixed help control; the whole screen
		now fits above the fold on the shortest target viewport.
	*/
	height: clamp(148px, 32vh, 290px);
	object-fit: cover;
	/* The portraits are painted off-centre with the subject to the right. */
	object-position: 64% 20%;
	background: var(--ink-3);
}
/*
	`display: block` is the fix, not the padding — the padding was already here.

	This element is a <span> wrapping two block children. An inline box does apply
	horizontal padding to itself, but block children inside it escape to the
	containing block's full width, so the name and the role sat hard against the
	card's border while the declared 0.95rem did nothing. Measured before the fix:
	1px from card edge to the first glyph.

	Nothing about the padding is selection-dependent, so the text cannot shift when
	a card gains its border colour — the selected state changes hue, not width.
*/
.charcard__text {
	display: block;
	padding: 0.85rem 1.05rem 1rem;
	/* Minimum 44px target is satisfied by the whole card, which is far larger. */
}
.charcard__name {
	display: block;
	font: 700 1.35rem/1.15 var(--display);
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: var(--paper);
}
.charcard__work {
	display: block;
	margin-top: 0.3rem;
	/*
		Shrinks and wraps rather than overflowing. At 360px wide "Delivery driver,
		contract" ran off the right edge of its card and lost its last letter, which
		reads as a broken layout rather than as a long job title.
	*/
	font: 500 clamp(0.7rem, 3.1vw, 0.82rem)/1.35 var(--body);
	color: var(--muted);
	overflow-wrap: break-word;
}

/*
	Order on this screen, since v0.7 changed it: title, the two cards, "Choose for me",
	then the composite note and the sound notice. The three options share the first
	frame and the two qualifying lines follow them — see the note in index.html.
*/
#charsel-random { margin-top: 0.9rem; }
.charsel__note {
	margin: 1rem 0 0.5rem;
	font: 500 0.72rem/1.6 var(--mono);
	letter-spacing: 0.08em;
	color: var(--muted-2);
}
.charsel__sound {
	margin: 0;
	font: 500 0.74rem/1.5 var(--mono);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--accent-dim);
}
.btn--wide { width: 100%; max-width: 22rem; }

/* ───────────────────────────────────────────────────── need help now */
/*
	One fixed button above every layer, instead of three links repeated inside
	every receipt and dilemma. Always reachable by pointer and by keyboard, and
	out of the way until it is wanted.
*/
.helpbtn {
	position: fixed;
	right: calc(0.7rem + env(safe-area-inset-right));
	bottom: calc(0.7rem + env(safe-area-inset-bottom));
	z-index: 90;			/* above every modal and scrim */
	appearance: none;
	min-height: 44px;
	padding: 0 0.85rem;
	display: inline-flex;
	align-items: center;
	gap: 0.4rem;
	background: rgba(12, 13, 16, 0.92);
	border: 1px solid var(--hair);
	color: var(--muted);
	cursor: pointer;
	font: 700 0.68rem/1 var(--mono);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	backdrop-filter: blur(3px);
}
.helpbtn:hover, .helpbtn:focus-visible { color: var(--accent); border-color: var(--accent); }
body[data-phase="boot"] .helpbtn,
body[data-phase="intro"] .helpbtn { display: none; }

.helppanel {
	position: fixed;
	right: calc(0.7rem + env(safe-area-inset-right));
	bottom: calc(3.6rem + env(safe-area-inset-bottom));
	z-index: 91;
	width: min(21rem, calc(100vw - 1.4rem));
	background: var(--ink-2);
	border: 1px solid var(--hair);
	padding: 1rem;
	box-shadow: 0 18px 50px rgba(0, 0, 0, 0.7);
}
.helppanel[hidden] { display: none; }
.helppanel h2 {
	font: 700 0.74rem/1.3 var(--mono);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--muted);
	margin: 0 0 0.7rem;
}
.helppanel .btn { margin-top: 0.6rem; width: 100%; }

/* ────────────────────────────────────────────────────── assigned identity */
.identity {
	border-top: 1px solid var(--hair);
	border-bottom: 1px solid var(--hair);
	padding: 1rem 0;
	margin-bottom: 1.6rem;
}
.identity[hidden] { display: none; }
.identity__who {
	font: 700 1.25rem/1.25 var(--display);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--paper);
	margin: 0 0 0.35rem;
}
.identity__note {
	font: 500 0.68rem/1.5 var(--mono);
	letter-spacing: 0.08em;
	color: var(--muted-2);
	margin: 0;
}

/* The Reckoning's verdict line, which names the person to acquit them. */
.beat__verdict {
	color: var(--accent);
	font-weight: 700;
}

/* ────────────────────────────────────────────────────────── boot / fatal */

#boot, #fatal {
	position: fixed;
	inset: 0;
	z-index: 60;
	display: grid;
	place-content: center;
	gap: 0.5rem;
	padding: 8vw;
	background: var(--ink);
	text-align: center;
}
#boot[hidden], #fatal[hidden] { display: none; }

.boot__mark {
	/*
		0.14em of tracking on a nineteen-character name is a lot of width, so this
		one came down further than the other two. See the note on .title.
	*/
	font: 700 clamp(1.5rem, 8vw, 2.6rem)/1.05 var(--display);
	letter-spacing: 0.14em;
	color: var(--paper-2);
	margin: 0;
}
.boot__line { color: var(--muted); font-size: 0.9rem; margin: 0; }

#fatal { text-align: left; place-content: center start; }
#fatal h1 { font: 700 clamp(1.5rem, 7vw, 2.4rem)/1.1 var(--display); letter-spacing: 0.04em; }
#fatal-message { color: var(--loss); font-family: var(--mono); font-size: 0.9rem; }
.fatal__why { color: var(--muted); font-size: 0.9rem; max-width: 44ch; }

/* ────────────────────────────────────────────────────────────── screens */

.screen {
	position: fixed;
	inset: 0;
	z-index: 40;
	overflow-y: auto;
	overscroll-behavior: contain;
	-webkit-overflow-scrolling: touch;
	background:
		linear-gradient(180deg, rgba(8, 9, 10, 0.94) 0%, rgba(8, 9, 10, 0.99) 60%);
	padding: max(var(--pad), env(safe-area-inset-top)) var(--pad)
		calc(var(--pad) + env(safe-area-inset-bottom));
}
.screen[hidden] { display: none; }

.entry__inner, .reckoning__inner {
	max-width: 40rem;
	margin: 0 auto;
	padding: 2.5rem 0 3rem;
}

.eyebrow {
	font: 500 0.72rem/1.4 var(--mono);
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-bottom: 1.2rem;
}

.title {
	/*
		The game's name must not be the first thing that breaks. At exactly
		360x640 a 22vw setting overflowed the 328px content box and rendered
		"FAIR DIC…" (AD-14), and 19vw fixed it for a nine-character name.

		v0.5 renamed the piece to a nineteen-character one, which broke it again:
		"IN TORONTO" set 379px wide in a 328px box. Measured, not guessed — the
		widest unbreakable run is now "IN TORONTO" rather than "FAIR DICE", and
		15.5vw is what fits it at 360 with the non-breaking space intact. The
		title sets on two lines on a phone now, which a three-word name should.
	*/
	font: 700 clamp(2.3rem, 15.5vw, 5.6rem)/0.88 var(--display);
	letter-spacing: 0.02em;
	text-transform: uppercase;
	color: var(--paper);
	margin: 0 0 1rem;
}

.thesis {
	font: 400 clamp(1.15rem, 5.6vw, 1.6rem)/1.3 var(--display);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--paper-2);
	margin-bottom: 2rem;
}
.thesis__turn { color: var(--accent); }

.warning {
	border-top: 1px solid var(--hair);
	border-bottom: 1px solid var(--hair);
	padding: 1.1rem 0;
	margin-bottom: 1.6rem;
}
.warning__head {
	font: 500 0.72rem/1.4 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-bottom: 0.6rem;
}
.warning p { font-size: 0.95rem; color: var(--paper-2); }
.warning p:last-child { margin-bottom: 0; }
.warning strong { color: var(--paper); }

.micro-head {
	font: 500 0.66rem/1.4 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin: 0 0 0.5rem;
}

/* ─────────────────────────────────────────────────────────────── buttons */

.btn {
	appearance: none;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	min-height: 44px;
	padding: 0.7rem 1.15rem;
	border: 1px solid var(--hair-soft);
	border-radius: 0;
	background: transparent;
	color: var(--paper);
	font: 700 0.86rem/1.2 var(--body);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	cursor: pointer;
	transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
		color var(--dur) var(--ease);
}
.btn:hover { border-color: var(--accent); color: var(--accent); }

.btn--primary {
	background: var(--paper);
	border-color: var(--paper);
	color: var(--ink);
}
.btn--primary:hover { background: var(--accent); border-color: var(--accent); color: var(--ink); }

.btn--block { width: 100%; }
.btn--tight { padding: 0.45rem 0.7rem; font-size: 0.72rem; }
/*
	Secondary to Begin, and visibly so. "Watch the intro" has to be findable
	without competing with the one action a first-time visitor came for, so it
	keeps the button's hit area and loses its weight.
*/
.btn--quiet {
	background: transparent;
	border-color: var(--hair);
	color: var(--muted);
}
.btn--quiet:hover { border-color: var(--paper-2); color: var(--paper-2); background: transparent; }
.btn--sound .btn__ico { color: var(--accent); font-size: 1.05em; }
.btn--sound[aria-pressed="true"] .btn__ico { color: var(--paper); }

.entry__actions {
	display: flex;
	flex-direction: column;
	gap: 0.6rem;
	margin-bottom: 1.6rem;
}

/*
	AQ-08. The action follows the reader down the entry screen on a phone.

	The entry screen is 1,381px of content at 360x640: title, thesis, the content
	warning, who you are playing, the action, the sound line, the exits and the crisis
	numbers. Begin cannot share a 640px frame with the title by layout alone, and the
	two candidate fixes were both wrong. Shrinking the type and the warning to make it
	fit degrades the one thing on the page nobody should skim. Moving Begin above the
	content warning puts the action before the warning, which is not a trade this piece
	makes.

	So the action sticks to the bottom of the scroll container instead. The title and
	the thesis own the first frame, the warning is read on the way down, and Begin is in
	every frame including the first — which is what the brief asked for. The backdrop and
	the hairline are there because a transparent button over body copy reads as a mistake.

	The bottom padding is --crisis-clear, the same number the receipt, dilemma and
	donation footers use, and it is here because pinning the row created the very overlap
	AQ-05 was about: the first shot of this screen had "Need help now" sitting across the
	right end of "Watch the intro". A row that is always at the bottom of the frame has to
	clear the button that is always in the corner of it.
*/
@media (max-width: 30rem) {
	.entry__actions {
		position: sticky;
		bottom: 0;
		z-index: 2;
		margin-bottom: 0;
		padding: 0.7rem 0 calc(var(--crisis-clear) + env(safe-area-inset-bottom));
		background: linear-gradient(to top, var(--ink) 62%, rgba(11, 11, 12, 0));
		border-top: 1px solid var(--hair);
	}
	/* The block that used to follow the actions now needs its own breathing room. */
	.soundline { margin-top: 1.4rem; }
}

/*
	v0.8's share surface on the entry screen. Quiet on purpose: it is the same
	`btn--quiet` weight as "Watch the intro", not the primary weight of Begin,
	because the first thing to do with this piece is play it.
*/
.sharebox { margin: 1.6rem 0 1.8rem; }
.sharebox__note {
	font: 500 0.72rem/1.5 var(--mono);
	letter-spacing: 0.06em;
	color: var(--muted-2);
	margin: 0.5rem 0 0;
	/* Two lines' worth, so a status message replacing the standing line cannot
	   reflow everything below it. */
	min-height: 2.2em;
}

.entry__exits { margin-bottom: 1.8rem; display: grid; gap: 0.1rem; }
.exit-link {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	font: 500 0.8rem/1.3 var(--body);
	color: var(--muted);
	justify-self: start;
}
.exit-link:hover { color: var(--paper); }

.entry__crisis { margin-bottom: 2rem; }

/* The in-modal strip: reachable by pointer and Tab while the modal is open. */
.crisis-strip {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0 1rem;
	font: 500 0.68rem/1.3 var(--mono);
	letter-spacing: 0.08em;
}
.crisis-strip__label {
	text-transform: uppercase;
	letter-spacing: 0.16em;
	color: var(--muted-2);
}
.crisis-strip a {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	color: var(--accent);
	font-weight: 700;
}
.receipt .crisis-strip { padding-bottom: 0.15rem; }
.receipt .crisis-strip__label { color: #6d675a; }
.receipt .crisis-strip a { color: #7a3a1f; }
.dilemma .crisis-strip {
	margin-top: 1rem;
	border-top: 1px solid var(--hair);
}
.crisis-strip--reckoning { margin-top: 0.4rem; }

.crisis { display: grid; gap: 0.35rem; }
.crisis a {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: 0.5rem;
	min-height: 44px;
	padding: 0.35rem 0;
	text-decoration: none;
	font-size: 0.9rem;
}
.crisis .crisis__label {
	font: 700 1rem/1 var(--mono);
	letter-spacing: 0.06em;
	color: var(--accent);
}
.crisis .crisis__detail { color: var(--muted); font-size: 0.82rem; }
/*
	`.crisis--inline` lived here for the footer strip on the play screen. Removed
	with its markup — the corner "Need help now" control covers that ground. The
	stacked variant below is still used by the help panel and the boot-failure card.
*/

.boot-report {
	border-top: 1px solid var(--hair);
	padding-top: 0.8rem;
	font-size: 0.82rem;
	color: var(--muted);
}
.boot-report summary {
	cursor: pointer;
	min-height: 44px;
	display: flex;
	align-items: center;
	font: 500 0.7rem/1.4 var(--mono);
	letter-spacing: 0.16em;
	text-transform: uppercase;
}
.boot-report ul { display: grid; gap: 0.5rem; padding: 0.5rem 0; }
.boot-report li {
	font-family: var(--mono);
	font-size: 0.74rem;
	line-height: 1.5;
	color: var(--muted-2);
	border-left: 2px solid var(--loss);
	padding-left: 0.7rem;
}

/* ────────────────────────────────────────────────────────────────── HUD */

#hud {
	position: fixed;
	top: 0; left: 0; right: 0;
	z-index: 30;
	/* The panel is a bordered box now, so it needs a margin to sit in rather than
	   being flush against the top edge with its own border clipped. */
	padding: calc(0.6rem + env(safe-area-inset-top)) var(--pad) 0.5rem;
	background: linear-gradient(180deg, rgba(8, 9, 10, 0.92), rgba(8, 9, 10, 0));
	pointer-events: none;
}
#hud[hidden] { display: none; }
#hud button { pointer-events: auto; }

/*
	THE HUD PANEL.

	A bordered translucent box divided into cells, to the owner's spec: money, day
	and month across the top with hairline dividers, a full-width arrears row
	beneath it when arrears exist, then the income-to-rent bar and its sentence.

	Money is still the hero — it keeps the largest type on screen by a wide margin,
	which is the constraint the panel had to be built around rather than a
	preference to balance against the other cells.
*/
.hudpanel {
	max-width: 40rem;
	margin: 0 auto;
	background: rgba(10, 11, 13, 0.82);
	border: 1px solid var(--hair);
	backdrop-filter: blur(4px);
	box-shadow: 0 10px 34px rgba(0, 0, 0, 0.55);
}

.hudpanel__cells {
	display: grid;
	/* Money takes the room it needs; day and month share what is left evenly. */
	grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr) minmax(0, 1fr);
}
.hudcell {
	padding: 0.5rem 0.7rem 0.6rem;
	min-width: 0;
}
/* Hairlines between cells, never on the outside edges. */
.hudcell + .hudcell { border-left: 1px solid var(--hair); }

.hudcell__key {
	display: block;
	font: 500 0.56rem/1.3 var(--mono);
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-bottom: 0.1rem;
}
.hudcell__val {
	display: block;
	font: 700 1.5rem/1.05 var(--display);
	letter-spacing: 0.02em;
	font-variant-numeric: tabular-nums;
	color: var(--paper);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}
.hudcell__val--money {
	/*
		The largest number on screen, and by enough that nothing else competes. It
		is what changes when the board takes something.
	*/
	font-size: clamp(2rem, 5.4vw, 3.3rem);
	line-height: 0.95;
	letter-spacing: 0;
}
.hudcell__sub {
	display: block;
	margin-top: 0.12rem;
	font: 500 0.62rem/1.3 var(--mono);
	letter-spacing: 0.06em;
	color: var(--muted);
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* A flash on every change, so money leaving is something you see happen. */
.hudcell__val--money[data-flash="down"] { animation: money-down 0.5s var(--ease); }
.hudcell__val--money[data-flash="up"] { animation: money-up 0.5s var(--ease); }
@keyframes money-down {
	0% { color: var(--loss); transform: translateY(-2px); }
	100% { color: var(--paper); transform: none; }
}
@keyframes money-up {
	0% { color: var(--gain); }
	100% { color: var(--paper); }
}

/*
	Arrears: a full-width row of its own, label left and value right, in the loss
	colour with a tinted ground so it reads as a different kind of fact from the
	cells above it. Present only when there is money owed.
*/
.hudpanel__arrears {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.8rem;
	padding: 0.34rem 0.7rem 0.4rem;
	border-top: 1px solid var(--hair);
	background: rgba(196, 84, 63, 0.1);
}
.hudpanel__arrears[hidden] { display: none; }
.hudpanel__arrearskey {
	font: 500 0.58rem/1.3 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--loss);
}
.hudpanel__arrearsval {
	font: 700 clamp(1.25rem, 3.4vw, 1.8rem)/1 var(--display);
	font-variant-numeric: tabular-nums;
	color: var(--loss);
}

/* Who this is happening to, and the sound toggle. One quiet row under the box. */
.hud__second {
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 0.5rem 0.8rem;
	margin-top: 0.45rem;
}
.hud__who {
	display: inline-flex;
	align-items: center;
	gap: 0.45rem;
}
.hud__who[hidden] { display: none; }
.hud__portrait {
	width: 34px; height: 34px;
	object-fit: cover;
	object-position: 64% 22%;
	border: 1px solid var(--hair);
	background: var(--ink-3);
}
.hud__name {
	font: 700 0.86rem/1 var(--display);
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--paper-2);
}
.hud__meta {
	font: 500 0.68rem/1.4 var(--mono);
	letter-spacing: 0.06em;
	color: var(--muted);
	font-variant-numeric: tabular-nums;
}
.hud__dot { color: var(--muted-2); margin: 0 0.15rem; }
#unmute-hud { pointer-events: auto; }

/* The bar now lives inside the panel, so it takes the panel's gutter. */
.incomebar {
	padding: 0.5rem 0.7rem 0.6rem;
	border-top: 1px solid var(--hair);
}

/* INCOME $2,210 ⟶ RENT $1,491 — the two ends named above the track. */
.incomebar__ends {
	display: flex;
	align-items: baseline;
	gap: 0.5rem;
	margin: 0 0 0.35rem;
	font: 500 0.6rem/1.3 var(--mono);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--muted-2);
}
.incomebar__end b {
	font-weight: 700;
	letter-spacing: 0.02em;
	font-variant-numeric: tabular-nums;
	color: var(--accent);
}
.incomebar__end--rent { margin-left: auto; }
.incomebar__end--rent b { color: var(--loss); }
.incomebar__arrow { color: var(--muted-2); }

.incomebar__track {
	position: relative;
	height: 10px;
	background: var(--ink-3);
	border: 1px solid var(--hair);
}
.incomebar__income {
	position: absolute;
	inset: 0 auto 0 0;
	background: var(--accent-dim);
	transition: width 0.6s var(--ease), background var(--dur) var(--ease);
}
.incomebar__rent {
	position: absolute;
	top: -5px; bottom: -5px;
	width: 2px;
	background: var(--loss);
}
/*
	The most affecting text in the HUD, and it must never be hidden — including at
	360x640, where the previous build let it wrap out of the visible band. It is
	given room rather than truncated.
*/
.incomebar__caption {
	margin: 0.4rem 0 0;
	font: 500 0.68rem/1.45 var(--mono);
	letter-spacing: 0.03em;
	color: var(--paper-2);
	text-align: center;
	text-wrap: balance;
}

/*
	Narrow: stack rather than squeeze, as the spec asks. Money keeps a full-width
	row of its own and day/month share the row beneath it — three cells across a
	360px screen would have left the largest number on screen about 110px to live
	in, which is the opposite of the hierarchy this panel exists to state.
*/
@media (max-width: 30rem) {
	.hudpanel__cells { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
	.hudcell--money {
		grid-column: 1 / -1;
		text-align: center;
		border-bottom: 1px solid var(--hair);
	}
	/* The money cell is now a row, so its left hairline would be a stray mark. */
	.hudcell--money + .hudcell { border-left: 0; }
	.hudcell--month { border-left: 1px solid var(--hair); }
	.incomebar__ends { font-size: 0.55rem; letter-spacing: 0.1em; }
}

/*
	The roll histogram's HUD styles used to live here — a `.dicelog` block, its
	columns, fills and ticks. All gone with the element. Reckoning beat 1 draws its
	own SVG chart and styles it as `.gapchart`, so nothing here was serving it.
	Deleting the markup and leaving the CSS is how a removed feature comes back the
	next time somebody adds a matching class name.
*/

/* ───────────────────────────────────────────────────────────── roll UI */

#play { position: fixed; inset: 0; z-index: 20; }
#play[hidden] { display: none; }

.roll {
	position: absolute;
	inset: 0;
	width: 100%;
	appearance: none;
	background: transparent;
	border: 0;
	cursor: pointer;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: flex-end;
	gap: 0.2rem;
	padding: 0 0 calc(4rem + env(safe-area-inset-bottom));
	color: var(--paper);
}
.roll:disabled { cursor: default; }
.roll__key {
	font: 700 1.1rem/1 var(--display);
	letter-spacing: 0.3em;
	text-transform: uppercase;
	padding: 0.55rem 1.4rem;
	border: 1px solid var(--hair-soft);
	background: rgba(8, 9, 10, 0.55);
	min-height: 44px;
	display: flex;
	align-items: center;
	transition: opacity var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.roll:hover:not(:disabled) .roll__key { border-color: var(--accent); color: var(--accent); }
.roll:disabled .roll__key { opacity: 0.25; }
.roll__hint {
	font: 500 0.62rem/1.4 var(--mono);
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: var(--muted-2);
}
.roll:disabled .roll__hint { opacity: 0; }
.roll:focus-visible { outline: none; }
.roll:focus-visible .roll__key { outline: 2px solid var(--accent); outline-offset: 3px; }

.dice-readout {
	position: absolute;
	left: 0; right: 0;
	bottom: calc(8.4rem + env(safe-area-inset-bottom));
	margin: 0;
	text-align: center;
	font: 700 0.78rem/1 var(--mono);
	letter-spacing: 0.28em;
	color: var(--accent);
	opacity: 0;
	transition: opacity var(--dur) var(--ease);
}
.dice-readout[data-show="true"] { opacity: 1; }

/* ───────────────────────────────────────────────── cards: receipt + dilemma */

#card-layer { position: fixed; inset: 0; z-index: 50; }
#card-layer[hidden] { display: none; }

.scrim {
	position: absolute;
	inset: 0;
	background: rgba(4, 5, 6, 0.6);
	opacity: 0;
	transition: opacity var(--dur) var(--ease);
}
.scrim[data-show="true"] { opacity: 1; }

/* Thermal paper. The state change above the stat, the source below it. */
.receipt, .dilemma {
	position: absolute;
	left: 0; right: 0; bottom: 0;
	max-height: 88vh;
	display: flex;
	flex-direction: column;
	overflow: hidden;
}
.receipt[hidden], .dilemma[hidden] { display: none; }

/* No background on the article itself, or it fills in the torn edge. */
.receipt {
	background: transparent;
	color: #16150f;
	filter: drop-shadow(0 -10px 26px rgba(0, 0, 0, 0.5));
}
.receipt__perf {
	height: 9px;
	flex: none;
	background:
		radial-gradient(circle at 50% 0, rgba(0, 0, 0, 0) 0 4.5px, var(--paper) 4.6px)
		0 0 / 13px 9px repeat-x;
}
/*
	The painted illustration, when a record names one. 16:9, full bleed across the
	top of the card, with the tear-strip above it — so the card reads as a printed
	receipt with a photograph stapled to it rather than as a web panel.
*/
.receipt__art {
	margin: 0;
	flex: none;
	background: #0e0d0b;
	line-height: 0;
	border-bottom: 1px solid #b9b2a2;
}
.receipt__art[hidden] { display: none; }
.receipt__art img {
	display: block;
	width: 100%;
	height: auto;
	max-height: 34vh;
	object-fit: cover;
}

.receipt__body {
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 0.35rem var(--pad) 1rem;
	font-family: var(--mono);
	/*
		tex-paper under the flat colour: multiply keeps the type contrast the flat
		paper had while giving the card a real fibre. The colour stays as the
		fallback, so a missing texture is simply the old card.
	*/
	background-color: var(--paper);
	background-image: var(--paper-tex, none);
	background-size: 420px 420px;
	background-blend-mode: multiply;
}
.receipt__kicker {
	font-size: 0.62rem;
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: #6d675a;
	margin: 0 0 0.5rem;
	display: flex;
	justify-content: space-between;
	gap: 1rem;
	border-bottom: 1px dashed #b9b2a2;
	padding-bottom: 0.5rem;
}
.receipt__title {
	font: 700 clamp(1.35rem, 7vw, 1.9rem)/1.05 var(--display);
	letter-spacing: 0.03em;
	text-transform: uppercase;
	color: #16150f;
	margin-bottom: 0.7rem;
}

/* The state change, above the stat. Always. */
.receipt__delta {
	display: flex;
	flex-wrap: wrap;
	gap: 0.35rem 1.2rem;
	border-top: 1px solid #16150f;
	border-bottom: 1px solid #16150f;
	padding: 0.6rem 0;
	margin-bottom: 0.9rem;
}
.receipt__deltaitem {
	display: flex;
	align-items: baseline;
	gap: 0.4rem;
	font-size: 0.7rem;
	letter-spacing: 0.14em;
	text-transform: uppercase;
	color: #56504a;
}
.receipt__deltaval {
	font: 700 1.4rem/1 var(--display);
	letter-spacing: 0.02em;
	font-variant-numeric: tabular-nums;
	color: #16150f;
}
.receipt__deltaval[data-sign="down"] { color: #9a3423; }
.receipt__deltaval[data-sign="up"] { color: #4a5c39; }

.receipt__prose {
	font-family: var(--body);
	font-size: 0.94rem;
	line-height: 1.55;
	color: #2a2721;
	margin-bottom: 0.9rem;
}
.receipt__prose p:last-child { margin-bottom: 0; }

.receipt__stat {
	font-size: 0.78rem;
	line-height: 1.6;
	color: #16150f;
	background: #ded8c9;
	border-left: 3px solid #9a3423;
	padding: 0.6rem 0.7rem;
	margin-bottom: 0.6rem;
}
.receipt__stat:empty { display: none; }
.receipt__source {
	font-size: 0.68rem;
	line-height: 1.6;
	letter-spacing: 0.04em;
	color: #6d675a;
	margin: 0;
}
.receipt__source a { color: #7a3a1f; }
.receipt__source a:hover { color: #16150f; }
/*
	AQ-05(b): clearance for the fixed crisis button.

	Measured at 360x640 before this: the button occupied x 200-349, y 585-629 while
	Continue spanned x 16-344, y 580-624 — the button covered the right 144px of the
	one control the player needs on every card. It sits at z-index 90 and always
	wins, so the receipt has to yield rather than the button.

	Bottom clearance rather than right clearance, because Continue keeps its full
	width and therefore its full touch target: 44px for the button, plus the 0.7rem
	it is inset by, plus a 0.45rem gap. The safe-area inset is added once here and
	not twice — the button already carries its own.

	--crisis-clear is declared so the dilemma and donation footers can use the same
	number without anybody re-deriving it.
*/
:root { --crisis-clear: calc(44px + 0.7rem + 0.45rem); }

.receipt__foot {
	flex: none;
	padding: 0 var(--pad) calc(var(--pad) + env(safe-area-inset-bottom));
	background: var(--paper);
}
.receipt__foot .btn--primary {
	background: #16150f;
	border-color: #16150f;
	color: var(--paper);
}
.receipt__foot .btn--primary:hover { background: #9a3423; border-color: #9a3423; }

/* The soft ask. Quiet, one line, and it leaves as easily as it arrives. */
.give {
	position: absolute;
	left: 0; right: 0; bottom: 0;
	background: var(--ink-2);
	border-top: 1px solid var(--hair);
	padding: 1.1rem var(--pad) calc(var(--pad) + env(safe-area-inset-bottom));
	box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.6);
}
.give[hidden] { display: none; }
.give__line {
	font-size: 0.95rem;
	color: var(--paper-2);
	margin-bottom: 0.9rem;
	max-width: 46ch;
}
.give__actions { display: flex; flex-wrap: wrap; gap: 0.6rem; }
.give__note {
	margin: 0.8rem 0 0;
	font: 500 0.64rem/1.5 var(--mono);
	letter-spacing: 0.06em;
	color: var(--muted-2);
}

/* Dilemmas are not paper. They are the board talking. */
.dilemma {
	background: var(--ink-2);
	border-top: 1px solid var(--accent-dim);
	box-shadow: 0 -14px 40px rgba(0, 0, 0, 0.6);
}
.dilemma__body {
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 1.1rem var(--pad) calc(var(--pad) + env(safe-area-inset-bottom));
}
.dilemma__kicker {
	font: 500 0.62rem/1.4 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--loss);
	margin-bottom: 0.5rem;
}
.dilemma__title {
	font: 700 clamp(1.3rem, 6.6vw, 1.9rem)/1.05 var(--display);
	letter-spacing: 0.03em;
	text-transform: uppercase;
	margin-bottom: 0.5rem;
}
.dilemma__stand { color: var(--accent); font-size: 0.95rem; }
.dilemma__prose { color: var(--paper-2); font-size: 0.92rem; }
.dilemma__options { display: grid; gap: 0.6rem; margin-top: 1rem; }

/*
	AQ-05(b). Clearance so the fixed crisis button never covers a sheet's action row.

	Only the narrow tier needs it: at 360x640 the bottom sheets run the full width and
	their buttons end up under the button, which sits at z-index 90 and always wins.
	Above 30rem the sheets are centred and the corner is empty — verified at 1920x1080,
	where Continue and the button do not intersect.

	All three sheets, not just the receipt. The reviewer found it on the receipt because
	that is the card you meet thirty times a game, but the dilemma and the donation ask
	put their controls in the same place and collide the same way.

	POSITION IN THIS FILE IS LOAD-BEARING. This block sits after the three base rules
	on purpose: the first attempt put it in the narrow-tier media query up at the HUD,
	which is *earlier* in the file than `.receipt__foot`'s own `padding` shorthand, so
	the shorthand won and the measured padding never changed. Equal specificity, later
	wins — a media query does not outrank source order.
*/
@media (max-width: 30rem) {
	.receipt__foot,
	.dilemma__body,
	.give { padding-bottom: calc(var(--pad) + var(--crisis-clear) + env(safe-area-inset-bottom)); }
}

.option {
	appearance: none;
	text-align: left;
	background: transparent;
	border: 1px solid var(--hair);
	color: var(--paper);
	padding: 0.85rem 1rem;
	min-height: 44px;
	cursor: pointer;
	display: grid;
	gap: 0.25rem;
	transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.option:hover { border-color: var(--accent); background: rgba(224, 164, 79, 0.06); }
.option__label {
	font: 700 0.98rem/1.2 var(--display);
	letter-spacing: 0.05em;
	text-transform: uppercase;
}
.option__detail { font-size: 0.84rem; color: var(--muted); }

/* ───────────────────────────────────────────────────────────── reckoning */

.beat { min-height: 52vh; }
.beat__eyebrow {
	font: 500 0.66rem/1.4 var(--mono);
	letter-spacing: 0.22em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-bottom: 1rem;
}
.beat__head {
	font: 700 clamp(1.6rem, 8vw, 2.9rem)/1.04 var(--display);
	letter-spacing: 0.02em;
	text-transform: uppercase;
	margin-bottom: 1rem;
}
.beat__lede { font-size: 1rem; color: var(--paper-2); max-width: 46ch; }
.beat__note { font-size: 0.82rem; color: var(--muted); }

/*
	Item 12 put two sections on one view. A section heading inside a beat is
	smaller than the beat's own head — otherwise the view reads as two beats that
	failed to separate — and a hairline rule carries the break, so the eye knows
	the quote is a new section rather than more of the chart's caption.
*/
.beat__rule {
	border: 0;
	border-top: 1px solid var(--hair);
	margin: 2.4rem 0 1.6rem;
}
.beat__subhead {
	font: 700 clamp(1.15rem, 5vw, 1.7rem)/1.1 var(--display);
	letter-spacing: 0.04em;
	text-transform: uppercase;
	margin-bottom: 0.85rem;
}
/* The eyebrow sits tight above its subhead, not a paragraph away from it. */
.beat__eyebrow + .beat__subhead { margin-top: -0.5rem; }

.reckoning__nav {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	border-top: 1px solid var(--hair);
	padding-top: 1rem;
	margin-top: 2rem;
}
.reckoning__count {
	margin: 0;
	font: 500 0.66rem/1 var(--mono);
	letter-spacing: 0.18em;
	color: var(--muted-2);
}

/* Beat 2: your choice beside the road not taken. */
.branches { display: grid; gap: 0.8rem; margin: 1.2rem 0; }
.branch {
	border: 1px solid var(--hair);
	padding: 0.9rem 1rem;
}
.branch__head {
	display: flex;
	justify-content: space-between;
	gap: 0.8rem;
	font: 500 0.62rem/1.4 var(--mono);
	letter-spacing: 0.16em;
	text-transform: uppercase;
	color: var(--muted-2);
	margin-bottom: 0.6rem;
}
.branch__title {
	font: 700 1rem/1.2 var(--display);
	letter-spacing: 0.05em;
	text-transform: uppercase;
	margin-bottom: 0.9rem;
}
.branch__cols { display: grid; gap: 0.8rem; }
.branch__col { border-left: 2px solid var(--hair); padding-left: 0.8rem; }
.branch__col--taken { border-left-color: var(--accent); }
.branch__coltag {
	font: 500 0.58rem/1.4 var(--mono);
	letter-spacing: 0.18em;
	text-transform: uppercase;
	color: var(--muted-2);
	display: block;
	margin-bottom: 0.3rem;
}
.branch__col--taken .branch__coltag { color: var(--accent); }
.branch__choice { font: 700 0.9rem/1.3 var(--body); margin-bottom: 0.3rem; }
.branch__outcome { font-size: 0.85rem; color: var(--paper-2); margin: 0 0 0.4rem; }
/* Every column, faced or not, ends in the same terminal arithmetic. */
.branch__terminal {
	border-top: 1px solid var(--hair);
	margin-top: 0.55rem;
	padding-top: 0.5rem;
	font: 500 0.7rem/1.5 var(--mono);
	letter-spacing: 0.04em;
	color: var(--paper-2);
}
.branch--unfaced { border-style: dashed; }
.branch__unfaced {
	font: 500 0.78rem/1.5 var(--body);
	color: var(--muted);
	margin: -0.3rem 0 0.8rem;
}
.branches__closing {
	border-top: 1px solid var(--hair);
	margin-top: 1.4rem;
	padding-top: 1.1rem;
}
.branch__converge {
	border-top: 1px dashed var(--hair);
	margin-top: 0.9rem;
	padding-top: 0.7rem;
	font: 500 0.82rem/1.5 var(--body);
	color: var(--accent);
}

/* Beat 3: three sourced points, and nothing between them. */
.gapchart { margin: 1.4rem 0; }
.gapchart svg { display: block; width: 100%; height: auto; overflow: visible; }
.gapchart__caption { font-size: 0.8rem; color: var(--muted); margin-top: 0.8rem; }
.gap-axis { stroke: var(--hair); stroke-width: 1; }
.gap-point { fill: var(--accent); }
.gap-point--derived { fill: var(--loss); }
.gap-label { fill: var(--paper-2); font: 700 12px var(--mono); letter-spacing: 0.06em; }
.gap-year { fill: var(--muted-2); font: 500 11px var(--mono); letter-spacing: 0.12em; }
.gap-guide { stroke: var(--hair-soft); stroke-width: 1; stroke-dasharray: 2 4; }

/* Beat 4/5: quotation and counts. */
.pull {
	border-left: 3px solid var(--accent);
	padding: 0.3rem 0 0.3rem 1.1rem;
	margin: 1.2rem 0;
	font: 400 clamp(1.05rem, 4.6vw, 1.35rem)/1.45 var(--body);
	color: var(--paper);
}
.pull__cite {
	display: block;
	margin-top: 0.8rem;
	font: 500 0.72rem/1.5 var(--mono);
	letter-spacing: 0.1em;
	color: var(--muted);
	font-style: normal;
}

.counts { display: grid; gap: 1rem; margin: 1.4rem 0; }
.count { border-top: 1px solid var(--hair); padding-top: 0.7rem; }
.count__num {
	display: block;
	font: 700 clamp(2.2rem, 13vw, 3.6rem)/0.95 var(--display);
	letter-spacing: 0.01em;
	font-variant-numeric: tabular-nums;
	color: var(--paper);
}
.count__what { display: block; font-size: 0.9rem; color: var(--paper-2); margin-top: 0.35rem; }
.count__src { display: block; font: 500 0.66rem/1.5 var(--mono); color: var(--muted-2); margin-top: 0.4rem; }

/* Beat 6: the slate. */
.orgs { display: grid; gap: 0.9rem; margin: 1.3rem 0; }
.org {
	border: 1px solid var(--hair);
	padding: 1rem;
	display: grid;
	gap: 0.45rem;
}
.org__serves {
	font: 500 0.6rem/1.4 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--accent);
}
.org__name { font: 700 1.15rem/1.15 var(--display); letter-spacing: 0.04em; text-transform: uppercase; }
.org__est { font: 500 0.66rem/1 var(--mono); color: var(--muted-2); }
.org__does { font-size: 0.88rem; color: var(--paper-2); margin: 0; }
.org--reserved { border-style: dashed; color: var(--muted); }
.org--reserved .org__name { color: var(--muted-2); }
.org__reserved-note { font-size: 0.84rem; color: var(--muted); margin: 0; }
.org .btn { justify-self: start; }

.credits { display: grid; gap: 1.2rem; margin: 1.2rem 0; }
.credits h3 {
	font: 500 0.66rem/1.4 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--muted-2);
}
.credits p { font-size: 0.9rem; color: var(--paper-2); }
.sourcelist { display: grid; gap: 0.7rem; }
.sourcelist li { border-left: 2px solid var(--hair); padding-left: 0.8rem; }
.sourcelist__group {
	border-left: 0 !important;
	padding-left: 0 !important;
	margin-top: 0.8rem;
	font: 500 0.64rem/1.4 var(--mono);
	letter-spacing: 0.2em;
	text-transform: uppercase;
	color: var(--accent);
}
.sourcelist__group:first-child { margin-top: 0; }
.sourcelist--excluded { border-left-color: var(--loss); opacity: 0.75; }
.sourcelist__what { display: block; font: 700 0.8rem/1.3 var(--body); }
.sourcelist__stat { display: block; font-size: 0.78rem; color: var(--muted); margin: 0.2rem 0; }
.sourcelist__cite { display: block; font: 500 0.68rem/1.5 var(--mono); }

/* ─────────────────────────────────────────────────────────────── footer */

#footer {
	position: fixed;
	left: 0; right: 0; bottom: 0;
	z-index: 35;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.2rem 1rem;
	padding: 0.4rem var(--pad) calc(0.4rem + env(safe-area-inset-bottom));
	background: linear-gradient(0deg, rgba(8, 9, 10, 0.94), rgba(8, 9, 10, 0));
	pointer-events: none;
}
#footer[hidden] { display: none; }
#footer a, #footer button { pointer-events: auto; }
/* The entry screen carries its own crisis block, so the footer must not
   duplicate those three links in the tab order while it is showing. */
body[data-phase="entry"] #footer .crisis { display: none; }
#footer .crisis a { min-height: 32px; font-size: 0.72rem; }
#footer .crisis .crisis__label { font-size: 0.8rem; }
#footer .crisis .crisis__detail { display: none; }
.footer__meta { display: flex; flex-wrap: wrap; gap: 0.2rem 1rem; }

/*
	The footer strip clears the crisis button on a phone — the same --crisis-clear
	the receipt, dilemma, donation and entry footers use.

	Found by measuring, and it is older than v0.8: at 360x640 "Leave this page" ran
	from x 119 to 237 against the button's 200 to 349, so the fixed control was
	sitting across an exit link in every frame of every game. Adding "Share" to the
	strip pushed that link 55px further under it, which is what made it obvious.

	Bottom clearance rather than right clearance, again, because three link-weight
	controls squeezed into the 195px left of a 360px viewport would wrap into a
	column and become a menu.
*/
@media (max-width: 30rem) {
	#footer {
		padding-bottom: calc(0.4rem + var(--crisis-clear) + env(safe-area-inset-bottom));
	}
	/*
		And the strip is gone from the entry screen on a phone, because there it is
		three controls the screen already has: the text version is linked in the
		exits, "Leave this page" is there in full rather than abbreviated, and v0.8
		gave the entry screen its own share button. Lifting the strip clear of the
		crisis button put it straight through the sticky action row — the fix for one
		overlap landing on top of another is a good sign the element does not belong
		there. It stays on wider screens, where the entry page has the room.
	*/
	body[data-phase="entry"] #footer { display: none; }

	/*
		The roll label and the dice readout move up to make room for the strip.

		Three things want the bottom of a 640px phone: this label, the footer, and
		the crisis button. The label is the one that can move without losing
		anything, because the tap target is the whole screen — `.roll` is `inset: 0`
		— so its position is information, not reach. Measured: hint ends at 527, the
		strip starts at 539.
	*/
	.roll { padding-bottom: calc(7.1rem + env(safe-area-inset-bottom)); }
	.dice-readout { bottom: calc(11.5rem + env(safe-area-inset-bottom)); }
}

.linkish {
	appearance: none;
	background: none;
	border: 0;
	padding: 0.4rem 0;
	min-height: 32px;
	color: var(--muted);
	font: 500 0.72rem/1.3 var(--mono);
	letter-spacing: 0.1em;
	text-transform: uppercase;
	cursor: pointer;
	text-decoration: none;
}
.linkish:hover { color: var(--accent); }

.sheet {
	position: fixed;
	inset: 0;
	z-index: 70;
	background: rgba(4, 5, 6, 0.94);
	overflow-y: auto;
	padding: max(var(--pad), env(safe-area-inset-top)) var(--pad)
		calc(var(--pad) + env(safe-area-inset-bottom));
}
.sheet[hidden] { display: none; }
.sheet__inner { max-width: 40rem; margin: 0 auto; padding: 2rem 0; display: grid; gap: 1.2rem; }
.sheet__inner h2 { font: 700 1.6rem/1.1 var(--display); letter-spacing: 0.04em; text-transform: uppercase; }
.sheet__inner .btn { justify-self: start; }

.noscript {
	position: relative;
	z-index: 80;
	max-width: 40rem;
	margin: 0 auto;
	padding: 3rem var(--pad);
}
.noscript h1 { font: 700 3rem/1 var(--display); letter-spacing: 0.06em; margin-bottom: 1rem; }
.noscript ul { display: grid; gap: 0.4rem; margin-top: 1rem; }

/* ──────────────────────────────────────────────────────── wider screens */

@media (min-width: 46rem) {
	:root { --pad: 28px; }

	/*
		This block used to widen the HUD into three columns and set
		`.dicelog { display: block }`, which turned the roll histogram back on at
		desktop width. Both are gone in v0.4. The histogram is deleted from the HUD
		at every viewport — a display rule in a media query is exactly how a deleted
		element comes back — and money is centred rather than spread across a row.

		The income bar is given a sane maximum so its sentence does not stretch to
		1900px on a wide screen.
	*/
	.incomebar { max-width: 42rem; margin: 0 auto; }

	/* On a wide screen the receipt is a slip, not a sheet. */
	.receipt {
		left: auto;
		right: var(--pad);
		bottom: auto;
		top: 50%;
		transform: translateY(-50%);
		width: min(27rem, 40vw);
		max-height: 82vh;
	}
	.dilemma {
		left: 50%;
		right: auto;
		bottom: auto;
		top: 50%;
		transform: translate(-50%, -50%);
		width: min(38rem, 82vw);
		max-height: 84vh;
		border: 1px solid var(--accent-dim);
	}
	.give {
		left: 50%;
		right: auto;
		bottom: 8vh;
		transform: translateX(-50%);
		width: min(34rem, 82vw);
		border: 1px solid var(--hair);
	}
	.dilemma__options { grid-template-columns: 1fr 1fr; }
	.branch__cols { grid-template-columns: 1fr 1fr; }
	.counts { grid-template-columns: repeat(3, 1fr); }
	.orgs { grid-template-columns: 1fr 1fr; }
	.entry__actions { flex-direction: row; }
	#footer .crisis .crisis__detail { display: inline; }
}

@media (min-width: 68rem) {
	.title { font-size: clamp(5rem, 9vw, 8.5rem); }
	.entry__inner, .reckoning__inner { max-width: 46rem; }
}

/* ────────────────────────────────────────────────── reduced motion */

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: 0.001ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.001ms !important;
		scroll-behavior: auto !important;
	}
	.dice-readout { transition: none; }
}

/* Same, for the ?motion=reduce override. */
:root[data-motion="reduce"] *,
:root[data-motion="reduce"] *::before,
:root[data-motion="reduce"] *::after {
	animation-duration: 0.001ms !important;
	animation-iteration-count: 1 !important;
	transition-duration: 0.001ms !important;
	scroll-behavior: auto !important;
}

@media (prefers-contrast: more) {
	:root { --muted: #b8b4ac; --muted-2: #9a968e; --hair: #4a4d52; }
}
