/* TrackingTCG — landing page (templates/main/home.php).

   SIZING RULE FOR THIS FILE: every clamp() — type and spacing alike — must
   reach its maximum at or below ~1400px viewport width. Above that the page is
   then pixel-identical on every monitor and only the centring gutter changes.
   A clamp whose cap lands at, say, 1500px keeps growing between a 1920 and a
   2560 screen while its neighbours have already stopped, and the proportions
   drift apart. Check the cap when adding one: cap_px / vw_fraction = the
   viewport at which it settles.
   Ported from .claude/references/handoff/landing-page.html, whose inline styles
   are turned into real classes here. Everything is `lp-` prefixed so nothing
   collides with the shared stylesheet or a module stylesheet.

   Requires, in order: shared/style.css (tokens), shared/ambient-cards.css
   (.ttcg-panel, [data-reveal]), shared/page-heading.css.

   Motion contract: every animation in here is switched off under
   prefers-reduced-motion at the bottom of the file. The reveal animation itself
   is owned by ambient-cards.css — do not redefine it. */

/* ─── page shell ─────────────────────────────────────────────────────────── */

/* <body> already supplies the page gutters (shared/style.css), so only the
   bottom rhythm is added here — doubling the horizontal padding would push the
   content column ~280px narrower than every other page on the site. */
.lp-main {
	padding: 0 0 clamp(64px, 8vw, 112px);
}

/* Rhythm between major sections. The handoff pins this at
   clamp(64px, 8vw, 112px) — the single most load-bearing spacing value on the
   page, so it lives in one place. */
.lp-section {
	margin-top: clamp(64px, 8vw, 112px);
	max-width: 1640px;
	margin-left: auto;
	margin-right: auto;
}

.lp-section--tight { margin-top: clamp(24px, 3vw, 40px); }
.lp-section--narrow { max-width: 860px; }
.lp-section--cta { max-width: 760px; text-align: center; }

/* Centred section intro: eyebrow + h2 + support line. */
.lp-intro {
	text-align: center;
	display: flex;
	flex-direction: column;
	gap: 12px;
	align-items: center;
}

.lp-eyebrow {
	margin: 0;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 13px;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: var(--colour-secondary-text); /* text-safe blue, like every eyebrow (rule 40) */
}

.lp-h2 {
	margin: 0;
	font-family: var(--font-header);
	font-weight: 700;
	font-size: clamp(1.6rem, 2.8vw, 2.44rem);
	line-height: 1.15;
	letter-spacing: -0.01em;
	text-wrap: balance;
}

.lp-h3 {
	margin: 0;
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 20px;
}

.lp-lede {
	margin: 0;
	font-family: var(--font-paragraph);
	font-size: 16px;
	line-height: 1.55;
	color: var(--colour-text-muted);
	max-width: 60ch;
}

.lp-note {
	font-family: var(--font-paragraph);
	font-size: 14px;
	color: var(--colour-text-muted);
	margin: 0;
}

/* ─── hero ───────────────────────────────────────────────────────────────── */

/* Equal air above and below. The gap below is set on the FOLLOWING section
   rather than as a margin-bottom here, because adjacent siblings collapse and
   the larger of the two would silently win; and it is scoped to
   `.lp-hero + .lp-section` rather than `.lp-section--tight` (line 40) because
   that class is shared with the logged-in Resume block. */
.lp-hero {
	margin-top: 48px;
}

.lp-hero + .lp-section {
	margin-top: 48px;
}

/* One centred column — there is no product-demo half any more, so nothing here
   is a grid track.

   `overflow: hidden` is load-bearing, not tidiness: ghost cards 5 and 6 carry
   negative offsets and hang off the bottom corners on purpose, and this clip is
   what cuts them off cleanly along the rounded corner. (The other four sit
   wholly inside — verified by measuring each card's rect against the panel's.)

   No background is declared: `.ttcg-panel` (shared/ambient-cards.css) supplies
   the translucent paper fill, so the site-wide drifting card layer still ghosts
   through the way it does on every other panel. */
.lp-hero-panel {
	position: relative;
	overflow: hidden;
	/* Matches .lp-section / .lp-tiles so the panel's edges line up with the TCG
	   tile row below it at every viewport. The handoff specified 1080px, which
	   left the hero visibly inset from everything under it. */
	max-width: 1640px;
	margin: 0 auto;
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-floating);
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: 24px;
	/* Top trimmed from the handoff's 84px so the copy block sits slightly above
	   centre in the taller box rather than low in it. */
	padding: 76px 64px 88px;
}

/* Every content child is PROMOTED into the positioned paint step so it lands
   above .lp-hero-bg. Painting order inside one stacking context puts in-flow
   inline text (step 5) BEFORE z-index:auto positioned descendants (step 6), so
   a static <h1> would be painted over by the absolutely-positioned background
   layer even though that layer comes first in the DOM. Do not remove these —
   no z-index is required, only the promotion. */
.lp-hero-title,
.lp-hero-sub,
.lp-hero-actions {
	position: relative;
}

.lp-hero-title {
	font-family: var(--font-header);
	font-weight: 700;
	/* 11vw reaches the 84px cap at a 764px viewport, so the headline is
	   pixel-identical on every desktop — well inside this file's "every clamp()
	   settles by ~1400px" rule. The floor is what the handoff specifies for
	   phones. */
	font-size: clamp(44px, 11vw, 84px);
	line-height: 1.06;
	letter-spacing: -0.025em;
	margin: 0;
	color: var(--colour-text);
}

/* Fixed-width slot for the cycling word, so the line never reflows as the word
   changes length. The two spans share ONE grid cell: the incoming word slides
   up into it while the outgoing word keeps rising out of it, and
   `overflow: hidden` is what clips both at the line box. `min()` keeps the slot
   inside the panel on a narrow phone, where a flat 6.2em would be wider than
   the column. */
.lp-word-slot {
	display: inline-grid;
	justify-items: center;
	min-width: min(6.2em, 100%);
	height: 1.06em;
	overflow: hidden;
	vertical-align: bottom;
}

.lp-word {
	grid-area: 1 / 1;
	white-space: nowrap;
}

/* "Every TCG." is the anchor word and reads in the accent in dark theme
   (the owner's ruling, 2 Oct 2026: what was the accent stays the accent). #FFCB77 on the light paper is 1.37:1, less
   than half the 3:1 large-text floor, so light theme uses the deep primary
   instead (7.81:1). Light is written as the BASE rather than as an override
   because :root IS the light theme here; that also means a visitor whose
   theme boot script failed (localStorage throwing leaves no data-theme
   attribute at all) gets the readable colour, not the unreadable one. */
.lp-word--all { color: var(--colour-primary); }
[data-theme="dark"] .lp-word--all { color: var(--colour-accent); }

/* Game names read blue. --colour-secondary is 3.53:1 on light paper but only
   2.82:1 on the dark panel — under the same 3:1 floor — so dark theme borrows
   --colour-chart-1, which is this palette's own theme-aware form of that blue:
   identical #1F8AC0 in light, brightened to #2BA0DB in dark, 3.70:1. No new hex
   is invented. */
.lp-word--game { color: var(--colour-secondary); }
[data-theme="dark"] .lp-word--game { color: var(--colour-chart-1); }

.lp-word--in  { animation: lp-word-in  0.6s cubic-bezier(0.2, 0.9, 0.2, 1) backwards; }
.lp-word--out { animation: lp-word-out 0.6s cubic-bezier(0.2, 0.9, 0.2, 1) forwards; }

.lp-hero-sub {
	font-family: var(--font-paragraph);
	font-weight: 400;
	font-size: 21px;
	line-height: 1.5;
	color: var(--colour-text-muted);
	margin: 0;
	max-width: 56ch;
	text-wrap: pretty;
}

.lp-hero-actions {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 22px;
	flex-wrap: wrap;
	margin-top: 6px;
}

/* ─── hero ambient background ────────────────────────────────────────────── */

/* Decorative only: no pointer events here, and the markup carries aria-hidden.
   Nothing in this layer reacts to the cursor — the hero's only motion is
   ambient.

   The whole layer SCALES with the panel, because it is authored in fixed
   pixels against a panel that is not fixed. Left alone, a card pinned at
   `left: 36px` keeps its 36px while the panel shrinks from 1080 to 351, so it
   marches inward across the copy: measured on the rendered page at 720px and
   below, ghost cards sat directly behind the cycling word and dropped
   "Pokémon." to 2.45:1 in light and 2.25:1 in dark, against a 3:1 floor.
   Suppressing the cards — and only the cards; not the glow, not the site-wide
   ambient layer — restored it, which is how the cause was pinned down.

   THIS element is the query container rather than .lp-hero-panel, and the
   difference is not cosmetic: cqw measures a container's CONTENT box, so with
   the container on the panel every figure below came out at 0.88x (1080 minus
   its 128px of side padding), and it would additionally have jumped 15% at the
   720px breakpoint where that padding changes. This box is `inset: 0` on the
   panel and has no padding of its own, so 100cqw is the panel's full width at
   every viewport.

   Safe to depend on: these cards already require color-mix() for their tint
   and border, and color-mix shipped LATER than container query units in all
   three engines — no browser can render the cards and still choke on cqw. */
.lp-hero-bg {
	position: absolute;
	inset: 0;
	pointer-events: none;
	container-type: inline-size;
}

/* --lp-u is ONE DESIGN PIXEL: 1px on any panel at least as wide as the 1080px
   the handoff was drawn at, shrinking proportionally below that. Every figure
   in this layer is therefore the handoff's own number written
   `calc(<n> * var(--lp-u))`, and a desktop card is pixel-identical to the
   design. The cap is what stops the cards inflating now that the panel runs to
   1640px — they hold their drawn size and simply spread further apart, which
   is the whole reason a wider panel needs no separate rule.

   It has to be a LENGTH, not a percentage. An earlier attempt derived the
   glow's height and offset from a percentage width held in a custom property;
   percentages resolve against the containing block's height in a `height` or
   `top` context, so the same stored value produced 760px wide and 203px tall.
   A cqw-based length means the same everywhere it is substituted.

   The @supports gate is load-bearing and NOT decoration. A bare pair of
   declarations would not fall back: custom properties accept almost any token
   stream at parse time, so `min(1px, 0.0926cqw)` would be kept even where cqw
   is unknown, and only fail later when it is substituted — taking the whole
   `width`/`left` declaration down with it. Feature-testing the unit is the
   only form of this that can actually fire. */
.lp-hero-glow,
.lp-ghost {
	--lp-u: 1px;
}

@supports (width: 1cqw) {
	.lp-hero-glow,
	.lp-ghost {
		--lp-u: min(1px, 0.0926cqw); /* 0.0926% of the panel = 1/1080th of it */
	}
}

/* A soft blue ellipse behind the headline.

   Sized in --lp-u like the cards, so it stays 760 × 440 at -140 on the full
   panel and shrinks with it. Left flat it stops being a halo on a phone:
   against a 351px panel a 760px ellipse is 216% of the panel, so its dense
   centre lands squarely under the headline. That matters here and not
   elsewhere because the glow is BLUE and so are the cycling game names —
   measured on the rendered page, "Pokémon." fell to 2.00:1 in light and 1.95:1
   in dark at 390px, against a 3:1 floor.

   The translateX(-50%) that centres it is repeated INSIDE the keyframe, not
   left to the static declaration below: an animation's transform keyframes
   REPLACE the element's own transform for the whole cycle, so the static value
   would be discarded the instant lp-hero-glow-float started and the glow would
   sit with its left edge on the panel's centre line. The static declaration is
   kept for the reduced-motion case, where the animation never runs. */
.lp-hero-glow {
	position: absolute;
	left: 50%;
	width: calc(760 * var(--lp-u));
	height: calc(440 * var(--lp-u));
	top: calc(-140 * var(--lp-u));
	border-radius: 50%;
	transform: translateX(-50%);
	background: radial-gradient(closest-side,
		color-mix(in srgb, var(--colour-secondary) 24%, transparent),
		transparent);
	animation: lp-hero-glow-float 12s ease-in-out infinite;
}

/* Six faint placeholder cards, always 2.5 : 3.5.
   The wrapper carries the drift and the inner card carries the rotation,
   deliberately split across two elements: on one element the float keyframe
   would overwrite the rotate the same way it overwrites the glow's centring
   above. Every float and shimmer starts on a NEGATIVE delay so the six are
   already out of phase on the first painted frame — without that they rise and
   fall in unison and read as one moving block rather than six drifting cards. */
.lp-ghost {
	position: absolute;
	animation: lp-float var(--lp-ghost-float) ease-in-out var(--lp-ghost-float-delay) infinite;
}

.lp-ghost-card {
	display: block;
	position: relative;
	width: var(--lp-ghost-w);
	height: var(--lp-ghost-h);
	border-radius: var(--lp-ghost-r);
	transform: rotate(var(--lp-ghost-rot));
	overflow: hidden;
	border: 1px solid color-mix(in srgb, var(--colour-text) 10%, transparent);
	background: color-mix(in srgb, var(--lp-ghost-tint) var(--lp-ghost-alpha), transparent);
}

.lp-ghost-holo {
	position: absolute;
	inset: 0;
	opacity: 0.5;
	background: var(--holo-gradient);
	background-size: 200% 200%;
	animation: lp-hero-shimmer var(--lp-ghost-shimmer) ease-in-out var(--lp-ghost-shimmer-delay) infinite;
}

/* One rule per card, in the same order as the design handoff's table so the two
   can be diffed row by row. The tints are the chart palette, which already has
   dark-mode variants — hence a var() per card rather than a hex. */
.lp-ghost--1 {
	left: calc(36 * var(--lp-u)); top: calc(44 * var(--lp-u));
	--lp-ghost-w: calc(120 * var(--lp-u)); --lp-ghost-h: calc(168 * var(--lp-u)); --lp-ghost-r: calc(10 * var(--lp-u)); --lp-ghost-rot: -14deg;
	--lp-ghost-tint: var(--colour-chart-1); --lp-ghost-alpha: 16%;
	--lp-ghost-float: 8s;   --lp-ghost-float-delay: -1s;
	--lp-ghost-shimmer: 7s; --lp-ghost-shimmer-delay: 0s;
}

.lp-ghost--2 {
	left: calc(150 * var(--lp-u)); bottom: calc(26 * var(--lp-u));
	--lp-ghost-w: calc(96 * var(--lp-u)); --lp-ghost-h: calc(134 * var(--lp-u)); --lp-ghost-r: calc(8 * var(--lp-u)); --lp-ghost-rot: 9deg;
	--lp-ghost-tint: var(--colour-chart-5); --lp-ghost-alpha: 16%;
	--lp-ghost-float: 9s;   --lp-ghost-float-delay: -4s;
	--lp-ghost-shimmer: 8s; --lp-ghost-shimmer-delay: -2s;
}

.lp-ghost--3 {
	right: calc(56 * var(--lp-u)); top: calc(32 * var(--lp-u));
	--lp-ghost-w: calc(110 * var(--lp-u)); --lp-ghost-h: calc(154 * var(--lp-u)); --lp-ghost-r: calc(9 * var(--lp-u)); --lp-ghost-rot: 12deg;
	--lp-ghost-tint: var(--colour-chart-4); --lp-ghost-alpha: 16%;
	--lp-ghost-float: 7.5s;   --lp-ghost-float-delay: -2.5s;
	--lp-ghost-shimmer: 6.5s; --lp-ghost-shimmer-delay: -1s;
}

.lp-ghost--4 {
	right: calc(176 * var(--lp-u)); bottom: calc(40 * var(--lp-u));
	--lp-ghost-w: calc(86 * var(--lp-u)); --lp-ghost-h: calc(120 * var(--lp-u)); --lp-ghost-r: calc(8 * var(--lp-u)); --lp-ghost-rot: -8deg;
	--lp-ghost-tint: var(--colour-chart-2); --lp-ghost-alpha: 16%;
	--lp-ghost-float: 10s;  --lp-ghost-float-delay: -6s;
	--lp-ghost-shimmer: 9s; --lp-ghost-shimmer-delay: -4s;
}

.lp-ghost--5 {
	right: calc(-18 * var(--lp-u)); bottom: calc(-40 * var(--lp-u));
	--lp-ghost-w: calc(120 * var(--lp-u)); --lp-ghost-h: calc(168 * var(--lp-u)); --lp-ghost-r: calc(10 * var(--lp-u)); --lp-ghost-rot: -18deg;
	--lp-ghost-tint: var(--colour-chart-6); --lp-ghost-alpha: 14%;
	--lp-ghost-float: 11s;    --lp-ghost-float-delay: -3s;
	--lp-ghost-shimmer: 7.5s; --lp-ghost-shimmer-delay: -5s;
}

.lp-ghost--6 {
	left: calc(-22 * var(--lp-u)); bottom: calc(-48 * var(--lp-u));
	--lp-ghost-w: calc(110 * var(--lp-u)); --lp-ghost-h: calc(154 * var(--lp-u)); --lp-ghost-r: calc(9 * var(--lp-u)); --lp-ghost-rot: 16deg;
	--lp-ghost-tint: var(--colour-chart-3); --lp-ghost-alpha: 14%;
	--lp-ghost-float: 9.5s;   --lp-ghost-float-delay: -7s;
	--lp-ghost-shimmer: 8.5s; --lp-ghost-shimmer-delay: -3s;
}

/* Phone. The two inboard cards (#2 and #4 sit closest to the copy) are dropped
   rather than repositioned: at this width they land under the sub-line and the
   button instead of framing them. The four corner cards stay, so the panel
   still has life in it. The headline needs no rule here — its clamp() floor
   already is the handoff's phone size. */
@media (max-width: 720px) {
	.lp-hero-panel {
		padding: 56px 24px 60px;
	}

	.lp-hero-sub {
		font-size: 18px;
	}

	.lp-ghost--2,
	.lp-ghost--4 {
		display: none;
	}
}

/* .signup-button still carries `margin: 0 16px clamp(24px, 6vw, 64px)` from the
   centred hero it was originally designed for. That breaks it in a flex row
   three ways: the 16px side margin indents it out of line with the headline,
   and because margin is part of the outer box, `align-items: center` centres
   the MARGIN box — so the button rides up while the "See How It Works" link
   beside it sits low, and the 24–64px bottom margin adds a band of dead space
   under the whole row. The landing page positions the button itself, so the
   legacy margin is cleared. (Left in place elsewhere: the stats page's guest
   CTA still relies on it.) */
.lp-hero-actions .signup-button,
.lp-cta .signup-button {
	margin: 0;
	/* The 32px allowance pairs with the side margins cleared above. */
	max-width: 100%;
}

.lp-textlink {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 16px;
	color: var(--colour-secondary);
	text-decoration: none;
}

.lp-textlink:hover { text-decoration: underline; }

/* The handoff wrapped the sign-up button in a `zoom: 0.78` span. `zoom` is
   non-standard and affects layout and hit-testing, so the button's own clamp()
   was retuned in shared/style.css instead — site-wide, as the handoff itself
   recommended — and the wrapper is gone.

   The wrapper had to go for a second reason: .signup-button carries
   `max-width: calc(100% - 32px)`, and inside a shrink-to-fit span that
   percentage is circular — the browser sizes the span to the button's
   max-content, then applies max-width against that, leaving the button 32px
   narrower than its own text and wrapping "Sign Up!" onto two lines. With the
   button as a direct flex child, the percentage resolves against the row and
   the constraint never binds. */

/* ─── TCG tiles ──────────────────────────────────────────────────────────── */

.lp-tiles {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
	gap: clamp(16px, 4vw, 32px);
	max-width: 1640px;
	margin: 0 auto;
	align-items: stretch;
	perspective: 1600px;
}

/* The reused hero-card internals are written for <div>/<img> children of a
   .hero-card; on the landing tile they hang off <span>s inside the front face,
   so the media wrapper needs an explicit box. */
.lp-tile .hero-card-media {
	display: block;
	position: absolute;
	inset: 0;
}

/* The tile REUSES the site's existing hero-card internals — .hero-card-media,
   .hero-filter, .hero-card-info, .hero-card-beta, .hero-card-palworld-logo and
   .hero-card-suggest-* all come from shared/style.css unchanged. Only the outer
   box is redefined, because the flip needs a 3D context and `.hero-card` sets
   `overflow: hidden`, which forces `transform-style` back to `flat` and would
   stop the back face ever showing. So: the tile is the 3D root with NO overflow,
   and each face clips itself. */
.lp-tile {
	position: relative;
	aspect-ratio: 437 / 608;
	width: 100%;
	max-width: var(--card-width-md);
	margin: 16px auto;
	border-radius: var(--radius-sm);
	box-shadow: var(--shadow-floating);
	border: none;
	padding: 0;
	cursor: pointer;
	display: block;
	text-decoration: none;
	color: inherit;
	transform-style: preserve-3d;
	transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.lp-tile:hover {
	transform: translateY(-4px);
	box-shadow: var(--shadow-lifted);
}

/* Both faces clip themselves and cull their own backside. */
.lp-tile-face,
.lp-tile-back {
	position: absolute;
	inset: 0;
	border-radius: inherit;
	overflow: hidden;
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
}

.lp-tile-face {
	background: var(--colour-popup-paper);
}

.lp-tile .hero-card-media img,
.lp-tile .hero-card-media picture img {
	animation: lp-kenburns 20s ease-in-out infinite alternate;
}

.lp-tile:nth-child(even) .hero-card-media img {
	animation-duration: 24s;
	animation-direction: alternate-reverse;
}

/* NOTE: do not force `picture { opacity: 1 }` here. .hero-card-media keeps every
   <picture> at opacity 0 except .active — that is exactly how heroSlideshow.js
   cross-fades the three banners on a tile. Overriding it shows all three at once. */

/* The card back shown mid-flip. It needs an OPAQUE base UNDER the holo gradient
   — the gradient carries alpha, so painted alone the tile's own front content
   shows through for the whole 0.9s rotation. */
.lp-tile-back {
	background-color: var(--colour-popup-paper);
	background-image: var(--holo-gradient);
	background-size: 200% 200%;
	animation: lp-shimmer 6s ease-in-out infinite;
	transform: rotateY(180deg);
	pointer-events: none;
	z-index: 3;
	display: flex;
	align-items: center;
	justify-content: center;
}

.lp-tile-back img { width: 34%; opacity: 0.55; }

/* Flip-in on scroll — resting state is face-down, landing.js adds .is-flipped on
   the stagger.

   Deliberately an animation with NO fill-mode rather than a transition to a
   `transform: none` end state: a `.is-flipped { transform: none }` rule would
   outrank `.lp-tile:hover { transform: translateY(-4px) }` forever and kill the
   hover lift. With no fill, the element falls back to the normal cascade the
   moment the flip finishes. The resting rule uses :not() for the same reason —
   it stops matching once flipped. */
.lp-tile[data-flip]:not(.is-flipped) {
	transform: rotateY(180deg);
}

.lp-tile[data-flip].is-flipped {
	animation: lp-flip 0.9s cubic-bezier(0.35, 0.9, 0.3, 1.04);
}

.lp-tile--suggest .lp-tile-face {
	background: linear-gradient(135deg, var(--colour-primary), var(--colour-secondary));
	display: flex;
	align-items: center;
	justify-content: center;
}

.lp-tile-suggest-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	text-align: center;
	padding: clamp(20px, 4vw, 40px);
	gap: 14px;
}

.lp-tile-suggest-inner svg {
	width: clamp(56px, 20%, 80px);
	height: auto;
	flex: 0 0 auto;
	color: rgba(255, 255, 255, 0.6);
	margin-bottom: 4px;
}

.lp-tile-suggest-title {
	font-family: var(--font-header);
	font-size: clamp(1.25rem, 2vw, 1.5rem);
	font-weight: 700;
	color: #FAFAFA;
	line-height: 1.2;
}

.lp-tile-suggest-body {
	font-family: var(--font-paragraph);
	font-size: 15px;
	color: rgba(255, 255, 255, 0.85);
	line-height: 1.5;
	max-width: 30ch;
}

.lp-tile-suggest-cta {
	display: inline-block;
	margin-top: 10px;
	padding: 12px 32px;
	background: rgba(255, 255, 255, 0.15);
	border: 2px solid rgba(255, 255, 255, 0.5);
	border-radius: var(--radius-sm);
	color: #FAFAFA;
	font-family: var(--font-header);
	font-size: 15px;
	font-weight: 600;
}

/* ─── stats band ─────────────────────────────────────────────────────────── */

.lp-stats {
	max-width: 1200px;
	margin: 0 auto;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
	gap: 32px;
	text-align: center;
}

.lp-stat {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

.lp-stat-value {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: clamp(2.2rem, 3.6vw, 3.05rem);
	font-variant-numeric: tabular-nums;
}

.lp-stat-label {
	font-family: var(--font-paragraph);
	font-size: 15px;
	color: var(--colour-text-muted);
}

/* ─── how it works ───────────────────────────────────────────────────────── */

.lp-steps {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
	gap: 24px;
	margin-top: 40px;
}

.lp-step {
	background: var(--colour-paper);
	border-radius: var(--radius-sm);
	box-shadow: var(--shadow-resting);
	padding: 28px;
	display: flex;
	flex-direction: column;
	gap: 14px;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.lp-step:hover {
	transform: translateY(-2px);
	box-shadow: var(--shadow-floating);
}

.lp-step-index {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 13px;
	letter-spacing: 0.12em;
	color: var(--colour-secondary);
}

.lp-step p {
	font-family: var(--font-paragraph);
	font-size: 15px;
	line-height: 1.55;
	color: var(--colour-text-muted);
	margin: 0;
}

.lp-step-demo {
	margin-top: auto;
	padding-top: 12px;
}

.lp-step-demo--stack {
	display: flex;
	flex-direction: column;
	gap: 10px;
}

/* ─── completion tiers ───────────────────────────────────────────────────── */

.lp-tiers {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
	gap: clamp(32px, 5vw, 64px);
	align-items: center;
}

.lp-tiers-copy {
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.lp-tier-rows {
	display: flex;
	flex-direction: column;
	gap: 22px;
	margin-top: 16px;
}

.lp-tier-row {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.lp-tier-row-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
}

.lp-tier-name {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 16px;
}

.lp-tier-desc {
	font-family: var(--font-paragraph);
	font-size: 14px;
	color: var(--colour-text-muted);
}

/* Marquee. The translate distance MUST equal the repeat period exactly, gap
   included — that is why the track holds two identical halves and shifts by
   -50%, and why the trailing gap lives on padding-right of each half rather
   than as a flex gap between them. Get this wrong and the loop snaps by the
   gap width once per cycle. */
.lp-marquee {
	position: relative;
	overflow: hidden;
	min-width: 0;
	-webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
	mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}

.lp-marquee-track {
	display: flex;
	width: max-content;
	animation: lp-marquee 48s linear infinite;
}


.lp-marquee-half {
	display: flex;
	gap: 24px;
	padding-right: 24px;
}

.lp-marquee-item {
	display: flex;
	flex-direction: column;
	gap: 10px;
	width: 286px;
	flex: 0 0 286px;
}

.lp-marquee-item a {
	text-decoration: none;
	color: inherit;
}

/* The tile keeps its production 286×400 box; only the marquee's own hover lift
   is suppressed, because the whole strip is already moving. */
.lp-marquee-item .expansion:hover {
	transform: none;
	box-shadow: var(--shadow-floating);
}

.lp-marquee-chip {
	align-self: flex-start;
	border-radius: 999px;
	padding: 4px 12px;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	background: color-mix(in srgb, var(--colour-secondary) 14%, transparent);
	color: var(--colour-secondary);
	border: 1px solid color-mix(in srgb, var(--colour-secondary) 40%, transparent);
}

/* Every marquee tile carries --hot ("Coming Soon" or "Latest Release"). It
   takes the neutral Soon look: gold means Premium (set-header decision 25,
   round 2, 2026-09-30); the accent text was 1.4:1 on its own tint. */
.lp-marquee-chip--hot {
	background: transparent;
	color: var(--colour-text-muted);
	border-color: var(--colour-border);
}

/* ─── dashboard preview ──────────────────────────────────────────────────── */

.lp-dash {
	margin-top: 40px;
	background: var(--colour-paper);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-floating);
	padding: clamp(16px, 3vw, 32px);
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.lp-dash-welcome {
	background: var(--welcome-gradient);
	border-radius: var(--radius-sm);
	padding: 24px 28px;
	color: #FAFAFA;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
}

.lp-dash-welcome-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	font-family: var(--font-header);
}

.lp-dash-welcome-text span:first-child { font-size: 14px; font-weight: 600; opacity: 0.7; }
.lp-dash-welcome-text span:last-child { font-size: 22px; font-weight: 700; }

.lp-dash-search {
	display: flex;
	gap: 8px;
	align-items: center;
}

.lp-dash-search-field {
	background: rgba(255, 255, 255, 0.1);
	border: 1.5px solid rgba(255, 255, 255, 0.2);
	border-radius: var(--radius-xs);
	padding: 8px 16px;
	font-family: var(--font-paragraph);
	font-size: 14px;
	color: rgba(255, 255, 255, 0.55);
	min-width: 180px;
}

.lp-dash-search-btn {
	background: rgba(255, 255, 255, 0.15);
	border: 1.5px solid rgba(255, 255, 255, 0.2);
	border-radius: var(--radius-xs);
	padding: 8px 16px;
	font-family: var(--font-header);
	font-size: 13px;
	font-weight: 600;
}

.lp-dash-body {
	display: flex;
	gap: 16px;
	flex-wrap: wrap;
	align-items: stretch;
}

.lp-dash-col-main {
	flex: 1 1 420px;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.lp-dash-tiles {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
	gap: 16px;
}

.lp-dash-box {
	border: 1px solid rgba(128, 128, 128, 0.18);
	border-radius: 10px;
	padding: 16px 18px;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.lp-dash-col-side {
	flex: 1 1 300px;
	max-width: 100%;
	border: 1px solid rgba(128, 128, 128, 0.18);
	border-radius: 10px;
	padding: 16px 18px;
	display: flex;
	flex-direction: column;
	gap: 4px;
}

.lp-dash-box-title {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 14px;
}

/* Stat tiles — same shape as the real dashboard's .stat-tile (pokemon/stats.css),
   restated here so the preview does not pull the whole stats stylesheet in. */
.lp-dash-tile {
	background: var(--colour-paper);
	border-radius: var(--radius-sm);
	border-left: 3px solid transparent;
	padding: 20px;
	display: flex;
	flex-direction: column;
	gap: 8px;
	box-shadow: var(--shadow-resting);
}

.lp-dash-tile--blue { border-left-color: var(--colour-chart-1); }
.lp-dash-tile--teal { border-left-color: var(--colour-chart-2); }
.lp-dash-tile--gold { border-left-color: var(--colour-chart-3); }

.lp-dash-tile-title,
.lp-dash-tile-label {
	font-family: var(--font-header);
	font-size: var(--text-xs);
	text-transform: uppercase;
	letter-spacing: 0.5px;
	color: var(--colour-text-muted);
}

/* Values read in the text colour, as on the real dashboard (Phase 8): the
   chart colours are fills, and as text they measured 2.06 to 3.53:1 on the
   light tile. The coloured rule on the left keeps each tile's identity. */
.lp-dash-tile-value {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 25px;
	line-height: 1;
	font-variant-numeric: tabular-nums;
	color: var(--colour-text);
}

.lp-activity {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 10px 0;
	border-top: 1px solid var(--colour-border);
}

.lp-activity:first-of-type { border-top: none; }

.lp-activity-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.lp-activity-text strong {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 14px;
}

.lp-activity-text span {
	font-family: var(--font-paragraph);
	font-size: 13px;
	color: var(--colour-text-muted);
}

.lp-activity-delta {
	flex-shrink: 0;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 12px;
	padding: 2px 8px;
	border-radius: 4px;
}

/* The chips' inks are pulled toward the text colour (Phase 8): the trend
   tokens are calibrated against the page, not their own tint, and on the
   chips "+2" measured 3.46:1 (light) and 3.96:1 (dark), the -1 4.26:1 and
   3.31:1. The added card's teal is pulled a quarter of the way (75%); the
   fall needs 60%, because its dark chip is the tighter. */
.lp-activity-delta--added {
	background: var(--colour-trend-positive-bg);
	color: color-mix(in srgb, var(--colour-trend-positive-text) 75%, var(--colour-text));
}

.lp-activity-delta--removed {
	background: var(--colour-trend-negative-bg, color-mix(in srgb, var(--colour-error, #C63B46) 16%, transparent));
	color: color-mix(in srgb, var(--colour-trend-negative, var(--colour-error, #C63B46)) 60%, var(--colour-text));
}

/* A price change, in the accent (the owner's ruling, 2 Oct 2026: what was the accent stays the accent). */
.lp-activity-delta--changed {
	background: color-mix(in srgb, var(--colour-accent) 18%, transparent);
	color: var(--colour-accent);
}

/* ─── premium ────────────────────────────────────────────────────────────── */

/* See-through at 80% in light and 88% in dark (the owner's answer of 2 Oct
   2026, "80% light, 88% dark (Recommended)", after asking for about 80%; 94%
   in both, the answer of 1 Oct, "C: see-through at 94%", stood here until
   then). The box is the solid tint (10% Premium gold on the page colour) at
   that opacity, so the ambient cards drifting behind it show at 20% (light)
   and 12% (dark) of their faint strength. Dark needs the stronger value: with
   two card outlines stacked behind the muted text it measured 4.35:1 at 80%
   and 4.57:1 at 88% (4.5:1 needed; 86% is the edge, 4.505 measured and
   4.483 by exact maths, and three stacked outlines need 91%); light reads
   6.04:1 at 80%. The same rules paint the
   Premium viewer's member box (.lp-premium--member). The old 10% tint
   (--colour-premium-bg) let two stacked card outlines take the muted lede to
   2.30:1 in dark theme (a single card in a real frame at 390, 2.51:1). The see-through surface is set in the @supports block after this
   rule. Browsers without color-mix() (Chrome before 111, Safari before
   16.2, Firefox before 113) skip that block and keep the old tint below;
   they also drop the ambient cards' outlines and tints (written with
   color-mix()), leaving only the faint holo cards. The gate is load-bearing,
   as for --lp-u above: a declaration holding var() is never dropped at parse
   time, so a see-through line written second in this rule would replace the
   first in every browser and leave those browsers with no background at all. */
.lp-premium {
	position: relative;
	overflow: hidden;
	border: 2px solid var(--colour-premium);
	background: var(--colour-premium-bg);
	border-radius: var(--radius-md);
	padding: clamp(28px, 5vw, 56px);
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), 1fr));
	gap: clamp(24px, 4vw, 56px);
	align-items: center;
}

@supports (background: color-mix(in srgb, color-mix(in srgb, red 10%, blue) 80%, transparent)) {
	.lp-premium {
		background: color-mix(in srgb, color-mix(in srgb, var(--colour-premium) 10%, var(--colour-bg)) 80%, transparent);
	}
	[data-theme="dark"] .lp-premium {
		background: color-mix(in srgb, color-mix(in srgb, var(--colour-premium) 10%, var(--colour-bg)) 88%, transparent);
	}
}

.lp-premium-copy {
	display: flex;
	flex-direction: column;
	gap: 16px;
	align-items: flex-start;
}

/* Premium copy keeps a gold eyebrow (rule 39), in the text-safe gold (rule 40). */
.lp-premium-copy .lp-eyebrow { color: var(--colour-premium-text); }

.lp-premium-features {
	display: flex;
	flex-direction: column;
	gap: 14px;
	margin-top: 8px;
}

.lp-premium-feature {
	display: flex;
	gap: 12px;
	align-items: flex-start;
}

.lp-premium-feature svg { flex-shrink: 0; margin-top: 1px; }

.lp-premium-feature-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.lp-premium-feature-text strong {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 15px;
}

.lp-premium-feature-text span {
	font-family: var(--font-paragraph);
	font-size: 14px;
	color: var(--colour-text-muted);
}

.lp-premium-actions {
	display: flex;
	align-items: center;
	gap: 20px;
	flex-wrap: wrap;
	margin-top: 12px;
}

.lp-premium-cta {
	display: inline-flex;
	align-items: center;
	min-height: 48px;
	padding: 14px 28px;
	background: var(--colour-premium);
	color: #1a1a1a;
	border-radius: var(--radius-xs);
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 16px;
	text-decoration: none;
	transition: background 0.2s ease, transform 0.2s ease;
}

.lp-premium-cta:hover {
	background: var(--colour-premium-hover);
	color: #1a1a1a;
	transform: translateY(-1px);
}

/* A Premium element, so it stays gold, in the text-safe ink (Important
   Note 40): the raw --colour-premium was 2.05:1 on the light band. */
.lp-premium-compare {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 15px;
	color: var(--colour-premium-text);
	text-decoration: none;
	border-bottom: 1px solid color-mix(in srgb, var(--colour-premium) 45%, transparent);
	padding-bottom: 2px;
}

.lp-premium-art { display: flex; justify-content: center; }

/* A gold playing card, deliberately not a bank card. */
.lp-goldcard {
	width: min(286px, 100%);
	aspect-ratio: 5 / 7;
	border-radius: 14px;
	background: linear-gradient(135deg, #E3BE66 0%, #D4A84B 45%, #B08A32 100%);
	box-shadow: var(--shadow-floating);
	position: relative;
	overflow: hidden;
	padding: 12px;
	display: flex;
	flex-direction: column;
	gap: 8px;
	color: #2A2008;
	transform: rotate(-3deg);
	transition: transform 0.35s var(--ease-spring-3, ease);
}

.lp-goldcard-sweep {
	position: absolute;
	inset: 0;
	background: linear-gradient(115deg, transparent 32%, rgba(255, 255, 255, 0.38) 50%, transparent 68%);
	background-size: 250% 100%;
	animation: lp-sweep 5s ease-in-out infinite;
	pointer-events: none;
	z-index: 2;
}

.lp-goldcard-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 2px 4px 0;
	font-family: var(--font-header);
	font-weight: 800;
}

.lp-goldcard-head span:first-child { font-size: 16px; }
.lp-goldcard-head span:last-child { font-size: 11px; letter-spacing: 0.06em; }

.lp-goldcard-art {
	flex: 1;
	border-radius: 8px;
	border: 1.5px solid rgba(42, 32, 8, 0.35);
	position: relative;
	overflow: hidden;
	background: var(--holo-gradient);
	background-size: 200% 200%;
	animation: lp-shimmer 6s ease-in-out infinite;
	display: flex;
	align-items: center;
	justify-content: center;
}

.lp-goldcard-art img {
	width: 44%;
	opacity: 0.85;
	filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.25));
}

.lp-goldcard-rarity {
	align-self: center;
	background: rgba(255, 251, 240, 0.55);
	border-radius: 999px;
	padding: 3px 14px;
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 10px;
	letter-spacing: 0.12em;
	text-transform: uppercase;
}

.lp-goldcard-text {
	background: rgba(255, 251, 240, 0.5);
	border: 1px solid rgba(42, 32, 8, 0.25);
	border-radius: 8px;
	padding: 8px 10px;
	font-family: var(--font-paragraph);
	font-size: 12px;
	line-height: 1.45;
}

.lp-goldcard-foot {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 0 4px 2px;
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 10px;
	letter-spacing: 0.08em;
}

/* ─── FAQ ────────────────────────────────────────────────────────────────── */

.lp-faq {
	display: flex;
	flex-direction: column;
	gap: 12px;
	margin-top: 32px;
}

.lp-faq-item {
	background: var(--colour-paper);
	border-radius: var(--radius-xs);
	box-shadow: var(--shadow-resting);
	overflow: hidden;
}

.lp-faq-q {
	width: 100%;
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16px;
	padding: 18px 22px;
	background: none;
	border: none;
	cursor: pointer;
	color: var(--colour-text);
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 16px;
	text-align: left;
}

.lp-faq-q svg {
	flex-shrink: 0;
	transition: transform 0.3s ease;
}

.lp-faq-q[aria-expanded="true"] svg { transform: rotate(180deg); }

.lp-faq-a {
	max-height: 0;
	overflow: hidden;
	transition: max-height 0.35s ease;
}

.lp-faq-item.is-open .lp-faq-a { max-height: 320px; }

.lp-faq-a p {
	margin: 0;
	padding: 0 22px 18px;
	font-family: var(--font-paragraph);
	font-size: 15px;
	line-height: 1.6;
	color: var(--colour-text-muted);
}

/* ─── waitlist ───────────────────────────────────────────────────────────── */

.lp-waitlist {
	background: var(--welcome-gradient);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-floating);
	padding: clamp(28px, 5vw, 56px);
	color: #FAFAFA;
	text-align: center;
}

.lp-waitlist h2 { color: #FAFAFA; }

.lp-waitlist-lede {
	font-family: var(--font-paragraph);
	font-size: 16px;
	line-height: 1.55;
	color: rgba(255, 255, 255, 0.85);
	max-width: 52ch;
	margin: 12px auto 0;
}

.lp-waitlist-form {
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 12px;
	flex-wrap: wrap;
	margin-top: 24px;
}

/* No waitlist table or endpoint exists, so this is a link to the contact form
   rather than an input that would silently discard an email address. */
.lp-waitlist-cta {
	display: inline-flex;
	align-items: center;
	min-height: 48px;
	background: rgba(255, 255, 255, 0.15);
	border: 1.5px solid rgba(255, 255, 255, 0.35);
	color: #FAFAFA;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 15px;
	padding: 13px 26px;
	border-radius: var(--radius-xs);
	text-decoration: none;
	transition: background 0.2s ease;
}

.lp-waitlist-cta:hover {
	background: rgba(255, 255, 255, 0.28);
	color: #FAFAFA;
}

.lp-roadmap {
	display: flex;
	justify-content: center;
	align-items: center;
	gap: 10px;
	flex-wrap: wrap;
	margin-top: 28px;
}

.lp-roadmap-label {
	font-family: var(--font-paragraph);
	font-size: 13px;
	color: rgba(255, 255, 255, 0.7);
}

/* The roadmap chips read in the accent (the owner's ruling, 2 Oct 2026: what was the accent stays the accent). */
.lp-roadmap-chip {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 12px;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	border: 1px solid rgba(255, 203, 119, 0.5);
	color: var(--colour-accent);
	border-radius: 999px;
	padding: 4px 12px;
}

/* ─── final CTA ──────────────────────────────────────────────────────────── */

.lp-cta {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 18px;
}

.lp-cta h2 { font-size: clamp(1.8rem, 3.2vw, 2.8rem); }

.lp-cta-lede {
	font-family: var(--font-paragraph);
	font-size: 17px;
	line-height: 1.55;
	color: var(--colour-text-muted);
	margin: 0;
	max-width: 46ch;
}

/* ─── sticky sign-up bar + scroll progress ───────────────────────────────── */

/* Below the modal scale (--z-modal 1000) and the navbar (200) on purpose: this
   bar must never cover a dialog. */
.lp-stickybar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 40;
	background: var(--colour-paper);
	border-top: 1px solid var(--colour-border);
	box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.18);
	transform: translateY(110%);
	transition: transform 0.4s ease;
}

.lp-stickybar.is-shown { transform: none; }

.lp-stickybar-inner {
	max-width: 1200px;
	margin: 0 auto;
	padding: 12px clamp(16px, 4vw, 32px);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
}

.lp-stickybar-copy {
	display: flex;
	align-items: center;
	gap: 12px;
	min-width: 0;
}

.lp-stickybar-copy img { width: 30px; height: 30px; flex-shrink: 0; }

.lp-stickybar-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.lp-stickybar-text strong {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 15px;
}

.lp-stickybar-text span {
	font-family: var(--font-paragraph);
	font-size: 13px;
	color: var(--colour-text-muted);
}

.lp-stickybar-actions {
	display: flex;
	align-items: center;
	gap: 10px;
}

.lp-stickybar-dismiss {
	background: none;
	border: none;
	cursor: pointer;
	color: var(--colour-text-muted);
	font-size: 22px;
	line-height: 1;
	padding: 8px;
}

.lp-stickybar-dismiss:hover { color: var(--colour-text); }

.lp-progress {
	position: fixed;
	top: 0;
	left: 0;
	height: 4px;
	width: 0;
	background: var(--colour-primary);
	z-index: 60;
	transition: background 0.4s ease;
	pointer-events: none;
}

.lp-progress-chip {
	position: fixed;
	right: 16px;
	bottom: 92px;
	z-index: 45;
	display: flex;
	align-items: center;
	gap: 7px;
	background: var(--colour-paper);
	border: 1px solid var(--colour-border);
	box-shadow: var(--shadow-resting);
	border-radius: 999px;
	padding: 6px 13px;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 11px;
	letter-spacing: 0.04em;
	color: var(--colour-text);
	pointer-events: none;
	opacity: 0;
	transform: translateY(8px);
	transition: opacity 0.3s ease, transform 0.3s ease, background 0.4s ease;
}

.lp-progress-chip.is-shown { opacity: 1; transform: none; }

.lp-progress-dot {
	width: 8px;
	height: 8px;
	border-radius: 999px;
	background: var(--colour-primary);
	transition: background 0.4s ease;
}

/* ─── logged-in sections ─────────────────────────────────────────────────── */

.lp-resume {
	background: var(--welcome-gradient);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-floating);
	padding: clamp(24px, 3.5vw, 40px);
	color: #FAFAFA;
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
	gap: clamp(20px, 3vw, 40px);
	align-items: center;
}

.lp-resume-copy {
	display: flex;
	flex-direction: column;
	gap: 6px;
	align-items: flex-start;
}

.lp-resume-copy h1 {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.4rem);
	margin: 0;
	letter-spacing: -0.01em;
	color: #FAFAFA;
}

.lp-resume-hello {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 14px;
	opacity: 0.75;
}

.lp-resume-card {
	background: rgba(255, 255, 255, 0.12);
	border: 1.5px solid rgba(255, 255, 255, 0.24);
	border-radius: var(--radius-sm);
	padding: 20px;
	display: flex;
	flex-direction: column;
	gap: 14px;
}

.lp-resume-card-head {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 12px;
	flex-wrap: wrap;
}

.lp-resume-card-head-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.lp-resume-card-label {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 12px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	opacity: 0.7;
}

.lp-resume-card-name {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 19px;
}

.lp-resume-card-when {
	font-family: var(--font-paragraph);
	font-size: 13px;
	opacity: 0.75;
}

/* The accent button (the owner's ruling, 2 Oct 2026: what was the accent stays the accent): #2A2008 on #FFCB77 is
   10.75:1. */
.lp-resume-continue {
	align-self: flex-start;
	margin-top: 4px;
	background: var(--colour-accent);
	border: none;
	color: #2A2008;
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 14px;
	padding: 13px 24px;
	min-height: 48px;
	border-radius: var(--radius-xs);
	cursor: pointer;
	text-decoration: none;
	display: inline-flex;
	align-items: center;
	transition: transform 0.2s ease;
}

.lp-resume-continue:hover { transform: translateY(-2px); color: #2A2008; }

.lp-rowhead {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
}

.lp-rowhead h2 {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 20px;
	margin: 0;
}

.lp-rowhead span {
	font-family: var(--font-paragraph);
	font-size: 14px;
	color: var(--colour-text-muted);
}

.lp-nearwins {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
	gap: 16px;
}

.lp-nearwin {
	text-align: left;
	border: 1px solid var(--colour-border);
	border-left: 3px solid var(--colour-chart-1);
	border-radius: var(--radius-xs);
	padding: 18px;
	cursor: pointer;
	display: flex;
	flex-direction: column;
	gap: 10px;
	color: var(--colour-text);
	text-decoration: none;
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.lp-nearwin:hover {
	transform: translateY(-2px);
	box-shadow: var(--shadow-floating);
	color: var(--colour-text);
}

/* Every tile is a Base Set near-win, so every rule is the Base tier colour
   (chart-1, on .lp-nearwin). The rules used to cycle blue, teal and gold, which
   read as Master and Grandmaster, and gold as Premium (Phase 8). */

.lp-nearwin-headline {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 15px;
}

.lp-nearwin-set {
	font-family: var(--font-paragraph);
	font-size: 13px;
	color: var(--colour-text-muted);
}

.lp-newsets {
	border-radius: var(--radius-sm);
	box-shadow: var(--shadow-resting);
	padding: 20px 22px;
	display: flex;
	flex-direction: column;
	gap: 12px;
}

.lp-newsets-title {
	font-family: var(--font-header);
	font-weight: 700;
	font-size: 16px;
}

.lp-newset {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 10px 0;
	border-top: 1px solid var(--colour-border);
}

.lp-newset-text {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

.lp-newset-text strong {
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 14px;
}

.lp-newset-text span {
	font-family: var(--font-paragraph);
	font-size: 13px;
	color: var(--colour-text-muted);
}

.lp-newset-cta {
	flex-shrink: 0;
	background: none;
	border: 1.5px solid var(--colour-secondary);
	color: var(--colour-secondary);
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 13px;
	padding: 12px 18px;
	min-height: 44px;
	border-radius: var(--radius-xs);
	cursor: pointer;
	text-decoration: none;
	display: inline-flex;
	align-items: center;
}

.lp-newset-cta:hover {
	background: color-mix(in srgb, var(--colour-secondary) 12%, transparent);
	color: var(--colour-secondary);
}

.lp-stack {
	display: flex;
	flex-direction: column;
	gap: 24px;
}

.lp-stack-sm {
	display: flex;
	flex-direction: column;
	gap: 14px;
}

/* ─── real components ───────────────────────────────────────────────────────
   The How-It-Works row renders PRODUCTION components —
   .card-tracking-container / .card-counter / .dropbtn / .progress-bar /
   .expansion — so home.php links the real /css/shared/tcg/set-detail.css and
   /css/shared/tcg/set-list.css instead of restating them here. (The hero was a
   second consumer until 2026-09, via a scripted card-tile demo; .card-tracking,
   .card, .card-info and the whole .exp-progression-* family went with it and no
   longer render anywhere on this page.)

   An earlier version of this file kept a hand-copy of those rules. It was
   incomplete on day one (it dropped .card, .card-info, .card-title and
   .popup-icon entirely) and the fan rendered nothing like the real grid. A copy
   of a component is a component that is already wrong; only true landing-page
   deviations belong below. */

/* The production counter uses Font Awesome glyphs. The landing page does not
   load the FA kit — a whole icon font for two glyphs — so its buttons carry
   inline SVG. Everything else about the control (grid, colour, 44px touch
   target) is the real .card-counter.

   margin-inline is load-bearing, not tidiness. .card-counter is a 3×1fr grid,
   so each button fills a ~46px cell; production's glyphs are INLINE text and
   the button's own text-align:center centres them, but `display: block` opts
   these SVGs out of that and a block box with no auto margin sits flush LEFT.
   Both glyphs were therefore 12px left of their cells, which left 42px of air
   between the − and the 0 and only 13px between the 0 and the +: the number
   read as shoved against the plus even though it is, and always was, dead on
   the card's centre line. Centre the glyph in its own cell and the two gaps
   come out equal. */
.lp-main .card-counter button svg {
	display: block;
	width: 22px;
	height: 22px;
	margin-inline: auto;
}

/* .progress-bar is pinned to 246px to fit the 286px expansion tile; the landing
   page uses it at full width. */
.lp-main .progress-bar {
	width: 100%;
	margin-bottom: 0;
}

/* ...except inside a real set tile, where 246px is the production width. */
.lp-main .expansion .progress-bar {
	width: 246px;
	margin-bottom: var(--space-16);
}

.lp-main .lp-bar + .lp-bar { margin-top: 10px; }

.lp-main .lp-bar p {
	margin: 0 0 4px;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 12px;
	color: var(--colour-text-muted);
}

/* On the gradient Resume panel the muted grey label disappears. */
.lp-resume .lp-bar p { color: rgba(255, 255, 255, 0.8); }
.lp-resume .progress-bar { outline-color: transparent; }
.lp-resume .progress-text { text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55); color: #FAFAFA; }

/* The How-It-Works row is the production .card-tracking-container minus its
   details icon: there is no card behind this demo for a popup to open, so the
   Poké Ball button would be a control that does nothing.

   The production grid is `69.5px 139px 69.5px` (icon | counter | fan) and its
   columns are FIXED and auto-placed, so simply deleting the first child slides
   the counter into the icon's column and the number stops being centred. Keep
   the three-column track and place the two survivors explicitly instead: the
   counter stays dead centre and the fan stays exactly where it was, so removing
   the icon removes the icon and moves nothing else. The empty first column is
   the point, not an oversight. */
.lp-hiw-controls {
	margin: 0 auto;
	justify-content: center;
}

.lp-hiw-controls > .card-counter { grid-column: 2; }
.lp-hiw-controls > .dropdown { grid-column: 3; }

/* The "Pokémon example" marker. The How-It-Works fan and the dashboard mock are
   both written in Pokémon — real variant names, ex/VSTAR card names — because a
   TCG-neutral mock is a mock nobody recognises. Labelling them is the honest
   fix; the products themselves are per-TCG. */
.lp-samplenote {
	margin: 0 0 10px;
	display: flex;
	justify-content: center;
}

.lp-intro .lp-samplenote { margin: 4px 0 0; }

.lp-samplechip {
	border-radius: 999px;
	padding: 4px 12px;
	font-family: var(--font-header);
	font-weight: 600;
	font-size: 11px;
	letter-spacing: 0.08em;
	text-transform: uppercase;
	background: color-mix(in srgb, var(--colour-secondary) 14%, transparent);
	color: var(--colour-secondary);
	border: 1px solid color-mix(in srgb, var(--colour-secondary) 40%, transparent);
}

/* The fan is a READOUT, not a picker — here exactly as on a real card tile. The
   menu rows are what select a printing (landing.js, setupPlayground); the fan
   cards only show what is owned, so they are not individually clickable and do
   not claim to be. Only the fan button, which opens the menu, and the menu rows
   take a pointer cursor. */
.lp-main [data-demo-fan-menu] .dropbtn,
.lp-main [data-demo-fan-menu] .dropdown-option { cursor: pointer; }

.lp-main [data-demo-fan-menu] .icon { cursor: default; }

/* The example menu opens UPWARD and stays inside the step card.

   Production anchors .dropdown-content below the fan (`top: 30px`), which is
   right on a card tile — there is a whole grid underneath it. Here the fan sits
   at the BOTTOM of the step card by design (.lp-step-demo is margin-top:auto),
   so a downward menu hangs 299px out of the card and into the Completion Tiers
   section below, where the marquee's tiles paint straight over it. Three rows
   used to fit; five printings plus "All Variants" do not. Flip it upward, cap
   it, and let it scroll if a future fan grows again. */
.lp-main [data-demo-fan-menu] .dropdown-content {
	top: auto;
	bottom: calc(100% + 8px);
	left: 50%;
	transform: translateX(-50%);
	max-height: 260px;
	overflow-y: auto;
	border-radius: var(--radius-sm);
	border: 1px solid var(--colour-border, rgba(128, 128, 128, 0.3));
}

/* Raising the card is what keeps the menu above its NEIGHBOURS, and it has to
   be conditional: `position: relative` on the card at all times would make the
   card a stacking context permanently, trapping the menu's own z-index inside
   it and re-creating the bug from the other direction. The class is toggled by
   landing.js alongside the existing .dropdown-open, so there is no :has()
   dependency. */
.lp-steps .lp-step.is-menu-open {
	position: relative;
	z-index: 5;
}

/* The picked row keeps the variant's colour as a 3px edge and a light TINT,
   never as the whole background. Painting the raw swatch behind the label is
   what production does on :hover, and with Standard's near-black #231F20 that
   is dark-text-on-black in the light theme — unreadable. A transient hover gets
   away with it; a state that persists after the menu closes does not. */
.lp-main [data-demo-fan-menu] .dropdown-option.is-picked {
	background-color: color-mix(in srgb, var(--dropdown-color, var(--colour-secondary)) 18%, var(--colour-paper));
	box-shadow: inset 3px 0 0 0 var(--dropdown-color, var(--colour-secondary));
	font-weight: 700;
}

/* ─── marquee drag ───────────────────────────────────────────────────────────
   The strip is grabbable. `touch-action: pan-y` is the load-bearing half on
   touch: it hands us the horizontal axis while leaving the VERTICAL one to the
   browser, so dragging the strip never traps a visitor trying to scroll the
   page past it. */
.lp-marquee {
	cursor: grab;
	touch-action: pan-y;
}

.lp-marquee.is-dragging {
	cursor: grabbing;
	user-select: none;
	-webkit-user-select: none;
}

/* Once landing.js has taken the transform over, the CSS animation must not
   fight it. The JS also sets `animation: none` inline at the moment it grabs
   the current translate; this is the declarative half, and it is what keeps a
   late-arriving stylesheet or a theme flip from restarting the keyframes. */
.lp-marquee.is-manual .lp-marquee-track { animation: none; }

/* The hover pause is the CSS animation's; in manual mode landing.js holds the
   strip still itself, and leaving this rule to match would do nothing but
   confuse the next reader. */
.lp-marquee:not(.is-manual):hover .lp-marquee-track { animation-play-state: paused; }

.lp-marquee img { -webkit-user-drag: none; user-drag: none; }

/* ─── TCG picker spotlight ───────────────────────────────────────────────────
   Step 01's link sends the visitor back up to the picker, which is a scroll
   that arrives with no explanation. A brief ring on the tiles is the
   explanation.

   A TRANSITION, deliberately not an animation: .lp-tile[data-flip].is-flipped
   already owns `animation` (the flip-in, with no fill-mode so the hover lift
   survives it), and a second animation on the same element would replace it
   outright — a tile spotlit mid-flip would snap face-up. Transitions compose
   with animations instead of competing for the property. */
.lp-tiles .lp-tile {
	outline: 2px solid transparent;
	outline-offset: 6px;
	transition: transform 0.3s ease, box-shadow 0.3s ease, outline-color 0.45s ease;
}

.lp-tiles.is-spotlit .lp-tile {
	outline-color: var(--colour-secondary);
	box-shadow: var(--shadow-lifted), 0 0 28px 2px color-mix(in srgb, var(--colour-secondary) 38%, transparent);
}

/* ─── keyframes ──────────────────────────────────────────────────────────── */

@keyframes lp-shimmer {
	0%   { background-position: 0% 50%; }
	50%  { background-position: 100% 50%; }
	100% { background-position: 0% 50%; }
}

@keyframes lp-kenburns {
	from { transform: scale(1.03) translate3d(0, 0, 0); }
	to   { transform: scale(1.13) translate3d(0, -2.5%, 0); }
}

/* Hero: the ghost cards' holo sheen. Distinct from lp-shimmer above — that one
   pans horizontally across a 200%-wide gradient; this one travels the diagonal,
   which is what the 135deg --holo-gradient wants. */
@keyframes lp-hero-shimmer {
	0%   { background-position: 0% 0%; }
	50%  { background-position: 100% 100%; }
	100% { background-position: 0% 0%; }
}

/* Hero: the ghost cards' drift. */
@keyframes lp-float {
	0%, 100% { transform: translateY(0); }
	50%      { transform: translateY(-10px); }
}

/* Hero: the glow's drift. Identical motion to lp-float, but it has to carry the
   centring translateX as well — an animation's transform keyframes REPLACE the
   element's own transform for the whole cycle, so .lp-hero-glow cannot centre
   itself with a static declaration while this is running. */
@keyframes lp-hero-glow-float {
	0%, 100% { transform: translateX(-50%) translateY(0); }
	50%      { transform: translateX(-50%) translateY(-10px); }
}

/* Hero: the headline word swap. Both words travel UP — the incoming one rises
   into the slot, the outgoing one keeps going and leaves through the top. */
@keyframes lp-word-in {
	from { transform: translateY(105%); opacity: 0; }
	to   { transform: none; opacity: 1; }
}

@keyframes lp-word-out {
	from { transform: none; opacity: 1; }
	to   { transform: translateY(-105%); opacity: 0; }
}

@keyframes lp-sweep {
	0%   { background-position: 180% 0; }
	55%  { background-position: -80% 0; }
	100% { background-position: -80% 0; }
}

@keyframes lp-marquee {
	to { transform: translateX(-50%); }
}

@keyframes lp-flip {
	from { transform: rotateY(180deg); }
	to   { transform: none; }
}

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

@media (prefers-reduced-motion: reduce) {
	.lp-hero-glow,
	.lp-ghost,
	.lp-ghost-holo,
	.lp-word,
	.lp-tile-back,
	.lp-goldcard-sweep,
	.lp-goldcard-art,
	.lp-marquee-track,
	.lp-tile .hero-card-media img,
	.lp-tile .hero-card-media picture img {
		animation: none !important;
	}

	/* Face-up immediately — never leave a tile stuck showing its back. */
	.lp-tile[data-flip]:not(.is-flipped) { transform: none !important; }
	.lp-tile[data-flip].is-flipped { animation: none !important; }

	/* setupHeroWords() never starts here, so normally only the first word
	   exists. This is the belt to that braces: the JS stops the cycle on a
	   mid-session toggle, but the CSS lands FIRST (a media query is live, a
	   change listener runs a beat later), so without this the two words in the
	   one grid cell would both be fully opaque for that beat — the outgoing one
	   has no animation left to hide it. */
	.lp-word--out { display: none !important; }

	.lp-tile,
	.lp-step,
	.lp-nearwin,
	.lp-resume-continue,
	.lp-stickybar {
		transition: none !important;
	}
}

@media (max-width: 640px) {
	.lp-main { padding-bottom: clamp(48px, 8vw, 80px); }
	.lp-progress-chip { display: none; }
}
