/*
 * Tenant public profile (/@handle) — Vectorheart-locked, self-contained.
 *
 * Palette mirrors shared/vectorheart.css [data-theme="vector"]. Single magenta
 * accent (#FF0066) reserved for the share + CTA + hover only; AA-safe dark ink
 * (#0A0A0A) on any magenta fill. Zero em-dashes in any content (hyphens only).
 * Mobile-first; min-height:100dvh; banner aspect held to avoid CLS.
 */

:root {
	--bp-void: #000000;
	--bp-canvas: #0A0A0A;
	--bp-elevated: #141414;
	--bp-elevated-2: #1C1C1C;
	--bp-recess: #050505;
	--bp-ink: #FAFAFA;
	--bp-ink-regular: #D4D4D4;
	--bp-ink-muted: #8C8C8C;
	--bp-ink-dim: #5A5A5A;
	--bp-rule: #2A2A2A;
	--bp-rule-hairline: #1F1F1F;
	--bp-magenta: #FF0066;
	--bp-magenta-text: #FF3380;
	--bp-cyan: #00E5FF;
	--bp-yellow: #EED202;
	/* Vectorheart is zero-radius by default (SSOT §5): square is the brand.
	   Circular things (avatar, spinner, live dots) keep their explicit 50%/999px
	   locally. Was 12px / 8px pre-teardown (2026-07-04 Full-Voltage pass). */
	--bp-radius: 0;
	--bp-radius-sm: 0;
	--bp-maxw: 640px;
	--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
}

/* [hidden] must win. Author component rules below (.bp-modal{display:flex},
 * .bp-panel{display:...}, .bp-toast, .bp-bio-toggle) otherwise OVERRIDE the UA
 * `[hidden]{display:none}` (author origin beats UA), which left the report/contact
 * modals + the inactive tab panel + the toast rendered on load and made the JS
 * `el.hidden = true` toggle a visual no-op (the "report modal won't close" bug).
 * Forcing [hidden] -> display:none here restores correct show/hide for all of them. */
[hidden] { display: none !important; }

* { box-sizing: border-box; }

html, body {
	margin: 0;
	padding: 0;
	background: var(--bp-canvas);
	color: var(--bp-ink);
	font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
	font-size: 16px;
	line-height: 1.5;
	min-height: 100dvh;
	-webkit-font-smoothing: antialiased;
}

a { color: var(--bp-ink); text-decoration: none; }

.bp-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	background: var(--bp-magenta);
	color: var(--bp-void);
	padding: 10px 16px;
	font-weight: 700;
	z-index: 100;
	border-radius: 0 0 var(--bp-radius-sm) 0;
}
.bp-skip-link:focus { left: 0; }

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

.bp-profile {
	max-width: var(--bp-maxw);
	margin: 0 auto;
	padding-bottom: 48px;
}

/* ── Header ─────────────────────────────────────────────────────────────── */
.bp-header { position: relative; }

/* Vectorheart telemetry ID stamp (handle) — the .vec-telemetry-corner base
 * (Space Mono, muted, absolute top-right, z-index 50) is supplied by
 * shared/vectorheart.css; this only uppercases it (vec doesn't set case).
 * The flat dark chip (a fill, not a shadow — SSOT-legal) keeps the muted mono
 * legible over a raw user banner: the .bp-banner-scrim is transparent 0–40%
 * (the top), so this stamp sits over the un-darkened image. rgba(0,0,0,0.55)
 * matches the scrim's bottom stop for visual consistency. Scoped to
 * .bp-telemetry ONLY — the generic .vec-telemetry-corner sits over solid
 * --bg-primary on /t/:slug and needs no backdrop. */
.bp-telemetry { text-transform: uppercase; background: rgba(0, 0, 0, 0.55); padding: 2px 6px; }

.bp-banner {
	position: relative;
	width: 100%;
	aspect-ratio: 3 / 1;
	overflow: hidden;
	background: var(--bp-recess);
}
/* Imageless fallback: a flat near-black Vectorheart field with a single magenta
 * hairline + telemetry-corner tick (bottom-right, clear of the centered avatar).
 * Flat by brand law — no glass, no grain, no soft shadow. */
.bp-banner--fallback {
	background: var(--bp-recess);
}
.bp-banner--fallback::before {
	content: "";
	position: absolute;
	z-index: 1;
	right: 18px;
	bottom: 22px;
	width: 64px;
	height: 1px;
	background: var(--bp-magenta);
	opacity: 0.55;
}
.bp-banner--fallback::after {
	content: "";
	position: absolute;
	z-index: 1;
	right: 18px;
	bottom: 14px;
	width: 14px;
	height: 14px;
	border-right: 1.5px solid var(--bp-magenta);
	border-bottom: 1.5px solid var(--bp-magenta);
}
.bp-banner-img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	display: block;
}
.bp-banner-scrim {
	position: absolute;
	inset: 0;
	background: linear-gradient(to bottom, rgba(0,0,0,0) 40%, rgba(0,0,0,0.55) 100%);
}

.bp-identity {
	text-align: center;
	padding: 0 20px;
	margin-top: -56px;
	position: relative;
}

.bp-avatar {
	width: 96px;
	height: 96px;
	border-radius: 50%;
	margin: 0 auto 12px;
	overflow: hidden;
	background: var(--bp-elevated);
	border: 4px solid var(--bp-canvas);
	display: flex;
	align-items: center;
	justify-content: center;
}
.bp-avatar--fallback {
	background: var(--bp-magenta);
}
.bp-avatar-img { width: 100%; height: 100%; object-fit: cover; }
.bp-monogram {
	font-family: 'Oswald', sans-serif;
	font-size: 44px;
	font-weight: 700;
	color: var(--bp-void);
	line-height: 1;
}

.bp-handle {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin: 0;
	font-family: 'Oswald', 'Inter', system-ui, sans-serif;
	font-size: 31px; /* Full-Voltage nameplate — scaled up (was 27px) */
	font-weight: 700;
	letter-spacing: -0.01em;
	text-wrap: balance;
	color: var(--bp-ink);
	word-break: break-word;
}
.bp-verified { color: var(--bp-cyan); display: inline-flex; }

.bp-realname {
	margin: 4px 0 0;
	font-size: 15px;
	font-weight: 500;
	color: var(--bp-ink-muted);
}

.bp-location {
	display: inline-flex;
	align-items: center;
	gap: 5px;
	margin: 8px 0 0;
	font-size: 14px;
	color: var(--bp-ink-muted);
}
.bp-location svg { flex: none; color: var(--bp-ink-dim); }

.bp-bio-wrap { margin: 14px auto 0; max-width: 60ch; }
.bp-bio {
	margin: 0;
	font-size: 15px;
	line-height: 1.55;
	color: var(--bp-ink-regular);
	white-space: pre-line;
	text-wrap: pretty;
	text-align: center;
	/* white-space:pre-line only wraps at whitespace, so a long unbroken token (a
	 * pasted URL, a run of glued text) would hard-clip mid-character at the box
	 * edge. Break inside the token as a last resort (NIT #25). */
	overflow-wrap: anywhere;
	word-break: break-word;
	display: -webkit-box;
	-webkit-line-clamp: 4;
	-webkit-box-orient: vertical;
	overflow: hidden;
}
.bp-bio.is-expanded {
	-webkit-line-clamp: unset;
	display: block;
	text-align: left;
}
.bp-bio-toggle {
	margin-top: 6px;
	background: none;
	border: none;
	color: var(--bp-magenta-text);
	font-size: 14px;
	font-weight: 600;
	cursor: pointer;
	padding: 4px 8px;
}

.bp-socials {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 10px;
	margin: 18px 0 0;
}
.bp-social {
	width: 44px;
	height: 44px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--bp-radius-sm);
	background: var(--bp-elevated);
	border: 1px solid var(--bp-rule);
	color: var(--bp-ink-regular);
	cursor: pointer;
	transition: color .15s ease, border-color .15s ease, background .15s ease, transform 140ms var(--ease-out);
}
.bp-social:hover,
.bp-social:focus-visible {
	color: var(--bp-magenta);
	border-color: var(--bp-magenta);
	background: var(--bp-elevated-2);
}
.bp-social:active { transform: scale(0.97); }

/* Contact organizer — the single primary action (the one magenta fill on the
 * page). Visitor-centric: it owns the magenta the growth CTA used to carry. */
.bp-contact {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 44px;
	margin-top: 18px;
	padding: 0 22px;
	background: var(--bp-magenta);
	color: var(--bp-void);
	border: none;
	border-radius: var(--bp-radius-sm);
	font-size: 15px;
	font-weight: 700;
	white-space: nowrap;
	cursor: pointer;
	transition: background .15s ease, transform 140ms var(--ease-out);
}
.bp-contact:hover { background: var(--bp-magenta-text); }
.bp-contact:active { transform: scale(0.97); }

/* ── Tabs ───────────────────────────────────────────────────────────────── */
.bp-tabs-section { margin-top: 28px; padding: 0 16px; }

.bp-tablist {
	display: flex;
	gap: 4px;
	border-bottom: 1px solid var(--bp-rule);
	margin-bottom: 12px;
}
.bp-tab {
	flex: 1;
	background: none;
	border: none;
	border-bottom: 2px solid transparent;
	color: var(--bp-ink-muted);
	font-size: 15px;
	font-weight: 600;
	/* Anchors (NIT #18) don't center their text the way a UA-styled <button> does,
	 * so center explicitly — harmless when this rule applied to buttons before. */
	text-align: center;
	padding: 12px 8px;
	cursor: pointer;
	min-height: 44px;
	transition: color .15s ease, border-color .15s ease, transform 140ms var(--ease-out);
}
.bp-tab[aria-selected="true"] {
	color: var(--bp-ink);
	border-bottom-color: var(--bp-magenta);
}
.bp-tab:hover { color: var(--bp-ink-regular); }
.bp-tab:active { transform: scale(0.97); }

/* Tab panels crossfade in on reveal (the [hidden] -> display flip restarts the
 * animation each switch). Movement is neutralized by the reduced-motion block. */
.bp-panel { animation: bp-panel-in 150ms var(--ease-out); }

.bp-rows { list-style: none; margin: 0; padding: 0; }
.bp-row {
	border-bottom: 1px solid var(--bp-rule-hairline);
	display: flex;
	align-items: center;
	gap: 8px;
}
.bp-row-link {
	flex: 1 1 auto;
	min-width: 0;
	display: block;
	padding: 14px 12px;
	border-radius: var(--bp-radius-sm);
	transition: background .12s ease, transform 140ms var(--ease-out);
}
.bp-row-link:hover,
.bp-row-link:focus-visible { background: var(--bp-elevated); }
.bp-row-link:active { transform: scale(0.99); }

/* Per-open-row Register: a magenta-TEXT link (not a fill) so the Contact button
 * stays the single most-prominent magenta. Sibling of the row link, never nested. */
.bp-row-register {
	flex: none;
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 8px 12px;
	color: var(--bp-magenta-text);
	font-size: 14px;
	font-weight: 600;
	white-space: nowrap;
	transition: color .15s ease, transform 140ms var(--ease-out);
}
.bp-row-register:hover { color: var(--bp-magenta); }
.bp-row-register:active { transform: scale(0.97); }

.bp-row-line1 {
	display: flex;
	align-items: center;
	gap: 10px;
	justify-content: space-between;
}
.bp-row-name {
	font-size: 16px;
	font-weight: 600;
	color: var(--bp-ink);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	flex: 0 1 auto;
	min-width: 0;
}
.bp-row-line2 {
	display: block;
	margin-top: 4px;
	font-size: 13.5px;
	color: var(--bp-ink-muted);
	font-variant-numeric: tabular-nums;
}
.bp-row-cap {
	display: block;
	margin-top: 3px;
	font-size: 12.5px;
	color: var(--bp-ink-muted);
	font-variant-numeric: tabular-nums;
}
.bp-dot { margin: 0 6px; color: var(--bp-ink-dim); }

.bp-badge {
	flex: none;
	font-family: var(--vec-font-mono-aesthetic, 'Space Mono', monospace);
	font-size: 11px;
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: .08em;
	padding: 3px 8px;
	border-radius: 0; /* Vectorheart square status chip (was 999px) — SSOT §5/§9 */
	border: 1px solid var(--bp-rule);
	color: var(--bp-ink-regular);
	white-space: nowrap;
}
/* Bracketed status vocabulary ([OPEN] [LIVE] [FINAL]) — pure visual framing via
 * pseudo-elements, so the server-provided badge text stays unchanged (copy lane).
 * a11y note (accepted trade-off): modern browsers expose this generated "[" / "]"
 * to the accessibility tree, so a screen reader may announce "[ Registration open ]".
 * Accepted as minor SR noise consistent with the HUD idiom — there is no reliable
 * cross-browser way to hide pseudo-content from SRs, so do NOT try to suppress it. */
.bp-badge::before { content: "["; }
.bp-badge::after { content: "]"; }
/* Roster hue divergence is DELIBERATE — do not "align" [LIVE] to cyan.
 * The SSOT chevron vocabulary (shared/vectorheart.css: .vec-chevron-pill.live =
 * signal-cyan, .final = signal-magenta) — mirrored on /t/:slug's single state pill —
 * is a ONE-state-at-a-time surface, so its live=cyan is unambiguous there.
 * This roster is different: it lists MANY tournaments at once, and [OPEN] and [LIVE]
 * routinely co-occur in the "Current & Upcoming" tab (computeBadge in
 * routes/public-profile.js emits kind:'open' for registration-open and kind:'live'
 * for underway). Distinct hues (cyan [OPEN] vs magenta [LIVE]) disambiguate them at a
 * glance down the list. Recoloring [LIVE] → cyan to match the chevron SSOT would make
 * it collide with the already-cyan [OPEN] below — a regression, not a fix. Every badge
 * also carries its label text, so color-never-alone holds regardless. */
.bp-badge--open { color: var(--bp-cyan); border-color: rgba(0,229,255,.4); }
.bp-badge--live { color: var(--bp-magenta-text); border-color: rgba(255,0,102,.45); }
.bp-badge--full { color: var(--bp-yellow); border-color: rgba(238,210,2,.4); }
.bp-badge--complete { color: var(--bp-ink-muted); border-color: var(--bp-rule); }
/* awaiting_review — play finished, results pending. Grouped visually with the
 * other done-ish state (muted), distinguished from "Completed" by its label. */
.bp-badge--review { color: var(--bp-ink-muted); border-color: var(--bp-rule); }
.bp-badge--upcoming { color: var(--bp-ink-regular); border-color: var(--bp-rule); }

.bp-empty {
	color: var(--bp-ink-muted);
	font-size: 15px;
	text-align: center;
	text-wrap: pretty;
	padding: 32px 12px;
}

.bp-loadmore {
	display: block;
	text-align: center;
	margin: 16px auto 0;
	padding: 10px;
	color: var(--bp-magenta-text);
	font-weight: 600;
	font-size: 14px;
}
.bp-loadmore:hover { color: var(--bp-magenta); }

/* ── Footer ─────────────────────────────────────────────────────────────── */
.bp-footer {
	margin-top: 36px;
	padding: 24px 16px 0;
	border-top: 1px solid var(--bp-rule-hairline);
	text-align: center;
}
.bp-footer-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	justify-content: center;
	margin-bottom: 18px;
}
.bp-share {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: 44px;
	padding: 0 16px;
	background: var(--bp-elevated);
	border: 1px solid var(--bp-rule);
	border-radius: var(--bp-radius-sm);
	color: var(--bp-ink);
	font-size: 14px;
	font-weight: 600;
	cursor: pointer;
	transition: border-color .15s ease, color .15s ease, transform 140ms var(--ease-out);
}
.bp-share:hover { border-color: var(--bp-magenta); color: var(--bp-magenta); }
.bp-share:active { transform: scale(0.97); }
/* Growth CTA demoted to a neutral outline secondary (no magenta fill) — the
 * magenta now belongs to the visitor-centric Contact button up top. */
.bp-cta {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: 0 18px;
	background: var(--bp-elevated);
	border: 1px solid var(--bp-rule);
	color: var(--bp-ink);
	border-radius: var(--bp-radius-sm);
	font-size: 14px;
	font-weight: 600;
	transition: border-color .15s ease, color .15s ease, transform 140ms var(--ease-out);
}
.bp-cta:hover { border-color: var(--bp-magenta); color: var(--bp-magenta); }
.bp-cta:active { transform: scale(0.97); }

.bp-stats {
	color: var(--bp-ink-muted);
	font-size: 13px;
	margin: 0 0 12px;
	font-variant-numeric: tabular-nums;
}
.bp-poweredby {
	color: var(--bp-ink-muted);
	font-size: 12.5px;
	margin: 0 0 8px;
}
/* Legal/report links on their own line, separated by spacing (no dot chain).
 * All footer text is >= AA on dark; --bp-ink-dim carries no readable text. */
.bp-footer-meta {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: 16px;
	color: var(--bp-ink-muted);
	font-size: 12.5px;
	margin: 0;
}
.bp-footer-meta a { color: var(--bp-ink-muted); }
.bp-footer-meta a:hover { color: var(--bp-ink-regular); }
.bp-report-link {
	background: none;
	border: none;
	color: var(--bp-ink-muted);
	font-size: 12.5px;
	cursor: pointer;
	padding: 0;
}
.bp-report-link:hover { color: var(--bp-ink-regular); text-decoration: underline; }

.bp-toast {
	position: fixed;
	left: 50%;
	bottom: 24px;
	transform: translateX(-50%);
	/* Below the modal (200) so an open dialog covers a lingering toast; above the
	 * skip link (100) and page content. The toast lives inside #profileMain, which
	 * makes no stacking context, so it shares the root context with the modal. */
	z-index: 150;
	max-width: 280px;
	background: var(--bp-elevated-2);
	border: 1px solid var(--bp-rule);
	border-radius: var(--bp-radius-sm);
	color: var(--bp-ink);
	font-size: 13px;
	padding: 10px 14px;
	/* The toast doubles as the page's aria-live=polite region. It must stay in the
	 * DOM + a11y tree at ALL times so a textContent change announces; the hidden
	 * state is opacity + pointer-events only. It is NOT toggled via [hidden] /
	 * display:none / visibility:hidden — any of those drop it from the a11y tree,
	 * so the old mutate-then-unhide could be missed by some SR/browser combos
	 * (NIT #20). profile.js adds/removes .is-visible instead. */
	opacity: 0;
	pointer-events: none;
	transition: opacity 200ms var(--ease-out);
}
.bp-toast.is-visible {
	opacity: 1;
	animation: bp-toast-in 200ms var(--ease-out);
}

/* ── Modals ─────────────────────────────────────────────────────────────── */
.bp-modal {
	position: fixed;
	inset: 0;
	background: rgba(0,0,0,0.7);
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 16px;
	z-index: 200;
	animation: bp-backdrop-in 180ms var(--ease-out);
}
.bp-modal-card {
	position: relative;
	width: 100%;
	max-width: 440px;
	background: var(--bp-elevated);
	border: 1px solid var(--bp-rule);
	border-radius: var(--bp-radius);
	padding: 24px;
	max-height: 90dvh;
	overflow-y: auto;
	transform-origin: center;
	animation: bp-modal-in 200ms var(--ease-out);
}
.bp-modal-close {
	position: absolute;
	top: 8px;
	right: 8px;
	width: 44px;
	height: 44px;
	background: none;
	border: none;
	color: var(--bp-ink-muted);
	font-size: 28px;
	line-height: 1;
	cursor: pointer;
}
.bp-modal-close:hover { color: var(--bp-ink); }
.bp-modal-title { margin: 0 0 16px; font-size: 20px; font-weight: 700; }

.bp-form { display: flex; flex-direction: column; gap: 14px; }
.bp-field { display: flex; flex-direction: column; gap: 6px; }
.bp-field > span { font-size: 13px; font-weight: 600; color: var(--bp-ink-muted); }
.bp-field input,
.bp-field textarea {
	background: var(--bp-recess);
	border: 1px solid var(--bp-rule);
	border-radius: var(--bp-radius-sm);
	color: var(--bp-ink);
	font: inherit;
	font-size: 15px;
	padding: 10px 12px;
	width: 100%;
}
/* Keep the magenta border shift AND the global 3px :focus-visible ring (never a
 * color-only focus indicator — the old outline:none killed the ring). */
.bp-field input:focus,
.bp-field textarea:focus { border-color: var(--bp-magenta); }
.bp-field input::placeholder,
.bp-field textarea::placeholder { color: var(--bp-ink-muted); }
.bp-field textarea { resize: vertical; }

/* Honeypot: visually + programmatically hidden, still in the DOM for bots. */
.bp-hp {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

.bp-submit {
	min-height: 46px;
	background: var(--bp-magenta);
	color: var(--bp-void);
	border: none;
	border-radius: var(--bp-radius-sm);
	font-size: 15px;
	font-weight: 700;
	cursor: pointer;
	transition: background .15s ease, transform 140ms var(--ease-out);
}
.bp-submit:hover { background: var(--bp-magenta-text); }
.bp-submit:active { transform: scale(0.97); }
.bp-submit:disabled { opacity: .55; cursor: not-allowed; }
.bp-form-msg { margin: 0; font-size: 13.5px; min-height: 18px; }
.bp-form-msg.is-error { color: var(--bp-magenta-text); }
.bp-form-msg.is-ok { color: var(--bp-cyan); }

/* Inline per-field validation error (Phase 3) — reuses the brand magenta token. */
.bp-field-error { font-size: 12.5px; font-weight: 600; color: var(--bp-magenta-text); }

/* Submit spinner (Phase 3) — shown while the request is in flight; the button
   label is hidden and a spinner rides on the magenta fill (white indicator, not
   a brand-palette change). */
.bp-submit.is-loading { position: relative; color: transparent; pointer-events: none; }
.bp-submit.is-loading::after {
	content: "";
	position: absolute;
	top: 50%; left: 50%;
	width: 16px; height: 16px;
	margin: -8px 0 0 -8px;
	border: 2px solid rgba(255, 255, 255, .55);
	border-top-color: #fff;
	border-radius: 50%;
	animation: bp-spin .6s linear infinite;
}
@keyframes bp-spin { to { transform: rotate(360deg); } }

/* ── Device-aware width. data-device-class is stamped on <html> by the per-route
 *    createDeviceContext (server, from UA) and corrected client-side on resize
 *    (mobile<768, tablet<1200, desktop<1600, tv>=1600). The hooks are preserved,
 *    but a link-in-bio reads best in a contained column — tablet caps at 720px,
 *    desktop/tv at 1080px rather than stretching to full viewport. ───────────── */
[data-device-class="tablet"] .bp-profile { max-width: 720px; }
[data-device-class="desktop"] .bp-profile,
[data-device-class="tv"]      .bp-profile { max-width: 1080px; }
/* Modest identity padding on wide screens; the column stays capped at 1080px. */
[data-device-class="desktop"] .bp-identity,
[data-device-class="tv"]      .bp-identity { padding: 0 32px; }

/* Wide-column composition (desktop/tv only). At 1080px the two flex:1 tabs would
 * stretch to ~520px each and every row would strand its name far from its badge.
 * Center the tab strip at content width, and lay the tournament rows out 2-up so
 * the column reads full, not sparse. Mobile/tablet keep the full-width tabs +
 * single-column list. */
[data-device-class="desktop"] .bp-tablist,
[data-device-class="tv"]      .bp-tablist { justify-content: center; gap: 8px; }
[data-device-class="desktop"] .bp-tab,
[data-device-class="tv"]      .bp-tab { flex: 0 0 auto; padding: 12px 36px; }
[data-device-class="desktop"] .bp-rows,
[data-device-class="tv"]      .bp-rows {
	display: grid;
	grid-template-columns: 1fr 1fr;
	column-gap: 24px;
	align-items: start;
}

@media (min-width: 600px) {
	.bp-avatar { width: 120px; height: 120px; }
	.bp-monogram { font-size: 54px; }
	.bp-identity { margin-top: -64px; }
	.bp-handle { font-size: 38px; } /* Full-Voltage nameplate (was 32px) */
}

/* ── Motion keyframes (transform/opacity only, <=200ms, ease-out applied at use) ─ */
@keyframes bp-panel-in {
	from { opacity: 0; transform: translateY(4px); }
	to   { opacity: 1; transform: none; }
}
@keyframes bp-modal-in {
	from { opacity: 0; transform: scale(0.96); }
	to   { opacity: 1; transform: none; }
}
@keyframes bp-backdrop-in {
	from { opacity: 0; }
	to   { opacity: 1; }
}
@keyframes bp-toast-in {
	from { opacity: 0; transform: translate(-50%, 6px); }
	to   { opacity: 1; transform: translate(-50%, 0); }
}

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
	}
	/* Remove press movement entirely (not just slowed). */
	.bp-cta:active, .bp-contact:active, .bp-share:active, .bp-submit:active,
	.bp-social:active, .bp-tab:active, .bp-row-link:active,
	.bp-row-register:active { transform: none !important; }
}
