/*
	Legacy-faithful skin, app-wide (ADR 017, which supersedes the earlier "rounded/pastel oklch
	facelift" this file used to describe — 011/013 had already narrowed that to a shrinking list
	of surfaces; 017 removes the last of them). The look is sampled from the original SuperPoke
	Pets reference screenshots: flat white panels, hairline blue-grey borders, the compact
	2009-era type. One `:root` token block below carries it, plus the type/elevation/radius/z
	scales added in the visual-polish pass (docs/backlog/planned-features/visual-polish-pass/).

	The sidebar shows real account data throughout — pet name/habitat, plus level/badges/friends/
	compliments live since Phase 6 (server/src/routes/habitat.ts) — and swaps to a visited
	friend's own data while visiting (chrome/profile.js), never a fabricated placeholder.
*/

/*
	`Porky's` (the rounded display face) and `Arial Rounded MT Bold` (the body / running-copy
	face) are the two typefaces the original SuperPoke Pets SWFs set their dynamic text in —
	neither embedded in the bundles, both self-hosted from ./fonts/ (gitignored, provisioned by
	tools/provision-fonts.sh) so the canvas and this chrome render the same faces everywhere.
	`Porky's` is declared here WITHOUT its apostrophe because the OpenFL canvas side
	single-quote-wraps the family name and `'Porky's'` is a broken CSS string — see UiFonts.hx.

	`Porky's` is display-only, and deliberately narrow: the top nav tabs (`.sp-nav-btn`) and the
	titles of cards and modals (`.sp-panel-head h2`, every `#sp-*-modal-title` / `#sp-*-card h2`,
	`#sp-item-modal-name`, `#sp-shop-cart-title`, `#sp-gift-sent-heading`, the auth-view `h2`s +
	logo banner). Everything else is on the body face — all other buttons (`.sp-btn` / `.sp-tab` /
	`.sp-icon-nav-btn` / category + toggle + tour buttons), the profile-sidebar section heads
	(`.sp-profile-section-head`) and the friends-tab section heads (`.sp-friend-section h3`),
	player-entered strings (profile username, pet name), the toolbar icon labels, every numeric
	readout (counts, XP, pagination, goal progress, share stats), and the "G" currency corner
	flags. The display face's ambiguous digits, control-font fallback on bare `<button>`s, and
	poor fit for arbitrary-length text all hurt legibility outside of titles.

	`Source Sans 3` (Adobe's open, OFL-licensed sans — the renamed Source Sans Pro) stands in for
	the legacy form face `Myriad Web Pro` on form controls and the adoption form's `error_txt`.
	It is committed rather than provisioned: its licence permits redistribution. One variable
	file covers the whole weight axis.
*/
@font-face {
	font-family: "Porkys";
	src: url("./fonts/porkys.ttf") format("truetype");
	font-weight: normal;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: "Arial Rounded MT Bold";
	src: url("./fonts/arial-rounded-mt-bold.ttf") format("truetype");
	font-weight: normal;
	font-style: normal;
	font-display: swap;
}
@font-face {
	font-family: "Source Sans 3";
	src: url("./fonts/source-sans-3.ttf") format("truetype-variations");
	font-weight: 200 900;
	font-style: normal;
	font-display: swap;
}

/*
	ONE token block. Until the visual-polish pass (docs/backlog/planned-features/visual-polish-pass/,
	ADR 017) there were two: this `--sp-*` set (the pastel/oklch "facelift") and a second `:root`
	further down holding `--spx-*` (the legacy-faithful values "sampled from the reference
	screenshot" — ADR 011/013). The app is now legacy-faithful app-wide, so the `--spx-*` palette is
	canonical and lives here; the `--sp-*` semantic names are kept (297 call sites) but re-pointed at
	the legacy values, which is what carries the re-skin onto the surfaces the ADR-011 block never
	reached (modals, Social, Snapshots, the logged-out auth views).

	Scales (type / elevation / radius / spacing / z) are new here — the review found none existed,
	which is most of why the UI read as unfinished. Use the tokens, not raw values.
*/
:root {
	/* --- legacy-faithful palette (was `--spx-*`; both name sets resolve here now) --- */
	--spx-sky: #bce4f7;          /* exact colour of background-tile.png's bottom row */
	--spx-panel: #ffffff;
	--spx-panel-border: #b7d9e8;
	--spx-ink: #3c4a52;
	--spx-ink-soft: #7c8a92;
	--spx-rule: #e2e8ec;
	--spx-inset-bg: #f4f7f8;
	--spx-link: #2f8fd6;
	--spx-stock-red: #cc3a30;

	/* --- semantic aliases (the `--sp-*` names the bulk of the file still uses) --- */
	--sp-sky-top: oklch(0.78 0.09 235);
	--sp-sky-mid: oklch(0.86 0.06 220);
	--sp-sky-bottom: oklch(0.9 0.04 210);

	--sp-card-bg: var(--spx-panel);
	--sp-card-border: var(--spx-panel-border);
	--sp-line: var(--spx-rule);

	--sp-text-dark: var(--spx-ink);
	--sp-text-mid: #5c6a72;
	--sp-text-muted: var(--spx-ink-soft);
	--sp-link: var(--spx-link);

	--sp-logo-bg: oklch(0.55 0.19 30);

	--sp-green-top: oklch(0.7 0.13 145);
	--sp-green-bottom: oklch(0.54 0.12 145);
	--sp-green-active-top: oklch(0.62 0.14 145);
	--sp-green-active-bottom: oklch(0.46 0.13 145);

	--sp-gold-top: oklch(0.85 0.13 85);
	--sp-gold-bottom: oklch(0.75 0.14 75);
	--sp-gold-border: oklch(0.6 0.14 75);

	--sp-blue-pill-bg: var(--spx-inset-bg);

	--sp-red-top: oklch(0.68 0.19 25);
	--sp-red-bottom: oklch(0.56 0.19 25);

	/* --- type scale (px). Nothing renders below --fs-2xs. The steps stay tight on purpose —
	   the legacy skin is a compact 2009 UI and the point here is one coherent ramp, not a
	   bigger UI. --fs-md is the body floor on surfaces with no pixel-reference (modals, auth,
	   Goals, Social). --- */
	--fs-2xs: 11px;
	--fs-xs: 12px;
	--fs-sm: 13px;
	--fs-md: 14px;
	--fs-lg: 15px;
	--fs-xl: 18px;
	--fs-2xl: 22px;
	--fs-3xl: 28px;

	/* --- motion --- */
	--t-fast: 120ms ease;
	--t-med: 200ms ease;

	/* --- elevation --- */
	--e-card: 0 1px 2px rgba(0, 0, 0, 0.08);
	--e-raised: 0 2px 6px rgba(0, 0, 0, 0.12);
	--e-modal: 0 16px 40px rgba(0, 0, 0, 0.28);

	/* --- radius --- */
	--r-sm: 4px;
	--r-md: 7px;
	--r-lg: 12px;
	--r-pill: 999px;
	/* legacy alias kept for existing call sites */
	--spx-panel-radius: var(--r-md);

	/* --- spacing --- */
	--space-1: 4px;
	--space-2: 8px;
	--space-3: 12px;
	--space-4: 16px;
	--space-5: 24px;
	--space-6: 32px;

	/* --- focus ring (was referenced as --sp-focus with a hardcoded #2b6cb0 fallback everywhere) --- */
	--sp-focus: #2b6cb0;

	/* --- stacking order --- */
	--z-nav: 100;
	--z-overlay: 200;
	--z-modal: 300;
	--z-modal-top: 340;
	--z-toast: 400;
}

/* One baseline focus ring for every interactive element. Individual components used to each
   define their own (three different treatments, and most controls had none) — see the review. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
	outline: 3px solid var(--sp-focus);
	outline-offset: 2px;
}

/* Quiet hover/press feedback on the small secondary controls that previously snapped. Kept as a
   zero-specificity :where() so any component rule still wins; the review flagged that ~7
   transition declarations covered a 5,000-line sheet. */
:where(
	.sp-left-category-btn, .sp-page-num, .sp-link-btn, .sp-shop-info, .sp-friend-bar-nav,
	.sp-qty-step, .sp-cart-qty-step, .sp-hud-icon-btn, .sp-toolbar-tab, .sp-friend-bar-card,
	.sp-badge-slot, .sp-tile, .sp-tile img, #sp-tour-skip, #sp-tour-button, .sp-fullscreen-toggle
) {
	transition: background-color var(--t-fast), color var(--t-fast), border-color var(--t-fast),
		box-shadow var(--t-fast), transform var(--t-fast), filter var(--t-fast);
}

/* Honour the OS "reduce motion" setting everywhere. Was a hand-kept selector list that had to
   mirror every transition/animation declaration in this 5,000-line sheet and had already fallen
   out of sync (.sp-toolbar-tab, #sp-tour-*, .sp-fullscreen-toggle, the modal-close buttons, the
   Decorate tile hover — all missed); a universal reset can't drift. !important so it also wins
   over the inline-style transitions chrome/*.js sets. */
@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		scroll-behavior: auto !important;
	}
}

/* Every modal's corner "×". They were an unpadded muted glyph with no hover — give them all one
   consistent 30px hit target and a hover state. `button#id` (1,0,1) out-specifies the per-modal
   `#id` rules so the shared size/colour win without editing ten near-identical blocks. */
button#sp-item-modal-close, button#sp-levels-modal-close, button#sp-badges-modal-close,
button#sp-friends-modal-close, button#sp-share-modal-close, button#sp-daily-reward-modal-close,
button#sp-salon-modal-close, button#sp-snapshot-modal-close, button#sp-gift-picker-close,
button#sp-gift-inbox-close {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 30px;
	height: 30px;
	padding: 0;
	border-radius: var(--r-sm);
	color: var(--sp-text-mid);
	transition: background-color var(--t-fast), color var(--t-fast);
}

button#sp-item-modal-close:hover, button#sp-levels-modal-close:hover, button#sp-badges-modal-close:hover,
button#sp-friends-modal-close:hover, button#sp-share-modal-close:hover, button#sp-daily-reward-modal-close:hover,
button#sp-salon-modal-close:hover, button#sp-snapshot-modal-close:hover, button#sp-gift-picker-close:hover,
button#sp-gift-inbox-close:hover {
	background: var(--spx-inset-bg);
	color: var(--sp-text-dark);
}

* {
	box-sizing: border-box;
}

html, body {
	margin: 0;
	padding: 0;
	min-height: 100%;
	font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
	color: var(--sp-text-dark);
	/* Solid base color first, same reasoning as before: this is what shows once Shop/Decorate
	   content pushes the page past one screen — a flat color sidesteps cross-engine ambiguity
	   in how a root-element background gradient sizes against full document height. */
	background-color: oklch(0.9 0.04 210);
}

body {
	/* icons/background-tile.png (the reference screenshot's sky+clouds art) tiles horizontally
	   over the same oklch gradient this used to be pure CSS — the gradient is what shows through
	   while the image is still loading, or fails, or the page scrolls past the image's height. */
	background-image: url("icons/background-tile.png"), linear-gradient(180deg, var(--sp-sky-top) 0%, var(--sp-sky-mid) 55%, var(--sp-sky-bottom) 100%);
	background-repeat: repeat-x, no-repeat;
	background-size: auto 480px, 100% 480px;
}

#sp-page {
	/* Wide enough that #sp-viewer-col (see below) always gets its full 825px, even with the
	   200px sidebar and card border alongside it — a narrower max-width let the sidebar squeeze
	   the viewer column below the OpenFL canvas's fixed 785px width, which silently clipped the
	   in-canvas mood bars/toolbar (positioned by the Haxe side against its own internal 785px
	   stage, with no awareness of how much of that is actually visible through the DOM). */
	max-width: 1100px;
	margin: 0 auto;
	padding: 0px 20px 32px;
}

/* ---- top bar: logo + nav ---- */

#sp-topbar {
	display: flex;
	/* Column, not row — the nav row (logo/nav/account) and the stats bar (#sp-hud) are two
	   separate lines stacked on the header art, each carrying its own horizontal inset (see
	   .sp-topbar-row/#sp-hud) so both align with #sp-card's edges below despite this element
	   having no horizontal padding of its own (the background art needs to run full-bleed). */
	flex-direction: column;
	/* No gap — #sp-hud butts directly against the header art's bottom edge instead of floating
	   below it. */
	gap: 0;
	padding: 12px 0 0;
	/* icons/petheader-spp.png (the reference screenshot's own rolling-hills strip, replacing
	   the original nav-bg.png — see docs/backlog/planned-features/social-friending-playdates/
	   05-*.md) over a solid fallback in the same green — the hills never cover the full box
	   height at every x, so gaps in the art show the matching flat color instead of a hole.
	   background-size keeps the art's own aspect ratio (unlike the pill/bar assets elsewhere in
	   this file) since it's a wide horizon silhouette, not a simple shape that tolerates
	   stretching. */
	background-image: url("icons/petheader-spp.png");
	background-repeat: no-repeat;
	background-size: 100% auto;
	overflow: hidden;
}

.sp-topbar-row {
	display: flex;
	/* flex-end, not center — the logo/nav/account-box should all sit on the same bottom baseline,
	   the way the original's tabs sit low in their bar rather than centered in it (the tab
	   selector itself sits directly above the info bar, tabs positioned near its bottom edge —
	   see TabSelector.as's TAB_TOP_MARGIN). */
	align-items: flex-end;
	gap: 14px;
	flex-wrap: wrap;
	padding: 0 20px;
}

#sp-logo {
	display: flex;
	align-items: center;
	flex-shrink: 0;
}

#sp-logo-img {
	height: 40px;
	width: auto;
	display: block;
	filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.15));
}

#sp-nav {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
}

/* Real header-art pills for the 3 top-level nav buttons only (a scoped override, not a change
   to the shared `.sp-nav-btn` rule below — that rule also drives `.sp-tab`/`.sp-btn`/
   `.sp-icon-nav-btn` well outside the header, e.g. Shop/Decorate tabs and the below-canvas
   toolbar, which are out of scope here and keep their existing pastel gel look). */
#sp-nav .sp-nav-btn {
	background: none;
	background-image: url("icons/nav-tab.png");
	background-size: 100% 100%;
	box-shadow: none;
	color: #fff;
	text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}

#sp-nav .sp-nav-btn.sp-nav-active {
	background-image: url("icons/nav-tab-active.png");
	color: var(--sp-text-dark);
	text-shadow: none;
}

/* Notification dot on the Friends nav button (chrome/socialNotifications.js sets
   data-social-badge = unseen visits + incoming friend requests) — same small-circle-with-a-
   number idiom as #sp-cart-count, just as a pseudo-element badge since this button has no
   existing child span to reuse. */
.sp-nav-btn[data-social-badge] {
	position: relative;
}

.sp-nav-btn[data-social-badge]:not([data-social-badge=""])::after {
	content: attr(data-social-badge);
}

/* Daily Goals nav dot — its own attr so it never collides with data-social-badge on a button. */
.sp-nav-btn[data-goals-badge] {
	position: relative;
}

.sp-nav-btn[data-goals-badge]:not([data-goals-badge=""])::after {
	content: attr(data-goals-badge);
}

.sp-nav-btn[data-social-badge]:not([data-social-badge=""])::after,
.sp-nav-btn[data-goals-badge]:not([data-goals-badge=""])::after {
	position: absolute;
	top: -6px;
	right: -6px;
	min-width: 16px;
	height: 16px;
	padding: 0 4px;
	border-radius: 999px;
	background: linear-gradient(180deg, var(--sp-red-top) 0%, var(--sp-red-bottom) 100%);
	color: #fff;
	font-size: var(--fs-2xs);
	font-weight: 800;
	line-height: 16px;
	text-align: center;
}

#sp-account-box {
	margin-left: auto;
	display: inline-flex;
	align-items: center;
	gap: 8px;
	/* Deliberately slimmer than the nav pills — this and its own Log Out button (below) are
	   the tallest thing in #sp-topbar's row, so an oversized account box was forcing the whole
	   bar taller and pushing the nav tabs up away from the seam with #sp-hud below. */
	padding: 4px 4px 4px 14px;
	background-image: url("icons/account-box-bg.png");
	background-size: 100% 100%;
	border-radius: 999px;
	font-size: var(--fs-xs);
	color: var(--sp-text-mid);
	white-space: nowrap;
}

#sp-topbar-username-value {
	color: var(--sp-text-dark);
}

#sp-account-box #sp-logout {
	padding: 5px 12px;
	font-size: var(--fs-2xs);
	box-shadow: 0 2px 0 oklch(0.46 0.19 25);
}

#sp-account-box #sp-logout:active {
	box-shadow: 0 1px 0 oklch(0.46 0.19 25);
}

/* ---- shared pill-button look (nav / tabs / actions) ---- */

.sp-nav-btn, .sp-tab, .sp-btn, .sp-icon-nav-btn {
	appearance: none;
	border: none;
	border-radius: 999px;
	padding: 9px 18px;
	/* Body face for buttons — Porky's is now display-only (top tabs + card/modal/section
	   titles). Set explicitly, not omitted: a bare <button> falls back to the UA control font. */
	font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
	background: linear-gradient(180deg, var(--sp-green-top) 0%, var(--sp-green-bottom) 100%);
	box-shadow: 0 4px 0 var(--sp-green-active-bottom);
	color: #fff;
	font-weight: 700;
	font-size: var(--fs-md);
	cursor: pointer;
	transition: transform var(--t-fast), box-shadow var(--t-fast), filter var(--t-fast);
}

/* The top nav tabs keep the Porky's display face. Scoped to #sp-nav on purpose: the below-canvas
   toolbar icon buttons also carry .sp-nav-btn (see the note above at #sp-nav .sp-nav-btn), and
   those are controls, not tabs — they stay on the body face from the shared rule. Social's
   sub-tabs are .sp-tab and likewise read as plain controls now. */
#sp-nav .sp-nav-btn {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
}

.sp-nav-btn:hover, .sp-tab:hover, .sp-btn:hover, .sp-icon-nav-btn:hover {
	filter: brightness(1.05);
}

.sp-nav-btn:active, .sp-tab:active, .sp-btn:active {
	transform: translateY(2px);
	box-shadow: 0 2px 0 var(--sp-green-active-bottom);
}

.sp-nav-btn.sp-nav-active, .sp-tab.sp-tab-active {
	background: linear-gradient(180deg, var(--sp-green-active-top) 0%, var(--sp-green-active-bottom) 100%);
	box-shadow: 0 4px 0 oklch(0.38 0.12 145);
}

.sp-btn-small {
	padding: 7px 14px;
	font-size: var(--fs-xs);
	box-shadow: 0 3px 0 var(--sp-green-active-bottom);
}

.sp-btn:disabled {
	opacity: 0.5;
	cursor: default;
	box-shadow: none;
}

/* Transient "Added ✓" confirmation on a shop tile's Add-to-Cart button (chrome/shop.js
   addToCart) — held ~900ms before the grid rebuilds. Overrides the disabled dimming so the
   acknowledgement reads clearly. */
.sp-btn.sp-added {
	opacity: 1;
	background: linear-gradient(180deg, var(--sp-green-active-top) 0%, var(--sp-green-active-bottom) 100%);
	animation: sp-added-pop 0.2s ease;
}

@keyframes sp-added-pop {
	from { transform: scale(0.92); }
	to { transform: scale(1); }
}

/* ---- card ---- */

#sp-card {
	position: relative;
	background: var(--sp-card-bg);
	/* Rounded on all four corners now that #sp-hud has moved up into #sp-topbar — this card no
	   longer sits flush against the header, just a floating panel a small gap below it. */
	border-radius: var(--r-lg);
	margin-top: 7px;
	box-shadow: var(--e-raised);
	overflow: hidden;
	border: 1px solid var(--sp-card-border);
}

/* ---- stats bar (currency) ---- */

#sp-hud {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	/* No horizontal margin: #sp-topbar has zero side padding of its own (so the header art can
	   run full-bleed), which means this bar's own box already spans exactly #sp-card's width —
	   same left/right edges as the card below, and as .sp-topbar-row's inset content above. */
	margin: 0;
	padding: 8px 20px;
	/* icons/hud-bg.png — the original InfoHeader's own panel background (bordered on all sides).
	   petheader-spp.png (the header art) was cropped down to just the hills silhouette, no longer
	   including a white bar of its own for this row to sit on, so this bar needs its own
	   background again rather than overlaying translucently on the green. */
	background-image: url("icons/hud-bg.png");
	background-size: 100% 100%;
	background-repeat: no-repeat;
}

#sp-currency {
	display: flex;
	gap: 10px;
}

/* Daily-spin + gift buttons, moved out of #sp-account-box into the HUD. Pinned right (the
   currency cluster is the first child, so space-between already parts them; margin-left:auto
   keeps it right even if the middle child is hidden). */
#sp-hud-actions {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin-left: auto;
}

.sp-currency-pill {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 7px 16px;
	border-radius: 999px;
	/* icons/currency-pill-bg.png (91x20, see docs/backlog/ship-blockers/02-header-hud-visual-
	   upgrade) stretched to the pill's actual box — a plain rounded rect, so stretching its
	   corners along with it doesn't visibly distort it. Coin vs. gold is told apart by icon
	   alone now (swc.NumericBackground was one shared asset for both in the source SWF). */
	background-image: url("icons/currency-pill-bg.png");
	background-size: 100% 100%;
	color: var(--sp-text-mid);
	font-weight: 700;
	font-size: var(--fs-sm);
}

.sp-currency-icon {
	width: 18px;
	height: 18px;
	object-fit: contain;
}

/* Sound backlog step 6 — the HUD mute toggle + volume slider. Reuses .sp-currency-pill for the
   pill background so it reads as one more chip in the currency cluster. The button is a bare
   icon (swc.SoundOnIcon / swc.SoundOffIcon 1.png), the slider a native range input kept short
   so the whole control stays roughly pill-width. */
#sp-sound {
	gap: 8px;
}

#sp-sound-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	border: 0;
	background: none;
	cursor: pointer;
	line-height: 0;
}

#sp-sound-toggle:disabled {
	cursor: default;
	opacity: 0.5;
}

#sp-sound-volume {
	width: 68px;
	accent-color: var(--sp-text-mid);
	cursor: pointer;
}

#sp-sound-volume:disabled {
	cursor: default;
	opacity: 0.5;
}

/* ---- main layout: sidebar + viewer column ---- */

#sp-main {
	display: flex;
	/* Flexbox stretch always sizes *both* items to the taller one's content — wrong here when
	   the sidebar (variable content: badge count, etc.) is the taller of the two, since that
	   would grow the viewer column with dead space under the toolbar instead of capping the
	   sidebar. The viewer column (canvas + toolbar) is meant to be the authority on this row's
	   height, so the sidebar's height is instead set explicitly to match it — see
	   syncSidebarHeight() in chrome.js — with overflow handling on #sp-sidebar for the case
	   where its own content is taller. */
	align-items: flex-start;
}

/* Same `[hidden]`-vs-`display:flex` specificity footgun as #sp-auth-page/#sp-viewer-toolbar —
   switchNav() hides this whole row for standalone Shop, needs the explicit override to work. */
#sp-main[hidden] {
	display: none;
}

#sp-sidebar {
	width: 200px;
	flex-shrink: 0;
	padding: 14px;
	border-right: 1px solid var(--sp-line);
	/* Height is set inline by syncSidebarHeight() to match #sp-viewer-col exactly — if the
	   sidebar's own content (badges, etc.) is taller than that, it scrolls within the capped
	   box rather than pushing the row taller. The sizing below (avatar/section spacing/badge
	   slots) is kept compact specifically so that doesn't happen for a typical amount of
	   content. */
	overflow-y: auto;
}

#sp-pet-avatar {
	width: 44px;
	height: 44px;
	border-radius: 10px;
	background: oklch(0.85 0.1 60);
	margin-bottom: 8px;
}

#sp-pet-name {
	/* Body face, not Porky's — the rounded display face is hard to read for a real pet name
	   (arbitrary length, mixed case) at this size. */
	font-weight: 800;
	font-size: var(--fs-lg);
	color: var(--sp-text-dark);
}

#sp-viewer-col {
	flex: 1 1 auto;
	/* Never shrink below the OpenFL canvas's own fixed 785px width + this column's own padding —
	   letting flex shrink it narrower than that is what caused the canvas-content clipping (see
	   the #sp-page comment above). If the viewport is too narrow for sidebar + this to fit
	   side-by-side, the page scrolls horizontally instead of silently cropping game content. */
	min-width: 825px;
	padding: 20px;
	display: flex;
	flex-direction: column;
	gap: 14px;
}

#sp-home-card {
	width: 785px;
	height: 300px;
	border-radius: 14px;
	overflow: hidden;
	border: 1px solid var(--sp-line);
	background: #000;
	position: relative;
}

#openfl-content {
	width: 785px;
	height: 300px;
}

/* ---- fullscreen canvas view — toggled by chrome/fullscreen.js. The OpenFL stage itself never
   resizes (still a fixed 785x300 internal render, see client/CLAUDE.md); this just CSS-scales
   #openfl-content up to fit the viewport and hides everything else, letterboxed by #sp-home-card's
   own black background on whichever axis doesn't match the 785:300 ratio. */

.sp-fullscreen-toggle {
	position: absolute;
	top: 10px;
	right: 10px;
	z-index: 10;
	width: 32px;
	height: 32px;
	border-radius: 8px;
	border: none;
	background: oklch(0 0 0 / 0.45);
	color: #fff;
	font-size: var(--fs-lg);
	line-height: 1;
	cursor: pointer;
}

.sp-fullscreen-toggle:hover {
	background: oklch(0 0 0 / 0.65);
}

.sp-fullscreen-toggle::before {
	content: "⛶";
}

body.sp-fullscreen .sp-fullscreen-toggle::before {
	content: "✕";
}

body.sp-fullscreen {
	overflow: hidden;
}

body.sp-fullscreen #sp-topbar,
body.sp-fullscreen #sp-hud,
body.sp-fullscreen #sp-sidebar,
body.sp-fullscreen #sp-viewer-toolbar,
body.sp-fullscreen #sp-friend-bar {
	display: none;
}

body.sp-fullscreen #sp-home-card {
	position: fixed;
	inset: 0;
	width: 100vw;
	height: 100vh;
	z-index: 1000;
	border: none;
	border-radius: 0;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* ---- below-viewport icon toolbar — contextual, see updateViewerToolbar() in chrome.js.
   #sp-viewer-toolbar itself just wraps whichever single .sp-toolbar-row is currently visible;
   the row that's actually flexed/centered is the row, not this outer wrapper. */

#sp-viewer-toolbar[hidden] {
	display: none;
}

.sp-toolbar-row {
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 22px;
}

/* Same footgun already hit and fixed for #sp-auth-page/#sp-viewer-toolbar earlier this session
   — a class selector still beats the UA stylesheet's `[hidden]` rule by cascade origin even at
   equal specificity, so `hidden` needs this explicit override to actually hide the row. */
.sp-toolbar-row[hidden] {
	display: none;
}

/* Group boundary in #sp-toolbar-home between the panel-switch buttons (Home / Decorate / Shop)
   and the action buttons (Salon / Snapshot) — see index.html. Thin, low-contrast; it reads as a
   separator, not a control. The flex row's own 22px gap sits on each side of it. */
.sp-toolbar-divider {
	flex: 0 0 auto;
	align-self: center;
	width: 1px;
	height: 34px;
	background: var(--sp-line);
}

#sp-status-message {
	padding: 9px 16px;
	border-radius: 999px;
	border: 1px solid var(--sp-line);
	background: var(--sp-card-bg);
	color: var(--sp-text-dark);
	font: inherit;
	font-size: var(--fs-sm);
	width: 220px;
}

#sp-toolbar-compliments {
	display: flex;
	gap: 18px;
}

/* `.sp-icon-nav-btn` on its own (specificity 0,1,0) loses to the shared `.sp-nav-btn.sp-nav-active`
   rule above (0,2,0) when a button is both — repeated as `.sp-icon-nav-btn.sp-nav-active` here to
   match that specificity so the icon variant's transparent background always wins; without this
   the active icon button painted a green pill behind an already-green label, hiding the text. */
.sp-icon-nav-btn, .sp-icon-nav-btn.sp-nav-active {
	background: none;
	box-shadow: none;
	padding: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	color: var(--sp-text-mid);
}

.sp-icon-nav-btn:active {
	transform: none;
}

.sp-icon-circle {
	width: 46px;
	height: 46px;
	border-radius: 50%;
	background: var(--sp-blue-pill-bg);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: var(--fs-xl);
	box-shadow: 0 3px 0 oklch(0.85 0.02 220);
}

.sp-icon-label {
	/* Body face — these utility labels under the toolbar icons inherit it from their
	   .sp-icon-nav-btn parent, which the shared button rule keeps on the body stack (Porky's is
	   scoped to #sp-nav .sp-nav-btn, and these buttons live in #sp-viewer-toolbar). */
	font-size: var(--fs-2xs);
	font-weight: 700;
}

.sp-icon-nav-btn.sp-nav-active .sp-icon-circle {
	background: linear-gradient(180deg, var(--sp-green-active-top) 0%, var(--sp-green-active-bottom) 100%);
	box-shadow: 0 3px 0 oklch(0.38 0.12 145);
}

.sp-icon-nav-btn.sp-nav-active .sp-icon-label {
	color: var(--sp-green-active-bottom);
}

/* ---- Shop / Decorate panels ---- */

.sp-panel {
	width: 100%;
	background: transparent;
	padding: 0;
}

.sp-panel-head {
	display: flex;
	flex-direction: column;
	gap: 10px;
	margin-bottom: 12px;
}

.sp-panel-head h2 {
	margin: 0;
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-size: var(--fs-xl);
	font-weight: 800;
	color: var(--sp-text-dark);
}

.sp-category-row {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

.sp-category-row .sp-tab {
	background: var(--sp-blue-pill-bg);
	box-shadow: none;
	color: var(--sp-text-mid);
	font-weight: 700;
}

.sp-category-row .sp-tab.sp-tab-active {
	background: linear-gradient(180deg, var(--sp-green-active-top) 0%, var(--sp-green-active-bottom) 100%);
	box-shadow: 0 3px 0 oklch(0.38 0.12 145);
	color: #fff;
}

/* ---- below-profile panels (Shop / Decorate / Social) ----
   All three now sit below #sp-main as full-width siblings (see index.html) rather than
   squeezed into the viewer column beside the sidebar — this border/margin is the "clean break"
   between the profile row above and whichever of these is showing. Shop hides #sp-main entirely
   (see switchNav in chrome.js) so nothing sits above it and this reads as a standalone page. */

.sp-panel {
	margin-top: 20px;
	padding-top: 20px;
	border-top: 1px solid var(--sp-line);
}

#sp-shop .sp-panel-head {
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
}

#sp-shop-body, #sp-decorate-body {
	display: flex;
	gap: 24px;
	align-items: flex-start;
}

/* Left category list — Shop's own layout, reused as-is for Decorate's filter nav. */

.sp-left-categories {
	width: 180px;
	flex-shrink: 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.sp-left-category-btn {
	appearance: none;
	border: none;
	background: none;
	box-shadow: none;
	border-radius: 10px;
	padding: 8px 10px;
	text-align: left;
	/* Body face — a left-rail category button is a control, not a title. Explicit (<button>). */
	font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
	font-weight: 700;
	font-size: var(--fs-sm);
	color: var(--sp-text-mid);
	cursor: pointer;
}

.sp-left-category-btn:hover {
	background: var(--sp-blue-pill-bg);
}

.sp-left-category-btn.sp-left-category-active {
	background: var(--sp-blue-pill-bg);
	color: var(--sp-green-active-bottom);
}

#sp-shop-main, #sp-decorate-main {
	flex: 1 1 auto;
	min-width: 0;
}

/* ---- item grid ---- */

.sp-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
	gap: var(--space-3);
}

.sp-tile {
	background: var(--sp-card-bg);
	border: 1px solid var(--sp-line);
	border-radius: var(--r-md);
	padding: 10px;
	text-align: center;
	font-size: var(--fs-2xs);
	box-shadow: var(--e-card);
}

.sp-tile.sp-tile-applied {
	border: 2px solid var(--sp-green-bottom);
	box-shadow: 0 0 0 2px oklch(0.7 0.13 145 / 0.3);
}

.sp-tile img {
	width: 100%;
	height: 128px;
	object-fit: contain;
	background: var(--sp-blue-pill-bg);
	border-radius: 12px;
	cursor: pointer;
}

.sp-tile img:hover {
	filter: brightness(1.03);
}

/* An item whose thumbnail 404'd and fell back to core.js's inline placeholder. Held at a
   smaller size and centred so a wall of them reads as "no picture yet" rather than as art —
   865 in-store items are in this state, almost all of Clothing (see
   docs/backlog/planned-features/shopping-checkout-revisit/04-missing-item-pictures.md). */
img.sp-item-pic-missing {
	object-fit: contain;
	padding: 12%;
	opacity: 0.75;
}

.sp-tile .sp-tile-name {
	margin: 6px 0 2px;
	line-height: 1.2;
	min-height: 2.4em;
	overflow: hidden;
	font-weight: 700;
	color: var(--sp-text-mid);
}

.sp-tile .sp-tile-meta {
	color: var(--sp-text-muted);
	font-size: var(--fs-2xs);
	margin-bottom: 6px;
}

/* The "Today's Price: <icon> N" / "Stock Left: N" rows (chrome/shop.js's shopItemMetaRows).
   Built once and rendered in two places: the legacy Shop tile, which re-weights and left-aligns
   them under #sp-shop, and the item modal, which keeps the facelift (ADR 011) and gets these
   centred defaults. */
.sp-shop-meta-row {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 4px;
}

.sp-shop-meta-value {
	font-weight: 700;
}

.sp-shop-cur-icon {
	width: 14px;
	height: 14px;
	object-fit: contain;
}

.sp-tile .sp-btn {
	width: 100%;
	padding: 6px 0;
	font-size: var(--fs-2xs);
	border-radius: 999px;
	box-shadow: 0 3px 0 var(--sp-green-active-bottom);
}

/* Not scoped to .sp-tile: also used by the friend-row decline/cancel/unfriend buttons and the
   topbar Log Out button, which aren't tiles but should read the same "destructive-ish" red. */
.sp-btn.sp-btn-remove {
	background: linear-gradient(180deg, var(--sp-red-top) 0%, var(--sp-red-bottom) 100%);
	box-shadow: 0 3px 0 oklch(0.46 0.19 25);
}

/* Quantity stepper — Shop tiles and the item modal, so multiple units of one item can be queued
   into the cart in a single "Add to Cart" click. */
.sp-qty-stepper {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin-bottom: 6px;
}

.sp-qty-input {
	width: 44px;
	padding: 5px 4px;
	text-align: center;
	font-family: "Source Sans 3", "Segoe UI", sans-serif;
	font-weight: 700;
	font-size: var(--fs-xs);
	color: var(--sp-text-dark);
	border: 1px solid var(--sp-line);
	border-radius: 8px;
	background: var(--sp-card-bg);
}

/* Hide the native spinner arrows — the ±  buttons are the intended control. */
.sp-qty-input::-webkit-inner-spin-button, .sp-qty-input::-webkit-outer-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

/* Overrides `.sp-tile .sp-btn { width: 100% }` — needs the extra `.sp-tile` specificity to win,
   plain `.sp-qty-step` alone loses to that more-specific rule. */
.sp-tile .sp-qty-step, .sp-qty-step {
	width: 26px;
	height: 26px;
	padding: 0;
	flex: none;
	font-size: var(--fs-md);
	line-height: 1;
	border-radius: 999px;
	box-shadow: 0 2px 0 var(--sp-green-active-bottom);
}

#sp-item-modal-qty:empty {
	display: none;
}

#sp-item-modal-qty {
	margin-bottom: 12px;
}

.sp-pager {
	margin-top: 12px;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 12px;
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
	font-weight: 700;
}

.sp-empty {
	grid-column: 1 / -1;
	text-align: center;
	color: var(--sp-text-muted);
	padding: 16px;
}

/* ---- item detail modal ---- */

#sp-item-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-item-modal[hidden] {
	display: none;
}

#sp-item-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-item-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 360px;
	max-width: calc(100vw - 40px);
	text-align: center;
}

#sp-item-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-item-modal-img {
	width: 100%;
	height: 260px;
	object-fit: contain;
	background: var(--sp-blue-pill-bg);
	border-radius: 14px;
	margin-bottom: 16px;
}

#sp-item-modal-name {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin-bottom: 6px;
}

#sp-item-modal-meta {
	color: var(--sp-text-muted);
	font-weight: 700;
	font-size: var(--fs-sm);
	margin-bottom: 18px;
}

#sp-item-modal-action {
	width: 100%;
	font-size: var(--fs-lg);
	padding: 12px 0;
}

/* ---- cart ----
   The cart was a modal (#sp-cart-modal) until the shopping/checkout revisit; it is now the Shop's
   own right-rail panel, #sp-shop-cart, so its overlay/backdrop/card rules are gone. The line rules
   below stay shared and unscoped — the panel's legacy skin layers on under #sp-shop. */

.sp-cart-line {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 8px 0;
	border-bottom: 1px solid var(--sp-line);
}

.sp-cart-line-thumb {
	width: 40px;
	height: 40px;
	flex: none;
	object-fit: contain;
}

.sp-cart-line-main {
	flex: 1;
	min-width: 0;
}

.sp-cart-line-name {
	font-weight: 700;
	font-size: var(--fs-xs);
	color: var(--sp-text-dark);
}

.sp-cart-line-price {
	color: var(--sp-text-muted);
	font-size: var(--fs-2xs);
	font-weight: 700;
}

.sp-cart-line-controls {
	display: flex;
	align-items: center;
	gap: 4px;
	flex: none;
}

.sp-cart-qty-step {
	width: 22px;
	height: 22px;
	padding: 0;
	font-size: var(--fs-md);
	line-height: 1;
}

.sp-cart-qty-value {
	min-width: 18px;
	text-align: center;
	font-weight: 700;
}

.sp-cart-line-error {
	color: var(--sp-red-bottom);
	font-size: var(--fs-2xs);
	font-weight: 700;
}

/* Applied to the rows a 409 checkout actually named, so the panel-level sentence isn't the only
   thing pointing at the problem (chrome/cart.js's cartLineErrors). */
.sp-cart-line-failed {
	background: oklch(0.95 0.03 25);
}

.sp-cart-total-row {
	display: flex;
	align-items: center;
	gap: 5px;
	font-weight: 700;
	color: var(--sp-text-mid);
}

#sp-cart-totals {
	margin: 12px 0;
}

#sp-cart-failures {
	color: var(--sp-red-bottom);
	font-size: var(--fs-xs);
	font-weight: 700;
	margin-bottom: 10px;
}

#sp-cart-checkout {
	width: 100%;
	font-size: var(--fs-lg);
	padding: 12px 0;
}

#sp-cart-done {
	margin-top: 10px;
	font-size: var(--fs-xs);
	text-align: center;
}

.sp-cart-done-summary {
	color: var(--sp-text-mid);
	font-weight: 700;
	margin-bottom: 6px;
}

/* ---- dev log ---- */

/* Hidden from players by default. chrome/main.js flips it to `block` (and starts the populate
   loop) only with ?debug=1 in the URL or on localhost. */
#sp-devlog {
	display: none;
	position: fixed;
	left: 8px;
	bottom: 8px;
	max-width: 300px;
	max-height: 90px;
	overflow: hidden;
	margin: 0;
	padding: 6px 8px;
	background: rgba(0, 0, 0, 0.55);
	color: #eee;
	font-size: var(--fs-2xs);
	border-radius: 6px;
	white-space: pre-wrap;
	pointer-events: none;
}

/* ---- narrow layout ---- */

@media (max-width: 640px) {
	#sp-main {
		flex-direction: column;
	}

	#sp-sidebar {
		width: auto;
		border-right: none;
		border-bottom: 1px solid var(--sp-line);
		display: flex;
		align-items: center;
		gap: 12px;
		padding: 14px 20px;
	}

	#sp-pet-avatar {
		margin-bottom: 0;
	}
}

/* ---- Social (Phase 6) — directory, friends, visiting, compliments ---- */

/* ---- Friend Bar — the persistent friend filmstrip on the Home view, ported from the legacy
   friendladder.swf (FriendBar.as/Friend.as). The legacy widget was its own standalone embeddable
   SWF with its own background card (swc.FriendLadderBackground) — not drawn inside the profile
   panel it sat below — so this is a standalone sibling of #sp-card in index.html, not nested in
   #sp-main/#sp-viewer-col; shown/hidden alongside it by switchNav() (see nav.js). */

#sp-friend-bar {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 16px;
	margin-top: 14px;
	/* Off-white/cream with a pale cyan border, sampled directly from swc.FriendLadderBackground's
	   own art (~#f6f5f4 body, ~#cfe9ea edge) rather than the profile card's plain white — subtly
	   distinct from #sp-card while still matching its rounding/shadow language. */
	background: #f6f5f4;
	border: 1px solid var(--spx-panel-border);
	border-radius: var(--r-lg);
	box-shadow: var(--e-raised);
}

/* Same `[hidden]`-vs-`display:flex` footgun already handled for #sp-main / #sp-viewer-toolbar /
   .sp-toolbar-row / #sp-bottom-row: the `display: flex` above (author origin) beats the UA
   stylesheet's `[hidden] { display: none }`, so chrome/nav.js setting `.hidden` on this element
   does nothing without this explicit override. Without it the Friend Bar stayed visible on
   Shop / Snapshots / Decorate despite switchNav() trying to hide it. */
#sp-friend-bar[hidden] {
	display: none;
}

#sp-friend-bar-filter {
	flex-shrink: 0;
	padding: 6px 10px;
	border-radius: 999px;
	border: 1px solid var(--sp-line);
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-dark);
	font: inherit;
	font-size: var(--fs-xs);
}

#sp-friend-bar-viewport {
	flex: 1 1 auto;
	display: flex;
	align-items: center;
	gap: 6px;
	min-width: 0;
}

#sp-friend-bar-track {
	display: flex;
	gap: 8px;
	overflow-x: auto;
	scroll-behavior: smooth;
	scrollbar-width: none;
}

#sp-friend-bar-track::-webkit-scrollbar {
	display: none;
}

.sp-friend-bar-nav {
	flex-shrink: 0;
	width: 26px;
	height: 26px;
	border-radius: 8px;
	border: 1px solid var(--sp-line);
	background: var(--sp-card-bg);
	color: var(--sp-text-mid);
	cursor: pointer;
	font-size: var(--fs-2xs);
	line-height: 1;
}

.sp-friend-bar-nav:disabled {
	opacity: 0.35;
	cursor: default;
}

/* Card size/proportions match the legacy Friend.as constants (WIDTH=80, HEIGHT=105 for the
   avatar+name band) rather than the earlier ad-hoc 88px square. */
.sp-friend-bar-card {
	flex: 0 0 80px;
	width: 80px;
	height: 100px;
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 4px;
	padding: 8px 4px;
	border-radius: 8px;
	cursor: pointer;
	position: relative;
	text-align: center;
	/* Light blue "not yet visited" gradient — sampled from the legacy's own
	   swc.FriendBackgroundLightBlue art (top #d8f1f6 fading to #c4e9f2). */
	background: linear-gradient(180deg, #d8f1f6 0%, #c4e9f2 100%);
	box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
}

.sp-friend-bar-avatar-wrap {
	position: relative;
	width: 52px;
	height: 52px;
}

.sp-friend-bar-card .sp-tile-avatar {
	width: 52px;
	height: 52px;
	background: none;
}

/* The level badge overlapping the avatar's bottom-left corner, same idiom (art + overlaid
   number) as #sp-level-icon-wrap in the header HUD, just smaller — mirrors swc.FriendLevelBadge
   sitting on top of the thumbnail in the original (Friend.as's `update()`). */
.sp-friend-bar-level-badge {
	position: absolute;
	left: -6px;
	bottom: -6px;
	width: 22px;
	height: 22px;
}

.sp-friend-bar-level-badge img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	display: block;
}

.sp-friend-bar-level-badge span {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 800;
	font-size: var(--fs-2xs);
	color: #3a2a05; /* same warm brown as #sp-level-value, sampled from the badge art's own ink */
}

.sp-friend-bar-name {
	font-size: var(--fs-2xs);
	font-weight: 700;
	color: var(--sp-text-dark);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	max-width: 100%;
}

/* Visited (by me) — the legacy's grey "already visited" card state
   (swc.FriendBackgroundGrey: ~#efefea fading to ~#e6e6df). */
.sp-friend-bar-card.sp-visited {
	background: linear-gradient(180deg, #efefea 0%, #e6e6df 100%);
}

.sp-friend-bar-card.sp-friend-bar-me {
	/* Clickable — it takes you home / ends a visit (chrome/friendBar.js meCard). */
	cursor: pointer;
}

/* Blank "Add Friends!" slot — the legacy's gold invite card
   (swc.FriendBackgroundGold: ~#ffd266 top, ~#ffde17 mid, ~#ffb617 bottom). */
.sp-friend-bar-card.sp-friend-bar-blank {
	background: linear-gradient(180deg, #ffd266 0%, #ffde17 45%, #ffb617 100%);
	color: #6b4a00;
	font-size: var(--fs-2xs);
	font-weight: 800;
	justify-content: center;
}

/* Muted "visited" look for the plain Friends-tab list rows (chrome/social.js's friendRow()) —
   kept separate from .sp-friend-bar-card.sp-visited above now that the ladder card uses the
   legacy's own grey gradient instead of this generic tint. */
.sp-friend-row.sp-visited {
	background: var(--sp-line);
	opacity: 0.75;
}

/* Checkmark badge for "visited me back" — shared class between the Friend Bar cards and the
   Friends-tab list rows (chrome/social.js's friendRow()) so the state reads the same in both
   places, per docs/backlog/planned-features/social-friending-playdates/06-*.md. Default position
   is the row's own top-right corner; the more specific rule below re-anchors it to the avatar's
   corner when it's inside a Friend Bar card, mirroring the checkmark overlaid on
   swc.FriendLevelBadge in the original. */
.sp-friend-bar-visited-badge {
	position: absolute;
	top: 2px;
	right: 2px;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	background: var(--sp-green-top);
	color: #fff;
	font-size: var(--fs-2xs);
	line-height: 16px;
	text-align: center;
}

.sp-friend-bar-avatar-wrap .sp-friend-bar-visited-badge {
	top: auto;
	right: -4px;
	bottom: -4px;
	width: 15px;
	height: 15px;
	line-height: 15px;
	font-size: var(--fs-2xs);
	border: 2px solid #fff;
}

.sp-friend-row {
	position: relative;
}

#sp-social-search-row,
#sp-shop-search-row,
#sp-decorate-search-row {
	margin-bottom: 14px;
}

#sp-social-search,
#sp-shop-search,
#sp-decorate-search {
	width: 100%;
	max-width: 320px;
	padding: 8px 12px;
	border-radius: var(--r-sm);
	border: 1px solid var(--spx-panel-border);
	background: #fff;
	color: var(--sp-text-dark);
	font: inherit;
}

#sp-social-search:focus,
#sp-shop-search:focus,
#sp-decorate-search:focus {
	border-color: var(--sp-focus);
}

.sp-tile-avatar {
	width: 100%;
	height: 96px;
	object-fit: contain;
	background: var(--sp-blue-pill-bg);
	border-radius: 12px;
}

.sp-friend-section {
	margin-bottom: 20px;
}

.sp-friend-section h3 {
	margin: 0 0 8px;
	font-size: var(--fs-sm);
	color: var(--sp-text-muted);
	text-transform: uppercase;
	letter-spacing: 0.04em;
}

.sp-friend-list {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.sp-friend-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 12px;
	background: var(--sp-card-bg);
	border: 1px solid var(--sp-line);
	border-radius: var(--r-md);
}

.sp-friend-row img.sp-tile-avatar {
	width: 40px;
	height: 40px;
	flex: none;
}

.sp-friend-row .sp-friend-name {
	flex: 1;
	font-weight: 700;
	color: var(--sp-text-mid);
}

.sp-friend-row .sp-btn {
	font-size: var(--fs-2xs);
	padding: 6px 12px;
	width: auto;
}

/* ---- Social directory grid (legacy skin) ----
   ADR 017 carried the legacy palette onto Social but not its geometry: the directory grid was
   still rendering on the facelift `.sp-tile` base (fat corners, drop shadow, wide gutter, green
   pill buttons) right next to the hairline legacy cards of Shop and Decorate in the same nav.
   Match those — flat white card, hairline border, tight gutter, flat buttons. Scoped through
   #sp-social-grid so the Friends tab's own rows (which reuse .sp-tile-avatar) are untouched. */

#sp-social .sp-grid
{
	grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
	gap: 10px;
}

#sp-social #sp-social-grid .sp-tile
{
	display: flex;
	flex-direction: column;
	align-items: stretch;
	border: 1px solid var(--spx-panel-border);
	border-radius: 5px;
	box-shadow: none;
	padding: 8px;
}

#sp-social #sp-social-grid .sp-tile .sp-tile-avatar
{
	height: 96px;
	border: 1px solid var(--spx-rule);
	border-radius: 4px;
	background: var(--spx-inset-bg);
}

#sp-social #sp-social-grid .sp-tile .sp-tile-name
{
	margin: 6px 0;
	min-height: 2.2em;
	font-size: var(--fs-2xs);
	font-weight: 800;
	color: var(--spx-ink);
}

/* Buttons: stacked, full width, flat. Visit (no `.sp-btn-small`) is the primary — flat legacy
   green; Add-friend and Gift are the quiet grey chip Shop's tiles use. `.sp-friend-action`'s
   own pending/accepted mute still layers on top. */
#sp-social #sp-social-grid .sp-tile .sp-btn
{
	width: 100%;
	padding: 6px 8px;
	border: 1px solid #b9b9b9;
	border-radius: 4px;
	box-shadow: none;
	background: linear-gradient(180deg, #fdfdfd 0%, #e3e3e3 100%);
	color: #4a4a4a;
	font-size: var(--fs-2xs);
	font-weight: 700;
}

#sp-social #sp-social-grid .sp-tile .sp-btn + .sp-btn
{
	margin-top: 5px;
}

#sp-social #sp-social-grid .sp-tile .sp-btn:not(.sp-btn-small)
{
	background: linear-gradient(180deg, #93d38c 0%, #4aa544 100%);
	border-color: #3f8c3a;
	color: #fff;
}

/* Already-friends / request-sent: not a call to action — drop to a flat inset chip. Mirrors the
   unscoped .sp-friend-action mute, kept here so it still wins over the grey-chip gradient above. */
#sp-social #sp-social-grid .sp-tile .sp-friend-action[data-friend-status="pending_sent"],
#sp-social #sp-social-grid .sp-tile .sp-friend-action[data-friend-status="accepted"]
{
	background: var(--spx-inset-bg);
	border-color: var(--spx-rule);
	color: var(--sp-text-mid);
}

/* ---- level badge / XP bar ---- */

#sp-level-badge {
	display: flex;
	align-items: center;
	gap: 8px;
}

/* A <button> since it opens the level ladder, but it must still read as the badge art alone —
   no UA button box, no focus-time layout shift. */
#sp-level-icon-wrap {
	position: relative;
	width: 32px;
	height: 32px;
	flex-shrink: 0;
	padding: 0;
	border: none;
	background: none;
	box-shadow: none;
	cursor: pointer;
	display: block;
}

#sp-level-icon-wrap:focus-visible {
	outline: 2px solid var(--sp-gold-border);
	outline-offset: 2px;
	border-radius: 50%;
}

/* Title above the XP bar, so the bar keeps its exact 120px width and position. */
#sp-level-bar-col {
	display: flex;
	flex-direction: column;
	gap: 1px;
}

#sp-level-title {
	font-size: var(--fs-2xs);
	font-weight: 800;
	letter-spacing: 0.02em;
	line-height: 1;
	color: var(--sp-text-dark);
	text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
	min-height: 10px;
	/* Follows the column, which is sized by the XP bar inside it — the legacy-skin block below
	   widens that bar to 150px, and the title has to track it rather than a fixed 120. */
	max-width: 100%;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sp-level-icon {
	width: 100%;
	height: 100%;
	object-fit: contain;
	display: block;
}

#sp-level-value {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	font-weight: 800;
	font-size: var(--fs-sm);
	/* Warm dark brown, sampled from the original badge's own digit ink (see level-badge.png's
	   extraction notes) rather than the app's usual --sp-text-dark, so the number reads as part
	   of the gold badge art instead of generic UI text. */
	color: #3a2a05;
}

#sp-xp-bar {
	width: 120px;
	height: 14px;
	padding: 2px;
	box-sizing: border-box;
	border-radius: 999px;
	/* icons/xp-bar-empty.png, stretched to the bar's own box like the currency pill above. */
	background-image: url("icons/xp-bar-empty.png");
	background-size: 100% 100%;
	background-repeat: no-repeat;
	overflow: hidden;
}

#sp-xp-bar-fill {
	height: 100%;
	width: 0%;
	border-radius: 999px;
	background-image: url("icons/xp-bar-full.png");
	background-repeat: no-repeat;
	background-position: left center;
	/* Fixed to the bar's interior width (120 outer - 2px padding * 2 = 116), not 100%, so
	   growing this element's own (percentage) width reveals more of the same fixed-size art
	   instead of squishing it — mirrors LevelBar.as's _fullBar sitting 2px inset in _emptyBar
	   and growing via a GTween on `width`, never a rescale. */
	background-size: 116px 100%;
	transition: width 0.3s ease;
}

/* ---- level ladder modal (docs/backlog/planned-features/level-titles-and-level-gifts/) ---- */

#sp-levels-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-levels-modal[hidden] { display: none; }

#sp-levels-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-levels-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 460px;
	max-width: calc(100vw - 40px);
	max-height: calc(100vh - 80px);
	display: flex;
	flex-direction: column;
}

#sp-levels-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
}

#sp-levels-modal-title {
	/* Modal title — Porky's, matching #sp-badges-modal-title et al. (this one had no rule and
	   was falling to the default h2 body face). */
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 4px;
}

#sp-levels-modal-sub {
	margin: 4px 0 14px;
	font-size: var(--fs-sm);
	color: var(--sp-text-muted);
}

/* The list scrolls, not the card — 37 rows is far taller than any viewport, and openLevelsModal()
   scrolls the current level into view, which needs a scroll container of its own. */
#sp-levels-modal-list {
	overflow-y: auto;
	min-height: 0;
}

.sp-level-ladder-row {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 8px 10px;
	border-radius: 12px;
}

.sp-level-ladder-row + .sp-level-ladder-row {
	border-top: 1px solid var(--sp-gold-border);
}

/* Locked levels stay visible and greyed rather than hidden — the ladder is a goal list, the same
   posture routes/badges.ts takes by returning every badge earned or not. */
.sp-level-locked { opacity: 0.45; }

.sp-level-current {
	background: var(--sp-gold-soft, rgba(255, 214, 102, 0.25));
	box-shadow: inset 0 0 0 2px var(--sp-gold-border);
}

.sp-level-ladder-icon {
	width: 34px;
	height: 34px;
	object-fit: contain;
	flex-shrink: 0;
}

.sp-level-ladder-text { min-width: 0; }

.sp-level-ladder-name {
	font-weight: 800;
	font-size: var(--fs-md);
	color: var(--sp-text-dark);
}

.sp-level-ladder-meta {
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
}

.sp-level-ladder-gift {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: 3px;
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
}

.sp-level-gift-owned {
	color: var(--sp-text-dark);
	font-weight: 700;
}

.sp-level-ladder-gift-thumb {
	width: 30px;
	height: 30px;
	object-fit: contain;
	flex-shrink: 0;
}

/* The gift plushie's row inside the shared level-up / badge award modal. */
.sp-level-gift-award {
	display: flex;
	align-items: center;
	gap: 12px;
}

.sp-level-gift-award-thumb {
	width: 48px;
	height: 48px;
	object-fit: contain;
	flex-shrink: 0;
}

/* ---- profile sidebar ---- */

#sp-profile-username {
	/* Body face, not Porky's — a real username (arbitrary length, digits, punctuation) needs a
	   legible face, not the rounded display one. */
	font-weight: 800;
	font-size: var(--fs-md);
	color: var(--sp-text-dark);
	margin-top: 2px;
}

.sp-profile-section {
	margin-top: 8px;
	padding-top: 6px;
	border-top: 1px solid var(--sp-line);
}

.sp-profile-section-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	font-size: var(--fs-2xs);
	font-weight: 700;
	color: var(--sp-text-muted);
	text-transform: uppercase;
	letter-spacing: 0.04em;
	margin-bottom: 4px;
}

.sp-link-btn {
	background: none;
	border: none;
	box-shadow: none;
	color: var(--sp-link);
	font-size: var(--fs-2xs);
	font-weight: 700;
	text-transform: none;
	letter-spacing: normal;
	cursor: pointer;
	padding: 0;
}

.sp-profile-count {
	/* Body face, not Porky's — the display face's digits are ambiguous (6/8/9, 1/7); numeric
	   readouts across the chrome stay on the running-copy face. */
	font-weight: 800;
	font-size: var(--fs-lg);
	color: var(--sp-text-dark);
}

.sp-profile-hint {
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
}

.sp-badges-row {
	display: flex;
	gap: 5px;
	flex-wrap: wrap;
}

/* Sized so the fixed 5-slot preview (see refreshBadgesPreview) fits in one row within the
   sidebar's 168px content width instead of wrapping to two — part of keeping the sidebar's
   default content short enough to not need syncSidebarHeight()'s scroll fallback. */
.sp-badge-slot {
	width: 26px;
	height: 26px;
	border-radius: 8px;
	background: var(--sp-blue-pill-bg);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: var(--fs-md);
	border: 2px solid transparent;
}

.sp-badge-slot.sp-badge-earned {
	background: linear-gradient(180deg, var(--sp-gold-top) 0%, var(--sp-gold-bottom) 100%);
	border-color: var(--sp-gold-border);
}

.sp-badge-slot.sp-badge-locked {
	opacity: 0.4;
	filter: grayscale(1);
}

#sp-share-btn {
	margin-top: 8px;
	width: 100%;
}

/* ---- badges modal ---- */

#sp-badges-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-badges-modal[hidden] { display: none; }

#sp-badges-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-badges-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 480px;
	max-width: calc(100vw - 40px);
	max-height: calc(100vh - 80px);
	overflow-y: auto;
}

#sp-badges-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-badges-modal-title {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 16px;
}

#sp-badges-modal-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
	gap: 14px;
}

.sp-badge-card {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 6px;
	padding: 10px 6px;
	border-radius: 14px;
	background: var(--sp-blue-pill-bg);
}

.sp-badge-card.sp-badge-earned {
	background: linear-gradient(180deg, oklch(0.95 0.05 85) 0%, oklch(0.9 0.06 80) 100%);
}

.sp-badge-card .sp-badge-icon {
	width: 44px;
	height: 44px;
	border-radius: 12px;
	background: linear-gradient(180deg, var(--sp-gold-top) 0%, var(--sp-gold-bottom) 100%);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: var(--fs-2xl);
}

.sp-badge-card.sp-badge-locked .sp-badge-icon {
	background: var(--sp-line);
	filter: grayscale(1);
	opacity: 0.6;
}

.sp-badge-card .sp-badge-name {
	font-weight: 700;
	font-size: var(--fs-2xs);
	color: var(--sp-text-dark);
}

.sp-badge-card.sp-badge-locked .sp-badge-name {
	color: var(--sp-text-muted);
}

.sp-badge-card .sp-badge-reward {
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
}

/* ---- visited player's friends modal (chrome/social.js openFriendsModalFor) ----
   Same overlay shape as #sp-badges-modal above; the list rows are plain .sp-friend-row. */

#sp-friends-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-friends-modal[hidden] { display: none; }

#sp-friends-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-friends-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 420px;
	max-width: calc(100vw - 40px);
	max-height: calc(100vh - 80px);
	overflow-y: auto;
}

#sp-friends-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-friends-modal-title {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 16px;
}

/* ---- share/invite modal ---- */

#sp-share-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-share-modal[hidden] { display: none; }

#sp-share-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-share-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 380px;
	max-width: calc(100vw - 40px);
}

#sp-share-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-share-modal-card h2 {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 8px;
}

#sp-share-modal-card p {
	font-size: var(--fs-sm);
	color: var(--sp-text-muted);
	margin: 0 0 16px;
}

#sp-share-link-row {
	display: flex;
	gap: 8px;
}

#sp-share-link {
	flex: 1;
	min-width: 0;
	padding: 10px 12px;
	border-radius: 10px;
	border: 1px solid var(--sp-line);
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-mid);
	font-size: var(--fs-xs);
}

#sp-share-stats {
	margin-top: 16px;
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
	display: flex;
	gap: 16px;
}

#sp-share-stats strong {
	display: block;
	/* Body face, not Porky's — these are numbers (invites / coins / gold earned). */
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
}

/* ---- badge-award popup ---- */

#sp-badge-award-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal-top);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-badge-award-modal[hidden] { display: none; }

#sp-badge-award-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-badge-award-card {
	position: relative;
	background: linear-gradient(180deg, oklch(0.98 0.02 85) 0%, var(--sp-card-bg) 100%);
	border-radius: var(--r-lg);
	border: 2px solid var(--sp-gold-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 340px;
	max-width: calc(100vw - 40px);
	text-align: center;
}

#sp-badge-award-card h2 {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 10px;
}

#sp-badge-award-body {
	margin-bottom: 18px;
}

/* One row per badge — several can be earned by a single action and are now shown together
   (chrome/badges.js flushBadgeAwards). Divider between rows, none after the last. */
#sp-badge-award-body .sp-badge-award-row + .sp-badge-award-row {
	margin-top: 12px;
	padding-top: 12px;
	border-top: 1px solid var(--sp-gold-border);
}

#sp-badge-award-body .sp-badge-award-name {
	font-weight: 800;
	font-size: var(--fs-lg);
	color: var(--sp-text-dark);
	margin-bottom: 6px;
}

#sp-badge-award-body .sp-badge-award-reward {
	font-size: var(--fs-sm);
	color: var(--sp-text-muted);
}

#sp-badge-award-close {
	width: 100%;
}

/* ---- daily login reward wheel (docs/backlog/planned-features/daily-login-rewards/, task 05) ----
   Same overlay pattern as the item/cart/badge/salon modals above (fixed full-viewport flex box,
   backdrop + centred card). The wheel is three stacked <img>s: #sp-wheel-face rotates,
   #sp-wheel-frame and #sp-wheel-pointer are fixed overlays. #sp-wheel-face's wedge 0 is baked
   centred under the top pointer, so chrome/dailyReward.js lands wedge k with
   rotate(SPINS*360 - k*45deg). */
#sp-daily-reward-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal-top);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-daily-reward-modal[hidden] { display: none; }

#sp-daily-reward-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-daily-reward-modal-card {
	position: relative;
	background: linear-gradient(180deg, oklch(0.98 0.02 85) 0%, var(--sp-card-bg) 100%);
	border-radius: var(--r-lg);
	border: 2px solid var(--sp-gold-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 400px;
	max-width: calc(100vw - 40px);
	text-align: center;
}

#sp-daily-reward-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-daily-reward-title {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 4px;
}

#sp-daily-reward-streak {
	color: var(--sp-text-muted);
	font-weight: 700;
	font-size: var(--fs-sm);
	margin-bottom: 14px;
	min-height: 16px;
}

#sp-wheel {
	position: relative;
	width: 340px;
	height: 340px;
	max-width: calc(100vw - 96px);
	margin: 0 auto 18px;
}

#sp-wheel-face {
	width: 100%;
	height: 100%;
	transform-origin: 50% 50%;
	transition: transform 4s cubic-bezier(0.15, 0.9, 0.25, 1);
}

#sp-wheel-frame {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	pointer-events: none;
}

#sp-wheel-pointer {
	position: absolute;
	top: -8px;
	left: 50%;
	width: 34px;
	margin-left: -17px;
	pointer-events: none;
	filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.3));
}

#sp-daily-reward-spin {
	min-width: 170px;
	font-size: var(--fs-lg);
	padding: 12px 28px;
}

/* Guard set on the card while a spin is in flight — the button is also `disabled` in JS, this
   just makes the inert state visible. */
#sp-daily-reward-modal-card.is-spinning #sp-daily-reward-spin {
	opacity: 0.5;
	pointer-events: none;
}

#sp-daily-reward-result {
	margin-top: 14px;
	min-height: 22px;
	/* Body face — this is the result sentence ("You won 50 coins!"), not the modal title. */
	font-weight: 800;
	font-size: var(--fs-lg);
	color: var(--sp-text-dark);
}

#sp-daily-reward-result.is-win {
	animation: sp-daily-reward-pulse 0.6s ease-out;
}

@keyframes sp-daily-reward-pulse {
	0% { transform: scale(0.6); opacity: 0; }
	60% { transform: scale(1.14); }
	100% { transform: scale(1); opacity: 1; }
}

/* The re-open pip in #sp-hud-actions — only in the DOM flow when a claim is available, and it
   bobs to draw the eye, the closest existing idiom to the legacy tray icon. */
#sp-daily-reward-open {
	font-size: var(--fs-lg);
	line-height: 1;
	padding: 4px 9px;
}

#sp-daily-reward-open:not([hidden]) {
	animation: sp-daily-reward-bob 1.4s ease-in-out infinite;
}

@keyframes sp-daily-reward-bob {
	0%, 100% { transform: translateY(0); }
	50% { transform: translateY(-3px); }
}

@media (prefers-reduced-motion: reduce) {
	#sp-wheel-face { transition-duration: 0.2s; }
	#sp-daily-reward-open:not([hidden]) { animation: none; }
	#sp-daily-reward-result.is-win { animation: none; }
}

/* ---- reward toasts ---- */

#sp-toast-stack {
	position: fixed;
	top: 16px;
	right: 16px;
	z-index: var(--z-toast);
	display: flex;
	flex-direction: column;
	gap: 8px;
	pointer-events: none;
}

.sp-toast {
	/* icons/toast-bg.png (198x88, a bordered rounded panel with its own drop shadow baked in) —
	   stretched to the toast's actual box like the currency pill/XP bar above, so no separate
	   CSS border/shadow is layered on top of the art's own. */
	background-image: url("icons/toast-bg.png");
	background-size: 100% 100%;
	border-radius: 12px;
	padding: 14px 18px;
	font-weight: 700;
	font-size: var(--fs-sm);
	color: var(--sp-text-dark);
	animation: sp-toast-in 0.2s ease, sp-toast-out 0.3s ease 2.2s forwards;
}

.sp-toast.sp-toast-level-up {
	/* No dedicated level-up panel art shipped with this asset set (com/spp/header/Notification.as
	   doesn't draw one either) — a translucent gold wash over the same toast-bg.png stands in for
	   the legacy "brighter/gold-tinted" level-up treatment. */
	background-image: linear-gradient(180deg, oklch(0.85 0.13 85 / 65%) 0%, oklch(0.75 0.14 75 / 65%) 100%),
		url("icons/toast-bg.png");
	background-size: 100% 100%, 100% 100%;
	color: oklch(0.3 0.05 75);
}

@keyframes sp-toast-in {
	from { opacity: 0; transform: translateY(-8px); }
	to { opacity: 1; transform: translateY(0); }
}

@keyframes sp-toast-out {
	to { opacity: 0; transform: translateY(-8px); }
}

/* ---- auth page (logged-out landing: welcome / log in / adopt-a-pet) ----
   A real page under "/", not a modal over the game — #sp-page stays hidden until a session
   exists (see chrome.js), so this never needs an overlay treatment. */

#sp-auth-page {
	min-height: 100vh;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-wrap: wrap;
	gap: 20px;
	padding: 32px 20px;
}

/* Full-viewport, not a box in the page's flex flow — the wizard canvas is the only thing on
   screen while it's up, so it fills the actual browser window edge to edge rather than sitting
   as a small fixed-size card. `position: fixed` takes it out of #sp-auth-page's flex layout
   entirely (its padding/gap stop applying to this element), so #sp-onboarding-viewport[hidden]
   below still fully removes it rather than leaving a stray fixed box. fitOnboardingCanvas() scales
   and centers #sp-onboarding-stage within this box (explicit left/top math, since the stage's own
   `position: absolute` takes it out of flow too — flex centering here wouldn't reach it), scaling
   up past native 760x565 on a large screen (task 09's original cap on that was "never upscale" —
   for a small centered card that
   made sense; full-bleed wants exactly the opposite). Background left transparent (not a solid
   card color) so any letterbox bar — the non-matching axis when the viewport's aspect ratio
   doesn't match 760:565 — shows the page's own sky-gradient body background instead of a flat
   panel edge. */
#sp-onboarding-viewport {
	position: fixed;
	inset: 0;
	z-index: 40;
	overflow: hidden;
}

#sp-onboarding-viewport[hidden] {
	display: none;
}

/* The overlay can be positioned in the same 760x565 coordinate space the canvas uses
   (AvatarModel.as's stageWidth/stageHeight), rather than relative to #sp-auth-page's own flex
   layout. Always its native 760x565 — never resized — and pulled out of #sp-onboarding-viewport's
   flow with `position: absolute` so that box's own (scaled-down) size doesn't fight this one;
   `transform: scale()` from fitOnboardingCanvas() does the actual shrinking, anchored at
   `top left` to match this element's own `left: 0; top: 0`, same technique
   chrome/fullscreen.js:10-19 uses for the game canvas's fullscreen toggle. Being the transformed
   element (not #sp-onboarding-viewport) means task 07's HTML overlay — a normal descendant —
   scales and tracks position with it for free. */
#sp-onboarding-stage {
	position: absolute;
	left: 0;
	top: 0;
	width: 760px;
	height: 565px;
	transform-origin: top left;
}

#sp-onboarding-canvas-mount {
	width: 100%;
	height: 100%;
	border-radius: 14px;
	overflow: hidden;
	background: var(--sp-blue-pill-bg);
	position: relative;
}

/* Task 10: shown while /PetOnboarding.js is fetching, per chrome/onboarding.js's
   showCanvasLoading()/hideCanvasLoading() — same shape as chrome/salon.js's
   .sp-salon-canvas-status (this file, above) but its own rule rather than a shared class, since
   the two mounts sit in unrelated markup trees. There is no equivalent "failed, tap to retry"
   state here — a canvas load failure routes straight to task 09's accessibility fallback form
   instead (see chrome/onboarding.js's fallThroughToAccessibleForm()), so this status element is
   always either "loading" or absent. */
.sp-onboarding-canvas-status {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--sp-text-mid);
	font-weight: 700;
	text-align: center;
	padding: 20px;
}

/* The transparent-input layer over adoption.swf's own form art (index.html's
   #sp-onboarding-swf-overlay; ADR 014). Full 760x565 stage box, because
   SomePetsOnboardingBridge.getFieldRects() reports in that space — every child's left/top/width/
   height/font-size is written by chrome/onboarding.js's positionSwfFields() from those rects, so
   there are deliberately no dimensions here. `pointer-events: none` on the layer with `auto` on
   the controls: the meadow behind it (the scene's own Back button) has to stay clickable. */
#sp-onboarding-swf-overlay {
	position: absolute;
	left: 0;
	top: 0;
	width: 760px;
	height: 565px;
	pointer-events: none;
}

#sp-onboarding-swf-overlay[hidden],
#sp-onboarding-swf-overlay .sp-swf-form[hidden] {
	display: none;
}

/* Meant to be invisible — the SWF already drew the box, the label and the button under it. The
   focus ring is the deliberate exception: it is an accessibility requirement rather than a
   styling preference, and the legacy field had a focus state of its own anyway. */
.sp-swf-field,
.sp-swf-button {
	position: absolute;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	pointer-events: auto;
	font-family: inherit;
	box-sizing: border-box;
}

.sp-swf-field {
	color: #000;
	line-height: 1;
	/* The SWF's field boxes have a couple of px of inset before the caret; matched here so the
	   typed text sits on the same baseline the art expects. font-size and text-align come from
	   the bundle itself (the field's own TextFormat), applied by positionSwfFields(). */
	padding: 0 3px;
}

.sp-swf-field:focus-visible,
.sp-swf-button:focus-visible {
	outline: 3px solid var(--sp-focus);
	outline-offset: 2px;
	border-radius: 3px;
}

/* Transparent hit target over the SWF's own button art. `color: transparent` rather than
   `visibility: hidden`/`text-indent` so the label stays in the accessibility tree. */
.sp-swf-button {
	color: transparent;
	cursor: pointer;
	font-size: 0;
}

.sp-swf-button[disabled] {
	cursor: default;
}

/* The classic-renderer checkbox, the one control form_b2 has no place for. It sits under the
   board on the meadow, so it needs its own legible ground — positioned by
   positionSwfAside() off the art's own rects, never a fixed coordinate. */
.sp-swf-aside {
	position: absolute;
	display: flex;
	align-items: center;
	gap: 8px;
	margin: 0;
	padding: 6px 10px;
	border-radius: 10px;
	background: rgba(255, 255, 255, 0.88);
	color: var(--sp-text-dark);
	font-size: var(--fs-2xs);
	line-height: 1.25;
	pointer-events: auto;
	cursor: pointer;
	box-sizing: border-box;
}

.sp-swf-aside input {
	margin: 0;
	flex: none;
}

/* The tour's guide layer (index.html's #sp-guide-layer; PetGuide.js). Above every normal surface
   but below modals, so a modal that does manage to open during a tour is not drawn under the
   mascot.

   `pointer-events: none` all the way down, canvas included. A browser canvas is a solid rectangle
   to the page however transparent it is painted, and this one is the whole viewport — leaving it
   hittable would swallow every click on the app underneath, which for a tour whose entire job is
   "click that button" would be fatal. So the guide's one interactive part, the bubble's ×, gets
   the same treatment the adoption forms' buttons get (ADR 014): the canvas paints it, and
   chrome/tour.js floats a transparent real <button> over the rect the canvas reports
   (SomePetsGuideBridge.getCloseRect()). That also gives it a focusable, labelled equivalent,
   which a canvas child could never have. */
#sp-guide-layer {
	position: fixed;
	inset: 0;
	z-index: 60;
	pointer-events: none;
}

#sp-guide-layer[hidden] {
	display: none;
}

#sp-guide-layer canvas {
	background: transparent;
}

/* The transparent hit target over the bubble's own ×, positioned from getCloseRect(). Same
   contract as .sp-swf-button on the adoption forms; its own rule rather than a shared class,
   since the two live in unrelated markup trees and only one of them is inside a scaled stage. */
#sp-guide-close {
	position: fixed;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	color: transparent;
	font-size: 0;
	cursor: pointer;
	pointer-events: auto;
	z-index: 61;
}

#sp-guide-close[hidden] {
	display: none;
}

#sp-guide-close:focus-visible {
	outline: 3px solid var(--sp-focus);
	outline-offset: 2px;
	border-radius: 50%;
}

/* ---- the first-run tour's own controls (chrome/tour.js) ---- */

/* Greyed out and non-interactive while a guide is up. One rule, keyed off one attribute that
   chrome/tour.js sets and clears in a single pass, so a gate cannot survive the tour and leave a
   player with an app they cannot use — which is the one failure mode here that matters. The
   `inert` attribute goes on the same elements, so this removes them from the accessibility tree
   too rather than only dimming them. Faithful to the screenshots: in step 7.png only Decorate is
   in colour and Garden, Shop and the rest are washed out. */
body.sp-touring [data-tour-dim] {
	opacity: 0.45;
	filter: grayscale(1);
	pointer-events: none;
}

/* The friend ladder and the Recent Activity / Scrapbook row, held back until the tour reaches its
   playdate leg (chrome/tour.js marks those steps `social: true` and toggles .sp-tour-social).
   Everything a brand-new account has to put in them is empty — an empty feed, an empty scrapbook,
   a strip of "ADD FRIENDS!" placeholders — so before the tour has anything to say about them they
   are just noise wrapped around the one control each guide is pointing at. `display: none` rather
   than the dim-and-inert treatment above: these are not controls a guide is deliberately greying
   out, they are furniture that has not arrived yet, and a greyed-out empty panel still occupies
   the screen and still pushes the habitat up the page. */
body.sp-touring:not(.sp-tour-social) #sp-friend-bar,
body.sp-touring:not(.sp-tour-social) #sp-bottom-row {
	display: none;
}

/* The 30-second idle nudge (GuideManager.as's third tick, ported in chrome/tour.js): a single
	pulse on whatever the current guide left clickable. Deliberately one shot rather than a loop —
	half a minute of idling earns one "over here", not a permanently throbbing button — and
	deliberately a transform + glow rather than a colour change, so it reads on a control the guide
	has already made the only coloured thing on the page. */
@keyframes sp-tour-pulse {
	0% { transform: scale(1); }
	35% { transform: scale(1.12); box-shadow: 0 0 0 6px rgba(124, 191, 46, 0.35); }
	100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(124, 191, 46, 0); }
}

.sp-tour-pulse {
	animation: sp-tour-pulse 0.7s ease-in-out 2;
}

@media (prefers-reduced-motion: reduce) {
	.sp-tour-pulse { animation: none; }
}

/* The transparent hit target over the bubble's own x — see #sp-guide-layer above for why the
   canvas cannot take the click itself. */
#sp-tour-close {
	position: fixed;
	margin: 0;
	padding: 0;
	border: 0;
	background: transparent;
	color: transparent;
	font-size: 0;
	cursor: pointer;
	z-index: 61;
}

/* The in-scene button four of the nine guides carry. Real HTML, styled to match the legacy's own
   yellow gel button, rather than canvas art — it has to be focusable and labelled, and it has to
   sit above a layer that does not take clicks. */
#sp-tour-button {
	position: fixed;
	z-index: 61;
	padding: 9px 22px;
	border: 2px solid #c08a00;
	border-radius: 8px;
	background: linear-gradient(#ffd75e, #f5b800);
	color: #5a3e00;
	/* Body face — the tour launcher is a button. Explicit (<button>). */
	font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
	font-weight: 800;
	font-size: var(--fs-lg);
	cursor: pointer;
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.28);
}

#sp-tour-button:hover {
	background: linear-gradient(#ffe17f, #ffc61a);
}

/* Deliberately unobtrusive and deliberately always there while a tour runs: it is the escape
   hatch, so it must never be the thing a player has to hunt for, and never the thing they click
   by accident either. */
#sp-tour-skip {
	position: fixed;
	right: 14px;
	bottom: 14px;
	z-index: 61;
	padding: 6px 12px;
	border: 1px solid rgba(0, 0, 0, 0.18);
	border-radius: 999px;
	background: rgba(255, 255, 255, 0.9);
	color: var(--sp-text-mid);
	font-size: var(--fs-xs);
	cursor: pointer;
}

#sp-tour-skip:hover {
	background: #fff;
	color: var(--sp-text-dark);
}

#sp-tour-close:focus-visible,
#sp-tour-button:focus-visible,
#sp-tour-skip:focus-visible {
	outline: 3px solid var(--sp-focus);
	outline-offset: 2px;
}

#sp-tour-close[hidden],
#sp-tour-button[hidden],
#sp-tour-skip[hidden] {
	display: none;
}

/* Visually hidden, still announced. Used for the canvas error mirrors — form_b1/form_b2 show the
   message on the board itself, which a screen reader cannot see. */
.sp-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* `display: flex` above wins specificity over the UA stylesheet's `[hidden] { display: none }`
   (an ID selector beats an attribute selector) — without this override, setting the `hidden`
   attribute from chrome.js wouldn't actually hide the page. Same pattern the existing modals
   already need (see e.g. #sp-badges-modal[hidden] below). */
#sp-auth-page[hidden] {
	display: none;
}

#sp-auth-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	/* The primary surface while logged out, floating on the busy sky art — it needs real
	   presence, so the modal elevation rather than the card one. */
	box-shadow: var(--e-modal);
	padding: 32px 36px;
	width: 640px;
	max-width: 100%;
}

/* While the wizard's own #sp-onboarding-viewport is up (class set by
   setOnboardingCanvasVisible()), #sp-auth-card holds only the "Step X of 5"/Back remnant
   (#sp-auth-adopt — everything else that box used to hold moved onto the canvas itself back in
   task 08) — shrink it from a full page card down to a small floating pill over the now
   full-bleed canvas, rather than leaving an empty-looking card-shaped hole in the flex layout
   underneath the fixed viewport. Reverts to the normal card treatment above the moment the
   wizard leaves (welcome/login/fallback all share this same element). */
#sp-auth-page.sp-onboarding-fullbleed #sp-auth-card {
	position: fixed;
	left: 50%;
	bottom: 20px;
	transform: translateX(-50%);
	z-index: 50;
	width: auto;
	padding: 10px 20px;
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2);
}

.sp-auth-view h2 {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 16px;
}

#sp-auth-welcome {
	text-align: center;
}

#sp-auth-logo {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 10px;
	margin-bottom: 12px;
}

#sp-auth-logo-badge {
	width: 64px;
	height: 64px;
	filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.15));
}

#sp-auth-logo-banner {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	line-height: 1;
	background: linear-gradient(180deg, var(--sp-green-top) 0%, var(--sp-green-bottom) 100%);
	border-radius: 999px;
	padding: 8px 20px 9px 18px;
	box-shadow: 0 3px 0 var(--sp-green-active-bottom);
}

#sp-auth-logo-small {
	font-size: var(--fs-sm);
	font-weight: 700;
	color: rgba(255, 255, 255, 0.9);
}

#sp-auth-logo-big {
	font-size: var(--fs-2xl);
	font-weight: 800;
	color: #fff;
	letter-spacing: 0.5px;
}

#sp-auth-pitch {
	color: var(--sp-text-mid);
	font-size: var(--fs-md);
	margin: 0 0 22px;
}

#sp-auth-welcome .sp-btn {
	display: block;
	width: 100%;
	max-width: 280px;
	margin: 0 auto 14px;
	padding: 12px 18px;
	font-size: var(--fs-lg);
}

/* Stacked, not run together on one line — as inline buttons the two of them read as a single
   garbled sentence ("...Log In Trouble with the pet picker?..."). */
#sp-auth-welcome .sp-link-btn {
	display: block;
	margin: 8px auto 0;
	font-size: var(--fs-sm);
}

/* ---- login / field styling (shared by login + adopt account step) ---- */

.sp-auth-field {
	display: block;
	margin-bottom: 14px;
}

.sp-auth-field span {
	display: block;
	font-size: var(--fs-xs);
	font-weight: 700;
	color: var(--sp-text-mid);
	margin-bottom: 4px;
}

/* Was a 999px pill with a 1px --sp-line border — near-invisible on the white card, and the pill
   shape read as a search box rather than a form field. A legacy form field: rectangular, a
   defined edge, a faint inset so it reads as something you type into. */
.sp-auth-field input {
	width: 100%;
	padding: 9px 12px;
	border-radius: var(--r-sm);
	border: 1px solid #a9bcc6;
	background: #fff;
	box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.06);
	color: var(--sp-text-dark);
	font: inherit;
	font-family: "Source Sans 3", "Segoe UI", sans-serif;
}

.sp-auth-field input:focus {
	border-color: var(--sp-focus);
}

.sp-auth-error {
	color: oklch(0.55 0.2 25);
	font-size: var(--fs-xs);
	font-weight: 700;
	margin: -6px 0 14px;
}

/* Task 10: the one-line explanation shown when chrome/onboarding.js routes here automatically
   after a canvas load failure (see #sp-auth-fb-notice's own comment in index.html) — informational,
   not an error, so it gets its own softer color rather than .sp-auth-error's. */
.sp-auth-notice {
	color: var(--sp-text-mid);
	font-size: var(--fs-sm);
	font-weight: 600;
	background: var(--sp-blue-pill-bg);
	border-radius: 10px;
	padding: 10px 14px;
	margin-bottom: 14px;
}

#sp-auth-login .sp-btn, #sp-auth-login .sp-link-btn {
	display: block;
	width: 100%;
	text-align: center;
}

#sp-auth-login .sp-btn {
	margin-bottom: 12px;
}

#sp-auth-tos {
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
	margin: 4px 0 16px;
}

#sp-auth-submit {
	width: 100%;
}

/* ---- adopt-a-pet wizard ---- */

.sp-auth-optional {
	font-weight: 400;
	font-size: 0.85em;
	color: var(--sp-text-muted);
}

/* The "Classic renderer" (Phase 7 v2.5) opt-in — a plain checkbox, since it's a single boolean
   choice about the pet's whole rendering style. Still used by the accessibility fallback form
   (chrome/onboardingFallback.js); the canvas path's copy of it is .sp-swf-aside above, which has
   to sit on the meadow under the noticeboard rather than in a card. */
.sp-auth-checkbox-field {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: var(--fs-sm);
	font-weight: 700;
	color: var(--sp-text-mid);
	cursor: pointer;
	margin-bottom: 4px;
}

.sp-auth-checkbox-field input {
	width: 16px;
	height: 16px;
	accent-color: var(--sp-green-bottom);
}

#sp-auth-adopt-back {
	margin-top: 6px;
}

/* ---- accessibility fallback wizard (#sp-auth-adopt-fallback, task 09) ----
   The flat tile-grid styling task 08 deleted along with the HTML species/dress/habitat steps
   it belonged to — restored verbatim (class selectors, so this also covers the fallback's own
   `sp-auth-fb-*-grid` elements without needing its own copy) now that those steps have a second
   home as the keyboard/screen-reader-usable escape hatch. See onboarding-refinement/
   09-responsive-fit-and-a11y.md. */

/* "Step N of 6" with a real progress track under it — --p (0..1) is set by
   chrome/onboardingFallback.js's showFallbackStep(). Previously bare uppercase text with no
   sense of how far along a 6-step flow you were. */
#sp-auth-fb-progress {
	--p: 0.16;
	font-size: var(--fs-xs);
	font-weight: 700;
	color: var(--sp-text-muted);
	text-transform: uppercase;
	letter-spacing: 0.04em;
	margin-bottom: 14px;
}

#sp-auth-fb-progress::after {
	content: "";
	display: block;
	margin-top: 6px;
	height: 6px;
	border-radius: 999px;
	background:
		linear-gradient(90deg, var(--sp-green-top), var(--sp-green-bottom)) left / calc(var(--p) * 100%) 100% no-repeat,
		var(--sp-line);
}

/* Fixed-basis tiles that wrap and centre, so the last (short) row sits under the grid instead
   of leaving one lonely tile hanging off the left — the species count doesn't divide evenly
   into the column count at any card width. */
.sp-auth-tile-grid {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 12px;
	margin-bottom: 18px;
}

.sp-auth-tile-grid > .sp-auth-tile {
	flex: 0 1 120px;
}

.sp-auth-tile {
	appearance: none;
	border: 2px solid var(--sp-line);
	border-radius: 16px;
	background: var(--sp-card-bg);
	padding: 10px;
	cursor: pointer;
	text-align: center;
	transition: transform 0.05s ease;
}

.sp-auth-tile:hover {
	border-color: var(--sp-green-bottom);
}

.sp-auth-tile:active {
	transform: translateY(1px);
}

.sp-auth-tile.sp-auth-tile-selected {
	border-color: var(--sp-green-bottom);
	box-shadow: 0 0 0 2px oklch(0.7 0.13 145 / 0.3);
}

.sp-auth-tile img {
	width: 100%;
	height: 88px;
	object-fit: contain;
	background: var(--sp-blue-pill-bg);
	border-radius: 12px;
}

.sp-auth-tile-label {
	margin-top: 6px;
	font-size: var(--fs-xs);
	font-weight: 700;
	color: var(--sp-text-dark);
}

.sp-auth-step-actions {
	display: flex;
	gap: 10px;
}

#sp-auth-fb-back {
	margin-top: 6px;
}

/* ---- Pet Salon modal ---- */
/* Same overlay pattern as #sp-item-modal above (backdrop + centered card), just
   wider — canvas mount left, control column right, sized to the salon canvas's own 900x600
   (client/project-salon.xml) plus the control column, rather than a small dialog. */

#sp-salon-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-salon-modal[hidden] {
	display: none;
}

#sp-salon-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-salon-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 24px;
	max-width: calc(100vw - 40px);
	max-height: calc(100vh - 40px);
	overflow: auto;
}

#sp-salon-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
	z-index: 1;
}

#sp-salon-modal-body {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	flex-wrap: wrap;
	gap: 20px;
}

#sp-salon-canvas-mount {
	width: 900px;
	height: 600px;
	flex-shrink: 0;
	border-radius: 14px;
	overflow: hidden;
	background: var(--sp-blue-pill-bg);
	position: relative;
}

.sp-salon-canvas-status {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	color: var(--sp-text-mid);
	font-weight: 700;
}

#sp-salon-controls {
	width: 320px;
	flex-shrink: 0;
	display: flex;
	flex-direction: column;
	gap: 16px;
	max-height: 600px;
}

.sp-salon-tabs {
	display: flex;
	gap: 8px;
}

.sp-salon-tab {
	flex: 1;
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-mid);
	box-shadow: none;
	padding: 9px 10px;
	font-size: var(--fs-sm);
}

.sp-salon-tab.sp-salon-tab-active {
	background: linear-gradient(180deg, var(--sp-green-active-top) 0%, var(--sp-green-active-bottom) 100%);
	color: #fff;
}

.sp-salon-tab-panel[hidden] {
	display: none;
}

.sp-salon-submode-switch {
	display: flex;
	gap: 6px;
	margin-bottom: 12px;
}

.sp-salon-submode {
	flex: 1;
	background: none;
	border: 1px solid var(--sp-line);
	color: var(--sp-text-mid);
	box-shadow: none;
	padding: 6px 10px;
	font-size: var(--fs-xs);
	border-radius: 999px;
}

.sp-salon-submode.sp-salon-submode-active {
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-dark);
	border-color: transparent;
}

.sp-salon-submode-panel[hidden] {
	display: none;
}

/* Scrollable rather than growing the whole control column open-ended — the 8-swatch grid, 3
   HSV wheels, and the markings tile grid all vary in height. */
.sp-salon-submode-panel, .sp-salon-tab-panel[data-panel="markings"] {
	max-height: 420px;
	overflow-y: auto;
}

.sp-salon-buttons {
	display: flex;
	gap: 8px;
	margin-top: auto;
	padding-top: 12px;
	border-top: 1px solid var(--sp-line);
}

.sp-salon-buttons .sp-btn {
	flex: 1;
	padding: 9px 10px;
	font-size: var(--fs-sm);
}

/* First use of a "primary" (gold, matching the currency/reward palette) button variant —
   distinguishes Apply (a committing action) from Cancel/Randomize's default green. */
.sp-btn-primary {
	background: linear-gradient(180deg, var(--sp-gold-top) 0%, var(--sp-gold-bottom) 100%);
	box-shadow: 0 4px 0 var(--sp-gold-border);
	color: var(--sp-text-dark);
}

.sp-btn-primary:active {
	box-shadow: 0 2px 0 var(--sp-gold-border);
}

.sp-salon-swatch-grid {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 10px;
}

.sp-salon-swatch {
	position: relative;
	width: 100%;
	aspect-ratio: 1;
	border-radius: 12px;
	border: 1px solid var(--sp-card-border);
	box-shadow: 0 2px 0 var(--sp-line);
	padding: 0;
	cursor: pointer;
}

.sp-salon-swatch:hover {
	filter: brightness(1.05);
}

.sp-salon-swatch.sp-salon-swatch-selected {
	border-color: var(--sp-green-active-bottom);
	box-shadow: 0 0 0 2px var(--sp-green-active-bottom);
}

.sp-salon-swatch-dot {
	position: absolute;
	top: 4px;
	right: 4px;
	width: 10px;
	height: 10px;
	border-radius: 50%;
	border: 1px solid rgba(255, 255, 255, 0.8);
}

.sp-salon-wheel {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	margin-bottom: 18px;
}

.sp-salon-wheel-label {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: var(--fs-xs);
	font-weight: 700;
	color: var(--sp-text-mid);
}

.sp-salon-wheel-swatch {
	display: inline-block;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	border: 2px solid var(--sp-card-border);
	box-shadow: 0 0 0 1px var(--sp-line);
}

.sp-salon-wheel-canvas-wrap {
	position: relative;
	width: 160px;
	height: 160px;
}

.sp-salon-wheel-canvas {
	cursor: crosshair;
	border-radius: 50%;
	touch-action: none;
}

.sp-salon-wheel-canvas:focus-visible {
	outline: 3px solid var(--sp-link);
	outline-offset: 2px;
}

.sp-salon-wheel-marker {
	position: absolute;
	width: 12px;
	height: 12px;
	margin: -6px 0 0 -6px;
	border-radius: 50%;
	border: 2px solid #fff;
	box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
	pointer-events: none;
}

.sp-salon-wheel-brightness {
	width: 160px;
	accent-color: var(--sp-green-bottom);
}

.sp-salon-wheel-hex {
	width: 140px;
	padding: 4px 8px;
	border-radius: 999px;
	border: 1px solid var(--sp-line);
	background: var(--sp-card-bg);
	color: var(--sp-text-dark);
	font: inherit;
	font-size: var(--fs-xs);
	text-align: center;
}

.sp-salon-marking-grid {
	display: grid;
	grid-template-columns: repeat(3, 1fr);
	gap: 8px;
	margin-bottom: 16px;
}

.sp-salon-marking-tile {
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-mid);
	box-shadow: none;
	padding: 10px 6px;
	font-size: var(--fs-xs);
	border-radius: 12px;
}

.sp-salon-marking-tile.sp-salon-marking-tile-selected {
	background: linear-gradient(180deg, var(--sp-green-active-top) 0%, var(--sp-green-active-bottom) 100%);
	color: #fff;
}

/* ---- friending simplification: inline "Add friend" button + Friend Bar request cards ---- */

/* The relationship-aware button (chrome/social.js's friendActionButton) — on Directory tiles
   and the visited-friend sidebar. `data-friend-status` mutes the two states that aren't a
   call to action. */
.sp-friend-action[data-friend-status="pending_sent"],
.sp-friend-action[data-friend-status="accepted"] {
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-mid);
	box-shadow: none;
}

#sp-profile-friend-action {
	margin-top: 8px;
}

#sp-add-friend-msg {
	flex-basis: 100%;
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
}

#sp-add-friend-msg:empty {
	display: none;
}

/* Incoming friend request, pinned to the front of the Friend Bar. A wide "someone wants to be
   your friend" panel, not a pet card jammed into an 80px slot — it needs room for a readable
   line and two real buttons. Gold like the invite slots, since it's the same "act on your
   friends list" family. Not itself clickable. */
.sp-friend-bar-card.sp-friend-bar-request {
	flex: 0 0 200px;
	width: 200px;
	height: auto;
	align-self: stretch;
	cursor: default;
	background: linear-gradient(180deg, #ffe08a 0%, #ffd257 100%);
	justify-content: center;
	gap: 8px;
	padding: 10px;
	text-align: left;
}

.sp-friend-bar-request-head {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
}

.sp-friend-bar-request .sp-friend-bar-avatar-wrap {
	flex: 0 0 40px;
	width: 40px;
	height: 40px;
}

.sp-friend-bar-request .sp-tile-avatar {
	width: 40px;
	height: 40px;
}

.sp-friend-bar-request-copy {
	min-width: 0;
	flex: 1 1 auto;
}

.sp-friend-bar-request-name {
	font-size: var(--fs-xs);
	font-weight: 800;
	color: #6b4a00;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.sp-friend-bar-request-line {
	font-size: var(--fs-2xs);
	font-weight: 700;
	line-height: 1.25;
	color: #6b4a00;
}

.sp-friend-bar-request-actions {
	display: flex;
	gap: 6px;
	width: 100%;
}

.sp-friend-bar-request-actions .sp-btn {
	flex: 1 1 0;
	padding: 6px 8px;
	font-size: var(--fs-2xs);
	line-height: 1.2;
	white-space: nowrap;
	box-shadow: none;
}

/* ---- Recent Activity feed (chrome/activity.js) ---- */

#sp-activity {
	margin-top: 14px;
	padding: 14px 16px;
	background: #f6f5f4;
	border: 1px solid var(--spx-panel-border);
	border-radius: var(--r-lg);
	box-shadow: var(--e-raised);
}

#sp-activity .sp-panel-head {
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 10px;
	margin-bottom: 10px;
}

#sp-activity-list {
	display: flex;
	flex-direction: column;
	gap: 6px;
	max-height: 320px;
	overflow-y: auto;
}

.sp-activity-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 12px;
	background: var(--sp-card-bg);
	border: 1px solid var(--sp-line);
	border-radius: var(--r-md);
	font-size: var(--fs-sm);
	color: var(--sp-text-mid);
}

.sp-activity-row.sp-activity-clickable {
	cursor: pointer;
}

.sp-activity-row.sp-activity-clickable:hover {
	border-color: var(--sp-green-top);
}

.sp-activity-icon {
	flex-shrink: 0;
	font-size: var(--fs-lg);
	line-height: 1;
}

.sp-activity-avatar {
	width: 26px;
	height: 26px;
	flex-shrink: 0;
	background: none;
}

.sp-activity-text {
	flex: 1 1 auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sp-activity-when {
	flex-shrink: 0;
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
}

/* ---- snapshot gallery (docs/backlog/planned-features/snapshot-gallery/) ---- */

#sp-snapshots-grid {
	grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

.sp-snapshot-tile {
	background: var(--sp-card-bg);
	border: 1px solid var(--sp-line);
	border-radius: var(--r-lg);
	padding: 8px;
	box-shadow: var(--e-card);
	cursor: pointer;
}

.sp-snapshot-tile:hover,
.sp-snapshot-tile:focus-visible {
	border-color: var(--sp-green-top);
	outline: none;
}

.sp-snapshot-tile img {
	width: 100%;
	height: auto;
	display: block;
	border-radius: 10px;
	background: var(--sp-blue-pill-bg);
}

.sp-snapshot-caption {
	margin-top: 6px;
	font-size: var(--fs-xs);
	color: var(--sp-text-dark);
	line-height: 1.3;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.sp-snapshot-counts {
	margin-top: 4px;
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
}

/* ---- snapshot detail modal (task 09) ---- */

#sp-snapshot-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-snapshot-modal[hidden] {
	display: none;
}

#sp-snapshot-modal-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-snapshot-modal-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 24px;
	width: 860px;
	max-width: calc(100vw - 40px);
	max-height: calc(100vh - 40px);
	overflow-y: auto;
	display: flex;
	gap: 20px;
}

#sp-snapshot-modal-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-snapshot-modal-main {
	flex: 1 1 60%;
	min-width: 0;
}

#sp-snapshot-modal-img {
	width: 100%;
	height: auto;
	border-radius: 14px;
	background: var(--sp-blue-pill-bg);
	display: block;
}

#sp-snapshot-modal-caption {
	margin-top: 10px;
	font-size: var(--fs-md);
	color: var(--sp-text-dark);
	line-height: 1.4;
}

#sp-snapshot-modal-owner {
	margin-top: 4px;
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
}

#sp-snapshot-modal-delete {
	margin-top: 12px;
}

#sp-snapshot-modal-reactions {
	flex: 1 1 40%;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

#sp-snapshot-modal-compliments {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}

#sp-snapshot-modal-compliments[hidden] {
	display: none;
}

#sp-snapshot-modal-compliment-list,
#sp-snapshot-modal-comment-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 6px;
}

#sp-snapshot-modal-compliment-list li {
	font-size: var(--fs-xs);
	color: var(--sp-text-mid);
}

#sp-snapshot-modal-comment-list {
	max-height: 260px;
	overflow-y: auto;
}

.sp-snapshot-comment {
	background: var(--sp-card-bg);
	border: 1px solid var(--sp-line);
	border-radius: 12px;
	padding: 8px 10px;
}

.sp-snapshot-comment-head {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
}

.sp-snapshot-comment-author {
	font-weight: 700;
	color: var(--sp-text-mid);
}

.sp-snapshot-comment-when {
	flex: 1 1 auto;
}

.sp-snapshot-comment-del {
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-lg);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 0 2px;
}

.sp-snapshot-comment-body {
	margin-top: 4px;
	font-size: var(--fs-sm);
	color: var(--sp-text-dark);
	line-height: 1.35;
	overflow-wrap: anywhere;
}

#sp-snapshot-modal-comment-form {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

#sp-snapshot-modal-comment-input {
	width: 100%;
	resize: vertical;
	font-family: "Source Sans 3", "Segoe UI", sans-serif;
	font-size: var(--fs-sm);
	padding: 8px;
	border: 1px solid var(--sp-line);
	border-radius: 10px;
	background: var(--sp-card-bg);
	color: var(--sp-text-dark);
}

@media (max-width: 720px) {
	#sp-snapshot-modal-card {
		flex-direction: column;
	}
}

/* ---- bottom widget row: Recent Activity + Scrapbook (task 08) ---- */
/* pet ui.png's layout — the two panels sit side by side below the Friend Bar, and stack once the
   viewport is too narrow to give each a sensible width. #sp-activity keeps its own card styling
   (defined above); #sp-scrapbook matches it. */

#sp-bottom-row {
	display: flex;
	gap: 16px;
	align-items: flex-start;
	flex-wrap: wrap;
}

#sp-bottom-row[hidden] {
	display: none;
}

#sp-bottom-row > #sp-activity {
	flex: 1 1 420px;
	min-width: 0;
}

#sp-scrapbook {
	flex: 1 1 320px;
	min-width: 0;
	margin-top: 14px;
	padding: 14px 16px;
	background: #f6f5f4;
	border: 1px solid var(--spx-panel-border);
	border-radius: var(--r-lg);
	box-shadow: var(--e-raised);
}

#sp-scrapbook .sp-panel-head {
	flex-direction: row;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 10px;
	margin-bottom: 10px;
}

.sp-scrapbook-count {
	font-size: var(--fs-sm);
	font-weight: 700;
	color: var(--sp-text-muted);
}

.sp-snapshots-strip {
	display: flex;
	gap: 8px;
	flex-wrap: wrap;
}

.sp-snapshot-thumb {
	width: 120px;
	height: 46px;
	object-fit: cover;
	border-radius: 8px;
	background: var(--sp-blue-pill-bg);
	cursor: pointer;
	border: 1px solid var(--sp-line);
}

.sp-snapshot-thumb:hover,
.sp-snapshot-thumb:focus-visible {
	border-color: var(--sp-green-top);
	outline: none;
}

/* ---- gifting (docs/backlog/planned-features/gifting/, task 05) ---- */

/* "🎁 Gift" button beside friendActionButton on directory tiles and the visited-profile
   sidebar. Neutral by default so it doesn't compete with the green friend-action button. */
.sp-gift-btn {
	margin-top: 6px;
	background: var(--sp-blue-pill-bg);
	color: var(--sp-text-dark);
	box-shadow: 0 3px 0 var(--sp-line);
}

/* Inbox pip on #sp-gift-inbox-open — same attr-driven circle idiom as the Friends nav dot
   (.sp-nav-btn[data-social-badge]), just on the HUD button (#sp-hud-actions). */
#sp-gift-inbox-open {
	position: relative;
}

#sp-gift-inbox-open[data-gift-badge]:not([data-gift-badge=""])::after {
	content: attr(data-gift-badge);
	position: absolute;
	top: -6px;
	right: -6px;
	min-width: 16px;
	height: 16px;
	padding: 0 4px;
	border-radius: 999px;
	background: linear-gradient(180deg, var(--sp-red-top) 0%, var(--sp-red-bottom) 100%);
	color: #fff;
	font-size: var(--fs-2xs);
	font-weight: 800;
	line-height: 16px;
	text-align: center;
}

/* Both modals reuse the shared overlay shape (fixed inset-0 flex-centered, backdrop + card),
   same as #sp-item-modal. */
#sp-gift-picker-modal,
#sp-gift-inbox-modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: flex;
	align-items: center;
	justify-content: center;
}

#sp-gift-picker-modal[hidden],
#sp-gift-inbox-modal[hidden] {
	display: none;
}

#sp-gift-picker-backdrop,
#sp-gift-inbox-backdrop {
	position: absolute;
	inset: 0;
	background: rgba(20, 25, 40, 0.55);
}

#sp-gift-picker-card,
#sp-gift-inbox-card {
	position: relative;
	background: var(--sp-card-bg);
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-card-border);
	box-shadow: var(--e-modal);
	padding: 28px;
	width: 460px;
	max-width: calc(100vw - 40px);
	max-height: calc(100vh - 80px);
	overflow-y: auto;
}

#sp-gift-picker-close,
#sp-gift-inbox-close {
	position: absolute;
	top: 10px;
	right: 12px;
	background: none;
	border: none;
	box-shadow: none;
	font-size: var(--fs-2xl);
	line-height: 1;
	color: var(--sp-text-muted);
	cursor: pointer;
	padding: 4px;
}

#sp-gift-picker-title,
#sp-gift-inbox-title {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-xl);
	color: var(--sp-text-dark);
	margin: 0 0 14px;
}

#sp-gift-picker-grid {
	max-height: 320px;
	overflow-y: auto;
	padding: 2px;
	margin-bottom: 14px;
}

/* The chosen tile in the picker — a distinct green outline, sitting alongside the inherited
   .sp-tile look. */
#sp-gift-picker-grid .sp-tile {
	cursor: pointer;
}

#sp-gift-picker-grid .sp-tile.is-selected {
	border: 2px solid var(--sp-green-top);
	box-shadow: 0 0 0 3px oklch(0.7 0.13 145 / 0.3);
}

#sp-gift-note-label {
	display: block;
	font-weight: 700;
	font-size: var(--fs-xs);
	color: var(--sp-text-muted);
	margin-bottom: 4px;
}

#sp-gift-note {
	width: 100%;
	padding: 8px 10px;
	border-radius: 10px;
	border: 1px solid var(--sp-line);
	font-family: "Source Sans 3", "Segoe UI", sans-serif;
	font-size: var(--fs-sm);
}

#sp-gift-note-count {
	text-align: right;
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
	margin: 4px 0 12px;
}

#sp-gift-send {
	width: 100%;
	font-size: var(--fs-lg);
	padding: 12px 0;
}

/* Inbox + sent rows — built on .sp-friend-row (see chrome/gifting.js inboxRow/sentRow). */
#sp-gift-sent-heading {
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-sm);
	color: var(--sp-text-muted);
	margin: 18px 0 8px;
}

.sp-gift-row {
	align-items: center;
	gap: 10px;
}

.sp-gift-row .sp-tile-avatar {
	width: 44px;
	height: 44px;
	object-fit: contain;
	background: var(--sp-blue-pill-bg);
	border-radius: 10px;
}

.sp-gift-row-text {
	flex: 1;
	min-width: 0;
}

.sp-gift-row-note {
	font-size: var(--fs-2xs);
	color: var(--sp-text-muted);
	font-style: italic;
	margin-top: 2px;
}

.sp-gift-row-revealed {
	justify-content: center;
	font-weight: 800;
	color: var(--sp-green-bottom);
}

.sp-gift-row-sent {
	opacity: 0.75;
	font-size: var(--fs-xs);
}

.sp-gift-row-status {
	font-size: var(--fs-2xs);
	font-weight: 800;
	text-transform: uppercase;
	color: var(--sp-text-muted);
}

/* ======================================================================
   Legacy-faithful skin for the Home (profile + habitat) + Decorate screen
   ----------------------------------------------------------------------
   ADR 011 (docs/decisions/011-legacy-faithful-skin-for-home-screen.md):
   the Home / Decorate screen is re-skinned to the original SuperPoke Pets
   "My Home" page rather than the pastel facelift.

   Two tiers of rule live in this block:
     - Shared logged-in chrome — the page background (html/body), the nav
       tabs (#sp-nav), the HUD strip (#sp-hud) and the #sp-page frame. These
       are one bar across every logged-in screen, so the legacy look here is
       app-wide by design: Shop / Social / Snapshots get it too (accepted —
       the user signed off on the Shop style following along).
     - Home / Decorate specifics — scoped to their own ids (#sp-card,
       #sp-sidebar, #sp-home-card, #sp-viewer-toolbar, #sp-decorate,
       #sp-friend-bar, #sp-bottom-row), which only exist on those screens.

   The facelift still owns everything the block can't reach: every modal
   (item / cart / badges / share / daily-reward / gift / salon / snapshot),
   the salon canvas, and the logged-out onboarding / auth views. Shared
   base rules (.sp-nav-btn/.sp-tab/.sp-btn at chrome.css:212, .sp-tile, …)
   are not edited.

   "Real data only" — elements with no backend (Community/Mbox/Free
   Gifts/Help tabs, Get Gold, Clubs, profile URL, Recycle, nested-container
   cards) are not added; see the ADR.
   ====================================================================== */

/* The `--spx-*` tokens this block used to define now live in the single `:root` at the top of the
   file, alongside the `--sp-*` names that alias them and the type/elevation/radius/z scales — see
   the visual-polish pass (ADR 017). */

/* ---- page frame ---- */

html {
	background-color: var(--spx-sky);
}

body {
	/* Two image layers, both horizontal-only. The rolling-hills footer strip (footer-tile.png,
	   1025x167) is pinned to the viewport bottom (background-attachment: fixed) so it stays put
	   as the page scrolls; the sky+clouds tile (500x1044) sits once at the top of the page. Below
	   where the sky band ends, background-color (--spx-sky, == the image's bottom row) carries the
	   same blue down to the hills, so there's no seam — the legacy page sat on wall-to-wall sky
	   with a hill horizon along the bottom. */
	background-image: url("icons/footer-tile.png"), url("icons/background-tile.png");
	background-repeat: repeat-x, repeat-x;
	background-position: bottom center, top left;
	background-size: auto, 500px auto;
	background-attachment: fixed, scroll;
	background-color: var(--spx-sky);
}

#sp-page {
	/* Sized so the fixed 785px canvas sits with an equal 20px gap on its left (from the sidebar),
	   top and right: sidebar 158 + #sp-viewer-col (785 + 20 + 20) = 983 of #sp-card's content box,
	   which is this element's content (max-width - 32) minus #sp-card's 1px borders. A narrower
	   value let #sp-viewer-col overflow #sp-card and pushed its 20px right pad off the edge, so the
	   canvas ended up ~3px from the right while 20px from the left. Bottom padding clears the
	   ~167px fixed hill strip so the last panel doesn't sit buried in it. */
	max-width: 1017px;
	padding: 0 16px 150px;
}

/* The two stacked panels this skin owns — the profile/habitat card and the inventory (Decorate)
   panel — each a flat white panel with a hairline border and a tight gap, not the floating
   rounded facelift card. Scoped to #sp-card + #sp-decorate ONLY: #sp-shop / #sp-social /
   #sp-snapshots keep their facelift `.sp-panel` (transparent, border-top break) untouched. */
#sp-page #sp-card,
#sp-page #sp-decorate.sp-panel {
	background: var(--spx-panel);
	border: 1px solid var(--spx-panel-border);
	border-radius: var(--spx-panel-radius);
	box-shadow: var(--e-card);
}

#sp-page #sp-card {
	margin-top: 8px;
	overflow: visible;
}

/* The legacy inventory panel is its own separate white card below the profile card — drop the
   facelift's "clean break" border-top + big top margin in favour of that card's own chrome. */
#sp-page #sp-decorate.sp-panel {
	margin-top: 10px;
	padding: 14px 16px;
	border-top: 1px solid var(--spx-panel-border);
}

/* ---- top bar + nav tabs ---- */

#sp-topbar {
	background-color: transparent;
}

/* Lift the "Logged in as …" pill off the HUD seam — the row is align-items:flex-end, so this
   just raises the account box (logo + nav stay pinned to the bottom edge). */
#sp-account-box {
	margin-bottom: 6px;
}

#sp-nav {
	gap: 3px;
	align-items: flex-end;
}

/* Scalloped green tabs (icons/nav-tab.png / nav-tab-active.png), sitting low and flush on the
   HUD strip's top edge — TabSelector.as's TAB_TOP_MARGIN look. The clip-path gives the angled
   shoulders the legacy tabs had (wider at the base than the top); it's applied on the base
   `.sp-nav-btn` rule with nothing overriding it on `.sp-nav-active`, so the selected tab keeps
   the exact same silhouette as the rest instead of reading as a plain rounded rectangle from
   its stretched art. `border-radius` still rounds the two top corners inside the clip. */
#sp-nav .sp-nav-btn {
	border-radius: 8px 8px 0 0;
	clip-path: polygon(9px 0, calc(100% - 9px) 0, 100% 100%, 0 100%);
	padding: 8px 22px 10px;
	margin-bottom: -2px;
	font-weight: 800;
	font-size: var(--fs-sm);
	letter-spacing: 0.01em;
}

#sp-nav .sp-nav-btn.sp-nav-active {
	padding-bottom: 12px;
	position: relative;
	z-index: 2;
}

/* ---- HUD strip ---- */

#sp-hud {
	padding: 6px 16px;
	border-radius: 0;
	gap: 16px;
}

/* Order left -> right: level badge + XP bar (hard left), currency counts, then the right-hand
   action group (sound + trophy + pips). #sp-hud-actions carries margin-left:auto already, so the
   first two stay grouped on the left. */
#sp-hud #sp-level-badge { order: 0; gap: 0; align-items: center; }
#sp-hud #sp-currency { order: 1; gap: 14px; }
#sp-hud #sp-hud-actions { order: 2; gap: 10px; }

/* No pillbox behind the coin / gold counts — just the icon + number sitting on the grey strip,
   like the reference HUD. (currency-pill-bg.png / the rounded chip are dropped here only.) */
#sp-hud .sp-currency-pill {
	background: none;
	background-image: none;
	padding: 0;
	border-radius: 0;
	font-size: var(--fs-md);
	color: var(--spx-ink);
}

/* Sound moved into the right-hand action group — bare speaker icon, no chip. */
#sp-hud-actions #sp-sound {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	background: none;
	padding: 0;
}

/* ---- level ring + XP capsule ----
   Rebuilt to read like the legacy InfoHeader (the two reference caps in
   docs/backlog/planned-features/visual-polish-pass/): the gold level ring overlaps the left end
   of ONE glossy blue pill that carries both the level name (over the fill, left) and the
   lifetime XP count (right). The port had split those onto two lines above a flat track. */
#sp-hud #sp-level-icon-wrap {
	width: 30px;
	height: 30px;
	z-index: 2;
}

#sp-hud #sp-level-value {
	font-size: var(--fs-xs);
}

/* The column is just the pill now — the title moved inside it. The ring overlaps the track's
   left end by a good chunk; #sp-level-title's own `left` keeps the label clear of it. */
#sp-hud #sp-level-bar-col {
	position: relative;
	margin-left: -14px;
	gap: 0;
}

#sp-hud #sp-xp-bar {
	position: relative;
	width: 172px;
	height: 20px;
	padding: 0;
	border: 1px solid #1b1b1b;
	/* Softly rounded, not a full pill — matches the legacy bar's gently-cornered track. */
	border-radius: var(--r-sm);
	background-image: none;
	/* Dark trough — the unfilled portion reads black, so the blue fill glows in it. */
	background: linear-gradient(180deg, #0e0e0e 0%, #333333 100%);
	box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.75);
	overflow: hidden;
}

/* Glossy blue fill — flat blue base with a white top-half sheen and a darker leading edge, in
   place of the base rule's stretched xp-bar-full.png. A slight radius softens the leading edge
   (the left corners are clipped by the track's own overflow:hidden). */
#sp-hud #sp-xp-bar-fill {
	border-radius: 3px;
	background-color: #3f9bd8;
	background-image: linear-gradient(180deg,
		rgba(255, 255, 255, 0.55) 0%,
		rgba(255, 255, 255, 0.14) 46%,
		rgba(255, 255, 255, 0) 47%,
		rgba(18, 88, 142, 0.22) 100%);
	background-size: 100% 100%;
	box-shadow: inset -1px 0 0 rgba(21, 84, 133, 0.5);
}

/* Level name printed over the fill's left end — white with a black drop shadow so it reads over
   both the blue fill and the dark empty trough. */
#sp-hud #sp-level-title {
	position: absolute;
	/* Clear of the ring, which now overlaps the track's first ~16px. */
	left: 26px;
	top: 50%;
	transform: translateY(-50%);
	z-index: 1;
	max-width: calc(100% - 64px);
	margin: 0;
	color: #fff;
	text-shadow: 0 1px 1px rgba(0, 0, 0, 0.6);
	font-size: var(--fs-2xs);
	font-weight: 800;
}

/* Lifetime XP count, right end of the same pill (LevelBar.as printed the raw point total here,
   e.g. "4,835"). */
#sp-xp-total {
	position: absolute;
	inset: 0;
	z-index: 1;
	display: flex;
	align-items: center;
	justify-content: flex-end;
	padding-right: 10px;
	/* Body face, not Porky's — this is the XP number over the bar. White on the dark trough,
	   black drop shadow, same as the level name. */
	font-weight: 700;
	font-size: var(--fs-2xs);
	color: #fff;
	text-shadow: 0 1px 1px rgba(0, 0, 0, 0.6);
	pointer-events: none;
}

/* HUD action icons (trophy, and the daily-spin / gift pips beside it) — bare icon buttons in
   the strip, no green gel pill. */
#sp-hud .sp-hud-icon-btn {
	appearance: none;
	border: none;
	background: none;
	box-shadow: none;
	padding: 2px;
	font-size: var(--fs-lg);
	line-height: 1;
	cursor: pointer;
	filter: grayscale(0.1);
}
#sp-hud .sp-hud-icon-btn:hover { filter: brightness(1.1); }

#sp-hud-actions #sp-daily-reward-open,
#sp-hud-actions #sp-gift-inbox-open {
	background: none;
	box-shadow: none;
	padding: 2px;
	font-size: var(--fs-lg);
	color: inherit;
}

/* The volume slider isn't in the legacy header — just the speaker icon. Keep the control for
   accessibility but collapse it to the icon until hover/focus. */
#sp-hud #sp-sound-volume {
	width: 0;
	opacity: 0;
	transition: width 0.12s ease, opacity 0.12s ease;
}
#sp-hud #sp-sound:hover #sp-sound-volume,
#sp-hud #sp-sound-volume:focus {
	width: 68px;
	opacity: 1;
}

/* ---- left profile sidebar ---- */

#sp-page #sp-sidebar {
	width: 158px;
	padding: 12px;
	border-right: 1px solid var(--spx-rule);
}

#sp-profile-identity {
	display: flex;
	gap: 8px;
	align-items: flex-start;
}

#sp-profile-identity-text { min-width: 0; }

/* Portrait — a bordered thumbnail like the legacy profile's pet headshot, not a plain rounded
   colour chip. chrome/profile.js fills it with the flat species icon then the dressed render. */
#sp-page #sp-pet-avatar {
	width: 52px;
	height: 52px;
	flex-shrink: 0;
	border-radius: 4px;
	border: 1px solid var(--spx-panel-border);
	background: var(--spx-inset-bg);
	object-fit: cover;
	margin-bottom: 0;
	box-shadow: inset 0 0 0 2px #fff;
}

#sp-page #sp-profile-username {
	font-size: var(--fs-lg);
	font-weight: 800;
	color: var(--spx-ink);
	margin-top: 0;
}

#sp-page #sp-pet-name {
	font-size: var(--fs-2xs);
	font-weight: 700;
	color: var(--spx-ink-soft);
}

.sp-profile-since {
	font-size: var(--fs-2xs);
	color: var(--spx-ink-soft);
	margin-top: 2px;
}

/* Section heads: normal-case bold with the blue "see all >" link at the right, divider rules
   between — the legacy sidebar's Badges / Friends / Compliments blocks. */
#sp-page .sp-profile-section {
	margin-top: 10px;
	padding-top: 8px;
	border-top: 1px solid var(--spx-rule);
}

#sp-page .sp-profile-section-head {
	text-transform: none;
	letter-spacing: normal;
	font-size: var(--fs-xs);
	font-weight: 800;
	color: var(--spx-ink);
	margin-bottom: 6px;
}

#sp-page .sp-profile-section-head .sp-link-btn {
	color: var(--spx-link);
	font-weight: 700;
}

#sp-badges-count { color: var(--spx-ink-soft); font-weight: 700; }

/* Friends / Compliments counts sit in an inset bordered box in the legacy layout, not as a bare
   big number. */
#sp-page .sp-profile-count {
	display: block;
	background: var(--spx-inset-bg);
	border: 1px solid var(--spx-rule);
	border-radius: 5px;
	padding: 5px 10px;
	font-size: var(--fs-md);
	font-weight: 800;
	color: var(--spx-ink);
}

#sp-page .sp-badge-slot {
	border-radius: 5px;
	width: 24px;
	height: 24px;
}

#sp-page #sp-share-btn {
	margin-top: 10px;
}

/* ---- canvas card ---- */

#sp-page #sp-home-card {
	border: 1px solid var(--spx-panel-border);
	/* Square corners: the in-canvas HUD (mood bars top-right, care toolbar bottom-centre) now
	   hugs these edges, and `overflow: hidden` on the base rule would round-clip a corner-flush
	   widget with any radius here. */
	border-radius: 0;
}

/* ---- under-canvas toolbar ---- */

#sp-viewer-toolbar .sp-toolbar-row {
	gap: 14px;
	flex-wrap: wrap;
	padding: 4px 0 2px;
}

#sp-viewer-toolbar .sp-icon-circle {
	width: 38px;
	height: 38px;
	font-size: var(--fs-lg);
	box-shadow: 0 2px 0 oklch(0.85 0.02 220);
}

#sp-viewer-toolbar .sp-icon-label { font-size: var(--fs-2xs); }

#sp-status-message {
	border-color: var(--spx-panel-border);
}

/* ---- inventory / Decorate panel ---- */

#sp-decorate .sp-panel-head h2 { font-size: var(--fs-lg); }

#sp-decorate-body { gap: 16px; }

/* Left rail — plain vertical list, active row on a pale blue fill, a divider between the rail
   and the grid. */
#sp-decorate-categories {
	width: 132px;
	border-right: 1px solid var(--spx-rule);
	padding-right: 8px;
	gap: 1px;
}

#sp-decorate-categories .sp-left-category-btn {
	border-radius: 4px;
	padding: 6px 8px;
	font-size: var(--fs-xs);
	color: var(--spx-link);
}

#sp-decorate-categories .sp-left-category-btn:hover {
	background: var(--spx-inset-bg);
}

#sp-decorate-categories .sp-left-category-btn.sp-left-category-active {
	background: #e4f1fa;
	color: var(--spx-ink);
	font-weight: 800;
}

/* Numbered pager ("1 2 Next"), top-right above the grid. #sp-decorate-prev/#sp-shop-prev/
   #sp-social-prev are kept for their shared click handler (core.js's renderNumberedPager) but
   hidden. Shop, Decorate and the Social directory share this exact pager markup/behaviour — see
   chrome/shop.js's renderShopPager, chrome/decorate.js's renderDecoratePager and
   chrome/social.js's loadSocialDirectory. */
#sp-decorate .sp-pager-numbered,
#sp-shop .sp-pager-numbered,
#sp-social .sp-pager-numbered {
	margin: 0 0 10px;
	justify-content: flex-end;
	gap: 6px;
}

#sp-decorate .sp-pager-numbered #sp-decorate-prev,
#sp-shop .sp-pager-numbered #sp-shop-prev,
#sp-social .sp-pager-numbered #sp-social-prev {
	display: none;
}

#sp-decorate .sp-pager-numbered #sp-decorate-page,
#sp-shop .sp-pager-numbered #sp-shop-page,
#sp-social .sp-pager-numbered #sp-social-page {
	display: inline-flex;
	gap: 4px;
}

.sp-page-num {
	appearance: none;
	border: 1px solid var(--spx-panel-border);
	background: #fff;
	color: var(--spx-link);
	/* Body face, not Porky's — pagination controls are just page numbers. Set explicitly (not
	   omitted): a <button> doesn't inherit font-family from the page, it falls back to the UA
	   control font. */
	font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
	font-weight: 700;
	font-size: var(--fs-xs);
	min-width: 22px;
	height: 22px;
	padding: 0 5px;
	border-radius: 4px;
	cursor: pointer;
}

.sp-page-num.sp-page-num-active {
	background: #e4f1fa;
	color: var(--spx-ink);
	border-color: var(--spx-link);
}

/* "…" between non-contiguous page numbers in the windowed pager. */
.sp-page-gap {
	display: inline-flex;
	align-items: flex-end;
	padding: 0 2px;
	color: var(--spx-ink-soft);
	font-size: var(--fs-xs);
}

#sp-decorate .sp-pager-numbered #sp-decorate-next,
#sp-shop .sp-pager-numbered #sp-shop-next,
#sp-social .sp-pager-numbered #sp-social-next {
	background: none;
	box-shadow: none;
	color: var(--spx-link);
	padding: 0 4px;
	font-size: var(--fs-xs);
}

/* Cards — flat white with a hairline border, item name on top, thumbnail in a light box, then
   the "You have N left" line. The whole tile is the control (one click equips/places); there is
   no per-card button. */
#sp-decorate .sp-grid {
	grid-template-columns: repeat(auto-fill, minmax(146px, 1fr));
	gap: 10px;
}

#sp-decorate .sp-tile {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--spx-panel-border);
	border-radius: 5px;
	box-shadow: none;
	padding: 8px;
}

#sp-decorate .sp-tile.sp-tile-applied {
	border: 2px solid var(--sp-green-bottom);
	box-shadow: none;
}

#sp-decorate .sp-tile .sp-tile-name {
	order: -1;
	margin: 0 0 6px;
	min-height: 2.2em;
	font-size: var(--fs-2xs);
	color: var(--spx-ink);
}

#sp-decorate .sp-tile img {
	height: 96px;
	background: var(--spx-inset-bg);
	border: 1px solid var(--spx-rule);
	border-radius: 4px;
}

/* Wraps the tile picture so the gold ribbon can anchor to it — the name row sits above the
   picture in this grid (`.sp-tile-name { order: -1 }`), so the tile's own corner is the wrong
   anchor. */
#sp-decorate .sp-tile-art {
	position: relative;
	display: block;
}

/* The folded gold "G" corner flag on a gold (premium) inventory item. Same construction as
   #sp-shop .sp-shop-ribbon, sized for the smaller Decorate tile; the top-left radius follows the
   picture's own 4px corner. */
#sp-decorate .sp-tile-art .sp-tile-ribbon {
	position: absolute;
	top: 0;
	left: 0;
	width: 28px;
	height: 28px;
	padding: 1px 0 0 3px;
	border-radius: 4px 0 0 0;
	/* Body face — the corner flag is a single "G" glyph, not a title. */
	font-weight: 800;
	font-size: var(--fs-2xs);
	line-height: 1;
	color: #fff;
	text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
	background: linear-gradient(135deg, #fbd85d 0%, #e0a512 46%, #ffffff 46%, #ffffff 50%, transparent 50%);
	pointer-events: none;
}

#sp-decorate .sp-tile .sp-tile-meta {
	color: var(--spx-ink-soft);
	font-size: var(--fs-2xs);
	margin: 6px 0 0;
}

/* The tile itself is the control now — one click equips/places the item (no Place/Equip button,
   no detail modal; removal is a canvas action). */
#sp-decorate .sp-tile-select {
	cursor: pointer;
	transition: transform 0.08s ease, box-shadow 0.08s ease;
}

#sp-decorate .sp-tile-select:hover {
	transform: translateY(-2px);
	box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}

#sp-decorate .sp-tile-select:focus-visible {
	outline: 2px solid var(--spx-link);
	outline-offset: 1px;
}

/* Every owned copy is already equipped / placed — nothing more to add from here. */
#sp-decorate .sp-tile-select.sp-tile-disabled {
	cursor: default;
	opacity: 0.5;
}

#sp-decorate .sp-tile-select.sp-tile-disabled:hover {
	transform: none;
	box-shadow: none;
}

/* ---- Shop panel (legacy) ----
   Scoped to #sp-shop for the same containment reason #sp-decorate's tile rules are: the shared
   base rules (.sp-tile / .sp-btn / .sp-left-categories, chrome.css:212 and :717) are what the
   modals, Social and the gift picker still use, and ADR 011 keeps them on the facelift. Nothing
   below reaches outside this id.

   Matched against docs/reference/legacy-shop-*.png: white square-cornered cards, left-aligned,
   a bordered art box with a corner currency flag and an `info` pill, then two labelled rows
   ("Today's Price:" with a per-currency icon, "Stock Left:"), then the quantity box beside a
   small grey gradient "Add to cart". */

#sp-shop .sp-panel-head h2 { font-size: var(--fs-lg); }

/* The cart rail's collapse toggle (chrome/cart.js toggleCartPanel). It sat in the panel head as a
   full green .sp-btn pill, right-aligned over the top of the cart column — loud, and detached from
   the panel it controls. Demoted here to the same quiet grey control the tiles' own buttons use so
   it reads as a utility toggle, not a second Checkout. */
#sp-shop #sp-cart-toggle {
	padding: 4px 10px;
	border: 1px solid #b9b9b9;
	border-radius: var(--r-sm);
	background: linear-gradient(180deg, #fdfdfd 0%, #e3e3e3 100%);
	box-shadow: none;
	color: #4a4a4a;
	/* Body face — the cart toggle is a utility control. Explicit (<button>). */
	font-family: "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", Verdana, sans-serif;
	font-size: var(--fs-xs);
	font-weight: 700;
}

#sp-shop #sp-cart-toggle:active {
	transform: none;
	background: linear-gradient(180deg, #e3e3e3 0%, #fdfdfd 100%);
}

#sp-shop #sp-cart-count {
	color: var(--spx-link);
}

#sp-shop-body { gap: 16px; }

/* Left rail — same shape as Decorate's, which already got this treatment. */
#sp-shop-categories {
	width: 132px;
	border-right: 1px solid var(--spx-rule);
	padding-right: 8px;
	gap: 1px;
}

#sp-shop-categories .sp-left-category-btn {
	border-radius: 4px;
	padding: 6px 8px;
	font-size: var(--fs-xs);
	color: var(--spx-link);
	text-align: left;
}

#sp-shop-categories .sp-left-category-btn:hover {
	background: var(--spx-inset-bg);
}

#sp-shop-categories .sp-left-category-btn.sp-left-category-active {
	background: #e4f1fa;
	color: var(--spx-ink);
	font-weight: 800;
}

/* The one rail row with an icon — it is the one row naming a currency, and the art already
   exists (the HUD pills use it). The category rows stay text-only rather than inventing an
   icon set the port doesn't have. */
#sp-shop-categories .sp-shop-gold-btn {
	display: flex;
	align-items: center;
	gap: 5px;
}

#sp-shop-categories .sp-shop-gold-btn img {
	width: 14px;
	height: 14px;
	object-fit: contain;
}

/* Search + "Sort by" on one line above the grid. */
#sp-shop-search-row {
	display: flex;
	align-items: center;
	gap: 8px;
	margin-bottom: 10px;
}

#sp-shop #sp-shop-search {
	flex: 1 1 auto;
	min-width: 0;
	border: 1px solid var(--spx-panel-border);
	border-radius: 4px;
	background: #fff;
	color: var(--spx-ink);
	font-size: var(--fs-xs);
	padding: 6px 8px;
}

#sp-shop-sort-label {
	color: var(--spx-ink-soft);
	font-size: var(--fs-xs);
	white-space: nowrap;
}

#sp-shop-sort {
	border: 1px solid var(--spx-panel-border);
	border-radius: 4px;
	background: #fff;
	color: var(--spx-ink);
	font-family: "Source Sans 3", "Segoe UI", sans-serif;
	font-size: var(--fs-xs);
	padding: 5px 6px;
	cursor: pointer;
}

/* ---- Shop tiles ---- */

#sp-shop .sp-grid {
	grid-template-columns: repeat(auto-fill, minmax(164px, 1fr));
	gap: 10px;
}

#sp-shop .sp-tile {
	display: flex;
	flex-direction: column;
	background: var(--spx-panel);
	border: 1px solid var(--spx-panel-border);
	border-radius: 2px;
	box-shadow: none;
	padding: 7px;
	text-align: left;
}

/* The border lives on the box, not the image: several source gifs carry their own flat-grey
   background right to their edge, and a border on the image itself cuts into that art. */
#sp-shop .sp-shop-art {
	position: relative;
	border: 1px solid var(--spx-rule);
	border-radius: 2px;
	background: #fff;
	overflow: hidden;
}

/* Scoped to the art box, not `#sp-shop .sp-tile img` — the price row's currency icon is an
   <img> inside the same tile, and a bare tile-img rule outweighs a class selector on it. */
#sp-shop .sp-shop-art img {
	display: block;
	width: 100%;
	height: 112px;
	object-fit: contain;
	background: none;
	border-radius: 0;
}

/* The corner flag. A triangle made out of the gradient itself rather than clip-path, so the
   white hairline can sit along the hypotenuse — at 135deg the gradient's iso-lines run parallel
   to that diagonal and its 50% mark lands exactly on it. */
#sp-shop .sp-shop-ribbon {
	position: absolute;
	top: 0;
	left: 0;
	width: 34px;
	height: 34px;
	padding: 2px 0 0 3px;
	/* Body face — the corner flag is a single "G" glyph, not a title. */
	font-weight: 800;
	font-size: var(--fs-2xs);
	line-height: 1;
	color: #fff;
	text-shadow: 0 1px 1px rgba(0, 0, 0, 0.35);
	pointer-events: none;
}

#sp-shop .sp-shop-ribbon-gold {
	background: linear-gradient(135deg, #fbd85d 0%, #e0a512 46%, #ffffff 46%, #ffffff 50%, transparent 50%);
}

/* Bottom-right of the art box. Clicking the art opens the same modal and always did — this is
   the part that says so. */
#sp-shop .sp-shop-info {
	position: absolute;
	right: 3px;
	bottom: 3px;
	appearance: none;
	border: 1px solid #b9b9b9;
	border-radius: 3px;
	background: linear-gradient(180deg, #ffffff 0%, #e4e4e4 100%);
	color: #57646b;
	font-family: "Source Sans 3", "Segoe UI", sans-serif;
	font-weight: 700;
	font-size: var(--fs-2xs);
	line-height: 1;
	padding: 2px 5px;
	cursor: pointer;
}

#sp-shop .sp-shop-info:hover {
	background: linear-gradient(180deg, #ffffff 0%, #f2f2f2 100%);
	color: var(--spx-link);
}

#sp-shop .sp-tile .sp-tile-name {
	margin: 5px 0 4px;
	min-height: 2.2em;
	font-size: var(--fs-2xs);
	font-weight: 800;
	line-height: 1.15;
	color: var(--spx-ink);
}

#sp-shop .sp-tile .sp-shop-meta {
	margin: 0 0 7px;
	font-size: var(--fs-2xs);
	color: var(--spx-ink-soft);
}

#sp-shop .sp-shop-meta-row {
	justify-content: flex-start;
	line-height: 1.5;
	white-space: nowrap;
}

#sp-shop .sp-shop-meta-value {
	font-weight: 800;
	color: var(--spx-ink);
}

#sp-shop .sp-shop-cur-icon {
	width: 13px;
	height: 13px;
}

#sp-shop .sp-shop-stock-value { color: var(--spx-link); }

#sp-shop .sp-shop-stock-value.sp-shop-stock-low { color: var(--spx-stock-red); }

/* Quantity box beside the button, not stacked above it. `margin-top: auto` pins the row to the
   bottom so tiles with a one-line and a two-line name still line their buttons up. */
#sp-shop .sp-shop-buy-row {
	display: flex;
	align-items: center;
	gap: 6px;
	margin-top: auto;
}

/* Habitats get a stepper element with nothing in it (buildQuantityStepper returns early, locked
   to 1) — without this the empty box still draws its border beside "Add to cart". */
#sp-shop .sp-qty-stepper:empty {
	display: none;
}

#sp-shop .sp-qty-stepper {
	display: grid;
	grid-template-columns: repeat(2, 20px);
	gap: 3px;
	justify-content: center;
	margin: 0;
	padding: 3px;
	border: 1px solid var(--spx-rule);
	border-radius: 3px;
	background: #fff;
}

#sp-shop .sp-qty-input {
	grid-column: 1 / -1;
	width: 100%;
	padding: 0;
	border: none;
	border-radius: 0;
	background: none;
	font-size: var(--fs-sm);
	font-weight: 800;
	color: var(--spx-stock-red);
}

/* Beats the base `.sp-tile .sp-qty-step` (chrome.css:806) on specificity — that rule exists to
   beat `.sp-tile .sp-btn { width: 100% }` and is documented there. */
#sp-shop .sp-tile .sp-qty-step {
	width: 20px;
	height: 20px;
	font-size: var(--fs-xs);
	box-shadow: none;
}

#sp-shop .sp-tile .sp-qty-minus {
	background: linear-gradient(180deg, #ef8b85 0%, #d0453d 100%);
}

#sp-shop .sp-tile .sp-qty-plus {
	background: linear-gradient(180deg, #93d38c 0%, #4aa544 100%);
}

/* Small, left-aligned, grey — nothing like the green gel pill the shared rule gives it. */
#sp-shop .sp-tile .sp-btn {
	width: auto;
	flex: 1 1 auto;
	padding: 6px 8px;
	border: 1px solid #b9b9b9;
	border-radius: 4px;
	background: linear-gradient(180deg, #fdfdfd 0%, #e3e3e3 100%);
	box-shadow: none;
	color: #4a4a4a;
	font-size: var(--fs-2xs);
	font-weight: 700;
}

#sp-shop .sp-tile .sp-btn:active {
	transform: none;
	background: linear-gradient(180deg, #e3e3e3 0%, #fdfdfd 100%);
	box-shadow: none;
}

/* The cart glyph is a background layer, not a child element: addToCart() swaps this button's
   label with textContent, which would delete an <img>/<svg> child on the first click. */
#sp-shop .sp-tile .sp-btn-cart {
	padding-left: 22px;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%234a4a4a' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.6h2.1l1.7 7.6h7l1.6-5.2H4.2'/%3E%3Ccircle cx='6.3' cy='13' r='1.3'/%3E%3Ccircle cx='11.6' cy='13' r='1.3'/%3E%3C/svg%3E"), linear-gradient(180deg, #fdfdfd 0%, #e3e3e3 100%);
	background-repeat: no-repeat, repeat;
	background-position: 6px center, 0 0;
	background-size: 12px 12px, auto;
}

/* ---- Shop cart panel ----
   Same legacy card as the tiles, sitting as the third column of #sp-shop-body. */

#sp-shop-cart {
	width: 250px;
	flex: none;
	align-self: flex-start;
	border: 1px solid var(--spx-panel-border);
	border-radius: var(--spx-panel-radius);
	background: var(--spx-panel);
	padding: 10px 12px 12px;
}

#sp-shop-cart[hidden] {
	display: none;
}

#sp-shop-cart-title {
	margin: 0 0 6px;
	font-family: "Porkys", "Trebuchet MS", sans-serif;
	font-weight: 800;
	font-size: var(--fs-md);
	color: var(--spx-ink);
	border-bottom: 1px solid var(--spx-rule);
	padding-bottom: 6px;
}

/* Two rows in a narrow rail rather than one long flex line: thumbnail beside name+price, then
   the quantity controls under them. As one row the name wrapped to a word per line. */
#sp-shop-cart .sp-cart-line {
	display: grid;
	grid-template-columns: 40px 1fr;
	gap: 4px 8px;
	align-items: start;
	border-bottom: 1px solid var(--spx-rule);
}

#sp-shop-cart .sp-cart-line-main,
#sp-shop-cart .sp-cart-line-controls {
	grid-column: 2;
}

#sp-shop-cart .sp-cart-line-price {
	display: flex;
	align-items: center;
	gap: 3px;
}

#sp-shop-cart .sp-cart-line .sp-btn-remove {
	padding: 3px 8px;
	font-size: var(--fs-2xs);
	border-radius: 4px;
	box-shadow: none;
	margin-left: auto;
}

#sp-shop-cart .sp-cart-line-name { color: var(--spx-ink); }

#sp-shop-cart .sp-cart-line-price { color: var(--spx-ink-soft); }

#sp-shop-cart .sp-cart-line-thumb {
	border: 1px solid var(--spx-rule);
	border-radius: 2px;
	background: #fff;
}

#sp-shop-cart .sp-cart-line-error { color: var(--spx-stock-red); }

#sp-shop-cart .sp-cart-line-failed { background: #fdeceb; }

#sp-shop-cart .sp-cart-total-row {
	color: var(--spx-ink);
	font-size: var(--fs-sm);
}

#sp-shop-cart .sp-cart-total-label { color: var(--spx-ink-soft); font-weight: 400; }

#sp-shop-cart #sp-cart-failures { color: var(--spx-stock-red); }

/* The one green button left in this panel — checkout is the commit, and the legacy shop's own
   checkout was the loudest control on the page too. */
#sp-shop-cart #sp-cart-checkout {
	font-size: var(--fs-sm);
	padding: 8px 0;
	border-radius: 4px;
	box-shadow: none;
}

#sp-shop-cart .sp-cart-qty-step {
	box-shadow: none;
}

/* The grid is the flexible column; the rail and the cart keep their widths. */
#sp-shop-main {
	flex: 1 1 auto;
	min-width: 0;
}

/* The "Added ✓" acknowledgement keeps its green, but in this skin's flat register. */
#sp-shop .sp-tile .sp-btn.sp-added {
	background: linear-gradient(180deg, #93d38c 0%, #4aa544 100%);
	border-color: #3f8c3a;
	color: #fff;
	padding-left: 8px;
}

/* ---- friend ladder (friendladder.swf) ---- */

#sp-page #sp-friend-bar {
	background: var(--spx-panel);
	border: 1px solid var(--spx-panel-border);
	border-radius: var(--spx-panel-radius);
	box-shadow: var(--e-card);
	margin-top: 10px;
	padding: 10px 12px;
	gap: 10px;
	align-items: stretch;
}

/* The recently-visited filter (a legacy DropDown.as widget originally) — kept, but shrunk so it
   doesn't dominate the strip the way a full-width <select> did. */
#sp-page #sp-friend-bar-filter {
	align-self: center;
	max-width: 96px;
	padding: 4px 6px;
	font-size: var(--fs-2xs);
	border-radius: 5px;
	border-color: var(--spx-panel-border);
	background: var(--spx-inset-bg);
}

#sp-page #sp-friend-bar-see-all {
	align-self: flex-start;
	color: var(--spx-link);
	font-weight: 700;
}

/* Stacked orange arrow columns, one per side. */
.sp-friend-bar-nav-col {
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: 4px;
	flex-shrink: 0;
}

#sp-page .sp-friend-bar-nav {
	width: 26px;
	height: 22px;
	border-radius: 5px;
	border: 1px solid #d99b1f;
	background: linear-gradient(180deg, var(--sp-gold-top) 0%, var(--sp-gold-bottom) 100%);
	color: #6b4a00;
	font-size: var(--fs-2xs);
	font-weight: 800;
	line-height: 1;
	box-shadow: 0 1px 0 #d99b1f;
}

#sp-page .sp-friend-bar-nav:disabled {
	opacity: 0.4;
}

/* Setting overflow-x on the track forces overflow-y to compute to `auto` too, which would clip
   the selected card's 6px lift and the level badge that hangs past the card's bottom edge. Pad
   the track on both axes to give them room inside the scroll box. */
#sp-page #sp-friend-bar-track {
	padding-top: 10px;
	padding-bottom: 14px;
}

/* Cards: flat white with a hairline border, the name on a strip across the top, the dressed-pet
   thumbnail framed below it, and the round level badge hanging off the bottom-left corner. */
#sp-page .sp-friend-bar-card {
	background: var(--spx-panel);
	border: 1px solid var(--spx-panel-border);
	border-radius: 6px;
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
	height: 108px;
	padding: 0 4px 8px;
	gap: 3px;
	justify-content: flex-start;
	overflow: visible;
	transition: transform 0.12s ease;
}

#sp-page .sp-friend-bar-name {
	order: -1;
	width: calc(100% + 8px);
	margin: 0 -4px 3px;
	padding: 4px 2px;
	border-bottom: 1px solid var(--spx-rule);
	border-radius: 6px 6px 0 0;
	background: var(--spx-inset-bg);
	font-size: var(--fs-2xs);
	font-weight: 800;
	color: var(--spx-ink);
}

#sp-page .sp-friend-bar-avatar-wrap {
	width: 54px;
	height: 54px;
	margin-top: 2px;
}

#sp-page .sp-friend-bar-card .sp-tile-avatar {
	width: 54px;
	height: 54px;
	border: 1px solid var(--spx-rule);
	border-radius: 4px;
	background: #fff;
}

#sp-page .sp-friend-bar-level-badge {
	left: -7px;
	bottom: -12px;
	width: 26px;
	height: 26px;
}

#sp-page .sp-friend-bar-level-badge span {
	font-size: var(--fs-2xs);
}

/* Already-visited — a muted grey card, not the light-blue default. */
#sp-page .sp-friend-bar-card.sp-visited {
	background: #f1f1ec;
}

/* The card for whichever pet is on screen — your own while home, the friend you're visiting
   otherwise (chrome/friendBar.js's refreshFriendBarSelection). The legacy ladder drew this one
   gold and lifted slightly proud of the row. Listed after .sp-visited / .sp-friend-bar-me so it
   wins the background at equal specificity when a card is both. */
#sp-page .sp-friend-bar-card.sp-friend-bar-selected {
	background: linear-gradient(180deg, var(--sp-gold-top) 0%, var(--sp-gold-bottom) 100%);
	border: 2px solid var(--sp-gold-border);
	box-shadow: 0 0 0 1px var(--sp-gold-top), 0 3px 6px rgba(0, 0, 0, 0.18);
	transform: translateY(-6px);
	z-index: 1;
}

/* Keep the name strip legible over the gold gradient. */
#sp-page .sp-friend-bar-card.sp-friend-bar-selected .sp-friend-bar-name {
	background: rgba(255, 255, 255, 0.72);
	border-bottom-color: var(--sp-gold-border);
}

/* Empty slots: grey card, big circled "+", embossed "ADD FRIENDS!" — except the last one, which
   is the gold call-to-action that caps the strip. */
#sp-page .sp-friend-bar-card.sp-friend-bar-blank {
	background: #ededea;
	border-color: #d9d9d0;
	color: #9a9a8c;
	justify-content: center;
	gap: 5px;
}

.sp-friend-bar-blank-plus {
	width: 30px;
	height: 30px;
	border-radius: 50%;
	border: 2px solid currentColor;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: var(--fs-xl);
	font-weight: 700;
	line-height: 1;
}

.sp-friend-bar-blank-label {
	font-size: var(--fs-2xs);
	font-weight: 800;
	text-transform: uppercase;
	letter-spacing: 0.03em;
}

#sp-page .sp-friend-bar-card.sp-friend-bar-blank-cta {
	background: linear-gradient(180deg, #ffd266 0%, #ffde17 45%, #ffb617 100%);
	border-color: #e0a200;
	color: #6b4a00;
}

/* The incoming-request panel in the legacy skin: undo the fixed card height and the framed
   54px thumbnail the ladder cards get, so the wider request layout (chrome/friendBar.js's
   requestCard) lays out from its own rules instead of the pet-card ones. */
#sp-page .sp-friend-bar-card.sp-friend-bar-request {
	height: auto;
	border-color: #e0a200;
	gap: 8px;
}

#sp-page .sp-friend-bar-card.sp-friend-bar-request .sp-tile-avatar {
	width: 40px;
	height: 40px;
	border: 0;
	border-radius: 6px;
}

/* ---- bottom row: match the tighter panel radius ---- */

#sp-page #sp-bottom-row > #sp-activity,
#sp-page #sp-scrapbook {
	border-radius: var(--spx-panel-radius);
}

/* ---- Daily Goals panel (chrome/dailyGoals.js) ---- */

/* Goals is a standalone destination like Shop, but it's a single narrow column — don't leave
   #sp-card stretched to full width behind it with ~40% empty (the review's "massive dead
   space"). Centre a comfortable reading column instead. */
#sp-page:has(#sp-daily-goals:not([hidden])) #sp-card {
	max-width: 640px;
	margin-left: auto;
	margin-right: auto;
}

#sp-daily-goals {
	padding: 4px 4px 8px;
}

#sp-daily-goals .sp-panel-intro {
	margin: 0 0 16px;
	font-size: var(--fs-sm);
	line-height: 1.5;
	color: var(--sp-text-muted);
}

.sp-goal-list {
	list-style: none;
	margin: 0 0 16px;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}

.sp-goal {
	display: flex;
	align-items: flex-start;
	gap: 12px;
	padding: 14px 16px;
	background: var(--sp-card-bg);
	border: 1px solid var(--sp-line);
	border-radius: var(--r-lg);
	transition: border-color var(--t-fast), background-color var(--t-fast);
}

.sp-goal.is-done {
	border-color: var(--sp-green-bottom);
	background: color-mix(in oklch, var(--sp-green-top) 12%, var(--sp-card-bg));
}

.sp-goal-mark {
	flex: 0 0 22px;
	width: 22px;
	height: 22px;
	margin-top: 1px;
	border-radius: 999px;
	border: 2px solid var(--sp-line);
	font-size: var(--fs-sm);
	font-weight: 800;
	line-height: 18px;
	text-align: center;
	color: #fff;
}

.sp-goal.is-done .sp-goal-mark {
	border-color: var(--sp-green-bottom);
	background: linear-gradient(180deg, var(--sp-green-top) 0%, var(--sp-green-bottom) 100%);
}

.sp-goal-body {
	flex: 1 1 auto;
	min-width: 0;
}

.sp-goal-label {
	font-size: var(--fs-lg);
	font-weight: 800;
	color: var(--sp-text-dark);
}

.sp-goal-blurb {
	margin-top: 3px;
	font-size: var(--fs-sm);
	color: var(--sp-text-muted);
}

.sp-goal-bar {
	margin-top: 10px;
	height: 8px;
	border-radius: 999px;
	background: var(--sp-line);
	overflow: hidden;
}

.sp-goal-bar span {
	display: block;
	height: 100%;
	border-radius: inherit;
	background: linear-gradient(90deg, var(--sp-green-top) 0%, var(--sp-green-bottom) 100%);
}

.sp-goal-count {
	flex: 0 0 auto;
	align-self: center;
	/* Body face, not Porky's — this is the "progress / target" number pair. */
	font-size: var(--fs-sm);
	font-weight: 700;
	color: var(--sp-text-mid);
	white-space: nowrap;
}

.sp-daily-goals-bonuses {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

/* The two payoff lines — "all three today" and the weekly streak. They were faint dashed pills
   the same weight as body text; this is the reward for the whole loop, so give it real presence:
   a gold banner with a coin-stack glyph, turning solid green on completion. */
.sp-goal-bonus {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 12px 16px;
	border-radius: var(--r-lg);
	border: 1px solid var(--sp-gold-border);
	background: linear-gradient(180deg,
		color-mix(in oklch, var(--sp-gold-top) 26%, var(--sp-card-bg)) 0%,
		color-mix(in oklch, var(--sp-gold-top) 14%, var(--sp-card-bg)) 100%);
	font-size: var(--fs-md);
	font-weight: 800;
	color: var(--sp-text-dark);
}

.sp-goal-bonus::before {
	content: "🪙";
	flex: 0 0 auto;
	font-size: var(--fs-xl);
	line-height: 1;
}

.sp-goal-bonus.is-done {
	border-color: var(--sp-green-bottom);
	background: linear-gradient(180deg,
		color-mix(in oklch, var(--sp-green-top) 26%, var(--sp-card-bg)) 0%,
		color-mix(in oklch, var(--sp-green-top) 14%, var(--sp-card-bg)) 100%);
}

.sp-goal-bonus.is-done::before {
	content: "✅";
}
