/* web_extras/hud.css — the DOM HUD theme for JellyChess.
 *
 * Clean "frosted" chrome: translucent WHITE surfaces (the pastel green/blue scene glows through),
 * dark-slate ink, a sky-blue accent — for all the calm chrome (New game, options, undo, rematch,
 * connection). Just TWO button families: this frosted white chrome, and the green CANDY pills for the
 * two hotspots (the jelly counter + the Invite pill, which carries a pale-green "Mist" badge behind a
 * dark person-plus icon). Chewy for headers/buttons, Nunito for body. No em/en dashes in any
 * user-facing copy (that's a game rule).
 *
 * Loaded from web_shell.html via <link rel="stylesheet" href="hud.css">. Both fonts are self-hosted
 * same-origin (chewy.woff2 / nunito.woff2 bundled from web_extras/ by export-web.sh): the page is
 * cross-origin isolated (COEP: require-corp), so a Google Fonts CDN copy would be blocked and
 * silently fall back to a system font. index.ts serves .css as text/css and .woff2 as font/woff2
 * (added in Wave 1) or the browser refuses them.
 *
 * Wave 1 (this file) lays down the :root tokens, the @font-faces, the HUD-root rules, the SHARED
 * component classes (panel, row button, switch, radio, slider, scrim, confirm card, pill button,
 * toast), the fully-moved invite button styles, and the version stamp. The per-component style
 * blocks are marked-empty banners the later waves fill in. */

/* ---- fonts ------------------------------------------------------------------ */
/* Chewy (display / headers / buttons). The shell KEEPS its OWN Chewy @font-face for the boot-splash
 * title, so the splash never depends on this stylesheet loading. font-display:swap → a brief
 * Trebuchet fallback until the small woff2 lands. */
@font-face {
	font-family: 'Chewy';
	font-style: normal;
	font-weight: 400;
	font-display: swap;
	src: url('chewy.woff2') format('woff2');
}

/* Nunito (body). Variable subset, weights 400 700. */
@font-face {
	font-family: 'Nunito';
	font-style: normal;
	font-weight: 400 700;
	font-display: swap;
	src: url('nunito.woff2') format('woff2');
}

/* ---- design tokens ---------------------------------------------------------- */
:root {
	/* Clean "frosted" chrome: translucent white surfaces (the pastel scene glows through), dark-slate
	 * ink, a sky-blue accent. Two button families only — this calm frosted chrome, and the green candy
	 * pills (jelly + invite). See docs/dom-hud.md. */
	--jc-bg: rgba(255, 255, 255, 0.94);      /* panels / dialogs: near-opaque frosted white */
	--jc-bg-raised: rgba(255, 255, 255, 0.82); /* buttons / rows: translucent frosted white */
	--jc-bg-scrim: rgba(22, 32, 46, 0.42);
	--jc-ink: #27313f;                        /* dark slate text */
	--jc-ink-dim: #6a7889;
	--jc-accent: #2f8fe0;        /* sky-blue accent — ACTIVE-STATE FILLS ONLY (selected pill, switch-on,
	                                slider thumb). Never text: all HUD text is --jc-ink. See docs/dom-hud.md. */
	--jc-border: rgba(40, 62, 92, 0.12);
	--jc-border-hi: rgba(40, 62, 92, 0.22);
	--jc-accent-soft: rgba(47, 143, 224, 0.18);  /* selected-switch / play-again tint */
	--jc-radius: 14px;
	--jc-radius-pill: 999px;
	--jc-shadow: 0 10px 30px rgba(30, 50, 80, 0.18);
	--jc-shadow-btn: 0 2px 0 rgba(30, 50, 80, 0.10), 0 6px 16px rgba(30, 50, 80, 0.16);
	/* EXCITING accents: ONLY invite + jelly (both green candy pills) */
	--jc-amber: #ffc747;         /* legacy token, retained */
	--jc-on-amber: #291c0d;
	--jc-green: #46c26a;
	--jc-green-hi: #7ee08f;
	--jc-on-green: #0e2c16;
	--jc-pink: #ff5da2;          /* candy pink — the jelly mascot's body; "Save my Jellies!" call to action */
	--jc-pink-hi: #ff7db4;
	--jc-on-pink: #4b1528;       /* dark plum, the mascot's face ink — reads clean on the pink */
	--jc-invite-badge: #e6f7ec;  /* Mist — pale-green badge behind the invite person-plus icon */
	--jc-conn-amber: #c98a00;
	--jc-conn-green: #1f9d52;
	--jc-conn-red: #d64545;
	--jc-font-head: 'Chewy', 'Trebuchet MS', system-ui, sans-serif;
	--jc-font-body: 'Nunito', 'Trebuchet MS', system-ui, sans-serif;
	--jc-safe-b: env(safe-area-inset-bottom, 0px);
	--jc-safe-t: env(safe-area-inset-top, 0px);
	--jc-inset: clamp(10px, 2.6vw, 16px);
}

/* ---- HUD root --------------------------------------------------------------- */
/* Fixed full-viewport overlay; z-index 9 (< confetti 10 < invite 11 < auth overlay). The root is
 * click-through so the board's plane-projection picking sees every click; interactive descendants
 * opt back in with pointer-events:auto. [hidden] until jcHud.notifyBootDone() reveals it. */
#jc-hud {
	position: fixed;
	inset: 0;
	z-index: 9;
	pointer-events: none;
	font-family: var(--jc-font-body);
	color: var(--jc-ink);
}

#jc-hud[hidden] {
	display: none;
}

/* Every interactive descendant re-enables pointer events for itself. Tap targets >= 44px. */
#jc-hud button,
#jc-hud input,
#jc-hud select,
#jc-hud [role="radio"],
#jc-hud [role="switch"],
#jc-hud .jc-interactive {
	pointer-events: auto;
}

/* ---- shared classes (used across components) -------------------------------- */

/* A raised chrome surface (options panel, confirm card). Warm-dark fill, faint gold rim, soft
 * shadow, rounded. */
#jc-hud .jc-panel {
	background: var(--jc-bg);
	border: 1px solid var(--jc-border);
	border-radius: var(--jc-radius);
	box-shadow: var(--jc-shadow);
	color: var(--jc-ink);
}

/* A full-width, left-aligned raised row button (menu items, radio rows). A selected row becomes a
 * solid accent-filled pill with white text (set aria-checked / a .is-selected class per component) —
 * a shape/fill cue, not just a colour shift, so the choice reads at a glance on the light chrome. */
#jc-hud .jc-row {
	display: flex;
	align-items: center;
	gap: 0.6em;
	width: 100%;
	min-height: 44px;
	padding: 0.55em 0.8em;
	border: 1px solid var(--jc-border);
	border-radius: 10px;
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
	font-family: var(--jc-font-body);
	font-size: 1rem;
	text-align: left;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

#jc-hud .jc-row[aria-checked="true"],
#jc-hud .jc-row.is-selected {
	background: var(--jc-accent);
	border-color: var(--jc-accent);
	color: #fff;
	font-weight: 700;
	box-shadow: 0 2px 8px rgba(47, 143, 224, 0.35);
}

/* The explicit display above beats the UA [hidden]{display:none}, so restore it here, so JS-driven
 * row visibility (e.g. the Mode/Difficulty nav rows, only shown on narrow viewports) actually hides. */
#jc-hud .jc-row[hidden] {
	display: none;
}

/* Header text: Chewy, dark-slate ink (like all text — the accent is reserved for active-state fills). */
#jc-hud .jc-head {
	font-family: var(--jc-font-head);
	font-weight: 400;
	color: var(--jc-ink);
}

/* Toggle switch: a CSS pill built on a <button role="switch">. Checked track gets a gold tint;
 * disabled greys out (e.g. shard_collisions while soft_shards is off). */
#jc-hud .jc-switch {
	position: relative;
	flex: 0 0 auto;
	width: 46px;
	height: 28px;
	padding: 0;
	border: 1px solid var(--jc-border);
	border-radius: var(--jc-radius-pill);
	background: var(--jc-bg-raised);
	cursor: pointer;
	transition: background 0.12s ease, border-color 0.12s ease;
}

#jc-hud .jc-switch::after {
	content: "";
	position: absolute;
	top: 3px;
	left: 3px;
	width: 20px;
	height: 20px;
	border-radius: 50%;
	background: var(--jc-ink-dim);
	transition: transform 0.12s ease, background 0.12s ease;
}

#jc-hud .jc-switch[aria-checked="true"] {
	background: var(--jc-accent-soft);
	border-color: var(--jc-border-hi);
}

#jc-hud .jc-switch[aria-checked="true"]::after {
	transform: translateX(18px);
	background: var(--jc-accent);
}

#jc-hud .jc-switch[disabled],
#jc-hud .jc-switch[aria-disabled="true"] {
	opacity: 0.4;
	cursor: default;
}

/* Radio row: reuse .jc-row + role="radio"; the selected fill/text come from the [aria-checked] rule
 * above, so no extra class is needed. */

/* Range slider with a live readout span alongside. Track/thumb kept simple and theme-tinted. */
#jc-hud .jc-slider {
	-webkit-appearance: none;
	appearance: none;
	width: 100%;
	height: 6px;
	border-radius: var(--jc-radius-pill);
	background: var(--jc-bg-raised);
	outline: none;
}

#jc-hud .jc-slider::-webkit-slider-thumb {
	-webkit-appearance: none;
	appearance: none;
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: var(--jc-accent);
	border: none;
	cursor: pointer;
}

#jc-hud .jc-slider::-moz-range-thumb {
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: var(--jc-accent);
	border: none;
	cursor: pointer;
}

#jc-hud .jc-readout {
	font-family: var(--jc-font-body);
	color: var(--jc-ink-dim);
	font-variant-numeric: tabular-nums;
}

/* Full-screen scrim behind a confirm card / open panel. pointer-events:auto so a click-off cancels
 * (the component wires the click handler). */
#jc-hud .jc-scrim {
	position: fixed;
	inset: 0;
	background: var(--jc-bg-scrim);
	pointer-events: auto;
}

/* Centered confirm card (mode confirm, rematch menu, undo/rematch prompts). */
#jc-hud .jc-confirm {
	position: fixed;
	left: 50%;
	top: 50%;
	transform: translate(-50%, -50%);
	max-width: min(88vw, 22rem);
	padding: 1.1em 1.2em;
	background: var(--jc-bg);
	border: 1px solid var(--jc-border-hi);
	border-radius: var(--jc-radius);
	box-shadow: var(--jc-shadow);
	text-align: center;
}

/* A generic pill button (Chewy face, raised, springy press). Component-specific fills (undo,
 * rematch, mode/difficulty) layer their own background on top. */
#jc-hud .jc-pill {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	padding: 0.5em 1.1em;
	border: 1px solid var(--jc-border-hi);
	border-radius: var(--jc-radius-pill);
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
	font-family: var(--jc-font-head);
	font-size: clamp(1rem, 4vw, 1.15rem);
	font-weight: 400;
	line-height: 1;
	cursor: pointer;
	box-shadow: var(--jc-shadow-btn);
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: transform 0.08s ease, box-shadow 0.08s ease;
}

#jc-hud .jc-pill:active {
	transform: translateY(2px);
	box-shadow: 0 1px 0 rgba(0, 0, 0, 0.22), 0 3px 10px rgba(0, 0, 0, 0.30);
}

/* A transient toast (undo/rematch feedback). Dark rounded chip, non-interactive. */
#jc-hud .jc-toast {
	padding: 0.4em 0.95em;
	border-radius: var(--jc-radius-pill);
	background: rgba(0, 0, 0, 0.82);
	color: #fff;
	font-family: var(--jc-font-body);
	font-size: 0.95rem;
	white-space: nowrap;
	pointer-events: none;
}

/* NOTE: the "frosted" look is carried by the translucent white fills alone (the pastel scene shows
 * through). We deliberately do NOT use backdrop-filter: blur() here — over the live WebGL board it's a
 * real mobile-GPU cost and it froze/mis-composited the renderer in testing. If a true glass blur is
 * wanted later, gate it carefully (small elements, @supports, device check) and re-verify on a phone.
 * See docs/dom-hud.md. */

/* ---- version stamp ---------------------------------------------------------- */
/* Build stamp, bottom-centre in red; dev-only (jcHud.setDev toggles [hidden]). Flush to the very
   bottom edge (its grey backdrop touches the screen bottom) so it's easy to read at a glance while
   dialling in a build; the safe-area inset is folded into the padding so the text still clears a
   phone's home indicator while the backdrop still reaches the edge. */
#jc-version {
	position: fixed;
	bottom: 0;
	left: 50%;
	transform: translateX(-50%);
	padding: 3px 8px calc(3px + var(--jc-safe-b));
	border-radius: 6px 6px 0 0;   /* flat bottom so it sits flush against the screen edge */
	background: rgba(0, 0, 0, 0.35);
	color: #ff2d2d;
	font-family: var(--jc-font-body);
	font-size: 13px;
	font-weight: 700;
	line-height: 1.2;
	pointer-events: none;
	user-select: none;
}

#jc-version[hidden] {
	display: none;
}

/* =========================================================================
 * Component style blocks. Each later wave OWNS its banner-marked block and
 * appends its component's rules there.
 * ========================================================================= */

/* ==== options (Wave 3a) ==== */

/* Top-right cluster: Mode + Difficulty pills, then the burger (right to left). */
#jc-opt-cluster {
	position: fixed;
	top: calc(var(--jc-inset) + var(--jc-safe-t));
	right: var(--jc-inset);
	display: flex;
	align-items: center;
	gap: 8px;
	pointer-events: none;   /* children opt back in */
}

/* The Mode + Difficulty pills wrapper. On a wide viewport it's `display: contents`, so the two pills
 * flow inline in the cluster row (Mode, Difficulty, jelly, burger — see the DOM order). */
#jc-cluster-pills {
	display: contents;
}

/* On a narrow (phone) viewport a horizontal row would slide the pills over the centre jelly counter,
 * so there the wrapper lifts out of the row and becomes an absolutely-positioned column hanging
 * directly beneath the burger, right-aligned. column-reverse keeps the DOM order (Mode, Difficulty)
 * but paints it Difficulty → Mode top-to-bottom, so the stack reads burger → Difficulty → Mode. The
 * top row stays just the jelly counter + burger, untouched. */
@media (max-width: 640px) {
	#jc-cluster-pills {
		position: absolute;
		top: calc(100% + 8px);
		right: 0;
		display: flex;
		flex-direction: column-reverse;
		align-items: flex-end;
		gap: 8px;
	}
}

/* Cluster buttons share sizing; the burger is square, the pills wrap their label. */
.jc-opt-clusterbtn {
	pointer-events: auto;
}

#jc-opt-burger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: clamp(44px, 11vw, 52px);
	height: clamp(44px, 11vw, 52px);
	padding: 0;
	border: 1px solid var(--jc-border-hi);
	border-radius: var(--jc-radius);
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
	cursor: pointer;
	box-shadow: var(--jc-shadow-btn);
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: transform 0.08s ease, box-shadow 0.08s ease;
}

#jc-opt-burger:active {
	transform: translateY(2px);
	box-shadow: 0 1px 0 rgba(0, 0, 0, 0.22), 0 3px 10px rgba(0, 0, 0, 0.30);
}

/* A small "receive" squash when the jelly counter arcs into the burger to minimize (JS toggles
 * .jc-burger-receive on landing — see flyJellyToCluster in hud.js). */
@keyframes jc-burger-receive {
	0% { transform: scale(1); }
	40% { transform: scale(1.16, 0.9); }
	70% { transform: scale(0.97, 1.03); }
	100% { transform: scale(1); }
}
#jc-opt-burger.jc-burger-receive {
	animation: jc-burger-receive 0.34s ease;
}
@media (prefers-reduced-motion: reduce) {
	#jc-opt-burger.jc-burger-receive { animation: none; }
}

.jc-burger-svg {
	width: 60%;
	height: 60%;
}

.jc-burger-svg rect {
	fill: var(--jc-ink);
	transform-box: fill-box;
	transform-origin: center;
	transition: transform 0.18s ease, opacity 0.14s ease;
}

/* Three bars morph to an X when the options panel is open. */
#jc-opt-burger[aria-expanded="true"] .jc-bar1 {
	transform: translateY(6px) rotate(45deg);
}
#jc-opt-burger[aria-expanded="true"] .jc-bar2 {
	opacity: 0;
}
#jc-opt-burger[aria-expanded="true"] .jc-bar3 {
	transform: translateY(-6px) rotate(-45deg);
}

/* Difficulty / Mode pills: smaller face than the main pills, still >= 44px tall. */
#jc-mode-btn,
#jc-diff-btn {
	min-height: 44px;
	padding: 0.4em 0.85em;
	font-size: clamp(0.9rem, 3.4vw, 1.05rem);
	border-color: var(--jc-border-hi);
}

#jc-mode-btn[hidden],
#jc-diff-btn[hidden] {
	display: none;
}

/* Scrim behind an open cluster panel: click-off closes (or confirms a dirty mode). Transparent so
 * the board stays visible; it only intercepts outside clicks. */
.jc-opt-panel-scrim {
	background: transparent;
	z-index: 1;   /* within #jc-hud stacking; below the panels */
}

.jc-opt-panel-scrim[hidden] {
	display: none;
}

/* A dropped panel under the cluster, right-aligned, clamped to the viewport with internal scroll. */
.jc-opt-panel {
	position: fixed;
	top: calc(var(--jc-inset) + var(--jc-safe-t) + clamp(44px, 11vw, 52px) + 10px);
	right: var(--jc-inset);
	z-index: 2;   /* above the panel scrim */
	width: min(20rem, calc(100vw - 2 * var(--jc-inset)));
	max-height: calc(100dvh - var(--jc-inset) - var(--jc-safe-t) - clamp(44px, 11vw, 52px) - 24px);
	overflow: auto;
	padding: 0.75em;
	pointer-events: auto;
	-webkit-overflow-scrolling: touch;
}

.jc-opt-panel[hidden] {
	display: none;
}

/* Screens stack; only the active one is shown (JS toggles [hidden]). */
.jc-opt-screen {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.jc-opt-screen[hidden] {
	display: none;
}

/* Panel header: optional back chevron, then the title. */
.jc-opt-header {
	display: flex;
	align-items: center;
	gap: 8px;
	min-height: 40px;
}

.jc-opt-title {
	font-size: 1.35rem;
	line-height: 1;
}

.jc-opt-back {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 1px solid var(--jc-border);
	border-radius: 10px;
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
	font-family: var(--jc-font-head);
	font-size: 1.5rem;
	line-height: 1;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

/* Nav rows: label left, chevron pushed to the right. */
.jc-opt-nav {
	justify-content: space-between;
}

.jc-opt-chevron {
	margin-left: auto;
	color: var(--jc-ink-dim);
	font-family: var(--jc-font-head);
	font-size: 1.2rem;
	line-height: 1;
}

.jc-row-label {
	flex: 1 1 auto;
}

/* The account row sits at the bottom; a little separation from the nav rows. */
.jc-opt-account {
	margin-top: 4px;
}

.jc-opt-account[disabled] {
	opacity: 0.5;
	cursor: default;
}

/* About blurb paragraphs. */
.jc-opt-blurb {
	margin: 0;
	color: var(--jc-ink-dim);
	font-family: var(--jc-font-body);
	font-size: 0.95rem;
	line-height: 1.4;
}

/* A toggle row: text block (label + note) on the left, the switch on the right. */
.jc-opt-toggle {
	display: flex;
	align-items: center;
	gap: 0.8em;
	min-height: 44px;
	padding: 0.35em 0.2em;
}

.jc-opt-toggle-text {
	flex: 1 1 auto;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

/* A dim explanatory note under a toggle / after a group. */
.jc-opt-note {
	color: var(--jc-ink-dim);
	font-family: var(--jc-font-body);
	font-size: 0.82rem;
	line-height: 1.35;
}

/* Radio groups (difficulty tiers, elo, mode, time controls). */
.jc-opt-radiogroup {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.jc-opt-radiogroup[hidden] {
	display: none;
}

/* A thin separator between grouped sections. */
.jc-opt-sep {
	height: 1px;
	margin: 4px 0;
	background: var(--jc-border);
	border: 0;
}

.jc-opt-sep[hidden] {
	display: none;
}

/* Graphics Advanced inline reveal. */
.jc-opt-adv-box {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding-left: 6px;
	border-left: 2px solid var(--jc-border);
}

.jc-opt-adv-box[hidden] {
	display: none;
}

/* Popup panels (Difficulty, Mode) share the panel look with a tighter column. */
.jc-opt-popup {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

/* Time-control + custom slider boxes reveal inline. */
.jc-opt-tcbox[hidden],
.jc-opt-custombox[hidden] {
	display: none;
}

.jc-opt-tcbox,
.jc-opt-custombox {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.jc-opt-slider-row {
	display: flex;
	flex-direction: column;
	gap: 8px;
	padding: 0.35em 0.2em;
}

.jc-opt-slider-top {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.8em;
}

/* The Save affordance in the Mode header, pushed to the right. */
.jc-opt-save {
	margin-left: auto;
	min-height: 40px;
	padding: 0.35em 0.9em;
	font-size: 1rem;
	background: rgba(255, 214, 107, 0.22);
	color: var(--jc-ink);
	border-color: var(--jc-border-hi);
}

/* Must out-specify `#jc-hud .jc-pill { display: inline-flex }` (id beats plain class), or the
 * hidden Save button stays painted. Prefix with #jc-hud to win on specificity + source order. */
#jc-hud .jc-opt-save[hidden] {
	display: none;
}

/* Mode confirm: reuse the shared full-screen scrim + centered card, but with a solid dim scrim so it
 * reads as a modal above the panel. */
.jc-opt-confirm-scrim {
	z-index: 12;   /* above panels; a genuine modal */
	background: var(--jc-bg-scrim);
}

.jc-opt-confirm-scrim[hidden] {
	display: none;
}

.jc-opt-confirm-text {
	font-family: var(--jc-font-head);
	color: var(--jc-ink);
	font-size: 1.15rem;
	line-height: 1.3;
	margin-bottom: 1em;
}

.jc-opt-confirm-btns {
	display: flex;
	gap: 10px;
	justify-content: center;
}

.jc-opt-confirm-yes {
	background: rgba(255, 214, 107, 0.22);
	color: var(--jc-ink);
	border-color: var(--jc-border-hi);
}

.jc-opt-confirm-no {
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
}

/* ---- shared confirm-card innards (undo / rematch prompts + menu) ------------ */
/* The options confirm card uses its own .jc-opt-confirm-* classes; these generic ones back the
 * undo/rematch cards so they share one look. */
#jc-hud .jc-confirm-text {
	font-family: var(--jc-font-head);
	color: var(--jc-ink);
	font-size: 1.15rem;
	line-height: 1.3;
	margin-bottom: 1em;
}

#jc-hud .jc-confirm-btns {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	justify-content: center;
}

/* Confirm-card button variants (layered on .jc-pill). */
#jc-hud .jc-confirm-yes {
	background: rgba(255, 214, 107, 0.22);
	color: var(--jc-ink);
	border-color: var(--jc-border-hi);
}

#jc-hud .jc-confirm-no {
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
}

#jc-hud .jc-confirm-neutral {
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
}

#jc-hud .jc-confirm-yes[hidden],
#jc-hud .jc-confirm-no[hidden],
#jc-hud .jc-confirm-neutral[hidden] {
	display: none;
}

/* A toast that fades in/out via a shared class (undo + rematch). Centred bottom, above its pill. */
#jc-hud .jc-toast {
	position: fixed;
	left: 50%;
	transform: translateX(-50%);
	opacity: 0;
	transition: opacity 0.28s ease;
}

#jc-hud .jc-toast.jc-visible {
	opacity: 1;
}

#jc-hud .jc-toast[hidden] {
	display: none;
}

/* ==== undo (Wave 3b) ==== */

/* Bottom-centre Undo pill. Its bottom is driven by the layout engine (--jc-undo-bottom). Calm raised
 * pill face inherited from .jc-pill; [disabled] greys it out. */
#jc-undo {
	position: fixed;
	left: 50%;
	bottom: var(--jc-undo-bottom, calc(24px + var(--jc-safe-b)));
	transform: translateX(-50%);
}

/* #jc-hud prefix so this out-specifies the generic `#jc-hud .jc-pill:active` (which sets a plain
 * translateY(2px)); without it the press would drop the translateX(-50%) and the pill would slide
 * sideways to the right. */
#jc-hud #jc-undo:active {
	/* keep the springy press but preserve the centring transform */
	transform: translateX(-50%) translateY(2px);
}

#jc-undo[disabled] {
	opacity: 0.45;
	cursor: default;
	box-shadow: 0 1px 0 rgba(0, 0, 0, 0.22);
}

/* #jc-hud prefix so a disabled undo still out-specifies `#jc-hud #jc-undo:active` and stays put
 * (no springy press) instead of dropping to translateY(2px). */
#jc-hud #jc-undo[disabled]:active {
	transform: translateX(-50%);
}

#jc-undo[hidden] {
	display: none;
}

/* Undo toast sits just above the Undo pill (one pill-height + gap above --jc-undo-bottom). */
.jc-undo-toast {
	bottom: calc(var(--jc-undo-bottom, calc(24px + var(--jc-safe-b))) + 56px);
}

/* Undo incoming-request modal scrim. */
.jc-undo-scrim {
	z-index: 12;
}

.jc-undo-scrim[hidden] {
	display: none;
}

/* ==== rematch (Wave 3c) ==== */

/* Result banner: centred, huge Chewy, dark outline for legibility over marble. Sits just BELOW the
 * jelly counter pill (--jc-banner-top, published by the layout engine to the pill's bottom + a gap)
 * so "You won!" never hides behind the counter; falls back to a fixed top-clearance before a board
 * rect arrives. */
#jc-banner {
	position: fixed;
	top: var(--jc-banner-top, calc(var(--jc-inset) + var(--jc-safe-t) + 84px));
	left: 50%;
	transform: translateX(-50%);
	max-width: calc(100vw - 2 * var(--jc-inset));
	text-align: center;
	font-family: var(--jc-font-head);
	font-size: clamp(2rem, 9vw, 3.6rem);
	line-height: 1.05;
	pointer-events: none;
	white-space: nowrap;
	/* Frosted-white glyphs (the same translucent white as the buttons) outlined in the buttons' dark-
	 * slate ink, so the result reads as our chrome rather than a bespoke colour. The 4-way ink outline
	 * carries legibility over confetti / marble; a soft ink drop underneath adds depth. Applies to every
	 * outcome (won / lost / draw and the "on time" variants) — no per-outcome colour any more. */
	color: var(--jc-bg-raised);
	text-shadow:
		-2px -2px 0 var(--jc-ink), 2px -2px 0 var(--jc-ink),
		-2px 2px 0 var(--jc-ink), 2px 2px 0 var(--jc-ink),
		0 3px 10px rgba(39, 49, 63, 0.55);
}

#jc-banner[hidden] {
	display: none;
}

/* Play-again button shares the Undo bottom slot (they never show together). */
#jc-play-btn {
	position: fixed;
	left: 50%;
	bottom: var(--jc-undo-bottom, calc(24px + var(--jc-safe-b)));
	transform: translateX(-50%);
	/* A touch of accent so "play again" reads as the primary action. */
	background: var(--jc-accent-soft);
	color: var(--jc-ink);
	border-color: var(--jc-border-hi);
}

/* #jc-hud prefix to out-specify `#jc-hud .jc-pill:active` (see the #jc-undo note): without it the
 * press drops translateX(-50%) and the button slides right by half its width. */
#jc-hud #jc-play-btn:active {
	transform: translateX(-50%) translateY(2px);
}

#jc-play-btn[hidden] {
	display: none;
}

/* Rematch toast just above the play-again button. */
.jc-rematch-toast {
	bottom: calc(var(--jc-undo-bottom, calc(24px + var(--jc-safe-b))) + 56px);
}

/* Rematch Accept/Decline modal scrim. */
.jc-rematch-scrim {
	z-index: 12;
}

.jc-rematch-scrim[hidden] {
	display: none;
}

/* Top-left "New game" opener — mirrors the burger's top-right placement + raised look. */
#jc-newgame-btn {
	position: fixed;
	top: calc(var(--jc-inset) + var(--jc-safe-t));
	left: var(--jc-inset);
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: clamp(44px, 11vw, 52px);
	padding: 0.4em 0.95em;
	border: 1px solid var(--jc-border-hi);
	border-radius: var(--jc-radius);
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
	font-family: var(--jc-font-head);
	font-size: clamp(0.95rem, 3.6vw, 1.1rem);
	font-weight: 400;
	line-height: 1;
	cursor: pointer;
	box-shadow: var(--jc-shadow-btn);
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: transform 0.08s ease, box-shadow 0.08s ease;
}

#jc-newgame-btn:active {
	transform: translateY(2px);
	box-shadow: 0 1px 0 rgba(0, 0, 0, 0.22), 0 3px 10px rgba(0, 0, 0, 0.30);
}

#jc-newgame-btn[hidden] {
	display: none;
}

/* New-game confirm menu modal scrim. */
.jc-newgame-scrim {
	z-index: 12;
}

.jc-newgame-scrim[hidden] {
	display: none;
}

/* Stack the menu buttons in a column so three+ options never crowd one line on a phone. */
.jc-newgame-btns {
	flex-direction: column;
}

/* ==== conn (Wave 3d) ==== */

/* Top-centre connection pill BELOW the jelly counter. --jc-conn-top is a fixed reserved offset
 * (jelly-pill height + gaps) so the pill's top never jumps as the jelly count changes width. */
#jc-conn {
	position: fixed;
	top: var(--jc-conn-top, calc(var(--jc-inset) + var(--jc-safe-t) + 52px));
	left: 50%;
	transform: translateX(-50%);
	max-width: calc(100vw - 2 * var(--jc-inset));
	padding: 0.4em 0.95em;
	border-radius: var(--jc-radius-pill);
	background: var(--jc-bg-raised);
	border: 1px solid var(--jc-border);
	font-family: var(--jc-font-body);
	font-size: 0.95rem;
	font-weight: 700;
	line-height: 1;
	white-space: nowrap;
	text-align: center;
	pointer-events: none;
	box-shadow: var(--jc-shadow-btn);
	opacity: 0;
	transition: opacity 0.3s ease;
}

#jc-conn.jc-visible {
	opacity: 1;
}

#jc-conn[hidden] {
	display: none;
}

#jc-conn.jc-conn-amber { color: var(--jc-conn-amber); }
#jc-conn.jc-conn-green { color: var(--jc-conn-green); }
#jc-conn.jc-conn-red { color: var(--jc-conn-red); }

/* ---- "This game is full" modal --------------------------------------------- */
/* Blocking popup shown when a third player is turned away from a full room. Replaces the old red conn
 * pill: a centred card (jc-confirm base) over a dim scrim, whose ONLY action is the New game button
 * (a fresh solo game vs the computer). No scrim-dismiss, no cancel. */
#jc-hud .jc-full-scrim {
	z-index: 13;   /* a genuine terminal modal — above the option panels and jelly popup */
	background: var(--jc-bg-scrim);
}

#jc-hud .jc-full-scrim[hidden] {
	display: none;
}

#jc-hud .jc-full-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.7em;
	/* Definite width so the blurb wraps naturally and the button's min-width resolves (same reasoning
	 * as the jelly popup — a centred flex column otherwise shrink-wraps to a narrow sliver). */
	box-sizing: border-box;
	width: min(90vw, 22rem);
}

.jc-full-title {
	font-family: var(--jc-font-head);
	color: var(--jc-ink);
	font-size: 1.5rem;
	line-height: 1;
}

.jc-full-blurb {
	margin: 0;
	color: var(--jc-ink);
	font-family: var(--jc-font-body);
	font-size: 1rem;
	line-height: 1.45;
}

#jc-hud .jc-full-newgame {
	margin-top: 0.2em;
	min-width: 60%;
}

/* ==== jelly (Wave 3e) ==== */

/* A small calm pill living in the top-right cluster, just LEFT of the burger — same raised-white
 * chrome and height as the burger / Mode / Difficulty buttons, so it sits quietly alongside them
 * rather than shouting from the centre. The candy-pink mascot on the left, the count (Chewy) on the
 * right. :active presses down like the burger; a tap fires a bounce. */
#jc-jelly {
	position: relative;   /* anchors the "tap me" sparkle overlay (.jc-jelly-sparkles) to the pill */
	display: inline-flex;
	align-items: center;
	gap: 7px;
	height: clamp(44px, 11vw, 52px);
	padding: 0 12px 0 8px;
	border: 1px solid var(--jc-border-hi);
	border-radius: var(--jc-radius-pill);
	background: var(--jc-bg-raised);
	color: var(--jc-ink);
	cursor: pointer;
	box-shadow: var(--jc-shadow-btn);
	pointer-events: auto;   /* the cluster is pointer-events:none; children opt back in */
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: transform 0.08s ease, box-shadow 0.08s ease;
}

/* Press down like the burger. */
#jc-jelly:active {
	transform: translateY(2px);
	box-shadow: 0 1px 0 rgba(0, 0, 0, 0.22), 0 3px 10px rgba(0, 0, 0, 0.30);
}

#jc-jelly[hidden] {
	display: none;
}

.jc-jelly-mascot {
	position: relative;
	width: 26px;
	height: 26px;
	flex: 0 0 auto;
	filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.2));
}

/* Small "already earned" badge appended after a Difficulty row's label (jc-row-label is flex:1, so
 * this sits flush right). See docs/computer-opponent.md. */
.jc-diff-jelly {
	width: 18px;
	height: 18px;
}

.jc-jelly-count {
	position: relative;
	font-family: var(--jc-font-head);
	font-size: 1.15rem;
	line-height: 1;
	font-variant-numeric: tabular-nums;
}

/* Quick bounce fired on tap (JS toggles .jc-jelly-bounce to retrigger). */
@keyframes jc-jelly-bounce {
	0% { transform: scale(1); }
	35% { transform: scale(1.14); }
	70% { transform: scale(0.96); }
	100% { transform: scale(1); }
}

#jc-jelly.jc-jelly-bounce {
	animation: jc-jelly-bounce 0.32s ease;
}

@media (prefers-reduced-motion: reduce) {
	#jc-jelly.jc-jelly-bounce { animation: none; }
}

/* ---- "Tap me" nudge: wiggle + gold sparkle -----------------------------------
 * Shown on the big CENTERED pill when the player OWNS jellies but has unlocked nothing yet (the JS adds
 * .jc-jelly-nudge only in the center placement — see applyJellyNudge), to teach that the counter is a
 * button into the jelly drop board. The minimized corner pill during a game stays calm. The mascot rocks
 * in slow periodic bursts and a few gold stars twinkle around the pill — the DOM analog of the game's 3D
 * "click me" sparkle (scripts/sparkle.gd). The wiggle rides the MASCOT (not the pill) so it never fights
 * the pill's own transforms — the tap bounce, the reward wobble/pulse, or the inline minimize flight,
 * which would clobber each other if they shared the button's transform. */

/* The mascot rocks: mostly at rest, then a short shake burst — reads as "hey, tap me" without nagging. */
@keyframes jc-jelly-mascot-wiggle {
	0%, 58%, 100% { transform: rotate(0deg); }
	64% { transform: rotate(-12deg); }
	72% { transform: rotate(9deg); }
	80% { transform: rotate(-6deg); }
	88% { transform: rotate(4deg); }
	94% { transform: rotate(-2deg); }
}

#jc-jelly.jc-jelly-nudge .jc-jelly-mascot {
	transform-origin: 50% 82%;   /* rock about the base, like a head shake */
	animation: jc-jelly-mascot-wiggle 2.6s ease-in-out infinite;
}

/* The sparkle overlay — hidden until the nudge is on. Sized to the pill; stars are placed just around its
 * edges (a couple spill slightly outside, which the corner cluster doesn't clip; the centred pill opts
 * into overflow:visible below). Never eats a tap. */
.jc-jelly-sparkles {
	position: absolute;
	inset: 0;
	pointer-events: none;
	display: none;
}

#jc-jelly.jc-jelly-nudge .jc-jelly-sparkles { display: block; }

/* One gold twinkle: a warm-cored star with a gold glow, scaling 0→1→0 and turning as it winks (mirrors
 * sparkle.gd's gold stars). transform-box:fill-box so scale/rotate pivot on the star's own centre. */
.jc-jelly-spark {
	position: absolute;
	width: 13px;
	height: 13px;
	fill: #fff6cf;
	filter: drop-shadow(0 0 3px rgba(255, 200, 70, 0.95));
	opacity: 0;
	transform-box: fill-box;
	transform-origin: center;
	animation: jc-jelly-twinkle 1.7s ease-in-out infinite;
}

@keyframes jc-jelly-twinkle {
	0%, 100% { opacity: 0; transform: scale(0.2) rotate(0deg); }
	50% { opacity: 1; transform: scale(1) rotate(60deg); }
}

/* Four stars scattered around the pill on their own staggers + sizes, so they twinkle out of sync. */
.jc-jelly-spark:nth-child(1) { top: -6px; left: -4px; animation-delay: 0s; }
.jc-jelly-spark:nth-child(2) { top: -8px; right: 2px; width: 16px; height: 16px; animation-delay: 0.55s; }
.jc-jelly-spark:nth-child(3) { bottom: -5px; right: -4px; width: 11px; height: 11px; animation-delay: 1.05s; }
.jc-jelly-spark:nth-child(4) { bottom: -3px; left: 10px; width: 9px; height: 9px; animation-delay: 1.5s; }

/* Centred state clips its children (overflow:hidden for the gloss + rounded corners), which would crop
 * the edge stars — let them spill while nudging. The background still clips to the border-radius. */
#jc-jelly.jc-jelly-center.jc-jelly-nudge { overflow: visible; }

@media (prefers-reduced-motion: reduce) {
	#jc-jelly.jc-jelly-nudge .jc-jelly-mascot { animation: none; }
	#jc-jelly.jc-jelly-nudge .jc-jelly-sparkles { display: none; }
}

/* ---- Centered state: a large candy hotspot floating top-centre. -------------
 * Used when the counter is a focal point: vs the computer (always), and the moment the local player wins
 * a human game (the celebration). Same element as the cluster pill, so the reward token still flies to
 * it; only these overrides differ. Horizontally centred with auto margins (NOT a transform) so the
 * bounce/press/wobble scale transforms stay free. --jc-jelly-top is published by the layout engine
 * (relayout); the fallback pins it under the top inset before a board rect lands. */
#jc-jelly.jc-jelly-center {
	display: flex;
	position: fixed;
	top: var(--jc-jelly-top, calc(var(--jc-inset) + var(--jc-safe-t)));
	left: 0;
	right: 0;
	margin-inline: auto;
	width: max-content;
	max-width: calc(100vw - 2 * var(--jc-inset));
	height: auto;
	min-height: 66px;
	gap: 10px;
	padding: 8px 26px 8px 14px;
	border-color: rgba(255, 255, 255, 0.28);
	background: linear-gradient(180deg, var(--jc-green-hi), var(--jc-green));
	color: var(--jc-on-green);
	box-shadow: 0 3px 0 rgba(14, 44, 22, 0.35), 0 6px 16px rgba(0, 0, 0, 0.30);
	overflow: hidden;
	z-index: 10;
}

/* Glossy top highlight, centred state only. */
#jc-jelly.jc-jelly-center::before {
	content: "";
	position: absolute;
	top: 2px;
	left: 8%;
	width: 84%;
	height: 42%;
	border-radius: var(--jc-radius-pill);
	background: linear-gradient(180deg, rgba(255, 255, 255, 0.55), rgba(255, 255, 255, 0));
	pointer-events: none;
}

#jc-jelly.jc-jelly-center:active {
	box-shadow: 0 1px 0 rgba(14, 44, 22, 0.35), 0 3px 10px rgba(0, 0, 0, 0.30);
}

#jc-jelly.jc-jelly-center .jc-jelly-mascot {
	width: 48px;
	height: 48px;
}

#jc-jelly.jc-jelly-center .jc-jelly-count {
	font-size: 2rem;
}

/* ---- Yielding to an emoji reaction -----------------------------------------
 * The opponent's emoji pops up INSIDE the WebGL canvas, dead centre above the far edge of the board
 * (emoji_reactions.gd, far_lateral = 0) — the same top band this centred pill floats in. Every DOM
 * pixel paints over every canvas pixel, so no z-index can put the emoji on top: the pill has to get
 * out of the light itself. It fades, rather than sliding: the band is too thin to move within, and
 * `transform` is already spoken for (the bounce/press/wobble keyframes and the fly-to-cluster
 * flight), while `opacity` is untouched by anything else on this element. All the way to zero, not a
 * dim ghost: at even 12% the green pill still tints the emoji behind it, and the whole point is that
 * the emoji wins this spot outright. It's a fade, not a blink, so the pill still reads as stepping
 * aside. CENTERED state only — the corner pill is nowhere near the far display, so the class is inert
 * there. hud.js owns the timing (jellyYield) and brings it back with a bounce. */
#jc-jelly.jc-jelly-center {
	transition: opacity 380ms ease;
}

#jc-jelly.jc-jelly-center.jc-jelly-yield {
	opacity: 0;
	transition: opacity 200ms ease;
	pointer-events: none;   /* faded out isn't a tap target; the emoji owns this spot for now */
}

/* ---- Jelly drop board popup (raised by tapping the jelly counter) ----------- */
/* A centred modal card (jc-confirm base) behind a dim scrim: mascot + explainer + Unlock button. */
#jc-hud .jc-jelly-scrim {
	z-index: 12;   /* above the option panels; a genuine modal, like the mode confirm */
	background: var(--jc-bg-scrim);
}

#jc-hud .jc-jelly-scrim[hidden] {
	display: none;
}

#jc-hud .jc-jelly-popup {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.6em;
	/* This card carries a paragraph of blurb plus two full-width buttons. In a centred flex column the
	 * card shrink-wraps to the blurb's min-content (a narrow ~150px sliver), so the shared .jc-confirm
	 * max-width cap never bites and the text wraps to many cramped short lines on a phone. Give it a
	 * DEFINITE width so the blurb wraps naturally and the buttons' 60% min-width has something real to
	 * resolve against, clamped by the viewport so it keeps a comfortable side margin. border-box so the
	 * viewport clamp accounts for the card's own padding (otherwise it spills to the screen edges). */
	box-sizing: border-box;
	width: min(90vw, 24rem);
}

.jc-jelly-popup-mascot {
	width: 58px;
	height: 58px;
	filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.35));
}

.jc-jelly-popup-title {
	font-family: var(--jc-font-head);
	color: var(--jc-ink);
	font-size: 1.6rem;
	line-height: 1;
}

.jc-jelly-popup-blurb {
	margin: 0;
	color: var(--jc-ink);
	font-family: var(--jc-font-body);
	font-size: 1rem;
	line-height: 1.45;
}

.jc-jelly-popup-balance {
	color: var(--jc-ink-dim);
	font-family: var(--jc-font-body);
	font-size: 0.9rem;
}

#jc-hud .jc-jelly-unlock {
	margin-top: 0.2em;
	min-width: 60%;
	background: rgba(255, 214, 107, 0.22);
	color: var(--jc-ink);
	border-color: var(--jc-border-hi);
}

/* "Save my Jellies!" — the pink call to save progress to an account (opens the account popup). Solid
 * candy pink so it reads as a distinct action from the (tinted) "Drop Jellies!" above it. */
#jc-hud .jc-jelly-save {
	min-width: 60%;
	background: var(--jc-pink);
	color: var(--jc-on-pink);
	border-color: rgba(75, 21, 40, 0.22);
}
#jc-hud .jc-jelly-save:active { background: var(--jc-pink-hi); }

/* ---- account / sign-in popup ----------------------------------------------- */
/* The white-themed modal that replaced the old dark /signin iframe overlay (see docs/auth.md). Opened
 * by "Save my Jellies!" and the options Account row. Two views toggled by the
 * pushed account state (signed out → Google button; signed in → account readout + Sign out). */
#jc-hud .jc-account-scrim {
	z-index: 13;   /* above the jelly popup (12) and the option panels; a genuine modal */
	background: var(--jc-bg-scrim);
}
#jc-hud .jc-account-scrim[hidden] { display: none; }

#jc-hud .jc-account-card {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 0.7em;
	max-width: min(90vw, 22rem);
}

.jc-account-view {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 0.7em;
}
/* The explicit display above beats the UA [hidden]{display:none}, so restore it for the hidden view. */
.jc-account-view[hidden] { display: none; }

.jc-account-title {
	font-family: var(--jc-font-head);
	color: var(--jc-ink);
	font-size: 1.4rem;
	line-height: 1.1;
	text-align: center;
}
.jc-account-title-signed { color: var(--jc-conn-green); }

.jc-account-blurb {
	margin: 0;
	color: var(--jc-ink);
	font-family: var(--jc-font-body);
	font-size: 1rem;
	line-height: 1.45;
	text-align: center;
}

/* Google button: the standard white sign-in button (bright, primary), the multicolour G on the left. */
#jc-hud .jc-account-google {
	gap: 0.6em;
	background: #ffffff;
	color: #1f1f1f;
	border-color: var(--jc-border-hi);
	font-family: var(--jc-font-body);
	font-weight: 600;
}
#jc-hud .jc-account-google:active { background: #f1f1f1; }
#jc-hud .jc-account-google:disabled { opacity: 0.6; }
.jc-account-g { width: 18px; height: 18px; flex: 0 0 auto; }

/* Signed-in readout: name in ink, email dimmed and wrap-safe. */
.jc-account-who {
	text-align: center;
	font-family: var(--jc-font-body);
	line-height: 1.4;
}
.jc-account-name { font-weight: 700; color: var(--jc-ink); }
.jc-account-email { color: var(--jc-ink-dim); font-size: 0.92rem; word-break: break-all; }

/* Sign out is the quiet, secondary action. */
#jc-hud .jc-account-signout {
	background: var(--jc-bg-raised);
	color: var(--jc-ink-dim);
	font-family: var(--jc-font-body);
	font-weight: 600;
}
#jc-hud .jc-account-signout:disabled { opacity: 0.6; }

.jc-account-status {
	min-height: 1.1em;
	text-align: center;
	font-family: var(--jc-font-body);
	font-size: 0.88rem;
	color: var(--jc-conn-green);
}
.jc-account-status.jc-account-err { color: var(--jc-conn-red); }

/* ---- win reward ceremony: the "+1 jelly" token ----------------------------- */
/* Fired by jcHud.jellyReward on a win (Godot's Jellies.award_ceremony). A jelly disc + "+1" blooms
 * centre-screen ~1s after the confetti; a tap anywhere (or a ~3s auto-timeout) sends it arcing into the
 * counter pill, which wobbles and ticks up. All body-level fixed layers, stacked above the confetti. */
.jc-jr-catch {
	position: fixed;
	inset: 0;
	z-index: 100000;
	background: transparent;
	cursor: pointer;
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
}

.jc-jr-token {
	position: fixed;
	width: 128px;
	height: 128px;
	margin-left: -64px;   /* centre the box on its (left, top) point so scale never shifts it */
	margin-top: -64px;
	z-index: 100002;
	pointer-events: none;
	will-change: transform, left, top;
}

.jc-jr-glow {
	position: absolute;
	inset: -20px;
	border-radius: 50%;
	background: radial-gradient(circle, rgba(126, 224, 143, 0.55) 0%, rgba(126, 224, 143, 0) 65%);
	filter: blur(2px);
}

.jc-jr-inner {
	position: absolute;
	inset: 0;
	display: grid;
	place-items: center;
}

/* Green disc to match the counter pill (the jelly mascot itself stays pink, floating on the green). */
.jc-jr-disc {
	position: absolute;
	inset: 8px;
	border-radius: 50%;
	background: radial-gradient(circle at 38% 32%, #d9f8e1 0%, #7ee08f 42%, #46c26a 74%, #2f9d51 100%);
	box-shadow: 0 10px 30px rgba(47, 157, 81, 0.5), inset 0 3px 8px rgba(255, 255, 255, 0.7);
}

.jc-jr-face {
	position: relative;
	width: 74px;
	height: 74px;
	z-index: 1;
}

/* "+1" badge on the disc. Follows the frosted chrome theme: white fill with a dark-slate (blue-ink)
 * outline, matching the result banner's legibility treatment rather than a bespoke colour. Fades out
 * over the flight (flyJellyToken) so it tucks away as the token merges into the pill. */
.jc-jr-plus {
	position: absolute;
	right: -8px;
	top: -10px;
	z-index: 2;
	font-family: var(--jc-font-head);
	font-size: 1.7rem;
	line-height: 1;
	color: #fff;
	-webkit-text-stroke: 3px var(--jc-ink);
	paint-order: stroke fill;
	text-shadow: 0 2px 5px rgba(30, 50, 80, 0.4);
}

/* A clean grow-in, no twist: fades up from a small scale to a single gentle overshoot (the back-out
 * bezier does the ~1.1 pop and settle), then rests. Starting a touch above zero avoids the harsh
 * snap-from-nothing that made the old spin read as a glitch. */
@keyframes jc-jr-bloom {
	0% { transform: scale(0.4); opacity: 0; }
	100% { transform: scale(1); opacity: 1; }
}
.jc-jr-token.jc-jr-bloom { animation: jc-jr-bloom 0.44s cubic-bezier(0.34, 1.56, 0.64, 1) both; }

@keyframes jc-jr-idle {
	0%, 100% { transform: scale(1); }
	50% { transform: scale(1.05); }
}
.jc-jr-token.jc-jr-idle .jc-jr-inner { animation: jc-jr-idle 1.3s ease-in-out infinite; }

/* Token impact plays in TWO deliberate beats (timed from JS in impactJelly):
 *  1. jc-jelly-reward-wobble — the ABSORB: a squash + slight tilt as the +1 lands. The count still
 *     shows the OLD number through this beat.
 *  2. jc-jelly-reward-pulse — the INCREMENT: after a clear gap, a rounder symmetric pop, and the
 *     counter ticks up ON this second beat (the new number is revealed by the pulse, not the absorb).
 * Both are transform-only (scale/rotate) so the centred pill's auto-margin layout stays free. */
@keyframes jc-jelly-reward-wobble {
	0% { transform: scale(1) rotate(0); }
	32% { transform: scale(1.12, 0.9) rotate(-2.5deg); }
	100% { transform: scale(1) rotate(0); }
}
#jc-jelly.jc-jelly-reward-wobble { animation: jc-jelly-reward-wobble 0.42s ease-out; }

@keyframes jc-jelly-reward-pulse {
	0% { transform: scale(1); }
	38% { transform: scale(1.16); }
	100% { transform: scale(1); }
}
#jc-jelly.jc-jelly-reward-pulse { animation: jc-jelly-reward-pulse 0.44s ease-out; }

@media (prefers-reduced-motion: reduce) {
	#jc-jelly.jc-jelly-reward-wobble,
	#jc-jelly.jc-jelly-reward-pulse { animation: none; }
}

/* "Already won a jelly on this difficulty" note — a decisive win that granted nothing (a computer
 * level already cleared). Frosted-white card, centred; dismissed only by tapping OK, so there's
 * always time to read it. */
.jc-jr-note {
	position: fixed;
	left: 50%;
	top: 38%;
	transform: translate(-50%, 10px);
	z-index: 100002;
	max-width: 78vw;
	text-align: center;
	padding: 14px 20px;
	border-radius: 18px;
	background: rgba(255, 255, 255, 0.92);
	color: #2a2036;
	box-shadow: 0 14px 44px rgba(0, 0, 0, 0.28);
	backdrop-filter: blur(6px);
	-webkit-backdrop-filter: blur(6px);
	opacity: 0;
	transition: opacity 0.3s ease, transform 0.3s ease;
	pointer-events: none;
	font-family: var(--jc-font-body);
	line-height: 1.35;
}
.jc-jr-note.jc-jr-show { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
.jc-jr-note-1 { margin-bottom: 4px; }
.jc-jr-note-2 { font-family: var(--jc-font-head); margin-bottom: 12px; }
/* "Don't show again" opt-out row — quiet, below the message, above the OK button. */
.jc-jr-note-hide {
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 8px;
	margin-bottom: 12px;
	font-family: var(--jc-font-body);
	font-size: 0.85rem;
	color: #6a6076;
	cursor: pointer;
	user-select: none;
	-webkit-user-select: none;
}
.jc-jr-note-hide-cb {
	width: 17px;
	height: 17px;
	margin: 0;
	accent-color: var(--jc-conn-green);
	cursor: pointer;
	flex: none;
}
#jc-hud .jc-jr-note-ok {
	background: rgba(255, 214, 107, 0.22);
	color: var(--jc-ink);
	border-color: var(--jc-border-hi);
}

/* The whole HUD is pulled while a full-screen Godot overlay (the jelly drop board) is up, so no flat
 * chrome floats over it. JS toggles this on the root; specificity beats the plain #jc-hud rule. */
#jc-hud.jc-suppressed {
	display: none;
}

/* =========================================================================
 * Invite button (moved verbatim from web_shell.html; visually identical).
 * A real DOM button so navigator.share()/clipboard fire inside a genuine user gesture. It floats
 * above the whole canvas with its own z-index (11, above confetti), so it lives OUTSIDE #jc-hud.
 * Its bottom offset is driven by the hud.js layout engine (inline style), stacking just above the
 * Undo pill and tucking under the board together on short screens. See docs/netcode-design.md.
 * ========================================================================= */
#jc-invite-btn {
	position: fixed;
	left: 50%;
	/* Fallback bottom until the layout engine sets it (matches the old shell value: it sat above the
	   Undo button, ~24px + its height). The engine overwrites this inline once a board rect lands. */
	bottom: calc(88px + env(safe-area-inset-bottom, 0px));
	transform: translateX(-50%);
	z-index: 11;
	display: none;               /* shown as inline-flex by window.jcInvite.show() */
	align-items: center;
	gap: 9px;
	margin: 0;
	/* Same candy-pill family as the jelly counter: green gradient, glossy highlight, white rim. */
	padding: 7px 20px 7px 7px;
	border: 1px solid rgba(255, 255, 255, 0.28);
	border-radius: 999px;
	background: linear-gradient(180deg, var(--jc-green-hi), var(--jc-green));
	color: var(--jc-on-green);
	font-family: 'Chewy', 'Trebuchet MS', 'Segoe UI', system-ui, sans-serif;
	font-size: clamp(1rem, 4.2vw, 1.2rem);
	font-weight: 400;
	line-height: 1;
	cursor: pointer;
	box-shadow: 0 3px 0 rgba(14, 44, 22, 0.35), 0 6px 16px rgba(0, 0, 0, 0.30);
	-webkit-tap-highlight-color: transparent;
	touch-action: manipulation;
	transition: transform 0.08s ease, box-shadow 0.08s ease;
}

#jc-invite-btn:active {
	transform: translateX(-50%) translateY(2px);
	box-shadow: 0 1px 0 rgba(14, 44, 22, 0.35), 0 3px 10px rgba(0, 0, 0, 0.30);
}

/* Pale-green (Mist) circle behind the person-plus icon, so the icon reads clearly on the green pill
 * instead of green-on-green. The icon itself is the same dark tone as the label + jelly count. */
.jc-invite-badge {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	border-radius: 50%;
	background: var(--jc-invite-badge);
}

.jc-invite-badge svg {
	width: 18px;
	height: 18px;
	display: block;
}

#jc-invite-toast {
	position: fixed;
	left: 50%;
	bottom: calc(142px + env(safe-area-inset-bottom, 0px));
	transform: translateX(-50%);
	z-index: 11;
	display: none;               /* shown briefly by the clipboard fallback */
	padding: 0.4em 0.95em;
	border-radius: 999px;
	background: rgba(0, 0, 0, 0.82);
	color: #fff;
	font-family: 'Trebuchet MS', 'Segoe UI', system-ui, sans-serif;
	font-size: 0.95rem;
	white-space: nowrap;
	pointer-events: none;
}
