/*
 * One screen, top to bottom: the creator, the card, what it is, price and progress, how it works, the button.
 * TikTok palette: black, with the card's own colour (--tint, from the art) glowing around the card only. The rest of the palette never changes, so the button and the numbers read the same on every card.
 * Phones and portrait tablets get the app full screen; wide screens get it in a phone-sized frame (#app is a size
 * container, so everything measures against the frame, exactly as on a phone).
 */
:root {
  --tint: #3a3a3a;
  --base: #000000;
  --fg: #ffffff;
  --dim: rgba(255, 255, 255, 0.66);
  --surf: #161616;
  --line: #2a2a2a;
  --cta: #fe2c55;
  --claw: #a0e000;
  --accent: #25f4ee;
  --danger: #ff5a5f;
  --sheet: #161616;
  --font: "Schibsted Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;
  color-scheme: dark;
}
*, *::before, *::after { box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }
html, body { height: 100%; overscroll-behavior: none; }
/* The page behaves like an app, not a document (owner, 2026-10-04): no double-tap zoom (a pinch still zooms), no grey
   flash under a tap, no long-press menu on a picture or a link, and no stray text selection while swiping or tapping.
   Reading text in the How it works sheet and the item's details stays selectable. */
html { touch-action: manipulation; }
body { -webkit-tap-highlight-color: transparent; -webkit-touch-callout: none; user-select: none; -webkit-user-select: none; }
.info-body :is(p, li, details, .facts, .step), .details-head { user-select: text; -webkit-user-select: text; }
body { margin: 0; overflow: hidden; background: var(--base); color: var(--fg); font-family: var(--font); font-size: 16px; }
/* No picture on the page can be dragged out (owner): Chrome and Safari here, Firefox in site/main.ts (dragstart). */
img { display: block; max-width: 100%; -webkit-user-drag: none; user-select: none; -webkit-user-select: none; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* isolation: the page is one layer under the sheets, so the reveal's sparkle canvas (z 11, above the 3D view) never
   draws over an open sheet. */
#app { position: relative; width: 100vw; height: 100vh; height: 100svh; overflow: hidden; container-type: size; background: var(--base); isolation: isolate; }

.screen {
  position: relative; height: 100%; max-width: 480px; margin: 0 auto;
  display: flex; flex-direction: column; gap: 12px; isolation: isolate;
  /* No top safe-area: without viewport-fit=cover the page never sits under a notch, yet Phantom on Android still
     reports its status bar as a top inset and the header drops by that much. */
  padding: 14px 16px calc(16px + env(safe-area-inset-bottom));
}
.screen > :empty:not(.reels) { display: none; }

.head { flex: none; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.who { display: flex; align-items: center; gap: 10px; min-width: 0; color: var(--fg); text-decoration: none; font-weight: 900; font-size: 17px; }
.who img { width: 34px; height: 34px; flex: none; border-radius: 50%; object-fit: cover; }
.who span { color: var(--dim); font-weight: 500; font-size: 14px; }
.tag { font-weight: 700; font-size: 12px; color: var(--dim); border: 1.5px solid rgba(255, 255, 255, 0.3); border-radius: 999px; padding: 4px 10px; white-space: nowrap; }
.right { display: flex; align-items: center; gap: 8px; }
/* Share and "?": solid dark circles, the same fill as the price plates. */
.round { display: grid; place-items: center; flex: none; width: 36px; height: 36px; padding: 0; border: 1px solid var(--line); border-radius: 50%; background: var(--surf); color: var(--fg); font: 900 16px var(--font); cursor: pointer; }
.round svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }

/* The card's area takes whatever height the rest of the screen leaves (it changes with the state: a wallet line, a
   notice), and the card is the largest square that fits in it, centred, so nothing is pushed off the screen.
   Under its siblings, so the halo tints the space around the header and the text, never the text itself. */
.art { position: relative; z-index: -1; flex: 1 1 0; min-height: 140px; display: grid; place-items: center; container-type: size; perspective: 900px; }
.art > * { grid-area: 1 / 1; }
/* The card: a looping stack of the cover and the minted items, swiped up and down (site/ui/reels.ts). */
.reels {
  /* 24px kept free above and below on every screen, so the visible area always has room to fade its edges. */
  position: relative; aspect-ratio: 1; width: min(100cqw, 100cqh - 48px);
  border-radius: 20px;
}
.window:has(.reels.swipeable) { cursor: grab; }
.window:has(.reels.swipeable):active { cursor: grabbing; }
/* The swipe hint (site/ui/reels.ts, owner 2026-10-04): a translucent white hand in the middle of the card comes with
   each peek of the feed, draws the swipe up as the feed lifts, and goes when the feed is back: one run per peek. */
.hint { position: relative; z-index: 2; width: 56px; height: 56px; margin: 40px 0 0 0; pointer-events: none; opacity: 0; filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.45)); }
.hint svg { width: 100%; height: 100%; }
/* The trail is a full-length bar scaled from its top end, not a bar growing in height: the height ran a layout and a
   paint every frame of the hint, the scale runs on the compositor (owner, 2026-10-03). Its lift and its scale are one
   `transform` list, not the `translate` and `scale` properties: under the hand's own transform animation, Chrome
   ticked the trail on the main thread with those (a style recalc, a paint and a commit on every frame of every peek,
   91 recalcs per peek measured at 4× CPU); as one transform it stays on the compositor (7 per peek), same motion.
   The lift ran from 0% (a `translate` the first keyframes left out starts from rest), the scale from 10%: at 10% the
   bar is already 22.5 px up (90 px × 10 / 40, linear), which the second keyframe spells out. */
.hint::after { content: ""; position: absolute; left: 14px; top: 0; width: 6px; height: 90px; border-radius: 3px; transform-origin: 50% 0; transform: scaleY(0); opacity: 0; background: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.5)); }
/* In step with the feed's peek (PEEK_MS, the same 1.5 s): press at 10%, up with the feed by 40%, hold, let go, gone by 85%. */
.hint.swipe { animation: hand 1.5s linear; }
.hint.swipe::after { animation: trail 1.5s linear; }
@keyframes hand { 0% { opacity: 0; transform: translateY(0) scale(1); } 6% { opacity: 0.9; } 10% { transform: translateY(0) scale(0.92); animation-timing-function: cubic-bezier(0.45, 0, 0.25, 1); } 40% { transform: translateY(-90px) scale(0.92); } 55% { opacity: 0.9; transform: translateY(-90px) scale(0.92); } 70% { opacity: 0.6; transform: translateY(-90px) scale(1); } 85%, 100% { opacity: 0; transform: translateY(-90px) scale(1); } }
@keyframes trail { 0% { transform: translateY(0) scaleY(0); opacity: 0; } 10% { transform: translateY(-22.5px) scaleY(0); opacity: 0; } 14% { opacity: 1; } 40% { transform: translateY(-90px) scaleY(1); opacity: 0.9; } 60%, 100% { transform: translateY(-90px) scaleY(0); opacity: 0; } }
/* The cover: the reveal's closed claw card without its rim and its light band (owner), its claw lit, the words under it. */
.reel.cover { background: radial-gradient(110% 80% at 50% 25%, #1b1b1f, #050506 72%); }
.reel.cover .cover-claw { position: absolute; left: 16%; top: 10%; width: 68%; height: 66%; max-width: none; object-fit: contain; pointer-events: none; }
.cover-say { position: absolute; left: 0; right: 0; bottom: 7%; z-index: 1; display: grid; justify-items: center; gap: 4px; padding: 0 12px; text-align: center; }
.cover-say b { font-weight: 900; font-size: clamp(20px, 7cqmin, 34px); letter-spacing: -0.3px; text-shadow: 0 2px 12px #000; }
.cover-say small { color: var(--dim); font-weight: 700; font-size: 13.5px; }
/* Nothing to swipe to yet (fewer than two minted): the cover only says the item is random. */
.reels:not(.swipeable) .cover-say small { display: none; }
#card.revealing .hint { display: none; }

/* Once a wallet is connected the buyer is past the pitch: the lead and the steps go (both live on in "How it works"),
   the wallet plate comes in, and the card takes the room. */
#card.connected .lead { display: none; }
/* Sold out: the pitch makes way for the sold-out line (still hidden for a connected wallet, like the pitch). */
.lead.sold { display: none; }
#card.sold-out:not(.connected) .lead.sold { display: block; }
.wallet-plate { display: flex; justify-content: space-between; align-items: center; gap: 12px; background: var(--surf); border: 1px solid var(--line); border-radius: 12px; padding: 8px 12px; font-weight: 700; font-size: 15px; }
.wallet-plate small { display: block; color: var(--dim); font-weight: 500; font-size: 12px; }
.wallet-plate .balance { text-align: right; }
.wallet-plate .balance .sk { width: 80px; margin-left: auto; }
.wallet-plate .low { color: var(--cta); }

/* Skeletons: until the data lands, the card, the numbers and the button keep their places and shimmer. */
.reels.loading, .sk, .btn.sk-btn { background: linear-gradient(100deg, rgba(255, 255, 255, 0.06) 30%, rgba(255, 255, 255, 0.14) 50%, rgba(255, 255, 255, 0.06) 70%) 0 0 / 300% 100%; animation: shimmer 1.4s linear infinite; }
.sk { display: block; width: 70%; height: 1.05em; margin-top: 3px; border-radius: 6px; }
.sk-btn { height: 52px; }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }
.reel { position: absolute; inset: 0; border-radius: 20px; overflow: hidden; background: #111; will-change: transform; }
/* The visible area is the whole space between the header and the text: the feed slides inside it, and whatever is
   past its top or bottom edge is out of sight. The glow sits behind it, outside the clip, so its edge never cuts it. */
.window { width: 100%; height: 100%; overflow: hidden; display: grid; place-items: center; touch-action: none; user-select: none; -webkit-user-select: none;
  /* Soft edges: a card fades out as it leaves the area and in as it enters, instead of being cut by a hard line.
     The fade only uses the room around the card at rest (24 to 56px), so a card on screen is never dimmed. */
  --edge: clamp(24px, (100cqh - min(100cqw, 100cqh - 48px)) / 2, 56px);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--edge), #000 calc(100% - var(--edge)), transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 var(--edge), #000 calc(100% - var(--edge)), transparent 100%); }
/* The halo in the colour of the card on screen, blending towards the next one as the finger moves: a blurred
   fill (a shadow would leave its own box empty, a black square once the card moves off it).
   Shifted down: the header stays on black, the glow runs on under the text. */
.glow { width: min(100cqw, 100cqh - 48px); aspect-ratio: 1; border-radius: 40px; pointer-events: none; will-change: transform;
  background: color-mix(in srgb, var(--tint) 72%, transparent); filter: blur(70px); transform: translateY(40px) scale(1.12); }
.reel img { width: 100%; height: 100%; object-fit: cover; pointer-events: none; -webkit-user-drag: none; }
.chip { position: absolute; left: 10px; top: 10px; max-width: calc(100% - 20px); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: rgba(0, 0, 0, 0.6); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); border-radius: 999px; padding: 4px 10px; font-weight: 700; font-size: 12px; }

h2 { margin: 0; font-weight: 900; font-size: 22px; line-height: 1.1; text-wrap: balance; overflow-wrap: anywhere; }
/* The phone says whose it is in the Details sheet's head; only a desktop shows it under the title. */
#headline .yours { display: none; }
.lead { margin: 0; padding: 11px 14px; background: var(--surf); border: 1px solid var(--line); border-radius: 14px; font-weight: 500; font-size: 15px; line-height: 1.4; }
.stack { display: grid; gap: 10px; }

.stats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.stats:has(.plate:empty) { display: none; }
.plate { background: var(--surf); border: 1px solid var(--line); border-radius: 12px; padding: 8px 12px; font-weight: 700; font-size: 15px; }
.plate small { display: block; color: var(--dim); font-weight: 500; font-size: 12px; }
.plate span { color: var(--dim); font-weight: 500; }

/* The steps live only in the "How it works" sheet now (owner): never on the main screen. */
.steps { margin: 0; padding: 12px 14px; list-style: none; counter-reset: step; display: none; gap: 9px; background: var(--surf); border: 1px solid var(--line); border-radius: 14px; }
.steps li { counter-increment: step; display: grid; grid-template-columns: 24px 1fr; gap: 10px; font-weight: 500; font-size: 14px; line-height: 1.35; }
/* Step numbers in the claw green of the art (the logo on the shirt, owner 2026-10-04): red stays the button's alone. */
.steps li::before { content: counter(step); display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: var(--claw); color: #000; font-weight: 900; font-size: 12px; }
.steps b { font-weight: 700; }
/* "How it works" reads as the end of the line under the card, like a "read more": bold and underlined. */
.steps.in-sheet { display: grid; }

.btn { display: block; width: 100%; text-align: center; text-decoration: none; font-family: var(--font); font-weight: 900; font-size: 18px; line-height: 1.2; padding: 15px 16px; border: 0; border-radius: 999px; background: var(--cta); color: #fff; cursor: pointer; touch-action: manipulation; }
.btn:active:not(:disabled) { transform: scale(0.98); }
.btn.ghost { background: transparent; color: var(--fg); border: 2px solid rgba(255, 255, 255, 0.85); font-weight: 700; font-size: 16px; padding: 13px 16px; }
.btn:disabled { opacity: 0.6; cursor: default; }
/* Working: the button keeps its look and place, a spinner before the words says the page is on it. */
.btn.busy, .btn.busy:disabled { opacity: 1; display: flex; align-items: center; justify-content: center; gap: 10px; }
/* "Already have one? See your item" under the main button: the same connect, for a buyer who fears a second charge. */
.have-one { justify-self: center; font: 700 14px var(--font); color: var(--dim); background: none; border: 0; padding: 4px 6px; cursor: pointer; }
.have-one b { color: var(--fg); text-decoration: underline; text-underline-offset: 3px; }
.owned-note { margin: 0; text-align: center; color: var(--dim); font-size: 13.5px; font-weight: 600; }
.owned-note b { color: var(--claw); }
.btn.busy::before { content: ""; flex: none; width: 1em; height: 1em; border-radius: 50%; border: 2.5px solid rgba(255, 255, 255, 0.35); border-top-color: #fff; animation: spin 0.8s linear infinite; }
.note { margin: 0; color: var(--dim); font-size: 14px; line-height: 1.4; }
.note b { color: var(--fg); }
/* A line under the main button belongs to it: centred like its label. */
#actions .note { text-align: center; }
.callout { margin: 0; font-weight: 700; font-size: 16px; line-height: 1.35; }
/* Something to do before buying: the one yellow on the page, so the eye catches it over the button. */
.callout.warn { display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border-radius: 14px; font-size: 15px; background: rgba(255, 190, 40, 0.14); border: 1.5px solid #ffbe28; }
.callout.warn::before { content: "!"; flex: none; width: 22px; height: 22px; margin-top: -1px; border-radius: 50%; background: #ffbe28; color: #000; font: 900 14px/22px var(--font); text-align: center; }
/* A state nobody can act on (sold out, not open, no connection) stands where the button would be, greyed. */
.btn.state, .btn.state:disabled { opacity: 1; background: #2a2a2a; color: var(--fg); cursor: default; font-size: 17px; }
.pair { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.pair .btn { font-size: 15px; }
.err { display: flex; align-items: flex-start; gap: 10px; background: rgba(255, 90, 95, 0.16); border: 1.5px solid var(--danger); border-radius: 14px; padding: 10px 8px 10px 12px; font-weight: 600; font-size: 14px; overflow-wrap: anywhere; }
.err span { flex: 1; padding-top: 3px; }
.dismiss { flex: none; width: 28px; height: 28px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.12); color: var(--fg); font-size: 18px; line-height: 1; cursor: pointer; }

.count { display: flex; gap: 8px; }
.count div { background: var(--surf); border-radius: 12px; padding: 8px 12px; text-align: center; min-width: 64px; }
.count b { display: block; font-weight: 900; font-size: 22px; font-variant-numeric: tabular-nums; }
.count small { font-size: 11px; color: var(--dim); }

/* The details sheet: the item in the head, traits as plates, then what to do next with links stacked under each step. */
.details-head { display: flex; align-items: center; gap: 10px; }
.details-head img { width: 40px; height: 40px; border-radius: 10px; object-fit: cover; }
.details-head b { display: block; font-weight: 900; font-size: 18px; }
.details-head small { display: block; color: var(--dim); font-weight: 500; font-size: 12.5px; }
/* The minted item's traits: the one that earned the rarity framed in the tier's colour (site/ui/rarity.ts), and every
   value's share as a chip coloured by how rare it is, on the marketplaces' scale (owner, 2026-10-04). Label, value,
   then the chip pinned to the bottom, so the chips of a row line up whatever the values' lengths. */
#details-body .facts div { display: grid; grid-template-rows: auto 1fr auto; align-items: start; }
/* The colour comes with the plate (the phone's Details sheet sits outside #app, where the reveal sets its colours). */
.facts .earned { border: 1.5px solid var(--earned); background: color-mix(in srgb, var(--earned) 12%, var(--surf)); box-shadow: 0 0 14px -4px color-mix(in srgb, var(--earned) 70%, transparent); }
.earned.bronze { --earned: #d39a68; }
.earned.silver { --earned: #e6ebf2; }
.earned.gold { --earned: #ffc94d; }
.earned.diamond { --earned: #7cb6ff; }
.share { justify-self: start; margin-top: 7px; padding: 2px 7px; border-radius: 6px; font: 700 12px/1.4 var(--font); font-style: normal; }
.share.most { background: rgba(255, 255, 255, 0.08); color: rgba(255, 255, 255, 0.55); }
.share.many { background: rgba(80, 160, 255, 0.18); color: #7cc0ff; }
.share.some { background: rgba(200, 80, 220, 0.2); color: #f08cff; }
.share.few { background: rgba(230, 180, 40, 0.22); color: #ffd25c; }
.share.rarest { background: #e8457e; color: #fff; }
.share.unique { color: var(--dim); border: 1px solid #3a3a3a; }
.next { display: grid; gap: 8px; }
.step { display: grid; gap: 2px; background: var(--surf); border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; font-size: 14.5px; }
.step small { color: var(--dim); font-size: 12.5px; line-height: 1.35; }
.step-links { display: grid; gap: 6px; margin-top: 8px; }
.step .sell-note { margin: 2px 0 0; color: var(--dim); font-size: 12.5px; line-height: 1.35; }

/* ---- bottom sheets: connect a wallet, buy DRMR, how it works ---- */
.sheet {
  position: fixed; z-index: 3; left: 50%; bottom: 0; width: min(100vw, 480px); translate: -50% 0;
  max-height: 88vh; overflow-y: auto; overscroll-behavior: contain;
  --surf: rgba(255, 255, 255, 0.07); --line: rgba(255, 255, 255, 0.12);
  background: var(--sheet); border-radius: 22px 22px 0 0; box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.5);
  padding: 0 0 calc(16px + env(safe-area-inset-bottom));
  animation: up 0.3s cubic-bezier(0.2, 0.9, 0.3, 1) backwards;
}
.sheet-head { position: sticky; top: 0; z-index: 1; display: flex; justify-content: space-between; align-items: center; padding: 34px 16px 14px 20px; background: var(--sheet); touch-action: none; cursor: grab; }
/* The grab handle: drag the header down to close. */
.sheet-head::before { content: ""; position: absolute; left: 50%; top: 14px; translate: -50% 0; width: 44px; height: 5px; border-radius: 999px; background: rgba(255, 255, 255, 0.28); }
.sheet-head b { font-weight: 900; font-size: 18px; letter-spacing: -0.2px; }
.close { flex: none; display: grid; place-items: center; width: 32px; height: 32px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.1); color: var(--fg); font-size: 20px; line-height: 1; cursor: pointer; }
@keyframes up { from { transform: translateY(100%); } }
.backdrop { position: fixed; inset: 0; z-index: 2; background: rgba(0, 0, 0, 0.5); animation: fade 0.25s ease-out backwards; }
.backdrop.closing { opacity: 0; transition: opacity 0.22s ease-in; }
@keyframes fade { from { opacity: 0; } }
.sheet .note { padding: 0 16px; }
.wallet-list { padding: 4px 16px 8px; }
.wallet-list .note { padding: 0; }
.wallet-list .note b { color: var(--fg); }
/* "Copy link": a plain link under the wallet apps. */
.text-link { justify-self: center; padding: 4px; border: 0; background: none; color: var(--fg); font: 700 14px var(--font); text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
/* Wallet apps that reopen the page in their own browser: logo tiles, two to a row. */
.apps { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.app { display: flex; align-items: center; gap: 9px; padding: 10px 11px; background: var(--surf); border: 1px solid var(--line); border-radius: 12px; color: var(--fg); font-weight: 700; font-size: 14px; text-decoration: none; }
.app img { width: 24px; height: 24px; flex: none; border-radius: 6px; }
/* A wallet the browser has: its own icon, then its name. */
.btn.wallet { display: flex; align-items: center; justify-content: center; gap: 10px; }
.btn.wallet img { width: 28px; height: 28px; flex: none; border-radius: 7px; }
/* The page's address for a phone camera: white quiet zone, sized to scan from a laptop screen. */
.qr { justify-self: center; width: 200px; max-width: 100%; border-radius: 12px; overflow: hidden; }
.qr svg { display: block; width: 100%; height: auto; }
/* "How it works": short sections, each a heading over text or plates; the questions open one at a time. */
.info-body { display: grid; gap: 18px; padding: 0 20px 12px; }
.info-body section { display: grid; gap: 8px; }
.info-body h3 { margin: 0; font-weight: 900; font-size: 15px; }
.info-body p { margin: 0; font-weight: 500; font-size: 14.5px; line-height: 1.45; color: rgba(255, 255, 255, 0.86); }
.info-body p b { color: var(--fg); }
/* Rarity in How it works (site/brand/render.ts): one plate, a row per tier, highest first: the metal, what earns it,
   its chance among the items for sale (owner's pick, 2026-10-04). */
.odds { background: var(--surf); border: 1px solid var(--line); border-radius: 14px; padding: 2px 12px; display: grid; }
.odds div { display: grid; grid-template-columns: 1fr auto; gap: 10px; align-items: center; padding: 9px 0; font-weight: 500; font-size: 14px; color: rgba(255, 255, 255, 0.86); }
.odds div + div { border-top: 1px solid var(--line); }
.odds b { font-weight: 900; font-size: 15px; color: var(--fg); text-align: right; }
.metal { justify-self: start; font: 900 11px var(--font); letter-spacing: 0.7px; text-transform: uppercase; padding: 5px 10px; border-radius: 999px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), 1px 2px 6px rgba(0, 0, 0, 0.4); }
.metal.bronze { background: var(--metal-bronze); color: #2a1606; }
.metal.silver { background: var(--metal-silver); color: #1a1d22; }
.metal.gold { background: var(--metal-gold); color: #2a1d04; }
.metal.diamond { background: var(--metal-diamond); color: #0a1c3a; }
.facts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.facts div { background: var(--surf); border: 1px solid var(--line); border-radius: 12px; padding: 9px 11px; font-weight: 700; font-size: 14px; }
.facts small { display: block; color: var(--dim); font-weight: 500; font-size: 12px; }
.facts span:not(.sk) { color: inherit; font-weight: inherit; }
.split div { padding: 10px 12px; }
.split small { margin-top: 2px; }
.burned { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 13px; color: var(--dim); }
.burned:empty { display: none; }
.burned b { color: var(--fg); font-size: 15px; font-variant-numeric: tabular-nums; }
.faq { gap: 0; border-top: 1px solid var(--line); }
.faq details { border-bottom: 1px solid var(--line); padding: 11px 0; }
.faq summary { cursor: pointer; font-weight: 700; font-size: 14.5px; list-style: none; display: flex; justify-content: space-between; gap: 12px; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--dim); }
.faq details[open] summary::after { content: "\2212"; }
.faq details p { margin-top: 8px; }
footer { padding: 6px 0 6px; display: grid; justify-items: center; gap: 14px; font-weight: 700; font-size: 14px; color: var(--dim); }
footer a { color: var(--fg); }
.socials { display: flex; justify-content: center; gap: 22px; }
.social { display: grid; place-items: center; width: 28px; height: 28px; color: var(--fg); }
.social svg { width: 24px; height: 24px; fill: currentColor; }
.social.later { opacity: 0.35; }

/* ---- wide screens: the phone app in a phone-sized frame in the middle ---- */
@media (min-aspect-ratio: 3 / 4) {
  body { --phone-w: min(100vw - 32px, (100vh - 32px) * 9 / 19.5); background: #050505; }
  #app { width: var(--phone-w); height: calc(100vh - 32px); margin: 16px auto; border-radius: 28px; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.12), 0 24px 80px rgba(0, 0, 0, 0.6); }
  .sheet { width: var(--phone-w); max-height: calc((100vh - 32px) * 0.8); bottom: 16px; border-radius: 22px; }
}

/* ---- desktop (owner, 2026-10-04, variant A): the card on a stage at the left, everything to read and press in a
   panel docked at the right, sheets slide into that panel. Phones and narrow or portrait windows keep the layout above. */
.feed-nav { display: none; }
.sheet { --sheet-away: translateY(105%); }
.ptitle { display: none; }
@media (min-width: 1000px) and (min-aspect-ratio: 5 / 4) {
  body { --panel: clamp(380px, 31vw, 460px); background: var(--base); }
  #app { width: 100vw; height: 100vh; margin: 0; border-radius: 0; box-shadow: none; }
  .screen { max-width: none; margin: 0; gap: 14px; padding: 24px 32px 28px calc(100% - var(--panel) + 32px); }
  /* The panel's ground, under its content and over nothing else. */
  .screen::before { content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: var(--panel); z-index: -2; background: #0a0a0b; border-left: 1px solid #1f1f22; }
  /* The header splits: the creator over the stage, share and "?" at the top of the panel. */
  .head { display: contents; }
  .who { position: absolute; top: 28px; left: 32px; font-size: 20px; }
  .who img { width: 40px; height: 40px; }
  /* The panel's title already says NFT (owner). */
  .who > span { display: none; }
  .right { justify-content: flex-end; }
  /* The stage: the feed, the reveal and the badge, centred in everything left of the panel. */
  .art { position: absolute; top: 0; bottom: 0; left: 0; width: calc(100% - var(--panel)); padding: 96px 120px 76px; flex: none; }
  .lead { font-size: 16px; padding: 14px 16px; }
  .plate, .wallet-plate { font-size: 16px; padding: 11px 14px; }
  .btn { font-size: 19px; padding: 17px 16px; }
  h2 { font-size: 30px; }
  /* Browsing without a finger: arrows at the stage's edge, the wheel and the arrow keys (main.ts). */
  .feed-nav { position: absolute; right: 28px; top: 50%; translate: 0 -50%; display: none; gap: 12px; z-index: 3; place-self: auto; }
  .art:has(.reels.swipeable) .feed-nav { display: grid; }
  #card.revealing .feed-nav { display: none; }
  .feed-nav .round { width: 48px; height: 48px; }
  .feed-nav .round svg { width: 20px; height: 20px; stroke-width: 2.4; }
  /* No hand for a mouse: the cover says "Scroll…" and the feed still peeks. */
  .hint { display: none; }
  /* Sheets open as the panel itself, from the right; the stage stays visible and a click on it closes them. */
  .sheet { --sheet-away: translateX(105%); left: auto; right: 0; top: 0; bottom: 0; width: var(--panel); max-height: none; translate: none; border-radius: 0; box-shadow: -12px 0 40px rgba(0, 0, 0, 0.45); padding-bottom: 24px; animation-name: from-right; }
  .sheet-head { padding-top: 26px; cursor: default; }
  .sheet-head::before { display: none; }
  .backdrop { right: var(--panel); background: transparent; }
  /* The panel scrolls on its own; the stage, the creator and the panel's ground stay where they are. */
  .screen { overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: #333 transparent; }
  .art, .who, .screen::before { position: fixed; }
  .ptitle { display: block; margin: 4px 56px 4px 0; font-weight: 900; font-size: 32px; line-height: 1.05; letter-spacing: -0.5px; }
  .right { position: absolute; top: 24px; right: 32px; }
  .right [data-action="info"] { display: none; }
  /* "How it works" lives under the button here (main.ts moves it in): the same texts as the phone's sheet, without the
     two sections the panel already says above it (the lead, and the button as the steps; owner 2026-10-04). */
  .screen > #info-body { display: grid; gap: 18px; margin-top: 8px; padding: 18px 0 0; border-top: 1px solid #1f1f22; }
  .screen > #info-body :is(.about, .buy-steps) { display: none; }
  /* After a mint: the details under Share instead of behind a Details button, and "You got…" as the only title. */
  .screen > #details-body { margin-top: 8px; padding: 18px 0 0; border-top: 1px solid #1f1f22; }
  #minted [data-action="details"] { display: none; }
  #card.revealing .ptitle { display: none; }
  /* "You got…" is then the panel's first line: clear of the share button like the title it replaces. */
  #headline { margin-right: 56px; }
  #headline .yours { display: block; margin-top: 8px; color: var(--dim); font-weight: 600; font-size: 15px; letter-spacing: 0; }
  #card.revealing > #info-body { display: none; }
}
@keyframes from-right { from { transform: translateX(100%); } }

/* ---- the reveal after a mint (site/ui/reveal.ts): suspense in the centre, flip, then up to its place ---- */
/* Before it (owner, 2026-10-04): the page fades out, the closed card fades in alone and breathes, and a pulsing
   prompt under it waits for the tap that starts the suspense. 0.8 s each way. */
#card.leaving > :not(.head) { opacity: 0; pointer-events: none; transition: opacity 0.8s ease; }
#card.staged .lift { animation: stage-in 0.8s ease both; }
@keyframes stage-in { from { opacity: 0; scale: 0.94; } }
#card.waiting .flip { animation: breathe 2.4s ease-in-out infinite 0.8s; cursor: pointer; }
@keyframes breathe { 0%, 100% { scale: 1; } 50% { scale: 1.025; } }
.reveal-tap { position: absolute; top: calc(100% + 16px); left: 50%; translate: -50% 0; z-index: 2; display: none; font: 800 15px var(--font); color: var(--fg); background: rgba(0, 0, 0, 0.55);
  border: 1px solid color-mix(in srgb, var(--claw) 55%, transparent); border-radius: 999px; padding: 10px 20px; white-space: nowrap; cursor: pointer; }
.reveal-tap::before { content: ""; position: absolute; inset: -1px; border-radius: inherit; border: 2px solid var(--claw); opacity: 0; pointer-events: none; }
#card.waiting .reveal-tap { display: block; animation: nudge 1.6s ease-in-out infinite; }
#card.waiting .reveal-tap::before { animation: ring 1.6s ease-out infinite; }
@keyframes nudge { 0%, 100% { opacity: 0.75; } 50% { opacity: 1; } }
.reveal-tap .wait, .reveal-tap.loading .say { display: none; }
.reveal-tap.loading .wait { display: inline; }
#card.waiting .reveal-tap.loading { animation: none; color: var(--dim); }
#card.waiting .reveal-tap.loading::before { animation: none; }
.reveal-tap:focus-visible { outline: 3px solid var(--claw); outline-offset: 3px; }
/* The charge colour: registered so it can glide from the claw's lime to the rarity's metal at the flip. Only the
   3D label's border still reads it: painted into a big layer (the rays, the card's glow, the flash) the glide
   re-rastered that whole layer on every frame of its 0.45 s, twice per reveal (perf audit, 2026-10-04). Those three
   are now two static layers each, lime and metal, cross-faded by opacity on the compositor; --tier is the metal
   (reveal.ts sets it with the card), --claw the lime. The glide itself runs on the label (.pulse below), not on #app:
   an inherited custom property transitioning on #app restyled its whole subtree (69 elements) and repainted the
   claw's twelve masked parts, re-rastering the card's back, on every frame of the glide, at the stage and at the flip
   (perf audit, 2026-10-04). #app's value now changes in one step; the label inherits it and glides alone. */
@property --charge { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
#app { --tier: var(--claw); }
/* Just before the flip (reveal.ts ARM_MS) the metal's layers are drawn at an opacity that rounds to nothing, so their
   tiles are rastered when the cross-fade starts instead of in its first frame (the front face's trick, F-A below).
   Before the tier rules, which take over at the flip. */
#app.armed :is(.rays, .aura, .flash)::after, #app.armed .flash { opacity: 0.001; }
#app:is(.tier-bronze, .tier-silver, .tier-gold, .tier-diamond) :is(.rays, .aura, .flash)::before { opacity: 0; }
#app:is(.tier-bronze, .tier-silver, .tier-gold, .tier-diamond) :is(.rays, .aura, .flash)::after { opacity: 1; }
#card:not(.revealing) .lift, #card:not(.revealing) .tierline { display: none; }
#card.revealing :is(.window, .glow) { display: none; }
/* Card and badge are one group, centred in the free space between the header and the name. */
#card.revealing .art { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px; perspective: 1000px; }
/* During the suspense only the header and the card are on screen. */
#card.revealing.intro > :not(.head):not(.art) { display: none; }
#card.revealing > :is(.lead, .wallet-plate, .stats, #count, #status, #actions) { display: none; }
/* Once the card settles, everything under it slides up from below, one after another. */
#card.revealing :is(.tierline, #headline, #minted, #details-body) { opacity: 0; transform: translateY(36px); transition: opacity 0.45s ease, transform 0.55s cubic-bezier(0.2, 0.9, 0.25, 1); }
#card.settled :is(.tierline, #headline, #minted, #details-body) { opacity: 1; transform: none; }
#card.settled .tierline { transition-delay: 0.15s; }
#card.settled #headline { transition-delay: 0.25s; margin-top: 6px; }
#card.settled #minted { transition-delay: 0.35s; }
#card.settled #details-body { transition-delay: 0.45s; }
/* Details wears the page's dark plate, like the header's round buttons, not a white outline. */
#minted .btn.ghost { background: var(--surf); border: 1px solid var(--line); font-weight: 800; font-size: 17px; padding: 14px 16px; }
.lift { position: relative; display: grid; place-items: center; will-change: transform; }
.lift.moving { transition: transform 0.65s cubic-bezier(0.2, 0.9, 0.25, 1); }
/* Rays: pre-rasterised layers that only rotate; paused while hidden. The lime rays (::before) under the metal's
   (::after), cross-faded by opacity. Laid over each other, a plain cross-fade dips at mid-fade (the lower layer is
   partly covered); the lower layer's easing compensates for the upper's 55 % alpha, (1 − u) / (1 − 0.55u) with u on
   `ease`, which gives exactly the premultiplied mix a colour transition gives wherever the rays are solid (measured:
   265 anti-aliasing pixels differ, against 193,000 uncompensated). No blend mode: plus-lighter was as exact, but a
   blend forces a render surface with a backdrop read on every frame (GPU time in the suspense ×4 on a desktop). */
.rays { position: absolute; width: 150%; aspect-ratio: 1; border-radius: 50%; opacity: 0; pointer-events: none; will-change: transform, opacity;
  -webkit-mask: radial-gradient(closest-side, #000 15%, transparent 75%); mask: radial-gradient(closest-side, #000 15%, transparent 75%);
  animation: spin 14s linear infinite paused; transition: opacity 0.6s ease; }
.rays::before, .rays::after { content: ""; position: absolute; inset: 0; will-change: opacity, transform; transition: opacity 0.45s ease; }
/* Fading in under the metal's rays fading out (begin): u / (0.45 + 0.55u). */
.rays::before { background: repeating-conic-gradient(from 0deg, color-mix(in srgb, var(--claw) 55%, transparent) 0deg 6deg, transparent 6deg 18deg);
  transition-timing-function: linear(0, 0.0551, 0.1448, 0.2606, 0.3862, 0.5046, 0.6055, 0.6868, 0.7511, 0.8017, 0.8419, 0.8742, 0.9002, 0.9215, 0.9388, 0.9531, 0.9647, 0.9742, 0.9818, 0.9878, 0.9925, 0.9959, 0.9982, 0.9996, 1); }
.rays::after { background: repeating-conic-gradient(from 0deg, color-mix(in srgb, var(--tier) 55%, transparent) 0deg 6deg, transparent 6deg 18deg); opacity: 0; }
/* Fading out under the metal's rays fading in (the flip): 0.45u / (1 − 0.55u). */
#app:is(.tier-bronze, .tier-silver, .tier-gold, .tier-diamond) .rays::before { transition-timing-function: linear(0, 0.0117, 0.0332, 0.0666, 0.113, 0.171, 0.2371, 0.3075, 0.3793, 0.4502, 0.5189, 0.5845, 0.6463, 0.7038, 0.7566, 0.8044, 0.847, 0.8843, 0.9162, 0.9427, 0.964, 0.9802, 0.9914, 0.9979, 1); }
.rays.on { animation-play-state: running; }
/* The badge line (about 30 px plus the gap) shares the height once it shows. */
.flip { position: relative; width: min(100cqw, calc(100cqh - 42px)); aspect-ratio: 1; transform-style: preserve-3d; transition: transform 0.9s cubic-bezier(0.2, 1.25, 0.3, 1); cursor: pointer; will-change: transform; }
#card.intro .flip { width: min(100cqw, 100cqh); }
.flip .face { position: absolute; inset: 0; border-radius: 20px; backface-visibility: hidden; overflow: hidden; }
/* The back: the claw logo on black, a thin green rim, and a rainbow band that keeps crossing it from the first frame,
   slowly and without a rest. Each mark is two masks cut from the logo: its torn black edge and its core, dark green until a beat of the
   suspense lights it lime with a glow (site/ui/reveal.ts). */
.back { background: radial-gradient(110% 80% at 50% 25%, #1b1b1f, #050506 72%); }
/* The closed card's glow, once the back's own shadow: a coplanar layer under the back, culled with it at the flip, with
   the lime glow (::before) and the metal's (::after) cross-faded by opacity. Laid over each other rather than added:
   at this glow's opacity the difference from the exact mix stays under one level of 255, and no isolated group is
   needed. The shadow is outside the box, so the layer is empty where the back covers it. */
.aura { position: absolute; inset: 0; border-radius: 20px; backface-visibility: hidden; pointer-events: none; }
.aura::before, .aura::after { content: ""; position: absolute; inset: 0; border-radius: inherit; will-change: opacity; transition: opacity 0.45s ease; }
.aura::before { box-shadow: 0 0 60px color-mix(in srgb, var(--claw) 25%, transparent); }
.aura::after { box-shadow: 0 0 60px color-mix(in srgb, var(--tier) 25%, transparent); opacity: 0; }
.back::before { content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1.5px; background: linear-gradient(135deg, #6e9e14, #142004 40%, #8cc81e 60%, #142004);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; }
.back::after { content: ""; position: absolute; inset: -40%; background: linear-gradient(115deg, transparent 42%, rgba(160, 224, 0, 0.1) 47%, rgba(255, 255, 255, 0.16) 50%, rgba(37, 244, 238, 0.1) 53%, transparent 58%);
  animation: holo 1.8s ease-in-out infinite; }
/* Gone once the card turns: a running animation gets its own layer, which a hidden back face may not hide. */
.flip.flipped .back::after { content: none; }
/* From the card's left edge to its right one (the layer is 1.8 cards wide): the band is on the card from the first
   frame and crosses it once in the suspense's 1.8 s (site/ui/reveal.ts CHARGE_MS). */
@keyframes holo { from { transform: translateX(-32%); } to { transform: translateX(32%); } }
.claws { position: absolute; inset: 15%; }
/* Each mark is two baked pictures (tools/bake-claw.py): dark, and lit with its glow; a beat fades the lit one in on
   the compositor. Drawn live from masks with a blurred glow, every beat re-rendered the card (owner, 2026-10-04). */
.mark, .mark img { position: absolute; inset: 0; width: 100%; height: 100%; max-width: none; object-fit: contain; }
.mark .on { opacity: 0; transition: opacity 0.12s; will-change: opacity; }
.mark.lit .on { opacity: 1; }
.front { transform: rotateY(180deg); box-shadow: 0 30px 110px 10px color-mix(in srgb, var(--tint) 65%, transparent); }
/* The front is drawn ahead of the flip: a back-face-hidden layer is never rastered, so its first raster (and the art's
   upload) used to land mid-turn and drop two frames (flip audit F-A, 2026-10-04). While the card is closed it is not
   culled, just at an opacity that still gets tiles yet rounds to nothing under the opaque back; will-change keeps
   those tiles when .flipped lands. */
.front { will-change: transform; }
.flip:not(.flipped) .front { backface-visibility: visible; opacity: 0.001; }
.front img { width: 100%; height: 100%; object-fit: cover; }
.flip.flipped { transform: rotateY(180deg); }
/* Behind the card in its own space, so after the flip it sits just in front of the art; the ring only scales. */
.pulse { position: absolute; bottom: 14px; left: 50%; translate: -50% 0; transform: translateZ(-2px) rotateY(180deg); padding: 8px 14px; border-radius: 999px; background: rgba(0, 0, 0, 0.72);
  font: 800 13px var(--font); white-space: nowrap; border: 1px solid color-mix(in srgb, var(--charge) 60%, transparent); opacity: 0; transition: opacity 0.5s ease 0.2s, --charge 0.45s ease; pointer-events: none; }
.pulse::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; border: 2px solid var(--charge); opacity: 0; will-change: transform, opacity; }
.flip.flipped .pulse { opacity: 1; }
.flip.flipped .pulse::after { animation: ring 1.6s ease-out infinite 1s; }
@keyframes ring { 0% { transform: scale(1); opacity: 0.8; } 100% { transform: scale(1.25, 1.6); opacity: 0; } }
/* The flash: the lime one (::before) and the metal's (::after) added inside the flash's isolated group (plus-lighter
   is the premultiplied mix exactly; the rays' compensation cannot be, the flash's alpha varies across it), under the
   flash's own fade. The blend's render surface exists only while the flash shows, 0.7 s. */
.flash { position: absolute; inset: 0; z-index: 5; opacity: 0; pointer-events: none; will-change: opacity; isolation: isolate; }
.flash::before, .flash::after { content: ""; position: absolute; inset: 0; mix-blend-mode: plus-lighter; will-change: opacity, transform; transition: opacity 0.45s ease; }
.flash::before { background: radial-gradient(circle at 50% 42%, #fff, color-mix(in srgb, var(--claw) 60%, transparent) 40%, transparent 70%); }
.flash::after { background: radial-gradient(circle at 50% 42%, #fff, color-mix(in srgb, var(--tier) 60%, transparent) 40%, transparent 70%); opacity: 0; }
.flash.go { animation: flash 0.7s ease-out; }
/* Diamond only: a rainbow ring that spreads from the card as it turns, the light of a cut stone. */
.prism { position: absolute; width: 150%; aspect-ratio: 1; border-radius: 50%; pointer-events: none; opacity: 0;
  background: conic-gradient(rgba(255, 120, 220, 0.4), rgba(255, 240, 140, 0.4), rgba(140, 255, 220, 0.4), rgba(130, 170, 255, 0.4), rgba(255, 120, 220, 0.4));
  -webkit-mask: radial-gradient(closest-side, transparent 52%, #000 58%, transparent 80%); mask: radial-gradient(closest-side, transparent 52%, #000 58%, transparent 80%); }
.prism.go { animation: prism-out 1s ease-out; }
@keyframes prism-out { 0% { opacity: 1; transform: scale(0.55) rotate(0deg); } 100% { opacity: 0; transform: scale(1.15) rotate(90deg); } }
@keyframes flash { 0% { opacity: 0.95; } 100% { opacity: 0; } }
canvas.fx { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 11; }
/* Above the rays, which are wider than the card. */
.tierline { position: relative; z-index: 1; display: flex; justify-content: center; }
/* The four metals, shared by the reveal's badge and the rarity table in How it works. */
:root {
  --metal-bronze: linear-gradient(125deg, #8a5a30 0%, #a8703f 24%, #c38450 36%, #d9a36f 45%, #e8bb8b 50%, #d9a36f 55%, #c38450 64%, #a8703f 76%, #8a5a30 100%);
  --metal-silver: linear-gradient(125deg, #8d939d 0%, #a6acb5 24%, #bcc2ca 36%, #d3d8de 45%, #e2e6eb 50%, #d3d8de 55%, #bcc2ca 64%, #a6acb5 76%, #8d939d 100%);
  --metal-gold: linear-gradient(125deg, #a7781f 0%, #c8952f 24%, #dcaa45 36%, #ecc566 45%, #f5d888 50%, #ecc566 55%, #dcaa45 64%, #c8952f 76%, #a7781f 100%);
  /* Diamond is a cut stone, not a metal (owner's pick, 2026-10-04): blue, white and violet facets. */
  --metal-diamond: conic-gradient(from 20deg at 30% 50%, #8fdcff, #ffffff 12%, #5bb8f2 22%, #c6b6ff 34%, #ffffff 44%, #6fd0ff 58%, #e7f9ff 70%, #7c9cff 84%, #8fdcff);
}
/* A metal plate turning slightly in the light: a soft bevel and shadow, and a wide diagonal band of light in the
   metal's own colour that sweeps across, rests, sweeps back, rests; the last frame equals the first, so it loops. */
.badge { font: 900 13px var(--font); letter-spacing: 0.8px; text-transform: uppercase; padding: 6px 14px; border-radius: 999px; color: #111;
  background-size: 360% 100%; background-position: 100% 0; transform: perspective(160px) rotateY(-5deg) rotateX(2deg); will-change: transform;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), 2px 3px 8px rgba(0, 0, 0, 0.4); animation: sheen 6.2s infinite; }
/* The sheen moves the background and the shadow, a paint on every frame: paused while the badge is still invisible
   under the suspense and the flip, so the frames they need are not spent on it (perf audit, 2026-10-04). */
#card.revealing:not(.settled) .badge { animation-play-state: paused; }
/* Bronze, silver, gold: the order everyone already knows; diamond above them, a cut stone whose edge flashes a glint. */
.tier-bronze .badge { background-image: var(--metal-bronze); color: #2a1606; }
.tier-silver .badge { background-image: var(--metal-silver); color: #1a1d22; }
.tier-gold .badge { background-image: var(--metal-gold); color: #2a1d04; animation-duration: 5.3s; }
.tier-diamond .badge { position: relative; background-image: var(--metal-diamond); background-size: 100% 100%; color: #0a1c3a; animation-duration: 4.6s; }
.tier-diamond .badge::after { content: "✦"; position: absolute; right: 7px; top: 0; font-size: 0.85em; color: #fff; text-shadow: 0 0 6px #fff; opacity: 0; animation: glint 2.2s ease-in-out infinite; }
@keyframes glint { 0%, 60%, 100% { opacity: 0; transform: scale(0.4); } 75% { opacity: 1; transform: scale(1.2); } }
@keyframes sheen {
  0% { background-position: 100% 0; transform: perspective(160px) rotateY(-5deg) rotateX(2deg); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), 2px 3px 8px rgba(0, 0, 0, 0.4); animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1); }
  43.2% { background-position: 0% 0; transform: perspective(160px) rotateY(5deg) rotateX(-1.5deg); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), -2px 3px 8px rgba(0, 0, 0, 0.4); }
  50% { background-position: 0% 0; transform: perspective(160px) rotateY(5deg) rotateX(-1.5deg); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), -2px 3px 8px rgba(0, 0, 0, 0.4); animation-timing-function: cubic-bezier(0.45, 0, 0.55, 1); }
  93.2% { background-position: 100% 0; transform: perspective(160px) rotateY(-5deg) rotateX(2deg); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), 2px 3px 8px rgba(0, 0, 0, 0.4); }
  100% { background-position: 100% 0; transform: perspective(160px) rotateY(-5deg) rotateX(2deg); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55), inset 0 -1.5px 0 rgba(0, 0, 0, 0.18), 2px 3px 8px rgba(0, 0, 0, 0.4); }
}

/* The 3D view: a solid dim; the card sways on its own; the art is never painted over (the metal is in the frame). */
/* The rest pose of the swaying card, its shadow and its reflection; while open, swayFrame (reveal.ts) writes their
   transforms inline each frame. */
.viewer { position: absolute; inset: 0; z-index: 10; display: grid; place-items: center; align-content: center; gap: 22px; background: rgba(4, 4, 6, 0.9); opacity: 0; pointer-events: none; transition: opacity 0.3s ease; container-type: size; }
/* Falling glitter behind the card (Glitter in reveal.ts); out of the grid so the card stays centred. */
.glitter { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.viewer > .holder, .viewer > .vcap { position: relative; }
.viewer.open { opacity: 1; pointer-events: auto; }
/* Its moving parts get their own layers only while it is open: shut, seven layers (one three cards wide) sat in every
   commit of the reveal under it (perf audit, 2026-10-04). */
.viewer.open :is(.holder, .drop, .big, .sheen), .viewer.open .frame::before { will-change: transform; }
/* The finger turns the card: no scrolling or zoom gestures inside the 3D view. */
.viewer { touch-action: none; user-select: none; -webkit-user-select: none; cursor: grab; }
.viewer:active { cursor: grabbing; }
/* ✕ where the page's "?" sits (same padding as .screen, same column on a wide screen). */
.viewer-close { position: absolute; top: 14px; right: max(16px, calc((100% - 480px) / 2 + 16px)); z-index: 2; cursor: pointer; }
/* The hand drawing a side-to-side turn over the card, until the first touch (Reveal.turned). */
.vhint { position: absolute; left: 50%; top: 50%; z-index: 1; width: 56px; height: 56px; margin: -10px 0 0 -28px; pointer-events: none; opacity: 0; filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.45)); }
.vhint svg { width: 100%; height: 100%; }
.vhint.play { animation: turn-hand 2.6s cubic-bezier(0.45, 0, 0.55, 1) 0.6s infinite; }
@keyframes turn-hand { 0% { opacity: 0; transform: translateX(0) scale(0.92); } 12% { opacity: 0.9; transform: translateX(0) scale(1); } 38% { transform: translateX(-56px) rotate(-8deg); } 70% { opacity: 0.9; transform: translateX(56px) rotate(8deg); } 88%, 100% { opacity: 0; transform: translateX(0) scale(0.94); } }
.holder { display: grid; place-items: center; perspective: 900px; transform: scale(var(--s, 0.7)); transition: transform 0.45s cubic-bezier(0.2, 1.3, 0.3, 1); }
.viewer.open .holder { --s: 1; }
.holder > * { grid-area: 1 / 1; }
.drop { width: min(78cqw, 290px); aspect-ratio: 1; border-radius: 24px; background: rgba(0, 0, 0, 0.8); filter: blur(22px); transform: translate(0px, 26px) scale(0.94); }
.big { position: relative; width: min(78cqw, 290px); aspect-ratio: 1; border-radius: 22px; transform: rotateX(0deg) rotateY(0deg); }
.frame { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; background: #9aa0aa; }
.frame::before { content: ""; position: absolute; inset: -50%; }
/* The metal frame turns only while the view is open: shut, its animation ticked on every frame for nothing. */
.viewer:not(.open) .frame::before { animation-play-state: paused; }
.tier-bronze .frame::before { background: conic-gradient(#6e4420, #e8b07a, #8a5a2b, #f3c99c, #6e4420, #c98a52, #6e4420); animation: spin 9s linear infinite; }
.tier-silver .frame::before { background: conic-gradient(#8f959e, #ffffff, #a9afb8, #f2f4f7, #7d838c, #e4e6ea, #8f959e); animation: spin 7s linear infinite; }
.tier-gold .frame::before { background: conic-gradient(#7d5714, #ffe7a3, #b07f26, #fff4d1, #8a6218, #ffd77a, #7d5714); animation: spin 5s linear infinite; }
.tier-diamond .frame::before { background: conic-gradient(#8fdcff, #ffffff, #5bb8f2, #c6b6ff, #ffffff, #6fd0ff, #7c9cff, #8fdcff); animation: spin 4s linear infinite; }
.tier-bronze .big { box-shadow: 0 0 34px rgba(210, 140, 80, 0.22); }
.tier-silver .big { box-shadow: 0 0 40px rgba(225, 232, 242, 0.28); }
.tier-gold .big { box-shadow: 0 0 46px rgba(255, 201, 77, 0.38), 0 0 100px rgba(255, 201, 77, 0.12); }
.tier-diamond .big { box-shadow: 0 0 50px rgba(124, 182, 255, 0.45), 0 0 110px rgba(198, 182, 255, 0.16); }
.art3d { position: absolute; inset: 3px; border-radius: 19px; overflow: hidden; background: #111; }
.art3d img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Glass: three times the card, so its ends never show; a wide band that fades through many steps. Its light takes a
   pale tint of the card's metal (owner): light enough at these strengths to leave the picture's colours alone. */
.tier-bronze { --glare: 255 214 178; }
.tier-silver { --glare: 236 243 255; }
.tier-gold { --glare: 255 230 160; }
.tier-diamond { --glare: 210 228 255; }
.sheen { position: absolute; left: -100%; top: -100%; width: 300%; height: 300%; pointer-events: none; transform: translate(0%, 0%);
  background:
    linear-gradient(115deg, transparent 36%, rgb(var(--glare, 255 255 255) / 0.03) 40%, rgb(var(--glare, 255 255 255) / 0.08) 44%, rgb(var(--glare, 255 255 255) / 0.15) 47.5%, rgb(var(--glare, 255 255 255) / 0.2) 50%, rgb(var(--glare, 255 255 255) / 0.15) 52.5%, rgb(var(--glare, 255 255 255) / 0.08) 56%, rgb(var(--glare, 255 255 255) / 0.03) 60%, transparent 64%),
    radial-gradient(circle at 50% 50%, rgb(var(--glare, 255 255 255) / 0.1), rgb(var(--glare, 255 255 255) / 0) 18%); }
.edge { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45), inset 0 0 0 1px rgba(255, 255, 255, 0.12); }
.vcap { text-align: center; display: grid; gap: 6px; opacity: 0; transition: opacity 0.4s ease 0.2s; }
.viewer.open .vcap { opacity: 1; }
.vcap b { font: 900 20px var(--font); }
@keyframes spin { to { transform: rotate(360deg); } }
/* Desktop, after the card's and the 3D view's own rules so it wins. The card is about twice the phone's (730 against
   380 px), so what sits on or around it grows with it: the halo and the revealed card's glow reach as far past its
   edge as on a phone, and the chip, the claw's rim and glow, the 3D label and the rarity badge keep their size relative to it (owner,
   2026-10-04). The close button sits where the page's "?" is. */
@media (min-width: 1000px) and (min-aspect-ratio: 5 / 4) {
  .drop, .big { width: min(62cqh, 520px); }
  .viewer-close { top: 24px; right: 32px; }
  .glow { filter: blur(130px); transform: translateY(76px) scale(1.18); border-radius: 76px; }
  .front { box-shadow: 0 56px 200px 18px color-mix(in srgb, var(--tint) 65%, transparent); }
  .reel, .flip .face, .aura { border-radius: 30px; }
  .chip { left: 16px; top: 16px; padding: 6px 14px; font-size: 16px; }
  .back::before { padding: 3px; }
  .pulse { bottom: 22px; padding: 11px 20px; font-size: 17px; }
  .badge { font-size: 18px; padding: 8px 20px; letter-spacing: 1.1px; }
  /* The reveal takes the whole window (owner, 2026-10-04): on Buy the panel slides out to the right and the suspense
     plays in the middle; once the card settles it glides left to its stage (Reveal.settle) as the panel slides back
     in, and the panel's lines follow it in from the right. */
  .screen::before, .right { transition: transform 0.8s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease; }
  #card:is(.leaving, .intro)::before { transform: translateX(100%); }
  #card:is(.leaving, .intro) .right { opacity: 0; pointer-events: none; }
  .reveal-tap { font-size: 18px; padding: 12px 26px; }
  #card.intro .art { width: 100%; }
  #card.revealing :is(#headline, #minted, #details-body) { transform: translateX(48px); }
  #card.settled :is(#headline, #minted, #details-body) { transform: none; }
  #card.settled #headline { transition-delay: 0.35s; }
  #card.settled #minted { transition-delay: 0.45s; }
  #card.settled #details-body { transition-delay: 0.55s; }
}
/* "Tap" for a finger, "Click" for a mouse on the revealed card's 3D label. */
:is(.pulse, .reveal-tap, .cover-say) .click { display: none; }
@media (hover: hover) and (pointer: fine) { :is(.pulse, .reveal-tap, .cover-say) .tap { display: none; } :is(.pulse, .reveal-tap, .cover-say) .click { display: inline; } }
.reveal-tap .owner, .reveal-tap.own .buyer { display: none; }
.reveal-tap.own .owner { display: inline; }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
