@property --mx { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --my { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
:root {
  color-scheme: dark;
  --ink: #0a0e1f; --ink2: #121833; --panel: rgba(21, 28, 56, .78); --line: rgba(160, 180, 255, .16);
  --text: #f1f4ff; --muted: #8e9ac2; --gold: #ffc93c; --gold2: #ff8a1f; --gem: #62e6ff; --red: #ff5a6e; --green: #5dfc9a;
  --display: 'Lilita One', 'Arial Rounded MT Bold', 'Trebuchet MS', system-ui, sans-serif;
  --body: 'Nunito', 'Segoe UI', system-ui, sans-serif;
}
html, body { height: 100%; margin: 0; }
html { overflow: hidden; } /* contain the stage: FX.shake nudges #stage a few px sideways, which briefly (and on some runs stickily) widened the document past the phone's width */
body { background: radial-gradient(900px 700px at 50% 0%, #1b2350, var(--ink) 70%); color: var(--text); font-family: var(--body); font-weight: 700; overflow: hidden; touch-action: manipulation; -webkit-user-select: none; user-select: none; }
button { font: inherit; color: inherit; cursor: pointer; }
button:focus-visible, [tabindex]:focus-visible { outline: 3px solid var(--gem); outline-offset: 2px; }
#stage { position: absolute; left: 50%; top: 50%; width: 430px; height: 900px; margin: -450px 0 0 -215px; overflow: hidden; background: var(--ink); border-radius: 18px; box-shadow: 0 30px 90px #000c; }
#screen { position: absolute; inset: 0; }
.fx-canvas { position: absolute; left: 0; top: 0; width: 430px; height: 900px; pointer-events: none; z-index: 60; }
.fx-layer { position: absolute; inset: 0; pointer-events: none; z-index: 61; }
.fx-text { position: absolute; transform: translate(-50%, -50%); font-family: var(--display); font-size: 34px; color: #fff; text-shadow: 0 3px 0 #000, 0 0 18px #000; animation: floatUp 1.3s ease-out forwards; white-space: nowrap; }
.fx-text.dmg { color: #ffe3e3; font-size: 44px; -webkit-text-stroke: 1.5px #b3122f; animation: dmgPop 1.3s cubic-bezier(.17, .89, .32, 1.28) forwards; }
.fx-text.dmg.big { -webkit-text-stroke-width: 2px; -webkit-text-stroke-color: var(--gold); }
.fx-text.dmg.huge { color: var(--gold); -webkit-text-stroke-color: #7a4a00; }
.fx-text.dmg.splash, .fx-text.status.splash { font-size: 30px; -webkit-text-stroke-width: 1px; }
.fx-text.big { font-size: 58px; }
.fx-text.heal { color: var(--green); }
.fx-text.gold { color: var(--gold); font-size: 24px; }
.fx-text.small { font-size: 20px; }
.fx-text.status { color: #d7b6ff; font-size: 22px; }
.fx-text.hunt { color: #ff8a3d; font-size: 24px; -webkit-text-stroke: 1px #5a1e00; }
.fx-text.block { color: #b9c2d6; font-size: 22px; -webkit-text-stroke: 1px #2a303f; }
@keyframes floatUp { 0% { opacity: 0; transform: translate(-50%, -30%) scale(.6); } 12% { opacity: 1; transform: translate(-50%, -50%) scale(1.15); } 25% { transform: translate(-50%, -55%) scale(1); } 100% { opacity: 0; transform: translate(-50%, -140%) scale(.95); } }
@keyframes dmgPop { 0% { opacity: 0; transform: translate(-50%, -30%) scale(.3) rotate(var(--tilt, 0deg)); } 14% { opacity: 1; transform: translate(-50%, -54%) scale(1.35) rotate(var(--tilt, 0deg)); } 28% { transform: translate(-50%, -56%) scale(1) rotate(var(--tilt, 0deg)); } 100% { opacity: 0; transform: translate(-50%, -140%) scale(.95) rotate(var(--tilt, 0deg)); } }
.fx-banner { position: absolute; left: 0; right: 0; top: 400px; display: flex; justify-content: center; animation: bannerIn 1.25s ease forwards; }
.fx-banner span { position: relative; overflow: hidden; font-family: var(--display); font-size: 40px; padding: 10px 40px; background: linear-gradient(90deg, transparent, #0a0e1fee 20%, #0a0e1fee 80%, transparent); color: var(--gold); letter-spacing: 1px; text-shadow: 0 3px 0 #000; }
.fx-banner span::before { content: ''; position: absolute; top: 0; bottom: 0; left: -60%; width: 35%; background: linear-gradient(100deg, transparent, #ffffffb0, transparent); animation: bannerStreak .7s ease-out .15s 1; }
.fx-banner.foe span { color: #ff9aa8; }
.fx-banner.foe span::before { background: linear-gradient(100deg, transparent, #ffd9e8b0, transparent); }
.fx-banner.god span { color: #fff; animation: godText 1.2s ease-in-out infinite alternate; }
@keyframes godText { from { text-shadow: 0 0 10px #ff2d55, 0 0 18px #64d2ff, 0 3px 0 #000; } to { text-shadow: 0 0 10px #ffd60a, 0 0 18px #bf5af2, 0 3px 0 #000; } }
.fx-banner.ur span { color: #ffd84a; text-shadow: 0 0 12px #ffb300, 0 0 22px #ffb300, 0 3px 0 #000; }
.fx-banner.sec span { color: #fff; animation: godText 1s ease-in-out infinite alternate; }
@keyframes bannerStreak { from { left: -60%; } to { left: 120%; } }
.reduce-motion .fx-banner.god span, .reduce-motion .fx-banner.sec span { animation: none; text-shadow: 0 0 14px #bf5af2, 0 3px 0 #000; } .reduce-motion .fx-banner span::before { animation: none; display: none; }
@keyframes bannerIn { 0% { opacity: 0; transform: scaleX(.3); } 15% { opacity: 1; transform: scaleX(1); } 80% { opacity: 1; } 100% { opacity: 0; transform: translateY(-10px); } }
.fx-flash { position: absolute; inset: 0; mix-blend-mode: screen; }
.fx-star { position: absolute; transform: translate(-50%, -50%); font-family: var(--display); font-size: 26px; color: var(--gold); text-shadow: 0 0 10px #ffb03d, 0 2px 0 #000; pointer-events: none; z-index: 62; }
.evo-beam { position: absolute; width: 46px; margin-left: -23px; pointer-events: none; z-index: 57; mix-blend-mode: screen; filter: blur(1px); }
.fx-shield { position: absolute; width: 74px; height: 74px; margin: -37px 0 0 -37px; pointer-events: none; z-index: 59; clip-path: polygon(50% 0%, 95% 25%, 95% 75%, 50% 100%, 5% 75%, 5% 25%); background: radial-gradient(circle, #8fd8ff55, #8fd8ff11 70%); border: 2px solid #8fd8ffaa; animation: shieldPulse .6s ease-out forwards; }
@keyframes shieldPulse { 0% { opacity: 0; transform: scale(.4) rotate(-15deg); } 30% { opacity: 1; transform: scale(1.08) rotate(0deg); } 100% { opacity: 0; transform: scale(1.25) rotate(8deg); } }
/* ultimate cut-in: dim + a diagonal banner sweeping across with the attacker's art and move name */
.ultdim { position: absolute; inset: 0; background: #000; opacity: 0; z-index: 63; pointer-events: none; }
.ultbanner { position: absolute; left: 50%; top: 50%; width: 150%; height: 210px; margin-top: -105px; z-index: 64; pointer-events: none; overflow: hidden; display: flex; align-items: center; animation: ubSweep .95s cubic-bezier(.2, .7, .2, 1) forwards; background: linear-gradient(100deg, transparent, #05070fee 30%, #05070fee 70%, transparent); }
.ultbanner.foe { animation-name: ubSweepFoe; }
.ultbanner .ub-art { position: absolute; inset: 0; background-size: cover; background-position: center 15%; opacity: .5; mix-blend-mode: screen; }
.ultbanner .ub-lines { position: absolute; inset: 0; background: repeating-linear-gradient(100deg, #fff3 0 3px, transparent 3px 22px); animation: ubLines .5s linear infinite; }
.ultbanner .ub-name { position: relative; font-family: var(--display); font-size: 46px; color: var(--gold); letter-spacing: 2px; text-shadow: 0 4px 0 #000, 0 0 30px #ffb03d; padding-left: 8%; white-space: nowrap; }
.ultbanner.foe .ub-name { color: var(--red); text-shadow: 0 4px 0 #000, 0 0 30px #ff5a6e; }
@keyframes ubSweep { 0% { transform: translate(-160%, -50%) rotate(-4deg); } 18% { transform: translate(-50%, -50%) rotate(-4deg); } 78% { transform: translate(-50%, -50%) rotate(-4deg); } 100% { transform: translate(60%, -50%) rotate(-4deg); } }
@keyframes ubSweepFoe { 0% { transform: translate(60%, -50%) rotate(-4deg); } 18% { transform: translate(-50%, -50%) rotate(-4deg); } 78% { transform: translate(-50%, -50%) rotate(-4deg); } 100% { transform: translate(-160%, -50%) rotate(-4deg); } }
@keyframes ubLines { from { background-position: 0 0; } to { background-position: 44px 0; } }
.wide .ultbanner .ub-name { font-size: 66px; }
.wide .ultbanner { height: 280px; margin-top: -140px; }
.emote-bubble { position: absolute; transform: translate(-50%, -50%); font-size: 30px; line-height: 1; background: #0e1424e8; border: 1px solid var(--line); border-radius: 16px; padding: 6px 10px; z-index: 62; pointer-events: none; box-shadow: 0 4px 10px #0008; }
.emote-bubble.foe { border-color: #ff9aa855; }
.emote-row { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
.icon-btn.emo { font-size: 24px; }

/* ---------- generic UI ---------- */
.scr { position: absolute; inset: 0; display: flex; flex-direction: column; }
.bg-art { position: absolute; inset: 0; background: center/cover; filter: saturate(1.1); }
.bg-art::after { content: ''; position: absolute; inset: 0; background: linear-gradient(#0a0e1f55, #0a0e1fcc 55%, #0a0e1f); }

/* ---------- fantasy page backgrounds (Keith: "plain colour is boring") ----------
   #pagebg sits behind #screen (inserted once, before it, in ui.js) so it survives every screen re-render — the
   .scr templates just set data-bg and call setPageBg(scene), which crossfades between two alternating .pglayer
   divs instead of the image popping instantly. Missing art/bg/<scene>_p|l.jpg files simply render nothing here
   (no JS error), leaving the plain #stage ink colour — the "current look" fallback the plan asks for. */
#pagebg { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.pglayer { position: absolute; inset: 0; background: center/cover; opacity: 0; transition: opacity .25s linear; animation: pgKen 40s ease-in-out infinite alternate; }
.pglayer.on { opacity: 1; }
.pglayer:not(.on) { animation-play-state: paused; } /* the faded-out layer kept its 40 s zoom running unseen */
@keyframes pgKen { from { transform: scale(1); } to { transform: scale(1.06); } }
.pgfade { position: absolute; inset: 0; background: linear-gradient(#0a0e1f80, #0a0e1f33 20%, #0a0e1f33 65%, #0a0e1fb3); }
/* dense reading screens (plain paragraph/list text, not inside a glass panel) get a stronger dim */
#pagebg[data-scene="rules"] .pgfade { background: linear-gradient(#0a0e1fb3, #0a0e1f80 18%, #0a0e1f80 60%, #0a0e1fd9); }
.pgmotes { position: absolute; inset: 0; overflow: hidden; }
.pgmote { position: absolute; bottom: 0; width: 3px; height: 3px; border-radius: 50%; background: #fff; box-shadow: 0 0 6px #fff9, 0 0 12px #ffc93c66; opacity: 0; animation: pgMote linear infinite; }
@keyframes pgMote { 0% { transform: translateY(0) scale(.7); opacity: 0; } 10% { opacity: .55; } 85% { opacity: .35; } 100% { transform: translateY(calc(-1 * var(--sh) - 40px)) scale(1.1); opacity: 0; } }
.pg-paused .pglayer, .pg-paused .pgmote { animation-play-state: paused; }
.reduce-motion .pglayer { animation: none; } .reduce-motion .pgmote { display: none; }
.logo { font-family: var(--display); font-size: 46px; letter-spacing: 1px; background: linear-gradient(#fff6c8, var(--gold) 45%, var(--gold2)); -webkit-background-clip: text; background-clip: text; color: transparent; filter: drop-shadow(0 3px 0 #3b1d00) drop-shadow(0 8px 18px #000a); line-height: 1; }
.btn { border: 0; border-radius: 16px; padding: 14px 20px; font-family: var(--display); font-size: 22px; letter-spacing: .5px; background: linear-gradient(#ffe07a, var(--gold) 40%, var(--gold2)); color: #3a1c00; box-shadow: 0 5px 0 #a3530a, 0 10px 24px #000a, inset 0 2px 0 #fff8; transition: transform .08s; }
.btn:active { transform: translateY(3px); box-shadow: 0 2px 0 #a3530a, 0 6px 14px #000a; }
.btn.ghost { background: var(--panel); color: var(--text); box-shadow: 0 4px 0 #05070f, inset 0 0 0 1px var(--line); }
.btn.cyan { background: linear-gradient(#b8f6ff, var(--gem) 45%, #2a9fd8); color: #002a3a; box-shadow: 0 5px 0 #145e87, 0 10px 24px #000a, inset 0 2px 0 #fff8; }
.btn:disabled { filter: grayscale(1) brightness(.6); pointer-events: none; }
.btn.sm { font-size: 17px; padding: 9px 14px; border-radius: 12px; }
.glass { background: var(--panel); border: 1px solid var(--line); border-radius: 18px; backdrop-filter: blur(10px); }
.muted { color: var(--muted); }
.pill { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px; background: #0008; border: 1px solid var(--line); font-size: 14px; }
.gem { color: var(--gem); text-shadow: 0 0 8px #62e6ff88; }
.dust { color: #d6b8ff; }
.topbar { position: relative; display: flex; align-items: center; gap: 8px; padding: 14px 16px 8px; z-index: 2; }
.topbar .sp { flex: 1; }
.lvl { position: relative; width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; font-family: var(--display); font-size: 18px; background: conic-gradient(var(--gold) calc(var(--p) * 1%), #ffffff1a 0); }
.lvl::before { content: ''; position: absolute; inset: 4px; border-radius: 50%; background: var(--ink2); }
.lvl span { position: relative; }
.icon-btn { width: 40px; height: 40px; border-radius: 12px; border: 1px solid var(--line); background: var(--panel); font-size: 18px; display: grid; place-items: center; }
.nav { position: absolute; left: 12px; right: 12px; bottom: 12px; display: flex; gap: 6px; padding: 6px; z-index: 5; }
.nav button { flex: 1; border: 0; background: transparent; border-radius: 12px; padding: 8px 0 6px; font-size: 12px; color: var(--muted); display: flex; flex-direction: column; align-items: center; gap: 2px; position: relative; }
.nav button b { font-size: 20px; font-weight: 400; }
.nav button.on { background: #ffffff14; color: var(--text); }
.nav .dot { position: absolute; top: 4px; right: 22%; width: 9px; height: 9px; border-radius: 50%; background: var(--red); box-shadow: 0 0 8px var(--red); }
.body { position: relative; flex: 1; overflow-y: auto; padding: 4px 16px 100px; scrollbar-width: none; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.body::-webkit-scrollbar { display: none; }
h2 { font-family: var(--display); font-weight: 400; font-size: 26px; margin: 10px 0 8px; text-wrap: balance; }
h3 { font-family: var(--display); font-weight: 400; font-size: 19px; margin: 16px 0 8px; letter-spacing: .3px; }
.bar { height: 8px; border-radius: 8px; background: #ffffff18; overflow: hidden; }
.bar i { display: block; height: 100%; border-radius: 8px; background: linear-gradient(90deg, var(--gold2), var(--gold)); transition: width .8s cubic-bezier(.2, .8, .2, 1); }

/* ---------- card ---------- */
.card { --w: 180px; --c: #49b04a; position: relative; width: var(--w); aspect-ratio: 2 / 3; font-size: calc(var(--w) / 13.5); border-radius: .75em; padding: .38em; background: var(--c); box-shadow: 0 .5em 1.4em #000b; flex: none; transition: transform .15s ease-out, box-shadow .2s; }
/* no transform-style: preserve-3d on .card (REVEAL blink fix): inside the reveal's 3D .flip it made every child (.in,
   ::before, ::after, badges) its own coplanar 3D layer, and the GPU re-sorted them frame to frame on phones — the art
   and the text panel swapped places ("the card keeps blinking"). Flat = the card is one plane, one layer. */
.card .in { position: absolute; inset: .38em; border-radius: .5em; overflow: hidden; background: #000; }
.card .art { position: absolute; inset: 0; background: center/cover no-repeat; }
.card .holo, .card .glit, .card .glare { position: absolute; inset: 0; pointer-events: none; opacity: 0; }
.card .nm { position: absolute; top: 0; left: 0; right: 0; display: flex; justify-content: space-between; align-items: baseline; gap: .3em; padding: .45em .55em 1.2em; background: linear-gradient(#000d, #0000); font-family: var(--display); font-size: 1.05em; line-height: 1; text-shadow: 0 .08em .15em #000; }
.card .nm .hp { font-size: .8em; color: #ffb3bd; } .card .nm .hp b { font-weight: 400; font-size: 1.25em; color: #ff5f73; }
.card .hab { position: absolute; top: 2.05em; right: .45em; width: 1.7em; height: 1.7em; border-radius: 50%; display: grid; place-items: center; font-size: .9em; background: #000a; border: .12em solid var(--c); }
.card .stg { position: absolute; top: 2.25em; left: .55em; font-size: .6em; padding: .15em .5em; border-radius: 1em; background: #000a; color: #dfe6ff; }
.card .txt { position: absolute; left: 0; right: 0; bottom: 0; padding: 2.2em .55em .45em; background: linear-gradient(#0000, #000c 22%, #000e); }
.card .mv { display: flex; align-items: center; gap: .35em; font-size: .72em; margin-top: .25em; }
.card .mv .cost { color: var(--gem); letter-spacing: -.1em; min-width: 2.2em; text-shadow: 0 0 .4em #62e6ff88; }
.card .mv .mn { flex: 1; font-weight: 800; }
.card .mv .dm { font-family: var(--display); font-size: 1.35em; }
.card .fxl { font-size: .54em; color: #c9d3f0; margin: .05em 0 0 2.9em; line-height: 1.25; font-weight: 700; }
.card .burst { font-size: .56em; color: var(--gold); margin-top: .3em; line-height: 1.25; }
.card .trait { font-size: .56em; color: #d8c2ff; margin-top: .2em; line-height: 1.25; }
.card .ft { display: flex; justify-content: space-between; font-size: .5em; color: #aab5d6; margin-top: .4em; letter-spacing: .05em; }
.card .ft .rr { font-weight: 900; }
.card.trick { --c: #d9b36a; }
.card.trick .tt { font-size: .66em; line-height: 1.3; margin-top: .2em; color: #e8ecff; }
.card.trick .kind { position: absolute; top: 2.2em; left: .55em; font-size: .6em; padding: .15em .5em; border-radius: 1em; background: #5b3d00cc; color: #ffe6a8; }
.card.back { background: #1a2350; }
.card.back .art { background-image: url(art/card_back.jpg); }
.card .newb { position: absolute; top: -.6em; right: -.6em; z-index: 3; font-family: var(--display); font-size: .8em; padding: .2em .55em; border-radius: .6em; background: var(--red); color: #fff; box-shadow: 0 .2em .6em #000a; transform: rotate(8deg); animation: newSlam .5s cubic-bezier(.2, .9, .3, 1.4) both; }
@keyframes newSlam { 0% { transform: rotate(-20deg) scale(2.4); opacity: 0; } 60% { transform: rotate(10deg) scale(.9); opacity: 1; } 100% { transform: rotate(8deg) scale(1); } }
.reduce-motion .card .newb { animation: none; }
/* rarities — base frame colour/pattern per rarity; the foil layers themselves are in HOLO v4 below */
.card.r-R { background: linear-gradient(135deg, #eef2f7, #8b95a5 25%, #fafcff 50%, #8b95a5 75%, #eef2f7); background-size: 250% 250%; background-position: var(--mx) var(--my); }
.card.r-SR { background: linear-gradient(115deg, #ff2d55, #ff9f0a, #ffd60a, #30d158, #64d2ff, #5e5ce6, #bf5af2, #ff2d55); background-size: 300% 300%; background-position: var(--mx) var(--my); }
.card.r-UR { background: linear-gradient(135deg, #fff1a8, #d4a017 18%, #fff8d6 36%, #b8860b 54%, #ffe680 72%, #c9a227 90%, #fff1a8); background-size: 250% 250%; background-position: var(--mx) var(--my); }
.card.r-SEC { background: linear-gradient(135deg, #0b0b0f, #2a2a35 20%, #0b0b0f 40%, #3a3a48 60%, #0b0b0f 80%, #2a2a35); box-shadow: 0 .5em 1.4em #000b, 0 0 1.6em #b88cff55; }
.card.r-SEC::after { content: ''; position: absolute; inset: 0; border-radius: .75em; padding: .38em; background: linear-gradient(115deg, #7a5200, #ffe680, #fff6cf, #b8860b, #ffd84a, #7a5200); background-size: 300% 300%; background-position: var(--mx) var(--my); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; opacity: .85; pointer-events: none; }
.card.r-UR .nm span:first-child, .card.r-SEC .nm span:first-child { background: linear-gradient(90deg, #fff1a8, #ffd84a, #fff8d6); -webkit-background-clip: text; background-clip: text; color: transparent; }
.card.r-SEC .nm span:first-child { background-image: linear-gradient(90deg, #ff6b88, #ffd60a, #5dfc9a, #64d2ff, #d69bff); }
.card.tilting { transition: none; }
.reduce-motion .card { animation: none !important; }

/* mini (battle) cards */
.card.mini { font-size: calc(var(--w) / 7.5); border-radius: .45em; padding: .22em; }
.card.mini .in { inset: .22em; border-radius: .3em; }
.card.mini .nm { font-size: .62em; padding: .35em .4em .9em; }
.card.mini .hpb { position: absolute; left: .3em; right: .3em; bottom: .3em; height: .55em; border-radius: .3em; background: #000b; overflow: hidden; box-shadow: 0 0 0 .08em #0008; }
.card.mini .hpb i { position: absolute; inset: 0 auto 0 0; background: linear-gradient(90deg, #36e07b, #9dff6b); border-radius: .3em; transition: width .6s cubic-bezier(.2, .8, .2, 1); }
.card.mini .hpb.mid i { background: linear-gradient(90deg, #f5b52e, #ffe16b); }
.card.mini .hpb.low i { background: linear-gradient(90deg, #ff3d5a, #ff8a6b); }
.card.mini .hpt { position: absolute; right: .35em; bottom: .95em; font-family: var(--display); font-size: .7em; text-shadow: 0 .1em .15em #000, 0 0 .4em #000; }
.card.mini .sts { position: absolute; left: .3em; bottom: .95em; display: flex; gap: .15em; font-size: .62em; }
.card.mini .sts span { background: #000b; border-radius: .4em; padding: .05em .25em; }
.card.mini .hab { top: auto; bottom: 1.9em; right: .3em; font-size: .55em; }
.card.mini .stg { display: none; }

/* ---------- home ---------- */
.home-hero { position: relative; display: flex; flex-direction: column; align-items: center; padding-top: 4px; }
.showcase { perspective: 900px; margin: 6px 0 4px; }
.showcase .card { --w: 186px; }
.cap { font-size: 13px; color: var(--muted); text-align: center; }
.cta { display: grid; grid-template-columns: 1.6fr 1fr; gap: 10px; margin-top: 14px; }
.cta .btn { padding: 16px 8px; }
.tile { display: flex; align-items: center; gap: 12px; padding: 12px; margin-top: 12px; }
.tile .pack-mini { width: 54px; height: 76px; flex: none; }
.tile .grow { flex: 1; min-width: 0; }
.tile .t1 { font-family: var(--display); font-size: 19px; }
.mis { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-top: 1px solid var(--line); }
.mis:first-of-type { border-top: 0; }
.mis .grow { flex: 1; }
.mis .rw { font-size: 12px; color: var(--gold); white-space: nowrap; }
.mis.done { opacity: .55; }
.mis .bar { margin-top: 5px; height: 6px; }

/* ---------- pack ---------- */
.pack { position: relative; width: 210px; height: 300px; border-radius: 16px; overflow: hidden; --pc: var(--gold); background: linear-gradient(135deg, color-mix(in srgb, var(--pc) 55%, #fff), color-mix(in srgb, var(--pc) 80%, #000) 20%, color-mix(in srgb, var(--pc) 35%, #fff) 40%, color-mix(in srgb, var(--pc) 85%, #000) 60%, color-mix(in srgb, var(--pc) 55%, #fff) 80%); padding: 7px; box-shadow: 0 20px 50px #000c; }
.pack[data-set="wild"] { background: conic-gradient(from 200deg, #ff5f6d, #ffc371, #47cf73, #3fa7ff, #a566ff, #ff5f6d); }
.pack .pin { position: absolute; inset: 7px; border-radius: 11px; overflow: hidden; background: #0b1230 var(--hero, url(art/key_art.jpg)) center 20%/cover; }
.pack .pin::after { content: ''; position: absolute; inset: 0; background: linear-gradient(#0000 55%, #0a0e1fdd); }
.pack .ptitle { position: absolute; left: 0; right: 0; bottom: 30px; text-align: center; font-family: var(--display); font-size: 26px; color: var(--pc); text-shadow: 0 3px 0 #000; z-index: 1; }
.pack[data-set="wild"] .ptitle { color: #fff; }
.pack .psub { position: absolute; left: 0; right: 0; bottom: 12px; text-align: center; font-size: 12px; letter-spacing: 2px; color: #fff; z-index: 1; }
.pack .sheen { position: absolute; inset: 0; background: linear-gradient(115deg, transparent 35%, #ffffff55 48%, transparent 60%); background-size: 300% 100%; animation: packSheen 3.2s linear infinite; mix-blend-mode: overlay; z-index: 2; }
@keyframes packSheen { from { background-position: 120% 0; } to { background-position: -120% 0; } }
.pack .top { position: absolute; left: 0; right: 0; top: 0; height: 46px; z-index: 3; background: inherit; border-bottom: 2px dashed #0006; }
.pack .cutline { position: absolute; left: 0; top: 20%; height: 4px; width: 0; border-radius: 2px; z-index: 4; opacity: 0; pointer-events: none;
  background: linear-gradient(90deg, #fff, #fffde0, #fff); box-shadow: 0 0 10px 3px #fff, 0 0 22px 8px var(--pc); }
/* light leaking through the cut as it grows (--cutpct, 0..100, set by packCut's onMove) */
.pack .slitlight { position: absolute; left: 0; right: 0; top: 0; height: calc(var(--cutpct, 0) * 1%); max-height: 30%; z-index: 3; pointer-events: none;
  background: linear-gradient(to bottom, #fff, transparent); opacity: calc(var(--cutpct, 0) / 100 * 0.9); mix-blend-mode: screen; }
/* orbiting motes in the pack's accent colour during the hero moment */
.pack .motes { position: absolute; inset: 0; pointer-events: none; z-index: 2; }
.pack .mote { position: absolute; top: 50%; left: 50%; width: 6px; height: 6px; margin: -3px; border-radius: 50%; background: var(--pc);
  box-shadow: 0 0 8px var(--pc), 0 0 16px var(--pc); opacity: .8; animation: moteOrbit 3.6s linear infinite; animation-delay: calc(var(--i) * -0.6s); }
@keyframes moteOrbit { from { transform: rotate(0deg) translateX(150px) rotate(0deg); } to { transform: rotate(360deg) translateX(150px) rotate(-360deg); } }
.reduce-motion .pack .mote { animation: none; opacity: .4; }
/* god pack: the pack itself shakes and glows rainbow before the cut — the moment people chase */
.pack.god { background: conic-gradient(from 0deg, #ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55); animation: godSpin 5s linear infinite; }
.pack.godshake { animation: godSpin 5s linear infinite, godShake .5s ease-in-out infinite, godPulse 1.4s ease-in-out infinite; }
@keyframes godSpin { to { filter: hue-rotate(360deg); } }
@keyframes godShake { 0%, 100% { transform: translate(0, 0) rotate(0); } 20% { transform: translate(-4px, 2px) rotate(-1.4deg); } 40% { transform: translate(4px, -2px) rotate(1.4deg); } 60% { transform: translate(-3px, -2px) rotate(-1deg); } 80% { transform: translate(3px, 2px) rotate(1deg); } }
@keyframes godPulse { 0%, 100% { box-shadow: 0 20px 50px #000c, 0 0 2.5em #ff2d55aa; } 50% { box-shadow: 0 20px 50px #000c, 0 0 3.5em #64d2ffaa; } }
.reduce-motion .pack.god, .reduce-motion .pack.godshake { animation: none; }
.pack-mini { border-radius: 7px; padding: 3px; }
.pack-mini .pin { inset: 3px; border-radius: 5px; }
/* pack cart: each set's box plus its quantity stepper and quick-select row */
.stepper { display: flex; align-items: center; gap: 10px; }
.stepper .btn.sm { width: 34px; height: 34px; padding: 0; line-height: 1; font-size: 18px; }
.stepn { min-width: 20px; text-align: center; font-family: var(--display); font-size: 16px; }
.quickrow { display: flex; gap: 6px; }
.quickrow .btn.sm { padding: 9px 14px; font-size: 12px; }
/* luminous pastel stage (TCG-Pocket style): a soft radial tinted by --bd (pack accent → per-card rarity colour),
   with slow drifting bokeh behind everything and an optional rainbow wash for SEC pulls / god packs */
.opening { position: absolute; inset: 0; z-index: 40; overflow: hidden; --bd: #bfe0ff; background: radial-gradient(circle at 50% 42%, color-mix(in srgb, var(--bd) 45%, #fff7fb), color-mix(in srgb, var(--bd) 26%, #eef3ff) 55%, color-mix(in srgb, var(--bd) 12%, #dfe6f7) 100%); transition: background 1s ease; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.opening::before { content: ''; position: absolute; inset: -10%; z-index: -1; pointer-events: none; opacity: .55; filter: blur(2px);
  background-image: radial-gradient(circle, #fff9 0 3%, transparent 60%), radial-gradient(circle, #fff7 0 4%, transparent 60%), radial-gradient(circle, #fff8 0 2.5%, transparent 60%), radial-gradient(circle, #fff6 0 5%, transparent 60%);
  background-size: 20% 20%, 28% 28%, 16% 16%, 32% 32%; background-position: 14% 22%, 74% 64%, 40% 80%, 82% 18%; animation: bokehDrift 24s ease-in-out infinite alternate; }
@keyframes bokehDrift { from { transform: translate(-2%, -1%) scale(1); } to { transform: translate(2%, 3%) scale(1.06); } }
.opening.rainbow::after { content: ''; position: absolute; left: 50%; top: 45%; width: 1100px; height: 1100px; margin: -550px; z-index: -1; pointer-events: none; opacity: .4; mix-blend-mode: color;
  background: conic-gradient(from 0deg, #ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55); animation: spin 7s linear infinite; }
.reduce-motion .opening::before, .reduce-motion .opening.rainbow::after { animation: none; }
/* pointer-drag pack cut: the browser must not steal a horizontal drag as a page pan or the "slash" gesture never fires on touch */
.opening, .pack { touch-action: none; }
/* fxgl.js's WebGL nebula/rays/pillar/shockwave layer — sits behind everything else in the overlay (see fxgl.js
   FXGL.start, which inserts it as the overlay's first child); a no-op, invisible div if WebGL isn't available */
.fxgl-canvas { position: absolute; inset: 0; z-index: 0; pointer-events: none; }
/* SEC reveal's "time stops" beat: freezes every CSS animation/transition in the overlay for a beat (FX.freeze()
   freezes the canvas particles at the same time) */
.opening.frozen * { animation-play-state: paused !important; transition: none !important; }
.opening .hint { position: absolute; bottom: 70px; left: 0; right: 0; text-align: center; color: #fff; background: #05070fcc; width: fit-content; margin: 0 auto; padding: 6px 16px; border-radius: 999px; font-size: 15px; animation: pulse 1.6s ease-in-out infinite; }
/* meteor tell: a streaking comet, coloured by the pack's best rarity (white/rainbow for god packs), that lands
   where the pack is about to drop in */
.meteor { position: absolute; left: 0; top: 0; width: 10px; height: 10px; margin: -5px; border-radius: 50%; z-index: 3; pointer-events: none;
  background: var(--mc, #fff); box-shadow: 0 0 12px 4px var(--mc, #fff), -34px 6px 26px 2px var(--mc, #fff); }
.meteor.big { width: 16px; height: 16px; margin: -8px; box-shadow: 0 0 20px 7px var(--mc, #fff), -60px 10px 40px 4px var(--mc, #fff); }
.meteor.rainbow { background: conic-gradient(#ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55); box-shadow: 0 0 22px 8px #fff, -60px 10px 40px 4px #fff; }
/* multi-open: the ceremony's own centred layout moves onto this fill-the-overlay child, so the persistent HUD
   (pack count + Skip controls) can sit above it without being pushed around by the ceremony's flex centering */
.opening-content { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
.packhud { position: absolute; top: 14px; left: 0; right: 0; text-align: center; font-family: var(--display); font-size: 15px; color: #fff; text-shadow: 0 2px 0 #000; z-index: 2; }
.packhudbtns { position: absolute; top: 42px; left: 0; right: 0; display: flex; gap: 8px; justify-content: center; z-index: 2; }
.packhudbtns .btn { font-size: 12px; padding: 6px 12px; }
/* single-pack ceremony: just the vibration toggle, tucked in the corner instead of the skip-controls row */
.packhudbtns.solo { top: 14px; right: 14px; left: auto; justify-content: flex-end; }
.vibetoggle { width: 34px; height: 34px; font-size: 16px; }
@keyframes pulse { 50% { opacity: .35; } }
.opening h2 { text-shadow: 0 2px 0 #000, 0 0 14px #0009; }
.stack { position: relative; width: 250px; height: 375px; perspective: 1200px; }
.stack.glow::before { content: ''; position: absolute; inset: -14px; border-radius: 22px; background: var(--edge, #bf5af2); filter: blur(26px); opacity: .55; z-index: -1; animation: aura 1.3s ease-in-out infinite; }
.reduce-motion .stack.glow::before { animation: none; }
.stack .slot { position: absolute; inset: 0; transition: transform .5s cubic-bezier(.2, .8, .2, 1), opacity .4s; }
.flip { position: relative; width: 250px; height: 375px; transform-style: preserve-3d; transition: transform .55s cubic-bezier(.3, 1.4, .5, 1); }
.flip > .card { position: absolute; inset: 0; --w: 250px; backface-visibility: hidden; }
.flip > .card.front { transform: rotateY(180deg); }
.flip.open { transform: rotateY(180deg); }
.aura { position: absolute; inset: -40px; border-radius: 40px; filter: blur(22px); opacity: 0; animation: aura 1.1s ease-in-out infinite; pointer-events: none; }
.aura.SR { background: conic-gradient(#ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55); opacity: .7; }
.aura.UR { background: radial-gradient(#fff3a8, #ffb300 60%, transparent 70%); opacity: .9; }
.aura.SEC { background: conic-gradient(#ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55); opacity: 1; filter: blur(30px); animation-duration: .6s; }
/* rarity-upgrade tease: the honest "one tier lower" glow shown first (SR+ only, 35% of the time) */
.aura.C { background: radial-gradient(#eaf3ff, #bcd6ff 55%, transparent 70%); opacity: .55; }
.aura.crackup { animation: auraCrack .42s ease-out; }
@keyframes auraCrack { 0% { opacity: 1; filter: blur(6px) brightness(2.4); transform: scale(1); } 40% { opacity: 1; filter: blur(2px) brightness(3); transform: scale(1.18); } 100% { opacity: 1; filter: blur(22px) brightness(1); transform: scale(1); } }
@keyframes aura { 50% { transform: scale(1.12); } }
.rays { position: absolute; left: 50%; top: 50%; width: 900px; height: 900px; margin: -450px; background: repeating-conic-gradient(from 0deg, #ffd84a33 0 6deg, transparent 6deg 18deg); animation: spin 14s linear infinite; pointer-events: none; mask: radial-gradient(#000 20%, transparent 60%); -webkit-mask: radial-gradient(#000 20%, transparent 60%); }
.rays.SEC { background: repeating-conic-gradient(from 0deg, #ff2d5533 0 6deg, #64d2ff33 6deg 12deg, transparent 12deg 18deg); }
@keyframes spin { to { transform: rotate(360deg); } }
/* SEC reveal: light-shard crack overlay, and a quick red/cyan chromatic-aberration pulse */
.crackoverlay { position: absolute; inset: 0; pointer-events: none; z-index: 3; opacity: 0;
  background: repeating-conic-gradient(from 0deg, #fff9 0 1.2deg, transparent 1.2deg 14deg), repeating-conic-gradient(from 8deg, #fff6 0 .6deg, transparent .6deg 22deg);
  mix-blend-mode: screen; animation: crackIn .5s ease-out forwards; }
@keyframes crackIn { 0% { opacity: 0; transform: scale(.7); } 50% { opacity: 1; transform: scale(1.05); } 100% { opacity: 0; transform: scale(1.25); } }
.chromapulse { position: absolute; inset: -6px; pointer-events: none; z-index: 5; mix-blend-mode: screen;
  background: linear-gradient(90deg, #ff2d5555 0%, transparent 3%), linear-gradient(90deg, transparent 97%, #64d2ff55 100%);
  animation: chromaPulse .35s ease-out forwards; }
@keyframes chromaPulse { from { transform: translateX(-7px); opacity: 1; } to { transform: translateX(7px); opacity: 0; } }
.reduce-motion .crackoverlay, .reduce-motion .chromapulse { display: none; }
/* SEC card materialising from glowing particles instead of an instant flip */
.matdissolve { filter: blur(0) brightness(1) saturate(1); opacity: 1; transition: filter .5s cubic-bezier(.2, .8, .2, 1), opacity .5s; }
.matdissolve.pre { filter: blur(16px) brightness(2.2) saturate(0); opacity: 0; }
.reduce-motion .matdissolve, .reduce-motion .matdissolve.pre { filter: none; opacity: 1; transition: none; }
/* dust-gain / collection counters ticking up (tickUp() in ui.js swaps the textContent every frame) */
.dustn { color: var(--gold); }
.rlabel { margin-top: 18px; font-family: var(--display); font-size: 26px; text-align: center; text-shadow: 0 3px 0 #000; min-height: 34px; }
.rlabel small { display: block; font-family: var(--body); font-size: 13px; color: var(--muted); }
.opening .rlabel { background: #05070fb3; padding: 8px 20px; border-radius: 16px; box-shadow: 0 6px 18px #0006; }
.sumgrid { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; padding: 0 12px; }
.sumgrid .card { --w: 118px; }
.sumgrid .card.best { --w: 138px; z-index: 2; box-shadow: 0 .8em 2em #000d, 0 0 2em var(--edge, #ffd84a88); }
.wide .sumgrid .card.best { --w: 205px; }

/* ---------- dex / deck ---------- */
.dexline { display: flex; gap: 8px; align-items: flex-start; margin-bottom: 12px; }
.dexline .card { --w: 104px; }
.dexline .lbl { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 11px; color: var(--muted); letter-spacing: 1px; text-transform: uppercase; align-self: center; }
/* CJK glyphs already stand upright in vertical-rl; the 180deg spine flip above (for Latin) turns them upside down */
:lang(zh) .dexline .lbl { transform: none; }
.card.missing .art { filter: blur(6px) brightness(.45) saturate(.35); transform: scale(1.15); }
.card.missing .in::after { content: '?'; position: absolute; inset: 0; display: grid; place-items: center; font-family: var(--display); font-size: 3.2em; color: #ffffff55; text-shadow: 0 .05em .2em #000; }
.card.missing { background: #2a3150 !important; animation: none !important; }
.card.missing .holo, .card.missing .glit, .card.missing .glare, .card.missing .txt, .card.missing::after { display: none; }
.card.missing .nm span:first-child { color: #8e9ac2; background: none; }
.card .cnt { position: absolute; bottom: -.5em; left: 50%; transform: translateX(-50%); z-index: 3; font-family: var(--display); font-size: .8em; padding: .1em .6em; border-radius: 1em; background: #000d; border: 1px solid var(--line); }
.fresh-dot { position: absolute; top: -.3em; left: -.3em; z-index: 3; width: .9em; height: .9em; border-radius: 50%; background: var(--red); box-shadow: 0 0 .6em var(--red); }
/* combined pack-opening results: which set a card came from */
.setdot { position: absolute; bottom: -.3em; right: -.3em; z-index: 3; width: .8em; height: .8em; border-radius: 50%; background: var(--c, #fff); box-shadow: 0 0 0 .15em #000a, 0 0 .5em var(--c, #fff); }
.filters { display: flex; gap: 6px; flex-wrap: wrap; margin: 8px 0; }
.filters button { border: 1px solid var(--line); background: var(--panel); border-radius: 999px; padding: 8px 12px; font-size: 13px; }
.filters button.on { background: #2b3563; border-color: var(--gem); }
.deckgrid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
.deckgrid .card { --w: 72px; }
.collgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 9px 7px; }
.collgrid .card { --w: 90px; }
/* long grids (collection/deck/dex can run 60+ cards): let the browser skip layout/paint for off-screen cards
   entirely, including their holo/glit/mask layers, instead of every card costing paint whether visible or not */
.deckgrid .card, .collgrid .card, .dexline .card { content-visibility: auto; contain-intrinsic-size: var(--w) calc(var(--w) * 1.5); }
.collgrid .card.out { opacity: .35; }
.probs { font-size: 13px; color: #ffb3bd; min-height: 18px; }
.warns { font-size: 12px; color: var(--gold); }

/* deck page: cards grouped by evolution line (S1 -> S2 -> S3), Duo pairing, summary strip, Auto-build overlay */
.deckgroup, .collgroup { display: flex; flex-direction: column; }
.linerow { display: flex; align-items: center; gap: 10px; padding: 9px 10px; margin-bottom: 7px; }
.linerow.dim { opacity: .5; padding: 7px 10px; }
.linehead { flex: 0 0 108px; font-size: 11px; line-height: 1.4; }
.linehead b { font-family: var(--display); font-size: 13px; display: block; }
.duob { display: block; margin-top: 2px; font-size: 10px; color: var(--muted); }
.duob.got { color: var(--green); }
.linecards { display: flex; align-items: center; gap: 4px; overflow-x: auto; flex: 1; min-width: 0; padding: 2px 0 6px; }
.linecards .arrow { flex: 0 0 auto; color: var(--muted); font-size: 16px; }
.linerow .card { --w: 64px; }
.linerow.compact .card { --w: 54px; }
.dsummary { padding: 9px 11px; margin: 8px 0; font-size: 12px; line-height: 1.7; }
.dsummary .chip, .chip { display: inline-block; background: #2b3563; border: 1px solid var(--line); border-radius: 999px; padding: 2px 9px; margin: 2px 4px 0 0; font-size: 11px; color: var(--gold); }
.winbanner { padding: 10px 12px; margin: 8px 0; font-size: 14px; text-align: center; color: var(--green); font-family: var(--display); }
.duopair { padding: 12px; margin-bottom: 10px; }
.duohead { margin-bottom: 6px; }
.duohead .got { color: var(--green); font-size: 12px; margin-left: 6px; }
.buildov .buildbar { width: 220px; margin: 4px auto 0; }

/* ---------- trials (legacy, still reachable via ACT.trial) ---------- */
.trial { display: flex; gap: 12px; align-items: center; padding: 10px; margin-bottom: 10px; width: 100%; text-align: left; }
.trial .av { width: 64px; height: 64px; border-radius: 16px; background: center 25%/cover; flex: none; border: 2px solid var(--c); }
.trial .grow { flex: 1; }
.trial .t1 { font-family: var(--display); font-size: 19px; }
.trial .t2 { font-size: 13px; color: var(--muted); }
.trial.lock { opacity: .45; }
.trial .done { color: var(--green); font-size: 13px; }

/* ---------- adventure map ---------- */
.advmap { max-width: 400px; margin: 0 auto; }
.wide .advmap { max-width: 1100px; }
.advregion { position: relative; border-radius: 20px; overflow: hidden; margin-bottom: 18px; background: center/cover; border: 1px solid var(--line); }
.advregion::before { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, #05070fcc, #05070fa0 30%, #05070fd8); }
.advhead { position: relative; z-index: 2; display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; font-family: var(--display); font-size: 16px; text-shadow: 0 2px 0 #000; }
.advhead .advstarcount { font-size: 13px; color: var(--gold); font-family: var(--body); font-weight: 800; }
.advtrack { position: relative; height: 640px; }
.advpath { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.advpath path { fill: none; stroke: var(--c); stroke-width: 4; stroke-dasharray: 10 9; stroke-linecap: round; vector-effect: non-scaling-stroke; opacity: .8; filter: drop-shadow(0 0 2px #000) drop-shadow(0 0 2px #000); }
.advnode { position: absolute; z-index: 2; transform: translate(-50%, -50%); width: 62px; height: 62px; border-radius: 50%; border: 3px solid var(--c); background: #0e1424; display: grid; place-items: center; font-family: var(--display); font-size: 20px; box-shadow: 0 6px 16px #000a; padding: 0; }
.advnode:not(.lock) { animation: canPulse 1.6s ease-in-out infinite; }
.advnode.done { animation: none; background: linear-gradient(#ffe07a, var(--gold2)); border-color: var(--gold); }
.advnode.lock { opacity: .4; filter: grayscale(.6); animation: none; }
.advnode.boss { width: 84px; height: 84px; font-size: 30px; border-width: 4px; box-shadow: 0 8px 22px #000c, 0 0 20px var(--c); }
.advface { width: 100%; height: 100%; border-radius: 50%; background: center 25%/cover; display: grid; place-items: center; font-size: 22px; }
.advstars { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); font-size: 12px; color: var(--gold); white-space: nowrap; text-shadow: 0 1px 0 #000; }
.advfinale { position: relative; display: flex; gap: 12px; align-items: center; padding: 14px; border-radius: 20px; background: linear-gradient(135deg, #2a0f3a, #05070f); border: 2px solid #d875ff; box-shadow: 0 0 24px #d875ff55; text-align: left; width: 100%; color: inherit; }
.advfinale.lock { opacity: .45; filter: grayscale(.6); }
.advfinale .advface { width: 68px; height: 68px; border: 2px solid #d875ff; flex: none; font-size: 30px; }
.advfinale .grow { flex: 1; min-width: 0; }
.advfinale .t1 { font-family: var(--display); font-size: 19px; }
.advfinale .t2 { font-size: 12px; color: var(--muted); }
.advgoals { display: flex; flex-direction: column; gap: 4px; font-size: 13px; color: var(--muted); margin: 8px 0; }
.advgoals .got { color: var(--gold); }
.stagestars { display: flex; gap: 10px; justify-content: center; font-size: 32px; }
.stagestars i { font-style: normal; opacity: .25; transform: scale(.6); display: inline-block; transition: transform .3s cubic-bezier(.3, 1.6, .5, 1), opacity .3s; }
.stagestars i.on { opacity: 1; transform: scale(1); color: var(--gold); text-shadow: 0 0 12px #ffc93caa; }
.wide .advnode { width: 88px; height: 88px; font-size: 28px; }
.wide .advnode.boss { width: 116px; height: 116px; font-size: 40px; }
.wide .advtrack { height: 780px; }
.wide .advhead { font-size: 22px; padding: 16px 22px; }
.wide .advfinale .t1 { font-size: 26px; }
.wide .advfinale .t2 { font-size: 15px; }
.wide .advfinale .advface { width: 96px; height: 96px; }
.wide .advgoals { font-size: 16px; }
.wide .stagestars { font-size: 44px; }

/* ---------- battle ---------- */
.battle { position: absolute; inset: 0; }
.battle .arena { position: absolute; inset: 0; background: center/cover; }
.battle .arena::after { content: ''; position: absolute; inset: 0; background: linear-gradient(#0a0e1fcc, #0a0e1f55 30%, #0a0e1f55 62%, #0a0e1fee 80%); }
.bt-top, .bt-bar { position: absolute; left: 10px; right: 10px; display: flex; align-items: center; gap: 8px; z-index: 3; }
.bt-top { top: 10px; }
.bt-bar { top: 702px; }
.who { display: flex; align-items: center; gap: 8px; font-size: 14px; }
.who .av { width: 34px; height: 34px; border-radius: 10px; background: center 25%/cover; border: 2px solid var(--line); }
.stars { font-size: 20px; letter-spacing: 1px; color: #ffffff30; }
.stars b { color: var(--gold); text-shadow: 0 0 10px #ffc93caa; font-weight: 400; }
.gems { display: flex; gap: 3px; }
.gems i { width: 14px; height: 14px; transform: rotate(45deg); border-radius: 3px; background: #ffffff18; border: 1px solid #ffffff22; }
.gems i.on { background: linear-gradient(135deg, #d8fbff, var(--gem) 50%, #1e8ec2); box-shadow: 0 0 8px #62e6ffaa; border-color: #fff8; }
.gems i.extra { background: linear-gradient(135deg, #fff2c0, var(--gold)); box-shadow: 0 0 8px #ffc93caa; }
.cnts { font-size: 12px; color: var(--muted); }
.row { position: absolute; left: 0; right: 0; display: flex; justify-content: center; gap: 12px; z-index: 2; }
.row.ob { top: 66px; } .row.oa { top: 180px; } .row.ma { top: 396px; } .row.mb { top: 588px; }
.slotx { position: relative; width: var(--w); height: calc(var(--w) * 1.5); border-radius: 10px; border: 2px dashed #ffffff22; flex: none; }
.row.ob .slotx, .row.mb .slotx, .row.ob .card, .row.mb .card { --w: 72px; }
.row.oa .slotx, .row.ma .slotx, .row.oa .card, .row.ma .card { --w: 120px; }
.row .card { cursor: pointer; }
.mid { position: absolute; top: 366px; left: 0; right: 0; height: 24px; display: flex; justify-content: center; align-items: center; gap: 8px; z-index: 3; font-size: 13px; }
.mid .hint { color: var(--text); background: #000a; padding: 4px 12px; border-radius: 999px; border: 1px solid var(--line); max-width: 360px; text-align: center; }
.arena-tag { position: absolute; left: 12px; top: 366px; z-index: 3; font-size: 12px; }
.help-tag { position: absolute; right: 12px; top: 363px; z-index: 3; }
.card.can, .slotx.can { box-shadow: 0 0 0 3px var(--gem), 0 0 22px var(--gem); animation: canPulse 1.1s ease-in-out infinite; }
@keyframes canPulse { 50% { box-shadow: 0 0 0 3px var(--gem), 0 0 8px var(--gem); } }
.card.atk-ready { box-shadow: 0 0 0 3px var(--gold), 0 0 26px var(--gold); }
.atk-badge { position: absolute; left: 50%; bottom: -11px; transform: translateX(-50%); z-index: 4; font-family: var(--display); font-size: 14px; padding: 3px 12px; border-radius: 999px; background: linear-gradient(#ffe07a, var(--gold2)); color: #3a1c00; box-shadow: 0 3px 0 #a3530a; white-space: nowrap; pointer-events: none; }
.hand { position: absolute; left: 0; right: 0; top: 748px; height: 150px; z-index: 4; }
.hand .card.mini .hpb, .hand .card.mini .sts { display: none; }
.hand .card.mini .hpt { bottom: .35em; }
.hand .card { --w: 88px; position: absolute; top: 12px; transition: transform .25s cubic-bezier(.2, .8, .2, 1), box-shadow .2s; cursor: pointer; }
.hand .card.sel { transform: translateY(-34px) scale(1.08) !important; z-index: 10; }
.hand .card.dim { filter: brightness(.55) saturate(.7); }
.endbtn { margin-left: auto; }
.sheet { position: absolute; left: 0; right: 0; bottom: 0; z-index: 30; padding: 16px 16px 22px; border-radius: 22px 22px 0 0; background: #0f1530f2; border-top: 1px solid var(--line); box-shadow: 0 -20px 50px #000c; animation: sheetIn .25s cubic-bezier(.2, .8, .2, 1); }
@keyframes sheetIn { from { transform: translateY(100%); } }
.sheet .row2 { display: flex; gap: 14px; align-items: flex-start; }
.sheet .card { --w: 150px; }
.sheet .acts { flex: 1; display: flex; flex-direction: column; gap: 9px; }
.movebtn { text-align: left; border: 1px solid var(--line); background: #1a2246; border-radius: 14px; padding: 10px 12px; display: block; width: 100%; }
.movebtn .l1 { display: flex; align-items: baseline; gap: 6px; }
.movebtn .l1 .gem { font-size: 14px; letter-spacing: -2px; }
.movebtn .l1 b { flex: 1; font-size: 15px; }
.movebtn .l1 .d { font-family: var(--display); font-size: 24px; }
.movebtn .l2 { font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.movebtn .tag { font-size: 11px; color: var(--gold); margin-left: 4px; }
.movebtn:disabled { opacity: .45; }
.movebtn.ko { border-color: var(--gold); box-shadow: 0 0 14px #ffc93c66; }
.shade { position: absolute; inset: 0; z-index: 29; background: #0007; }
.modal { position: absolute; inset: 0; z-index: 45; background: #05070fe8; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; overflow-y: auto; gap: 14px; padding: 20px; text-align: center; }
.modal:not(.settings):not(.fuseinfo) > :first-child { margin-top: auto; } .modal:not(.settings):not(.fuseinfo) > :last-child { margin-bottom: auto; } /* centred when it fits; when it is taller than the screen (card + buttons on a short phone) it scrolls so Close is always reachable */
.modal .card { --w: 280px; }
.coins { display: flex; gap: 10px; justify-content: center; }
.coin { width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center; font-family: var(--display); font-size: 26px; background: radial-gradient(circle at 35% 30%, #fff6c8, var(--gold) 45%, #b8710a); color: #5a3300; box-shadow: 0 4px 0 #7a4a06; animation: coinSpin .5s ease-out both; }
.coin.t { background: radial-gradient(circle at 35% 30%, #e6e9f5, #8e9ac2 50%, #4d5578); color: #22283f; box-shadow: 0 4px 0 #2d3350; }
@keyframes coinSpin { from { transform: rotateY(900deg) scale(.4); opacity: 0; } }
.coinbox { position: absolute; left: 0; right: 0; top: 330px; z-index: 50; display: flex; justify-content: center; pointer-events: none; }
.result ~ .opening { z-index: 55; } /* reveals and pack openings launched from the result screen sit above it */
.result { position: absolute; inset: 0; z-index: 50; background: radial-gradient(circle at 50% 30%, #2a2f6a, #05070f 70%); display: flex; flex-direction: column; align-items: center; padding: 70px 24px 30px; gap: 12px; }
.result .big { font-family: var(--display); font-size: 64px; line-height: 1; text-shadow: 0 5px 0 #000; }
.result .big.win { text-shadow: none; background: linear-gradient(#fff6c8, var(--gold) 50%, var(--gold2)); -webkit-background-clip: text; background-clip: text; color: transparent; filter: drop-shadow(0 4px 0 #3b1d00); }
.result .big.lose { color: #aab3d6; }
.rw-list { width: 100%; display: flex; flex-direction: column; gap: 8px; }
.rw-list > div { display: flex; justify-content: space-between; padding: 10px 14px; border-radius: 12px; background: #ffffff0d; animation: rwIn .4s both; }
@keyframes rwIn { from { opacity: 0; transform: translateY(10px); } }
.rules { font-size: 14px; line-height: 1.45; color: #d6dcf5; }
.rules b { color: var(--text); }
.rules li { margin-bottom: 7px; }
.log { position: absolute; left: 12px; right: 12px; top: 116px; z-index: 5; font-size: 12px; }
.title-scr .bg-art { background-image: url(art/key_art.jpg); background-position: center 20%; }
.title-scr .bg-art::after { background: linear-gradient(#0a0e1f00 45%, #0a0e1fee 78%); }
.starters { display: flex; gap: 10px; justify-content: center; perspective: 900px; }
.starters .card { --w: 124px; }
.starters .opt { display: flex; flex-direction: column; align-items: center; gap: 8px; border: 0; background: none; padding: 0; }
.starters .opt .nm2 { font-family: var(--display); font-size: 16px; }
.starters .opt .d2 { font-size: 11px; color: var(--muted); max-width: 124px; line-height: 1.3; text-align: center; }
.starters .opt.on .card { box-shadow: 0 0 0 3px var(--gold), 0 0 30px var(--gold); }

/* spells & traps */
.card.spell .kind { background: #0d5c49cc; color: #b8ffe9; }
.card.trap .kind { background: #6a1a3dcc; color: #ffc4dd; }
.trapz { display: flex; gap: 4px; margin-left: 4px; }
.trapz .tb { width: 17px; height: 25px; border-radius: 3px; background: #1a2350 url(art/card_back.jpg) center/cover; border: 1px solid #ff6fae; box-shadow: 0 0 8px #c8457faa; animation: trapPulse 2.4s ease-in-out infinite; }
.trapz.mine .tb { cursor: pointer; }
@keyframes trapPulse { 50% { box-shadow: 0 0 2px #c8457f55; } }

.card.spell .in::after, .card.trap .in::after { content: ''; position: absolute; inset: 0; z-index: 2; pointer-events: none; border-radius: inherit; }
.card.spell .in::after { box-shadow: inset 0 0 0 .2em #2fbf9b, inset 0 0 1.4em #2fbf9b88; }
.card.trap .in::after { box-shadow: inset 0 0 0 .2em #e0508f, inset 0 0 1.4em #c8457f88; }

/* ---------- widescreen (desktop Chrome): 1440x900 stage, bigger cards ---------- */
#stage.wide { border-radius: 18px; }
/* the stage's own width is now elastic (see fit() in ui.js), so the content column tracks it with fixed side
   gutters instead of a fixed 1000px column — that fixed column was the other half of Keith's "empty bands"
   complaint: even once the stage filled the window, its content stayed pinned to a narrow centred strip. Reading
   content (text lists, forms) still gets a sensible max-width below so paragraphs don't stretch edge-to-edge on
   an ultrawide monitor; screens that should use the extra room (home, deck) override it. */
.wide #screen > .scr:not(.title-scr) { left: 40px; right: 40px; width: auto; margin-left: 0; }
.wide .showcase .card { --w: 190px; }
.wide .scr .body { padding-bottom: 120px; max-width: 1200px; margin-left: auto; margin-right: auto; } /* clear the taller wide nav so the last row can scroll into view */
/* home: hero card beside an info column (rank/CTAs/pack tile) instead of one narrow stacked strip, using a wider
   cap than the generic reading column above since it's laying out cards/buttons, not paragraphs */
.wide .home-scr .body { display: flex; align-items: flex-start; gap: 40px; max-width: 1600px; padding-top: 20px; }
.wide .home-scr .home-hero { flex: 0 0 420px; }
.wide .home-scr .home-side { flex: 1 1 480px; min-width: 0; }
.wide .dexline { gap: 16px; }
.wide .dexline .card { --w: 138px; }
.wide .collgrid { grid-template-columns: repeat(auto-fill, 150px); justify-content: center; gap: 16px 12px; }
.wide .collgrid .card { --w: 150px; }
.wide .deckgrid { grid-template-columns: repeat(8, 1fr); gap: 12px; }
.wide .deckgrid .card { --w: 108px; }
.wide .starters .card { --w: 190px; }
.wide .starters .opt .d2 { max-width: 190px; font-size: 13px; }
.wide .modal .card { --w: 430px; }
.wide .stack, .wide .flip { width: 340px; height: 510px; }
.wide .flip > .card { --w: 340px; }
.wide .sumgrid .card { --w: 175px; }
.wide .sumgrid { gap: 16px; }
.wide .opening .pack, .wide #bigpack { transform: scale(1.3); }
.wide .fx-banner { top: 330px; }
.wide .fx-banner span { font-size: 54px; }
/* battle */
.wide .bt-top { left: 24px; right: 24px; top: 14px; }
.wide .row.ob .slotx, .wide .row.mb .slotx, .wide .row.ob .card, .wide .row.mb .card { --w: 150px; }
.wide .row.oa .slotx, .wide .row.ma .slotx, .wide .row.oa .card, .wide .row.ma .card { --w: 195px; }
.wide .row.oa { top: 84px; }
/* bench rows start at 60.4% of the elastic stage width (870/1440 at the canonical size) instead of a fixed 870px,
   so the active/bench gap grows along with --sw instead of staying glued to the old fixed-1440 layout */
.wide .row.ob { top: 116px; left: calc(var(--sw) * .6042); right: auto; gap: 18px; }
.wide .row.ma { top: 412px; }
.wide .row.mb { top: 444px; left: calc(var(--sw) * .6042); right: auto; gap: 18px; }
.wide .mid, .wide .arena-tag, .wide .help-tag { top: 377px; }
.wide .mid .hint { font-size: 15px; }
.wide .bt-bar { top: 722px; left: 24px; right: 24px; }
.wide .hand { top: 794px; height: 240px; }
.wide .hand .card { --w: 150px; top: 4px; }
.wide .hand .card:hover { transform: translateY(-150px) scale(1.06) !important; z-index: 20; }
.wide .hand .card.sel { transform: translateY(-160px) scale(1.1) !important; }
.wide .atk-badge { font-size: 17px; bottom: -14px; }
.wide .card.mini .nm { font-size: .72em; }
.wide .card.mini .hpt { font-size: .8em; }
.wide .sheet { left: 36px; right: auto; bottom: auto; top: 96px; width: 600px; border-radius: 22px; animation: none; border: 1px solid var(--line); }
.wide .sheet .card { --w: 260px; }
.wide .peek { position: absolute; left: 36px; top: 70px; z-index: 3; pointer-events: none; }
.wide .peek .card { box-shadow: 0 20px 60px #000d; }
.wide .peek-hp { margin-top: 10px; text-align: center; font-family: var(--display); font-size: 20px; text-shadow: 0 2px 0 #000; }
.wide .coinbox { top: 300px; }

/* ---------- readability pass ---------- */
.card.mini.trick .kind { top: auto; bottom: .55em; left: .35em; font-size: .62em; }
.card.mini .mcost { position: absolute; right: .4em; bottom: .45em; font-size: .75em; font-family: var(--display); }
.card.mini.trick .nm { font-size: .66em; line-height: 1.05; white-space: normal; }
.mid .hint { font-size: 14px; font-weight: 800; background: #05070fd9; box-shadow: 0 4px 14px #000a; }
.cnts { font-size: 13px; color: #b7c1e2; }
.arena-tag { font-size: 13px; background: #05070fcc; }
.who { font-size: 15px; font-weight: 800; }
.stars { color: #ffffff45; }
/* PC layout: text sized for a board seen from arm's length */
.wide .bt-top, .wide .bt-bar { padding: 8px 16px !important; gap: 14px; }
.wide .who { font-size: 20px; }
.wide .who .av { width: 46px; height: 46px; }
.wide .cnts { font-size: 16px; }
.wide .stars { font-size: 30px; }
.wide .gems { gap: 6px; }
.wide .gems i { width: 20px; height: 20px; }
.wide .bt-bar .gem { font-size: 26px !important; }
.wide .endbtn { font-size: 22px; padding: 12px 26px; }
.wide .icon-btn { width: 46px; height: 46px; font-size: 22px; }
/* same elastic-width treatment as the bench rows above: these were pinned to the old fixed-1440 stage in px */
.wide .mid { top: 382px; left: calc(var(--sw) * .6042); right: 24px; justify-content: flex-start; }
.wide .mid .hint { font-size: 20px; padding: 8px 18px; max-width: 540px; }
.wide .arena-tag { top: 382px; left: calc(var(--sw) * .2917); font-size: 16px; padding: 6px 12px; }
.wide .help-tag { top: 330px; left: calc(var(--sw) * .2917); right: auto; width: 40px !important; height: 40px !important; font-size: 20px !important; }
.wide .card.mini .nm { font-size: .8em; }
.wide .card.mini .hpt { font-size: .95em; }
.wide .card.mini .hpb { height: .7em; }
.wide .card.mini .sts { font-size: .8em; }
.wide .atk-badge { font-size: 20px; padding: 5px 16px; bottom: -16px; }
.wide .trapz .tb { width: 24px; height: 34px; }
.wide .movebtn { padding: 14px 16px; }
.wide .movebtn .l1 b { font-size: 21px; }
.wide .movebtn .l1 .d { font-size: 32px; }
.wide .movebtn .l1 .gem { font-size: 19px; }
.wide .movebtn .l2 { font-size: 15px; }
.wide .movebtn .tag { font-size: 14px; }
.wide .sheet .acts > div:first-child { font-size: 30px !important; }
.wide .sheet .muted, .wide .sheet .btn.sm { font-size: 17px; }
.wide .sheet .btn { font-size: 24px; }
.battle:has(.sheet) .peek { display: none; }
.wide .fx-text { font-size: 46px; }
.wide .fx-text.dmg { font-size: 60px; }
.wide .fx-text.big { font-size: 78px; }
.wide .fx-text.gold, .wide .fx-text.status { font-size: 32px; }
.wide .fx-text.hunt, .wide .fx-text.block { font-size: 30px; }
.wide .fx-text.small { font-size: 28px; }
.wide .fx-text.dmg.splash, .wide .fx-text.status.splash { font-size: 40px; }
.wide .emote-bubble { font-size: 40px; padding: 8px 14px; }
/* PC menus */
.wide .topbar { padding: 18px 20px 10px; }
.wide .pill { font-size: 17px; padding: 7px 14px; }
.wide .cap { font-size: 16px; }
.wide h2 { font-size: 34px; }
.wide h3 { font-size: 24px; }
.wide .mis { font-size: 17px; }
.wide .mis .rw { font-size: 15px; }
.wide .tile .t1, .wide .trial .t1 { font-size: 24px; }
.wide .tile .muted, .wide .trial .t2, .wide .muted { font-size: 15px; }
/* the nav spans the .scr content column (already elastic with --sw), but its buttons shouldn't stretch edge-to-
   edge on an ultrawide stage — cap it as a centred group instead */
.wide .nav { left: 50%; right: auto; transform: translateX(-50%); width: calc(var(--sw) - 80px); max-width: 1100px; }
.wide .nav button { font-size: 15px; }
.wide .nav button b { font-size: 26px; }
.wide .filters button { font-size: 16px; padding: 8px 16px; }
.wide .rules { font-size: 18px; }
.wide .rw-list > div { font-size: 18px; }
.wide .rlabel { font-size: 34px; }
.wide .rlabel small { font-size: 16px; }
.wide .opening .hint { font-size: 20px; }

/* phone battle band: short arena label, slim hint */
#stage:not(.wide) .arena-tag { left: 8px; top: 367px; padding: 3px 8px; font-size: 12px; }
#stage:not(.wide) .arena-tag .an { display: none; }
#stage:not(.wide) .mid { top: 370px; }
#stage:not(.wide) .mid .hint { font-size: 13px; padding: 3px 10px; max-width: 290px; line-height: 1.25; }

/* grid thumbnails: art + name + HP, full text lives in the tap-to-inspect view */
.card.thumb .hpb { display: none; }
.card.thumb .hpt { bottom: .35em; }
.card.thumb.missing .hpt, .card.thumb.missing .mcost { display: none; }
.card.thumb .nm { font-size: .7em; }
.dexline .lbl { font-size: 12px; }
.wide .dexline .lbl { font-size: 15px; }

.wide [style*="font-size:12px"], .wide [style*="font-size:13px"], .wide [style*="font-size:14px"] { font-size: 16px !important; }
.wide .pack .psub { font-size: 14px; }

.wide .result { padding: 90px calc(50% - 380px) 60px; gap: 16px; }
.wide .result .big { font-size: 96px; }
.wide .result .muted { font-size: 18px !important; }
.wide .result .btn { font-size: 24px; padding: 16px 28px; }
.wide .opening > .btn, .wide .opening .btn.sm { font-size: 22px; padding: 12px 22px; }

.wide .dexline { display: inline-flex; width: 49%; vertical-align: top; margin-bottom: 18px; }

/* ---------- online + missions ---------- */
.cta.three { grid-template-columns: 1.4fr 1fr 1fr; }
.cta.three .btn { font-size: 19px; padding: 16px 6px; }
.tag-bot, .tag-online { font-size: 10px; padding: 2px 6px; border-radius: 6px; vertical-align: middle; letter-spacing: 1px; }
.tag-bot { background: #ffffff1c; color: var(--muted); }
.tag-online { background: #1f8f4e; color: #d9ffe9; }
.ttimer { font-family: var(--display); font-size: 16px; padding: 3px 10px; border-radius: 999px; background: #0008; }
.ttimer.low { color: #ff8a8a; animation: pulse .8s ease-in-out infinite; }
.lobby { gap: 10px; }
.radar { position: relative; width: 140px; height: 140px; margin-bottom: 10px; }
.radar i { position: absolute; inset: 0; border-radius: 50%; border: 3px solid var(--gem); opacity: 0; animation: radar 2.4s ease-out infinite; }
.radar i:nth-child(2) { animation-delay: .8s; } .radar i:nth-child(3) { animation-delay: 1.6s; }
@keyframes radar { 0% { transform: scale(.2); opacity: 1; } 100% { transform: scale(1.2); opacity: 0; } }
.nav .dot.num { width: auto; height: 16px; min-width: 16px; padding: 0 4px; font-size: 10px; line-height: 16px; color: #fff; text-align: center; top: 0; right: 14%; border-radius: 8px; }
.mtabs .badge { background: var(--red); color: #fff; border-radius: 8px; padding: 0 6px; font-size: 11px; }
.mrow { display: flex; align-items: center; gap: 12px; padding: 12px; margin-bottom: 10px; }
.mrow.claimed { opacity: .55; }
.mrow .grow { flex: 1; min-width: 0; }
.mrow .mt { font-weight: 800; font-size: 15px; }
.mrow .ms { font-size: 12px; margin: 2px 0 4px; }
.mrow .mr { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }
.mrow .chip { font-size: 11px; color: var(--gold); white-space: nowrap; }
.mrow .got { font-size: 12px; color: var(--green); }
.medal { width: 44px; height: 44px; flex: none; border-radius: 50%; display: grid; place-items: center; font-family: var(--display); font-size: 16px; background: radial-gradient(circle at 35% 30%, #fff6c8, #c89b2a 60%, #6b4a0a); color: #3a2400; box-shadow: 0 3px 0 #4a3206; }
.medal.t0 { background: radial-gradient(circle at 35% 30%, #f0d6b8, #b87333 60%, #5c3514); }
.medal.t1 { background: radial-gradient(circle at 35% 30%, #ffffff, #b8c2d4 60%, #5b6477); }
.medal.t3 { background: conic-gradient(#ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55); color: #fff; }
.medal.draw { background: radial-gradient(circle at 35% 30%, #b8f6ff, #2a9fd8 60%, #0d3f5c); font-size: 20px; }
.claimable { color: #fff; background: var(--red); padding: 2px 8px; border-radius: 8px; font-size: 11px; }
.wide .mrow { padding: 16px 18px; }
.wide .mrow .mt { font-size: 20px; }
.wide .mrow .ms { font-size: 15px; }
.wide .mrow .chip { font-size: 15px; }
.wide .medal { width: 58px; height: 58px; font-size: 20px; }
.wide .cta.three .btn { font-size: 24px; }
.wide .ttimer { font-size: 22px; }
.wide .tag-bot, .wide .tag-online { font-size: 13px; }

/* ---------- ranked / login ---------- */
.rankbadge { display: flex; align-items: center; gap: 10px; padding: 9px 12px; margin-top: 10px; width: 100%; border: 0; color: inherit; text-align: left; }
.rbicon { width: 36px; height: 36px; border-radius: 50%; flex: none; display: grid; place-items: center; font-size: 18px; background: radial-gradient(circle at 35% 30%, #fff6c8, var(--c) 60%, #000); box-shadow: 0 3px 0 #0006; }
.rbicon.big { width: 52px; height: 52px; font-size: 26px; }
.rankbadge .grow { flex: 1; min-width: 0; }
.rankbadge .rbtitle { font-family: var(--display); font-size: 15px; }
.rankbadge .rbend { font-size: 11px; white-space: nowrap; }
.mrow.cur { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold) inset; }
.rpup { color: var(--green); }
.rpdn { color: var(--red); }
.rpbarwrap { margin-top: 10px; width: min(320px, 100%); }
.rpbarhead { display: flex; justify-content: space-between; font-size: 12px; color: var(--muted); margin-bottom: 4px; }
.lgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; width: 100%; margin: 6px 0; }
.ltile { border-radius: 12px; padding: 8px 4px; text-align: center; background: #ffffff0d; border: 1px solid var(--line); opacity: .55; }
.ltile.done { opacity: 1; border-color: var(--green); }
.ltile.today { opacity: 1; border-color: var(--gold); box-shadow: 0 0 12px #ffc93c66; animation: pulse 1.6s ease-in-out infinite; }
.ltile .ld { font-size: 10px; color: var(--muted); }
.ltile .lr { font-size: 13px; margin-top: 2px; }

/* ============ HOLO v2/v4 shared scaffolding — tilt/gyro drives --mx/--my/--hyp, see HOLO v4 below for the per-rarity foil ============ */
@property --hyp { syntax: '<number>'; inherits: true; initial-value: 0; }
@property --bgx { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
@property --bgy { syntax: '<percentage>'; inherits: true; initial-value: 50%; }
:root { --sunpillar: #ff7773, #ffed5f, #a8ff5f, #83fff7, #7894ff, #d875ff, #ff7773; }
.card.tilt { will-change: transform; } /* only cards that actually tilt: every card promoted put 130+ GPU layers (~350 MB at DPR 3) on screen during a pack reveal */
.card.tilting { transition: box-shadow .2s; box-shadow: 0 1.4em 3em #000c, 0 0 2em #ffffff1a; z-index: 30; }
.card:not(.tilting) { transition: transform .5s cubic-bezier(.2, 1.4, .4, 1), box-shadow .3s; }
.card .holo, .card .glit, .card .glare { transition: opacity .3s; }

/* glare: the bright spot of light that follows the pointer/gyro on every rarity (opacity tuned per rarity in HOLO v4) */
.card .glare { mix-blend-mode: overlay;
  background: radial-gradient(farthest-corner circle at var(--mx) var(--my), hsla(0,0%,100%,.85) 8%, hsla(0,0%,100%,.35) 20%, hsla(0,0%,0%,.45) 90%); }
.card.mini .glare { opacity: calc(var(--hyp) * .4); }

/* UR gets a thread of rainbow iridescence woven through its gold frame — stronger than SR's plain rainbow, but gold-biased */
.card.r-UR::after { content: ''; position: absolute; inset: 0; border-radius: .75em; padding: .38em; background: linear-gradient(115deg, #ff2d55, #ffd60a, #30d158, #64d2ff, #bf5af2, #ff2d55);
  background-size: 300% 300%; background-position: var(--bgx) var(--bgy); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: calc(.18 + var(--hyp) * .22); mix-blend-mode: color-dodge; pointer-events: none; box-shadow: 0 0 1em #ffcf4d44; }

/* twinkle sparkles (SR/UR/SEC, big cards only) + a diagonal light sweep gliding across the foil every ~6s (idle
   grid motion — transform-only, cheap; cardHTML() gives each .sweep a random animation-delay so a grid of foils
   doesn't flash in unison) */
.card .sparkles, .card .sweep { position: absolute; inset: 0; pointer-events: none; overflow: hidden; border-radius: .5em; }
.spk { position: absolute; width: .5em; height: .5em; background: #fff; opacity: 0; --op: .7;
  clip-path: polygon(50% 0%, 63% 37%, 100% 50%, 63% 63%, 50% 100%, 37% 63%, 0% 50%, 37% 37%); filter: drop-shadow(0 0 .3em #fff); animation: spkPop 2.4s ease-in-out infinite; }
.card.r-SR .spk { width: .38em; height: .38em; --op: .55; }
.card.r-UR .spk { background: #ffe680; filter: drop-shadow(0 0 .35em #ffd84a); --op: .85; }
.card.r-SEC .spk { filter: drop-shadow(0 0 .4em #fff); --op: 1; }
@keyframes spkPop { 0%, 100% { transform: scale(0) rotate(0deg); opacity: 0; } 50% { transform: scale(1) rotate(90deg); opacity: var(--op); } }
.card .sweep::after { content: ''; position: absolute; top: -30%; bottom: -30%; left: -60%; width: 35%; mix-blend-mode: overlay;
  background: linear-gradient(75deg, transparent, #ffffffb0 45%, #ffffffb0 55%, transparent); transform: translateX(-120%); animation: sweepMove 6s ease-in-out var(--sweepdelay, 0s) infinite; }
@keyframes sweepMove { 0%, 15% { transform: translateX(-120%); } 45%, 100% { transform: translateX(220%); } }
.reduce-motion .spk, .reduce-motion .card .sweep::after { animation: none; opacity: 0; }
.card.missing .sweep, .card.missing .sparkles { display: none; }

/* frames catch the light too */
.card.r-R { background-position: var(--mx) var(--my); background-image: var(--glitter), linear-gradient(135deg, #eef2f7, #8b95a5 25%, #fafcff 50%, #8b95a5 75%, #eef2f7); background-size: 120px 120px, 250% 250%; background-blend-mode: overlay, normal; }
.card.r-UR { background-image: var(--glitter), linear-gradient(135deg, #fff1a8, #d4a017 18%, #fff8d6 36%, #b8860b 54%, #ffe680 72%, #c9a227 90%, #fff1a8); background-size: 100px 100px, 250% 250%; background-position: 0 0, var(--mx) var(--my); background-blend-mode: overlay, normal; }
/* keep battle boards light: minis get the finish but no glitter mask work */
.card.mini .glit { display: none; }
/* face-down cards: plain back only; no name/moves, no foil or rarity frame leaking through */
.card.back.card { background: #1a2350; background-image: none; box-shadow: 0 .5em 1.4em #000b; animation: none; }
.card.back .in > :not(.art) { display: none; }
/* my Active row sits above the info bar so the "Attack" badge hanging off the card is never cut off */
.row.ma { z-index: 4; }
.battle .row { pointer-events: none; }
.battle .row > * { pointer-events: auto; } /* the row box spans the board: only its cards may catch taps, never the empty strip over the bench */

/* deck page: info button on every card + widescreen side preview */
.dcell { position: relative; }
.dinfo { position: absolute; top: -6px; right: -6px; z-index: 3; width: 26px; height: 26px; border-radius: 50%; border: 2px solid #fff; background: var(--gem); color: #002a3a; font-family: Georgia, serif; font-style: italic; font-weight: 700; font-size: 15px; line-height: 1; display: grid; place-items: center; padding: 0; box-shadow: 0 2px 6px #000a; }
.wide .dinfo { width: 32px; height: 32px; font-size: 18px; }
.wide .deckscr .body { padding-right: 460px; max-width: none; } /* collection grid should use the full elastic width, not the generic reading cap */
.wide .deckpeek { left: auto; right: 40px; top: 90px; }
.wide .deckscr .deckgrid { grid-template-columns: repeat(auto-fill, 108px); justify-content: start; }
.wide .deckscr .collgrid { grid-template-columns: repeat(auto-fill, 150px); justify-content: start; }
.wide .linehead { flex-basis: 160px; font-size: 15px; }
.wide .linehead b { font-size: 18px; }
.wide .linerow .card { --w: 108px; }
.wide .linerow.compact .card { --w: 86px; }
/* line rows (add-from-collection and already-in-deck views): one per row normally, two side by side once there's
   genuinely enough width to spare (≥1700px real viewport, not just --sw, since this is about reading two full-
   width rows comfortably side by side rather than the stage's own elastic width) */
@media (min-width: 1700px) { .wide .lines-wrap { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; align-items: start; } }

/* ============ universal screen sizes: elastic stage (see plans/plan_responsive.md) ============
   #stage's WIDTH is fixed per mode (430 phone / 760 tab / 1440 wide) and mapped to the real screen via a JS
   transform:scale(); its HEIGHT (--sh) is elastic — base-width/aspect, clamped in ui.js — so a taller/shorter
   screen gets real extra (or less) vertical layout room instead of empty bars. Everything below that used to
   assume a fixed 900-tall stage now reads --sh instead, so it keeps working at any clamped height. */
:root { --sw: 430px; --sh: 900px; }
/* safe areas: notches / foldable hinge gaps, on the two chrome bars the spec calls out */
.topbar { padding-top: calc(14px + env(safe-area-inset-top)); }
.nav { bottom: calc(12px + env(safe-area-inset-bottom)); }

/* battle layout: rows/bars/hint/labels anchored by % of --sh instead of a fixed 900-tall px offset, so the gaps
   between them grow with the elastic stage instead of staying glued to their canonical-900 pixel position. */
.row.ob { top: 7.333%; } .row.oa { top: 20%; } .row.ma { top: 44%; } .row.mb { top: 65.333%; }
.bt-bar { top: 78%; }
.mid { top: 40.667%; }
.arena-tag { top: 40.667%; }
.help-tag { top: 40.333%; }
.hand { top: 83.111%; }
.coinbox { top: 36.667%; }
.fx-banner { top: 44.444%; }
#stage:not(.wide) .arena-tag { top: 40.778%; }
#stage:not(.wide) .mid { top: 41.111%; }
.wide .row.oa { top: 9.333%; }
.wide .row.ob { top: 12.889%; }
.wide .row.ma { top: 45.778%; }
.wide .row.mb { top: 49.333%; }
.wide .mid, .wide .arena-tag, .wide .help-tag { top: 41.889%; }
.wide .bt-bar { top: 80.222%; }
.wide .hand { top: 88.222%; }
.wide .coinbox { top: 33.333%; }
.wide .fx-banner { top: 36.667%; }
.wide .mid { top: 42.444%; }
.wide .arena-tag { top: 42.444%; }
.wide .help-tag { top: 36.667%; }

/* readable, bigger board cards (phone + tab): width is a fixed fraction of --sh (same ratio the canonical 900-tall
   design already used), so cards grow/shrink exactly in step with the row gaps above — never overlapping — and a
   taller elastic stage (more --sh) gives visibly bigger, easier-to-read Active/bench/hand cards for free. Clamped
   only as a hard safety bound; at canonical --sh:900 these resolve to the original 72/120/88px, unchanged. */
.row.ob .slotx, .row.mb .slotx, .row.ob .card, .row.mb .card { --w: clamp(50px, calc(var(--sh) * 0.08), 110px); }
.row.oa .slotx, .row.ma .slotx, .row.oa .card, .row.ma .card { --w: clamp(85px, calc(var(--sh) * 0.1333), 190px); }
.hand .card { --w: clamp(60px, calc(var(--sh) * 0.0978), 140px); }
/* the selected hand card lifts and enlarges further, so its text is readable before you commit to playing it */
.hand .card.sel { transform: translateY(-52px) scale(1.2) !important; }

/* tap/hold any card to read it full-size: modal card fills most of the stage width on phones, and on tab is capped
   by both width and height so a tall card never overflows a short elastic stage. */
.modal .card { --w: min(calc(var(--sw) * .92), max(240px, calc((var(--sh) - 235px) / 1.55)), 400px); } /* leave room under the card for its info line + buttons */
.tab .modal .card { --w: min(calc(var(--sw) * .5), max(200px, calc((var(--sh) - 235px) / 1.55)), 380px); }

/* ---------- tab mode: square/foldable/tablet — the phone design's screens, roomier ---------- */
#stage.tab { border-radius: 18px; }
.tab #screen > .scr:not(.title-scr) { left: 50%; right: auto; width: 680px; margin-left: -340px; }
.tab .scr .body { padding-bottom: 110px; }
.tab .showcase .card { --w: 190px; }
.tab .dexline { gap: 14px; }
.tab .dexline .card { --w: 118px; }
.tab .dexline .lbl { font-size: 14px; }
.tab .collgrid { grid-template-columns: repeat(5, 1fr); gap: 14px 10px; }
.tab .collgrid .card { --w: 118px; }
.tab .deckgrid { grid-template-columns: repeat(6, 1fr); gap: 10px; }
.tab .deckgrid .card { --w: 96px; }
.tab .linehead { flex-basis: 128px; font-size: 12px; }
.tab .linerow .card { --w: 86px; }
.tab .linerow.compact .card { --w: 70px; }
.tab .starters .card { --w: 160px; }
.tab .starters .opt .d2 { max-width: 160px; }
.tab .stack, .tab .flip { width: 290px; height: 435px; }
.tab .flip > .card { --w: 290px; }
.tab .sumgrid .card { --w: 150px; }
.tab .sumgrid { gap: 14px; }
.tab .opening .pack, .tab #bigpack { transform: scale(1.15); }
.tab .fx-banner span { font-size: 46px; }
/* battle */
.tab .bt-top, .tab .bt-bar { left: 18px; right: 18px; }
.tab .who { font-size: 17px; }
.tab .who .av { width: 38px; height: 38px; }
.tab .cnts { font-size: 14px; }
.tab .stars { font-size: 24px; }
.tab .icon-btn { width: 42px; height: 42px; font-size: 20px; }
.tab .endbtn { font-size: 18px; padding: 10px 20px; }
.tab .mid .hint { font-size: 14px; max-width: 480px; }
.tab .card.mini .nm { font-size: .68em; }
.tab .card.mini .hpt { font-size: .82em; }
.tab .atk-badge { font-size: 15px; }
.tab .sheet { left: 24px; right: 24px; }
.tab .sheet .card { --w: 190px; }
.tab .movebtn .l1 b { font-size: 17px; }
.tab .movebtn .l1 .d { font-size: 26px; }
.tab .hand .card.sel { transform: translateY(-64px) scale(1.18) !important; }
/* chrome + menus */
.tab .topbar { padding-left: 20px; padding-right: 20px; }
.tab h2 { font-size: 29px; }
.tab h3 { font-size: 21px; }
.tab .nav button { font-size: 13px; }
.tab .nav button b { font-size: 22px; }
.tab .rules, .tab .rw-list > div { font-size: 16px; }
/* home: hero card beside the rank/CTA stack instead of stacked underneath it */
.tab .home-scr .body { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px; }
.tab .home-scr .home-hero { flex: 0 0 300px; }
.tab .home-scr .home-side { flex: 1 1 320px; min-width: 280px; }
/* adventure map */
.tab .advmap { max-width: 640px; }
.tab .advnode { width: 74px; height: 74px; font-size: 24px; }
.tab .advnode.boss { width: 100px; height: 100px; font-size: 34px; }
.tab .advtrack { height: 700px; }
/* missions / ranked */
.tab .mrow { padding: 14px 16px; }
.tab .mrow .mt { font-size: 17px; }
.tab .medal { width: 50px; height: 50px; font-size: 18px; }

/* ---------- Expansion 4 "Fusion Frontier": Fusion cards, Duo combo chips, field-attack warnings ---------- */
/* Fusion cards get a dual-habitat split border instead of the usual rarity-colour frame (they're always UR/SEC). */
.card.fusion, .card.fusion.r-R, .card.fusion.r-SR, .card.fusion.r-UR, .card.fusion.r-SEC { background: linear-gradient(135deg, var(--c) 0 50%, var(--c2, var(--c)) 50% 100%); }
.card.fusion.r-UR::after, .card.fusion.r-SEC::after { display: none; }
.card.fusion .hab { width: auto; min-width: 2.6em; height: 1.5em; border-radius: 1em; font-size: .68em; padding: 0 .3em; gap: .15em; display: flex; align-items: center; justify-content: center; }
.card.mini.fusion .hab { min-width: 2em; height: 1.3em; font-size: .5em; }
.card.fusion .fuse-ribbon { position: absolute; top: .15em; left: .15em; z-index: 2; font-family: var(--display); font-size: .42em; font-weight: 800; letter-spacing: .08em; padding: .25em .6em; border-radius: .4em; background: linear-gradient(90deg, var(--c), var(--c2)); color: #fff; text-shadow: 0 .1em .2em #0006; box-shadow: 0 .15em .4em #0008; }

/* Duo combo chip on the battle board, near the side's Active */
.duo-chip { align-self: center; display: flex; align-items: center; gap: 4px; font-size: 10.5px; font-weight: 800; letter-spacing: .02em; padding: 5px 9px; border-radius: 999px; background: linear-gradient(#2b3563, #1a2246); border: 1px solid var(--gem); color: #d8f4ff; white-space: nowrap; cursor: pointer; animation: duoGlow 1.8s ease-in-out infinite; }
@keyframes duoGlow { 0%, 100% { box-shadow: 0 0 8px #62e6ff55; } 50% { box-shadow: 0 0 16px #62e6ffaa; } }
.row.oa, .row.ma { flex-wrap: wrap; row-gap: 4px; }
.wide .duo-chip { font-size: 13px; padding: 6px 13px; }
.duo-portrait { position: absolute; width: 56px; height: 56px; margin: -28px 0 0 -28px; border-radius: 50%; background-size: cover; background-position: center; z-index: 60; pointer-events: none; border: 2px solid #fff8; }

/* attack-sheet field-hit warning colour */
.movebtn.move-quake .l2 { color: var(--red); font-weight: 700; }
.movebtn.move-all .l2 { color: var(--gold); }

/* ---------- shop ---------- */
.shop-sec { margin: 16px 0; }
.shop-sec h3 { font-family: var(--display); font-size: 17px; margin: 0 0 8px; font-weight: 400; letter-spacing: .3px; text-shadow: 0 1px 4px #000d, 0 0 10px #000a; }
.shop-scr p.muted, p.plate { color: #dbe2fc; background: rgba(10, 14, 31, .8); padding: 6px 10px; border-radius: 10px; } p.plate:empty { display: none; } /* these notes sat straight on bright page art (contrast ~1.3-2.5): give them a dark plate */
.shopgrid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.shopcard { position: relative; text-align: left; padding: 12px; border-radius: 16px; display: flex; flex-direction: column; gap: 2px; }
.shopcard.wide { grid-column: 1 / -1; }
.shopcard .scicon { font-size: 26px; }
.shopcard .sctitle { font-family: var(--display); font-size: 16px; color: var(--text); }
.shopcard .scprice { font-size: 15px; font-weight: 800; color: var(--gold); }
.shopcard .scsub { font-size: 11px; }
.shopcard:disabled { filter: grayscale(1) brightness(.7); pointer-events: none; }
.shopcard .ribbon { position: absolute; top: 8px; right: -26px; transform: rotate(35deg); background: linear-gradient(90deg, var(--gold2), var(--gold)); color: #3a1c00; font-family: var(--display); font-size: 10px; padding: 2px 26px; box-shadow: 0 2px 6px #000a; }
.pill.pop { animation: ftpop .35s ease; }
@keyframes ftpop { 0% { transform: scale(1); } 40% { transform: scale(1.35); } 100% { transform: scale(1); } }
.wide .shopgrid { grid-template-columns: repeat(4, 1fr); }
.wide .shopcard .sctitle { font-size: 19px; }
.wide .shopcard .scprice { font-size: 18px; }
.wide .shopcard .scsub { font-size: 13px; }
.tab .shopgrid { grid-template-columns: repeat(2, 1fr); }
/* self-contained rewarded-ad placeholder used by store.js's test adapter (not part of the .scr/.modal screen system) */
.ft-ad-overlay { position: fixed; inset: 0; z-index: 999; background: #05070fee; display: flex; align-items: center; justify-content: center; }
.ft-ad-box { text-align: center; color: var(--text); font-family: var(--body); }
.ft-ad-label { font-size: 15px; color: var(--muted); margin-bottom: 10px; }
.ft-ad-count { font-family: var(--display); font-size: 54px; color: var(--gold); margin-bottom: 18px; }
.ft-ad-skip { border: 1px solid var(--line); background: var(--panel); color: var(--text); border-radius: 12px; padding: 8px 18px; font-size: 14px; }

/* soft green-gold glow pulse for heals and Revive */
.fx-healglow { position: absolute; width: 90px; height: 90px; margin: -45px 0 0 -45px; pointer-events: none; z-index: 57; border-radius: 50%; background: radial-gradient(circle, #5dfc9a55, #ffd84a22 55%, transparent 72%); animation: healPulse .65s ease-out forwards; }
@keyframes healPulse { 0% { transform: scale(.3); opacity: 0; } 30% { opacity: 1; } 100% { transform: scale(1.3); opacity: 0; } }

/* ============ HOLO v4 — Pokémon-TCG-style foil per rarity, art window stays readable ============
   Keith: "shining card like the Pokémon card game... without affecting the artwork too much."
   One shared idea for every rarity: the art window (the vertical band between the name bar and the text panel,
   roughly 15%-60% of the card's height) gets at most a light sheen; the frame above/below carries the full foil.
   --fm-hard hides that band completely (used by R/UR/SEC, whose over-art job is only tiny sparkle points, never
   a wash); --fm-soft leaves ~29% alpha there (SR's low-strength band sweep). Textures are pure CSS or the
   canvas-generated --glitter/--cosmos data URLs already made once at load (ui.js foil textures) — no image files. */
:root {
  --fm-hard: linear-gradient(#000 0 15%, transparent 15% 60%, #000 60%);
  --fm-soft: linear-gradient(#000 0 15%, #0000004a 15% 60%, #000 60%);
}
.card.r-R { --edge: #c7d3e688; } .card.r-SR { --edge: #d875ff88; } .card.r-UR { --edge: #ffd84a88; } .card.r-SEC { --edge: #ffe68099; }
.card.tilting:not(.r-C) { box-shadow: 0 1.4em 3em #000c, 0 0 2em var(--edge); }

/* pointer/gyro glare — every rarity, brightest and most colourful on the rarest cards */
.card.r-C .glare { opacity: calc(var(--hyp) * .25); }
.card.r-R .glare, .card.r-SR .glare { opacity: calc(.2 + var(--hyp) * .5); }
.card.r-UR .glare { opacity: calc(.28 + var(--hyp) * .55); }
.card.r-SEC .glare { opacity: calc(.3 + var(--hyp) * .55); mix-blend-mode: color-dodge; background: conic-gradient(from calc(var(--mx) * 3.6deg), var(--sunpillar)); }

/* R — reverse holo: rainbow sheen + fine grain on the frame; the art window is untouched apart from the glare */
.card.r-R .holo { opacity: calc(.55 + var(--hyp) * .35); mix-blend-mode: color-dodge;
  background: repeating-linear-gradient(110deg, var(--sunpillar)); background-size: 320% 320%; background-position: var(--mx) var(--my);
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }
.card.r-R .glit { opacity: .5; mix-blend-mode: overlay; background: repeating-linear-gradient(45deg, #fff0 0 2px, #ffffff26 2px 3px);
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }

/* SR — full-art rainbow: diagonal bands sweep the whole card at low strength over the art, full on the frame;
   thin etched lines stay frame-only */
.card.r-SR .holo { opacity: calc(.5 + var(--hyp) * .4); mix-blend-mode: color-dodge;
  background: repeating-linear-gradient(110deg, var(--sunpillar)); background-size: 280% 280%; background-position: var(--mx) var(--my);
  -webkit-mask-image: var(--fm-soft); mask-image: var(--fm-soft); }
.card.r-SR .glit { opacity: .4; mix-blend-mode: overlay; background: repeating-linear-gradient(-45deg, #fff0 0 3px, #ffffff30 3px 4px, #fff0 4px 9px);
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }

/* UR — cosmos/galaxy: nebula foil + star grain on the frame; only tiny sparkle points drift over the art, never a wash */
.card.r-UR .holo { opacity: calc(.6 + var(--hyp) * .35); mix-blend-mode: color-dodge; filter: saturate(1.3);
  background-image: var(--cosmos), linear-gradient(135deg, #2a0a4d, #5e2a8f 30%, #b23fd6 55%, #2f6fd6 75%, #2a0a4d);
  background-size: 200px 200px, 260% 260%; background-position: var(--bgx) var(--bgy), var(--mx) var(--my); background-blend-mode: screen, normal;
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }
.card.r-UR .glit { opacity: .55; mix-blend-mode: screen; background-image: var(--glitter); background-size: 130px 130px;
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }
.card.r-UR::after { opacity: calc(.3 + var(--hyp) * .3); }

/* SEC — gold secret: embossed gold frame that catches the light, rainbow glare on top, gold sparkle points over the
   art, and the border ring is a slow-shimmering gold edge */
.card.r-SEC .holo { opacity: calc(.6 + var(--hyp) * .35); mix-blend-mode: color-dodge;
  background: repeating-linear-gradient(115deg, #3a2400 0 4%, #ffe680 5%, #fff6cf 6%, #b8860b 7%, #3a2400 12%);
  background-size: 240% 240%; background-position: var(--mx) var(--my);
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }
.card.r-SEC .glit { opacity: .6; mix-blend-mode: overlay; background-image: var(--glitter); background-size: 90px 90px;
  -webkit-mask-image: var(--fm-hard); mask-image: var(--fm-hard); }
.card.r-SEC::after { opacity: calc(.85 + var(--hyp) * .15); animation: secEdge 5s linear infinite; }
@keyframes secEdge { to { background-position: 300% 50%; } }
.reduce-motion .card.r-SEC::after { animation: none; }

/* sparkle points (SR/UR/SEC, big cards) stay small over the creature's face and drift gently with the pointer/gyro
   — the `translate` property is independent of the `transform` the spkPop keyframe animates, so they compose */
.card .spk { --op: .45; }
.card.r-UR .spk, .card.r-SEC .spk { --op: .6; translate: calc((var(--mx) - 50%) * .12) calc((var(--my) - 50%) * .12); }
.card.r-SEC .spk { background: #ffe680; filter: drop-shadow(0 0 .35em #ffd84a); }

/* names on bright foil: gold/rainbow lettering with a dark outline so they always read */
.card.r-UR .nm span:first-child, .card.r-SEC .nm span:first-child {
  filter: drop-shadow(0 .06em 0 #000) drop-shadow(.04em 0 0 #000) drop-shadow(-.04em 0 0 #000) drop-shadow(0 0 .15em #000c); }
.card .nm { background: linear-gradient(#000e, #0006 70%, #0000); }

/* the multi-pack "Your pull" results grid (#resgrid, up to 50+ cards, HOLO v4 done-criteria): no per-frame tilt
   work and no automatic idle sweep there — a static foil look, with a sweep only on hover and sparkles held still */
#resgrid .card .sweep::after { animation: none; }
#resgrid .card:hover .sweep::after { animation: sweepMove 1.1s ease-out 1; }
#resgrid .spk { animation: none; opacity: var(--op); }

/* ============ FOIL v5 — a material per rarity (plans/plan_foil_v5.md, Keith's TCG rarity & foil style guide) ============
   Rarity reads from the MATERIAL, not brightness: C matte cardstock · R classic holo · SR textured foil ·
   UR brushed silver · SEC metallic-gold trophy. Everything is scoped to html.foil-v5 (FOIL_STYLE in ui.js, or
   ?foil=v4 to compare), so deleting this block + flipping the switch restores HOLO v4 exactly.
   Optical layers, each answering the light differently (--mx/--my from tilt/gyro; var() fallbacks = the resting light):
     frame  = the .card background: the material itself, plus a reflection band that slides with the tilt
     bevel  = inset/outset box-shadows on .card and .in: emboss, printed line or metal bezel round the art window
     rim    = ::after, big cards only: a thin outer edge that throws a sharp white specular toward the light
     plates = .nm / .txt: stay dark for legible text, tinted and etched in the rarity's material
     art    = .holo/.glit only as texture or points at low strength (art-safety rule), never a colour wash
     glare  = the moving highlight, shaped per material (broad satin → tight mirror hits)
   Grid thumbs (minis) get frame + bevel + symbol only: no rims, no idle animation, no per-frame work (HOLO v4 fixes).
   Guide tiers we don't have yet, kept for a future alt-art variant (don't add rarities for them):
     Uncommon "refined": C's cardstock + a satin-silver inner line and a faint pearl sheen on the name plate.
     Illustration Rare "artistic": full-bleed art, frame down to a hairline, soft canvas-texture foil on the painting.
     Special Illustration Rare "premium chase": full-bleed art + textured foil following the painting's shapes, gold rim. */
/* the frame's emboss lives on ::before (inset shadows over the frame edge), NOT on the card's own box-shadow, so
   gameplay rings (.can / .atk-ready / .best / selected starter) keep working exactly as before */
.foil-v5 .card::before { content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; box-shadow: var(--bevel, none); }
.foil-v5 .card.tilting { box-shadow: 0 1.2em 2.6em #000d; } /* lift without v4's coloured halo (guide: no excessive glow) */
.foil-v5 .card.r-SEC:not(.can, .atk-ready, .best, .tilting) { box-shadow: 0 .5em 1.4em #000b; } /* drop v4's purple SEC halo */
.foil-v5 .wide .peek .card.r-SEC { box-shadow: 0 20px 60px #000d; }
.foil-v5 .card .holo, .foil-v5 .card .glit { display: none; background: none; filter: none; } /* background:none too, so v4's data-URL textures aren't re-resolved every tilt frame */
.foil-v5 .card::after { content: none; animation: none; }
.foil-v5 .card:not(.mini)::after { content: ''; position: absolute; inset: 0; border-radius: inherit; padding: .1em; pointer-events: none;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude;
  background: radial-gradient(farthest-corner circle at var(--mx, 22%) var(--my, 12%), #fff 0, #fffc 8%, #fff0 38%); mix-blend-mode: overlay; opacity: calc(.35 + var(--hyp) * .65); box-shadow: none; }
.foil-v5 .card.r-C::after { content: none; }
.foil-v5 .card .nm span:first-child { filter: none; }
/* sparkle points: the v4 drop-shadow glow is clipped away by the star clip-path anyway (filter runs before
   clip-path), so v5 drops it — same look, ~12 fewer filter passes per frame on a tilting SR/UR/SEC */
.foil-v5 .card .spk { filter: none; }

/* C — matte cardstock: habitat colour printed flat, fine paper grain, soft top light, a clean printed line round
   the art; no foil anywhere, only a broad satin glare while tilting */
.foil-v5 .card.r-C:not(.fusion) {
  background: linear-gradient(165deg, #ffffff2e, #fff0 40%, #0000 60%, #00000047), var(--grain, none) 0 0 / 96px 96px, var(--c); }
.foil-v5 .card.r-C { --bevel: inset 0 0 0 .05em #0000002e, inset 0 .05em 0 #ffffff26; }
.foil-v5 .card.r-C .in { box-shadow: 0 0 0 .06em #ffffff45, 0 0 0 .11em #00000026; }
.foil-v5 .card.r-C .glare { mix-blend-mode: soft-light; opacity: calc(var(--hyp) * .55);
  background: radial-gradient(farthest-corner circle at var(--mx, 50%) var(--my, 50%), #fff 0%, #ffffff80 22%, #fff0 62%); }

/* R — classic holo: bright silver foil with fine diagonal foil lines, a pastel rainbow diffraction that slides with
   the tilt, a few star glints, and a polished metal edge round the art; the art itself only gets one diagonal
   shine that sweeps across as you tilt */
.foil-v5 .card.r-R:not(.fusion) {
  background:
    var(--glints, none) 0 0 / 7em 7em,
    repeating-linear-gradient(120deg, #fff0 0 .1em, #ffffff5c .1em .15em, #fff0 .15em .3em),
    linear-gradient(120deg, #ffb8c8, #ffe9a3, #bff5b3, #aee6ff, #c9bcff, #ffbdea, #ffb8c8) var(--mx, 30%) var(--my, 30%) / 200% 200%,
    linear-gradient(160deg, #fbfcfe, #bfc6d1 40%, #f4f6fa 62%, #a7afbc);
  background-blend-mode: screen, normal, multiply, normal; }
.foil-v5 .card.r-R { --bevel: inset 0 .05em 0 #ffffffb0, inset 0 -.05em 0 #00000059; }
.foil-v5 .card.r-R .in { box-shadow: 0 0 0 .05em #0000008c, 0 0 0 .1em #ffffffd9, 0 0 0 .15em #5f677666; }
.foil-v5 .card.r-R .glare { mix-blend-mode: overlay; opacity: calc(var(--hyp) * .9);
  background: linear-gradient(115deg, #fff0 calc(var(--mx, 50%) - 20%), #ffffffa6 var(--mx, 50%), #fff0 calc(var(--mx, 50%) + 20%)),
    radial-gradient(farthest-corner circle at var(--mx, 50%) var(--my, 50%), #ffffff59, #fff0 50%); }

/* SR — textured foil: anodised-titanium stock (deep blue/violet/teal/rose that shifts with the tilt) embossed with a
   fine crosshatch, gold accents on the art bezel; a faint crosshatch over the art so the whole card feels
   physically textured, and a soft prismatic reflection over the art only while tilting */
.foil-v5 .card.r-SR:not(.fusion) {
  background:
    linear-gradient(115deg, #fff0 calc(var(--mx, 30%) - 14%), #ffffff38 var(--mx, 30%), #fff0 calc(var(--mx, 30%) + 14%)),
    repeating-linear-gradient(45deg, #fff0 0 .1em, #ffffff33 .1em .14em),
    repeating-linear-gradient(-45deg, #fff0 0 .1em, #00000059 .1em .14em),
    linear-gradient(125deg, #2f4fb0, #7a36a8 17%, #1d8a9a 34%, #c0508a 51%, #d8a24a 60%, #3160c0 76%, #5a2f9a) var(--mx, 30%) var(--my, 30%) / 260% 260%;
  background-blend-mode: normal, normal, normal, normal; }
.foil-v5 .card.r-SR { --bevel: inset 0 .06em 0 #ffffff66, inset 0 -.06em 0 #000000a6, inset .06em 0 0 #ffffff26, inset -.06em 0 0 #0000004d; }
.foil-v5 .card.r-SR .in { box-shadow: 0 0 0 .05em #0d0a04, 0 0 0 .1em #e9c46a, 0 0 0 .15em #5a431a99; }
.foil-v5 .card.r-SR .glit { display: block; mix-blend-mode: overlay; opacity: calc(.1 + var(--hyp) * .1);
  background: repeating-linear-gradient(45deg, #fff0 0 .1em, #fff .1em .14em), repeating-linear-gradient(-45deg, #fff0 0 .1em, #000 .1em .14em); }
.foil-v5 .card.r-SR .holo { display: block; mix-blend-mode: soft-light; opacity: calc(var(--hyp) * .25);
  background: linear-gradient(115deg, #ff6f8e, #ffc45c, #7aeb8e, #5ccfff, #8c7bff, #ff6fd2, #ff6f8e) var(--mx, 50%) var(--my, 50%) / 300% 300%; }
.foil-v5 .card.r-SR .glare { mix-blend-mode: overlay; opacity: calc(.1 + var(--hyp) * .6);
  background: radial-gradient(farthest-corner circle at var(--mx, 50%) var(--my, 50%), #ffffffb3, #ffffff26 25%, #fff0 55%); }
.foil-v5 .card.r-SR .txt { background: linear-gradient(#0000, #0b0c12cc 22%, #08090ee6); }
.foil-v5 .card.r-SR .nm span:first-child { text-shadow: 0 -.05em 0 #ffffff59, 0 .07em 0 #000, 0 0 .25em #000a; } /* embossed, letterpress-style name */

/* UR — brushed-silver luxury: chrome bands with anisotropic brushing, micro-glitter, an embossed silver bevel, a
   dramatic diagonal reflection that races across the frame with the tilt, and several small sharp specular hits */
.foil-v5 .card.r-UR:not(.fusion) {
  background:
    linear-gradient(118deg, #fff0 calc(var(--mx, 30%) - 9%), #ffffffc7 var(--mx, 30%), #fff0 calc(var(--mx, 30%) + 9%)),
    var(--mglitter, none) 0 0 / 90px 90px,
    var(--brushed, none) 0 0 / 180px 180px,
    linear-gradient(125deg, #f4f7fb, #b3bbc6 11%, #3d444f 19%, #fafcfe 31%, #c6cdd6 47%, #444c58 58%, #eef2f6 70%, #97a0ac 86%, #f6f8fb) var(--mx, 30%) var(--my, 30%) / 220% 220%;
  background-blend-mode: overlay, soft-light, normal, normal; }
.foil-v5 .card.r-UR { --bevel: inset 0 .07em 0 #ffffffe0, inset 0 -.07em 0 #0000008c, inset .07em 0 0 #ffffff66, inset -.07em 0 0 #00000059; }
.foil-v5 .card.r-UR .in { box-shadow: 0 0 0 .05em #1b1f27, 0 0 0 .1em #f5f7fa, 0 0 0 .16em #4f5663; }
.foil-v5 .card.r-UR .glit { display: block; mix-blend-mode: screen; opacity: calc(.12 + var(--hyp) * .13); background: var(--mglitter, none) 0 0 / 130px 130px; }
.foil-v5 .card.r-UR .glare { mix-blend-mode: overlay; opacity: calc(.12 + var(--hyp) * .8);
  background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), #fff 0, #ffffffb3 3%, #fff0 13%),
    radial-gradient(circle at calc(var(--mx, 50%) * .5 + 40%) calc(var(--my, 50%) * .4 + 6%), #ffffffcc 0, #fff0 5%),
    radial-gradient(circle at calc(var(--mx, 50%) * .6 + 8%) calc(var(--my, 50%) * .3 + 62%), #ffffffa6 0, #fff0 4%),
    linear-gradient(118deg, #fff0 calc(var(--mx, 50%) - 10%), #ffffff73 var(--mx, 50%), #fff0 calc(var(--mx, 50%) + 10%)); }
.foil-v5 .card.r-UR .txt { background: linear-gradient(#0000, #0d1117cc 22%, #0a0d12e6); }
/* names stay solid ink with a dark outline — gradient-clipped lettering turned to mush at thumbnail size */
.foil-v5 .card.r-UR .nm span:first-child, .foil-v5 .card.r-SEC .nm span:first-child { background: none; color: #eef2f7;
  text-shadow: 0 .07em 0 #000, .05em 0 0 #000, -.05em 0 0 #000, 0 -.04em 0 #000, 0 0 .3em #000c; }
.foil-v5 .card.r-UR .spk { background: #fff; }

/* SEC — metallic-gold trophy: real gold is contrast, not yellow — deep brown shadows, warm mids, near-white
   mirror highlights, brushed grain, engine-turned micro-etching, a faint holo diffraction under the gold, an
   embossed gold bezel and a sharp warm mirror hit; the name is struck in gold */
.foil-v5 .card.r-SEC:not(.fusion) {
  background:
    linear-gradient(118deg, #fff0 calc(var(--mx, 30%) - 8%), #fff6dcc7 var(--mx, 30%), #fff0 calc(var(--mx, 30%) + 8%)),
    repeating-radial-gradient(circle at 50% 118%, #fff0 0 .16em, #ffffff29 .16em .21em),
    var(--brushed, none) 0 0 / 160px 160px,
    linear-gradient(100deg, #ff8fa8, #ffe08a, #a6f59a, #8fe0ff, #a898ff, #ff8fa8) var(--mx, 30%) 0 / 300% 100%,
    linear-gradient(125deg, #fff1c2, #c8962f 15%, #6a4610 28%, #e9c463 41%, #fffae6 48%, #c59434 57%, #5a3b0b 73%, #e3b957 87%, #fff0b5) var(--mx, 30%) var(--my, 30%) / 220% 220%;
  background-blend-mode: overlay, normal, normal, soft-light, normal; }
.foil-v5 .card.r-SEC { --bevel: inset 0 .07em 0 #fff6d6e6, inset 0 -.07em 0 #2e1d0599, inset .07em 0 0 #fff2c466, inset -.07em 0 0 #2e1d0566; }
.foil-v5 .card.r-SEC .in { box-shadow: 0 0 0 .05em #2a1904, 0 0 0 .1em #ffe6a0, 0 0 0 .16em #7a5414; }
.foil-v5 .card.r-SEC .glare { mix-blend-mode: overlay; opacity: calc(.12 + var(--hyp) * .75);
  background: radial-gradient(circle at var(--mx, 50%) var(--my, 50%), #fffbe8 0, #ffe7a0b3 4%, #fff0 16%),
    radial-gradient(circle at calc(var(--mx, 50%) * .5 + 42%) calc(var(--my, 50%) * .4 + 8%), #fff5d0bf 0, #fff0 5%),
    linear-gradient(118deg, #fff0 calc(var(--mx, 50%) - 10%), #ffe9b066 var(--mx, 50%), #fff0 calc(var(--mx, 50%) + 10%)); }
.foil-v5 .card.r-SEC .txt { background: repeating-radial-gradient(circle at 50% 130%, #fff0 0 .3em, #ffd98a0d .3em .36em), linear-gradient(#0000, #140d04d1 22%, #0e0902eb); }
.foil-v5 .card.r-SEC .nm { background: linear-gradient(#140d04f0, #140d0466 70%, #0000); }
.foil-v5 .card.r-SEC .nm span:first-child { color: #ffe39a; }
.foil-v5 .card.r-SEC .spk { background: #fff0c4; }
.foil-v5 .card.r-SEC::after { background: radial-gradient(farthest-corner circle at var(--mx, 22%) var(--my, 12%), #fffbe8 0, #ffe7a0cc 8%, #fff0 38%); }

/* body plates: on big cards the text panel carries the rarity's material under the text (like a reverse-holo body),
   faded in from the art edge by a mask; static except R's diffraction, which slides with the tilt. C stays plain. */
.foil-v5 .card:not(.mini):not(.r-C) .txt { z-index: 0; }
.foil-v5 .card:not(.mini):not(.r-C) .txt::before { content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  -webkit-mask-image: linear-gradient(#0000 12%, #000 34%); mask-image: linear-gradient(#0000 12%, #000 34%); }
.foil-v5 .card.r-R .txt::before { opacity: .2;
  background: var(--glints, none) 0 0 / 9em 9em, repeating-linear-gradient(120deg, #fff0 0 .1em, #ffffff38 .1em .15em, #fff0 .15em .3em),
    linear-gradient(120deg, #ff7f9c, #ffd86b, #8ff08a, #6fd6ff, #9a86ff, #ff86d8, #ff7f9c) var(--mx, 30%) var(--my, 30%) / 320% 320%; }
.foil-v5 .card.r-SR .txt::before { opacity: .2;
  background: repeating-linear-gradient(45deg, #fff0 0 .1em, #ffffffa6 .1em .14em), repeating-linear-gradient(-45deg, #fff0 0 .1em, #000 .1em .14em),
    linear-gradient(125deg, #2f4fb0, #7a36a8 25%, #1d8a9a 50%, #c0508a 75%, #3160c0); }
.foil-v5 .card.r-UR .txt::before { opacity: .3; background: var(--brushed, none) 0 0 / 180px 180px, linear-gradient(125deg, #8a93a0, #3a414c 40%, #aab2bd 70%, #454c57); }
.foil-v5 .card.r-SEC .txt::before { opacity: .26;
  background: repeating-radial-gradient(circle at 50% 135%, #fff0 0 .22em, #ffe7a8 .22em .29em), var(--brushed, none) 0 0 / 160px 160px, linear-gradient(125deg, #a87a26, #4a3009 45%, #c9983a 75%, #5a3b0b); }

/* grid thumbs: a slightly wider frame (.22em → .3em) so the material is there to see at 70-110px */
.foil-v5 .card.thumb { padding: .3em; }
.foil-v5 .card.thumb .in { inset: .3em; }

/* idle sweep (big SR/UR/SEC, HOLO v4 timing and #resgrid rules untouched): a narrower, material-coloured streak */
.foil-v5 .card .sweep::after { width: 22%; background: linear-gradient(75deg, transparent, #ffffff8c 48%, #ffffff8c 52%, transparent); }
.foil-v5 .card.r-SEC .sweep::after { background: linear-gradient(75deg, transparent, #fff1c48c 48%, #fff1c48c 52%, transparent); }

/* rarity symbols: original CSS-masked shapes (a dot; 4-point sparkle stars), in the footer on big cards and in the
   bottom-left corner of grid thumbs. C ● · R ✦ · SR ✦✦ · UR ✦✦✦ silver · SEC ✦✦✦ gold. The text stars (.rst) are the v4 fallback. */
.foil-v5 .card .rst { display: none; }
.card .rsym { display: none; }
.foil-v5 .card .ft .rsym, .foil-v5 .card.thumb .rsym { display: inline-block; line-height: 0; vertical-align: -.2em; }
.foil-v5 .card .rsym::before { content: ''; display: block; width: calc(var(--n, 1) * 1.2em); height: 1.2em; background: var(--symc, #eef1f6);
  -webkit-mask: var(--symsh) 0 0 / 1.2em 1.2em repeat-x; mask: var(--symsh) 0 0 / 1.2em 1.2em repeat-x; }
.foil-v5 .card .rsym { --symsh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-2 -2 24 24'%3E%3Cpath d='M10 0Q12 8 20 10Q12 12 10 20Q8 12 0 10Q8 8 10 0Z'/%3E%3C/svg%3E"); }
.foil-v5 .card.r-C .rsym { --symsh: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-2 -2 24 24'%3E%3Ccircle cx='10' cy='10' r='5.5'/%3E%3C/svg%3E"); --symc: #c9ced8; }
.foil-v5 .card.r-SR .rsym { --n: 2; }
.foil-v5 .card.r-UR .rsym { --n: 3; --symc: linear-gradient(180deg, #ffffff, #dfe4ea 45%, #9aa3b0 58%, #ffffff); }
.foil-v5 .card.r-SEC .rsym { --n: 3; --symc: linear-gradient(180deg, #fff6d0, #f0c75e 45%, #9c6b1a 55%, #ffe391); }
.foil-v5 .card.thumb .rsym { position: absolute; left: .3em; bottom: .3em; z-index: 1; font-size: .5em; padding: .2em .3em; border-radius: 1em; background: #000000b3; }
.foil-v5 .card.missing .rsym { display: none; }

/* resets: face-down cards stay the plain back; missing (unowned) dex cards stay flat; fusions keep their habitat split */
.foil-v5 .card.back.card { background: #1a2350; }
.foil-v5 .card.back .in, .foil-v5 .card.missing .in { box-shadow: none; }
.foil-v5 .card.back::before, .foil-v5 .card.back::after, .foil-v5 .card.missing::before, .foil-v5 .card.missing::after { content: none; }

/* ============ REVEAL v5 — no-blink pack reveal + 闪卡 charm (Keith's phone video, 2026-09-27) ============
   Blink fixes (all modes): .card is no longer preserve-3d and only .tilt cards are GPU layers (card rules above),
   one tilt owner at a time (ui.js), and nothing animates under a full-screen reveal: */
#screen:has(> .opening) > :not(.opening, .modal), #stage:has(> #screen > .opening) > #pagebg { visibility: hidden; }
/* v5 stage: a dark, rich per-rarity backdrop so the foil has contrast (the pale pastel stage washed it out on the
   phone). background-color is its own longhand so an old WebView without color-mix still gets a solid dark stage. */
.foil-v5 .opening { --rv1: #1c3656; --rv2: #0a1324; background-color: #05070f;
  background-image: radial-gradient(120% 78% at 50% 40%, var(--rv1) 0%, var(--rv2) 50%, #04060c 100%); }
.foil-v5 .opening[data-rar="R"] { --rv1: #29507f; --rv2: #0c182f; }
.foil-v5 .opening[data-rar="SR"] { --rv1: #57227d; --rv2: #190a2c; }
.foil-v5 .opening[data-rar="UR"] { --rv1: #4b5873; --rv2: #10151f; }
.foil-v5 .opening[data-rar="SEC"], .foil-v5 .opening[data-rar="god"] { --rv1: #6a4a12; --rv2: #1b1104; }
/* same looks, cheaper: no blur filters (each was a full-size offscreen pass every frame) and no blend-mode group */
.foil-v5 .opening::before { filter: none; opacity: .14; }
.foil-v5 .opening.rainbow::after { mix-blend-mode: normal; opacity: .14; }
.foil-v5 .stack.glow::before { filter: none; inset: -46px; border-radius: 50%; background: radial-gradient(closest-side, var(--edge, #bf5af2), #0000); }

/* live tilt on a big card (reveal, inspect, showcase): the foil answers the light strongly — a rainbow diffraction band
   slides across the body with the tilt (screen blend: white text stays white), the art gets the same band only
   through the soft art mask (≤ .2, art-safety rule), sparkles brighten */
.foil-v5 .card.tilting:not(.mini):not(.r-C) .holo { display: block; mix-blend-mode: color-dodge; opacity: calc(var(--hyp) * .6);
  background: linear-gradient(115deg, #0000 30%, #ff5f7e 38%, #ffcf5c 43%, #6ff09a 48%, #5ccfff 53%, #9a7bff 58%, #ff6fd2 63%, #0000 71%) var(--mx, 50%) var(--my, 50%) / 260% 260%;
  -webkit-mask-image: var(--fm-soft); mask-image: var(--fm-soft); }
.foil-v5 .card.tilting:not(.mini):not(.r-C) .txt::after { content: ''; position: absolute; inset: 0; pointer-events: none; mix-blend-mode: screen; opacity: calc(.1 + var(--hyp) * .75);
  background: linear-gradient(115deg, #0000 30%, #ff5f7e66 38%, #ffcf5c66 43%, #6ff09a66 48%, #5ccfff66 53%, #9a7bff66 58%, #ff6fd266 63%, #0000 71%) var(--mx, 50%) var(--my, 50%) / 260% 260%;
  -webkit-mask-image: linear-gradient(#0000 12%, #000 34%); mask-image: linear-gradient(#0000 12%, #000 34%); }
.foil-v5 .card.r-SEC.tilting:not(.mini) .txt::after { background: linear-gradient(115deg, #0000 32%, #ffe7a066 42%, #fff6dc99 50%, #ffcf6a66 58%, #0000 68%) var(--mx, 50%) var(--my, 50%) / 260% 260%; }
.foil-v5 .card.tilting .spk { --op: .8; }

/* reveal beat (ui.js revealPop): flash pop, one specular sweep, star glints — own elements, own transforms */
.rvfx { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; pointer-events: none; z-index: 3; }
.rvflash { position: absolute; inset: 0; opacity: 0; mix-blend-mode: screen; background: radial-gradient(circle at 50% 42%, #fff 0, #ffffffcc 22%, #fff0 68%); animation: rvFlash .5s ease-out; }
@keyframes rvFlash { 0% { opacity: 0; } 18% { opacity: .9; } 100% { opacity: 0; } }
.rvsweep { position: absolute; top: -25%; bottom: -25%; left: 0; width: 38%; mix-blend-mode: screen; transform: translateX(-130%) skewX(-18deg);
  background: linear-gradient(90deg, #fff0, #9fe6ff55 30%, #ffffffe6 50%, #ffd6f555 70%, #fff0); animation: rvSweep .8s .1s cubic-bezier(.35, .6, .2, 1) forwards; }
.card.r-SEC .rvsweep { background: linear-gradient(90deg, #fff0, #ffd98a66 30%, #fff8e6ee 50%, #ffcf6a66 70%, #fff0); }
@keyframes rvSweep { to { transform: translateX(330%) skewX(-18deg); } }
.rvspk { position: absolute; width: 1.2em; height: 1.2em; margin: -.6em; background: #fff; opacity: 0; transform: scale(0);
  clip-path: polygon(50% 0%, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0% 50%, 40% 40%); animation: rvSpk .7s ease-out forwards; }
.card.r-SEC .rvspk { background: #ffe8a6; }
@keyframes rvSpk { 0% { opacity: 0; transform: scale(0) rotate(0deg); } 35% { opacity: 1; transform: scale(1.25) rotate(50deg); } 100% { opacity: 0; transform: scale(.2) rotate(110deg); } }
/* no-gyro fallback (ui.js armShimmer): the slot sways on `rotate` (the stack's perspective makes it a real tilt) and a
   light band crosses the card; both pause/hide the moment a pointer or the gyro tilts the card */
.foil-v5 .slot.autoshim { animation: rvSway 3.8s ease-in-out infinite; }
@keyframes rvSway { 0%, 100% { rotate: y -7deg; } 50% { rotate: y 7deg; } }
.foil-v5 .slot.autoshim:has(.card.tilting), .foil-v5.gyro-live .slot.autoshim { animation-play-state: paused; }
.rvshim { position: absolute; inset: 0; border-radius: inherit; overflow: hidden; pointer-events: none; z-index: 2; }
.rvshim::after { content: ''; position: absolute; top: -25%; bottom: -25%; left: 0; width: 36%; mix-blend-mode: screen; opacity: 0; transform: translateX(-130%) skewX(-18deg);
  background: linear-gradient(90deg, #fff0, #ff8fa84d 25%, #9fe6ff66 40%, #ffffffb3 50%, #c9a8ff66 60%, #ffe08a4d 75%, #fff0); animation: rvShim 3.8s ease-in-out infinite; }
@keyframes rvShim { 0%, 12% { opacity: 0; transform: translateX(-130%) skewX(-18deg); } 22% { opacity: 1; } 52% { opacity: 1; } 62%, 100% { opacity: 0; transform: translateX(330%) skewX(-18deg); } }
.card.tilting .rvshim, .gyro-live .rvshim { display: none; }
.reduce-motion .rvfx, .reduce-motion .rvshim { display: none; }
.reduce-motion .slot.autoshim { animation: none; }

/* ---------- story: card lore, Story menu, dialogue player, cinema player (plan_story_ui.md) ---------- */
.modal .lore, .lore { font-style: italic; color: var(--muted); font-size: 13px; max-width: 320px; margin: -4px 0 0; }
.mrow.lock { opacity: .45; }
.mrow[data-act] { cursor: pointer; }
.mrow .mt { display: flex; align-items: center; gap: 6px; }

.dlgbox { position: absolute; inset: 0; z-index: 46; display: flex; flex-direction: column; justify-content: flex-end; background: linear-gradient(#0a0e1f00 40%, #05070fcc 75%, #05070fee); padding: 0 14px 18px; }
.dlgpanel { position: relative; display: flex; align-items: flex-end; gap: 12px; padding: 14px; animation: sheetIn .25s cubic-bezier(.2, .8, .2, 1); }
.dlgport { flex: none; width: 72px; height: 72px; border-radius: 50%; object-fit: cover; border: 3px solid var(--c, var(--gold)); box-shadow: 0 0 0 3px #05070fcc, 0 4px 14px #000a; }
.dlgmain { flex: 1; min-width: 0; }
.dlgname { font-family: var(--display); font-size: 17px; letter-spacing: .5px; text-shadow: 0 2px 0 #000; }
.dlgtext { font-size: 15px; line-height: 1.35; min-height: 2.7em; margin-top: 2px; }
.dlgnext { position: absolute; right: 16px; bottom: 10px; font-size: 13px; color: var(--muted); opacity: 0; animation: dlgBounce 1s ease-in-out infinite; }
.dlgnext.on { opacity: 1; }
@keyframes dlgBounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
.dlgskip { position: absolute; top: -34px; right: 4px; border: 1px solid var(--line); background: #0e1424e8; color: var(--muted); border-radius: 999px; padding: 5px 14px; font-size: 12px; font-family: var(--body); font-weight: 700; }
.wide .dlgpanel { max-width: 1000px; margin: 0 auto; }
.dlgport.sil { filter: brightness(.25) saturate(0) contrast(1.2); }
/* Story dialogue: big talking face above the text box (the small round .dlgport stays for tips/tutorial only) */
.dlgbox .dlgport { display: none; }
.dlgface { align-self: flex-start; width: min(58%, 260px); aspect-ratio: 3 / 4; margin: 0 0 -18px 6px; position: relative; z-index: 1; pointer-events: none;
  border-radius: 22px; overflow: hidden; border: 3px solid var(--c, var(--gold)); box-shadow: 0 0 0 3px #05070fcc, 0 10px 30px #000c, 0 0 26px color-mix(in srgb, var(--c, #ffc93c) 45%, transparent);
  animation: faceIn .28s cubic-bezier(.2, .9, .3, 1.2); transform-origin: 50% 100%; }
.dlgface img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wide .dlgface { width: 300px; } .tab .dlgface { width: 280px; }
@keyframes faceIn { from { transform: translateY(14px) scale(.94); opacity: 0; } }
.dlgface.emo-happy { animation: faceIn .28s ease-out, faceHop .5s .28s ease-out; }
.dlgface.emo-shock { animation: faceIn .2s ease-out, faceShake .45s .2s ease-in-out; }
.dlgface.emo-sad { animation: faceIn .35s ease-out; filter: saturate(.75) brightness(.9); transform: translateY(4px) rotate(-1.5deg); }
.dlgface.emo-mystery { animation: faceIn .4s ease-out; filter: brightness(.82) contrast(1.1); box-shadow: 0 0 0 3px #05070fcc, 0 10px 30px #000c, 0 0 34px #8a6bff99; }
.dlgface.emo-angry { animation: faceIn .2s ease-out, faceShake .3s .2s ease-in-out; box-shadow: 0 0 0 3px #05070fcc, 0 10px 30px #000c, 0 0 30px #ff4d4d88; }
@keyframes faceHop { 30% { transform: translateY(-12px) scale(1.03); } 60% { transform: translateY(0) scale(.99); } }
@keyframes faceShake { 20% { transform: translateX(-7px) rotate(-2deg); } 40% { transform: translateX(7px) rotate(2deg); } 60% { transform: translateX(-4px); } 80% { transform: translateX(3px); } }
.reduce-motion .dlgface { animation: none !important; }
.dlgopts { display: flex; flex-direction: column; gap: 8px; padding-top: 6px; }
.dlgopt { text-align: left; padding: 11px 14px; border-radius: 14px; border: 1px solid var(--gold); background: #ffc93c1f; color: var(--text, #fff); font-family: var(--body); font-weight: 700; font-size: 15px; line-height: 1.3; cursor: pointer; }
.dlgopt:active { background: #ffc93c55; }
/* Story Cards (plan_story_quests.md): small ribbon like the Fusion one, gold on the card's habitat colour */
.card.story .story-ribbon { position: absolute; top: .15em; left: .15em; z-index: 2; font-family: var(--display); font-size: .42em; font-weight: 800; letter-spacing: .08em; padding: .25em .6em; border-radius: .4em; background: linear-gradient(90deg, #ffe07a, var(--gold2, #e0a020)); color: #3b1d00; box-shadow: 0 .15em .4em #0008; }
.card.story.mini .story-ribbon { font-size: .5em; padding: .15em .4em; }
/* Story menu quest log */
.qlog { padding: 12px 14px; margin: 4px 0 12px; }
.qtitle { font-family: var(--display); font-size: 16px; color: var(--gold); margin-bottom: 6px; }
.qrow { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-top: 1px solid var(--line); flex-wrap: wrap; }
.qname { flex: 1; min-width: 120px; font-weight: 700; font-size: 14px; }
.qbtn { border: 1px solid var(--line); background: #0e1424e8; color: var(--text, #fff); border-radius: 999px; padding: 4px 12px; font-size: 12px; font-family: var(--body); font-weight: 700; }
button.qbtn { cursor: pointer; }
button.qbtn:not(.lock) { border-color: var(--green); }
.qbtn.lock { opacity: .45; }
.qcard { display: flex; align-items: center; gap: 12px; margin-top: 8px; }
.qcard .card { --w: 76px; flex: none; }
/* "To be continued…" card, above the result screen (z 50) */
.modal.tbc { z-index: 56; }
.tbc-done { font-family: var(--display); font-size: 18px; color: var(--gold); }
.tbc-next { font-weight: 800; font-size: 16px; }
.tbc-teaser { font-style: italic; color: var(--muted); font-size: 15px; max-width: 320px; margin: 0; }

.wide .dlgport { width: 128px; height: 128px; border-width: 4px; }
.wide .dlgname { font-size: 22px; }
.wide .dlgtext { font-size: 19px; min-height: 3.2em; }
.wide .dlgskip { top: -40px; font-size: 14px; padding: 6px 16px; }
@media (prefers-reduced-motion: reduce) { .dlgnext { animation: none; opacity: .8; } }

.cinema { position: absolute; inset: 0; z-index: 48; background: #000; display: flex; align-items: center; justify-content: center; }
.cinvid { width: 100%; height: 100%; object-fit: contain; background: #000; }
.cinskip, .cinsound { position: absolute; border: 1px solid var(--line); background: #0e1424e8; color: var(--text); border-radius: 999px; padding: 8px 16px; font-size: 13px; font-family: var(--body); font-weight: 700; }
.cinskip { top: 22px; right: 18px; }
.cinsound { bottom: 26px; left: 50%; transform: translateX(-50%); }
/* guest sign-in reminder on Home (Firebase backend) */
.signin-tile { border: 1px solid #e0a93a88; }

/* ---------- Nim's Training: coach overlay (plan_tutorial.md) — appended by tutorial.js, own block, own prefix.
   .tut-root holds the "do" step spotlight (dim-with-a-hole + ring + arrow) and the "info"/"wait" tap-anywhere
   catcher; .tut-bubble is a separate, SMALL, non-full-screen panel (unlike a normal Story dialogue box it has to
   coexist with a live battle underneath, so it never covers the spotlight hole — it docks to whichever half of
   the stage isn't currently spotlit). ---------- */
.tut-root { position: absolute; inset: 0; z-index: 44; pointer-events: none; }
.tut-dim { position: absolute; inset: 0; background: #05070fb0; pointer-events: auto; }
.tut-catcher { position: absolute; inset: 0; pointer-events: auto; background: transparent; }
.tut-ring { position: absolute; border: 3px solid var(--gold); border-radius: 14px; box-shadow: 0 0 0 4px #ffc93c55, 0 0 22px #ffc93c99; pointer-events: none; animation: tutPulse 1.1s ease-in-out infinite; }
@keyframes tutPulse { 0%, 100% { transform: scale(1); opacity: .85; } 50% { transform: scale(1.06); opacity: 1; } }
.tut-arrow { position: absolute; font-size: 26px; line-height: 1; color: var(--gold); text-shadow: 0 2px 0 #000; pointer-events: none; animation: tutBounce 1s ease-in-out infinite; }
@keyframes tutBounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(6px); } }
.tut-bubble { position: absolute; left: 10px; right: 10px; z-index: 46; pointer-events: auto; animation: sheetIn .25s cubic-bezier(.2, .8, .2, 1); }
.tut-bubble.bottom { bottom: 10px; }
.tut-bubble.top { top: 10px; }
.tut-skip { position: absolute; top: 14px; right: 14px; z-index: 48; pointer-events: auto; }
.wide .tut-skip { top: 14px; } /* beat the .wide .dlgskip top:-40px, which put Skip training off-screen on desktop */
.tut-confirm { display: flex; gap: 8px; margin-top: 8px; }
.tut-menu .tut-lessons { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 320px; margin-top: 4px; }
@media (prefers-reduced-motion: reduce) { .tut-ring, .tut-arrow { animation: none; } }
/* Home: Nim's Training call-to-action (until the tutorial is done) */
.tut-tile { border: 1px solid #62e6ff88; box-shadow: 0 0 14px #62e6ff33; }

/* ---------- Friends + direct duels (plan_friends.md) ---------- */
.friends-btn { position: relative; align-self: center; }
.frbadge { display: inline-block; min-width: 18px; height: 18px; margin-left: 6px; padding: 0 5px; border-radius: 9px; background: var(--red); color: #fff; font-size: 11px; line-height: 18px; text-align: center; vertical-align: middle; box-shadow: 0 0 8px var(--red); }
.frbadge[hidden] { display: none; }
.fr-card { padding: 14px; margin: 8px 0; display: flex; flex-direction: column; gap: 8px; }
.fr-card p { margin: 0 0 6px; font-size: 14px; }
.fr-lbl { font-size: 12px; }
.fr-code { font-family: var(--display); font-size: 30px; letter-spacing: 2px; color: var(--gold); user-select: all; }
.fr-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.fr-input { flex: 1; min-width: 0; font: inherit; font-size: 17px; padding: 9px 12px; border-radius: 12px; border: 1px solid var(--line); background: #0008; color: var(--text); }
.fr-input:focus-visible { outline: 2px solid var(--gem); outline-offset: 1px; }
.fr-msg { font-size: 14px; display: flex; flex-direction: column; gap: 8px; }
.fr-msg:empty { display: none; }
.fr-list { display: flex; flex-direction: column; gap: 8px; }
.fr-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; }
.fr-item .grow { flex: 1; min-width: 0; }
.fr-item .t1 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fr-sub { font-size: 12px; }
.fr-empty { font-size: 14px; padding: 6px 2px; }
.fr-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; background: #5a6488; }
.fr-dot.on { background: var(--green); box-shadow: 0 0 8px var(--green); }
.fr-x { min-width: 44px; }
.duel-pop { position: absolute; inset: 0; z-index: 70; display: flex; align-items: center; justify-content: center; padding: 16px; background: #05070fcc; }
.duel-card { width: 100%; max-width: 340px; padding: 18px 16px; text-align: center; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.duel-card h2 { margin: 0; overflow-wrap: anywhere; }
.duel-card p { margin: 0; font-size: 14px; }
.duel-time { font-family: var(--display); font-size: 26px; color: var(--gold); }

/* ---------- LANGUAGE (plan_i18n_zh.md) ---------- */
/* Chinese mode: i18n.js sets <html lang="zh-CN"> and loads Noto Sans SC + ZCOOL KuaiLe from Google Fonts. The Latin
   faces stay first so digits and FANTATON keep their look; CJK glyphs fall through to the Chinese faces. */
:root:lang(zh) {
  --display: 'Lilita One', 'ZCOOL KuaiLe', 'Arial Rounded MT Bold', 'Microsoft YaHei', 'PingFang SC', sans-serif;
  --body: 'Nunito', 'Noto Sans SC', 'Microsoft YaHei', 'PingFang SC', system-ui, sans-serif;
}
:lang(zh) body { line-break: strict; }
.logosub { font-family: var(--display); font-size: 20px; letter-spacing: .35em; margin: 6px 0 0 .35em; color: var(--gold); text-shadow: 0 2px 0 #3b1d00, 0 4px 12px #000a; }
/* card name bar: a 4+ character Chinese name (cardHTML adds .nm-long) shrinks instead of overflowing the frame */
:lang(zh) .card .nm span:first-child { min-width: 0; white-space: nowrap; }
:lang(zh) .card .nm span.nm-long { font-size: .86em; letter-spacing: -.02em; }

/* ---------- ⚙ Settings panel + first-run language chooser (plan_settings.md) ---------- */
/* the panel is long (5 sections + footer): unlike a normal centered .modal it scrolls, top-aligned */
.modal.settings { justify-content: flex-start; overflow-y: auto; padding-top: calc(28px + env(safe-area-inset-top)); }
.modal.settings h2 { margin-top: 0; }
.setsec { width: 100%; max-width: 340px; margin: 0 auto 20px; text-align: left; }
.setlbl { font-family: var(--display); font-size: 15px; color: var(--muted); letter-spacing: .3px; margin-bottom: 8px; }
.setlbl2 { font-family: var(--display); font-size: 13px; color: var(--muted); margin: 14px 0 6px; }
/* segmented control (language, foil style): two big thumb-friendly halves, the active one gold-filled */
.seg { display: flex; gap: 8px; }
.seg button { flex: 1; padding: 13px 8px; border-radius: 12px; border: 1px solid var(--line); background: var(--panel); color: var(--text); font-family: var(--display); font-size: 15px; }
.seg button.on { background: linear-gradient(#ffe07a, var(--gold) 40%, var(--gold2)); color: #3a1c00; border-color: transparent; }
/* sound/display toggles: a label row + an On/Off pill button, big enough to tap comfortably on a phone */
.volrow { padding: 0 0 8px; }
.volrow input[type=range] { width: 100%; height: 28px; accent-color: var(--gold); touch-action: pan-y; }
.swrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 9px 0; font-size: 15px; }
.sw { min-width: 74px; padding: 9px 16px; border-radius: 999px; border: 1px solid var(--line); background: var(--panel); color: var(--muted); font-family: var(--display); font-size: 14px; }
.sw.on { background: linear-gradient(#ffe07a, var(--gold) 40%, var(--gold2)); color: #3a1c00; border-color: transparent; }
.ver { font-size: 11px; opacity: .6; margin: 2px 0 -2px; }
.wide .setsec, .tab .setsec { max-width: 380px; }
/* first-run "Choose your language · 选择语言": shown once, before the title screen, for a brand-new player */
.lang-scr { align-items: center; justify-content: center; text-align: center; gap: 14px; padding: 24px; }
.lang-scr .btn { width: 240px; }
.lang-prompt { font-size: 17px; margin: 4px 0 12px; }
.lang-pick.on { box-shadow: 0 0 0 3px var(--gold), 0 5px 0 #a3530a, 0 14px 28px #000a; }

/* ---------- plan_fusion_clarity.md: "How to fuse" box + Fusions ⓘ explainer ---------- */
/* the box (card + rules + owned materials) can run taller than the screen on mobile, so — like .modal.settings —
   this inspect modal scrolls top-aligned instead of trying to vertically center everything */
.modal.fuseinfo { justify-content: flex-start; overflow-y: auto; padding-top: 20px; }
.fusebox { text-align: left; padding: 12px 14px; font-size: 14px; }
.fusebox .fusehead { font-family: var(--display); font-size: 16px; margin-bottom: 6px; }
.fusebox ul { padding-left: 18px; margin: 0 0 8px; }
.fusebox li { margin-bottom: 5px; line-height: 1.4; }
.fusebox .fusemats { margin-top: 8px; }

/* ---------- plan_screen_help.md: per-screen "?" help sheets, first-visit Nim tips, deck health checklist ---------- */
/* a small round ? button, top of each screen's own scrolling body (not the topbar) — opens help.<key> as a modal */
.help-row { display: flex; justify-content: flex-end; width: 100%; margin-bottom: 2px; }
.help-fab { border-radius: 50%; width: 34px; height: 34px; font-family: var(--display); font-size: 16px; }
/* first-visit tip: reuses the coach-bubble look (.dlgpanel/.dlgport/.dlgname/.dlgtext, tutorial.js) but stands
   alone — no dim, no spotlight, no forced tap. Sits above the bottom nav; .top docks it under the topbar instead
   (used in battle, where the bottom is the hand of cards). */
.tip-bubble { position: absolute; left: 10px; right: 10px; bottom: 78px; z-index: 28; /* under .shade/.sheet (29/30), .modal (45) and Nim's .dlgbox (46): those cover it instead of stacking under it (a :has() rule did this but cost style recalcs on every re-render) */ animation: sheetIn .25s cubic-bezier(.2, .8, .2, 1); }
.tip-bubble.top { bottom: auto; top: 66px; }

.tip-actions { display: flex; gap: 8px; margin-top: 8px; }
.wide .tip-bubble { max-width: 640px; margin: 0 auto; }
/* deck health checklist: a row of small chips above the full deck summary — ✓/⚠ only for real rules/recommendations
   (card count, Stage 1s, evolution lines), plain info chips (no icon) for Spells/Traps and Habitat counts */
.deckhealth { display: flex; flex-wrap: wrap; gap: 6px 10px; padding: 8px 12px; margin-bottom: 8px; font-size: 12px; }
.hchip { color: var(--muted); }
.hchip.ok { color: var(--green); }
.hchip.warn { color: var(--gold); }
/* Settings: one short explainer line per section (plan_screen_help.md) */
.setline { font-size: 12px; margin-top: 8px; line-height: 1.35; }
/* Fusion recipe icons on every Fusion card face (incl. thumbnails): FUSION ⚡+🌲 */
.fuse-ribbon .fuse-rec { white-space: nowrap; letter-spacing: 0; }

/* ---------- plan_deck_slots.md: 5 saved deck slots + "choose your deck" before every match ---------- */
/* Deck screen slot bar: 5 tabs, each a tiny hero thumb + name + ✓/count. Scrolls sideways if it overflows. */
.slotbar { display: flex; gap: 6px; overflow-x: auto; margin: 8px 0; padding-bottom: 2px; }
.slottab { display: flex; align-items: center; gap: 6px; flex: none; max-width: 150px; padding: 6px 10px 6px 6px; border-radius: 12px; border: 1px solid var(--line); background: var(--panel); color: var(--muted); }
.slottab.on { border-color: var(--gem); background: #2b3563; color: var(--text); }
.slotthumb { width: 30px; height: 30px; border-radius: 8px; background: center 25%/cover #0e1424; flex: none; }
.slotinfo { display: flex; flex-direction: column; align-items: flex-start; overflow: hidden; }
.slotname { font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 96px; }
.slotstat { font-size: 10px; color: var(--muted); }
.deckslotbtns { display: flex; gap: 8px; flex-wrap: wrap; margin: 6px 0; }
/* empty-slot friendly state, above the (still-present) collection/Auto-build controls */
.deckempty { padding: 14px; margin: 8px 0; text-align: center; font-size: 14px; }
.deckempty .btn { margin-top: 8px; }
.card.mini .fuse-ribbon { font-size: .8em; padding: .05em .35em; }

/* ---------- plan_tower.md: 🗼 Infinite Tower ---------- */
.tw-tile .rbicon { font-size: 20px; }
.tw-intro { font-size: 14px; margin-top: 0; text-shadow: 0 1px 3px #000, 0 0 8px #000; }
.tw-small { font-size: 12px; }
.tw-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin: 4px 0 10px; }
.tw-stats .pill { font-size: 13px; }
.tw-main { display: flex; gap: 12px; align-items: stretch; }
/* the tower itself: a CSS column of stone floors around the current one, spire on top, base below */
.tw-art { flex: none; width: 104px; display: flex; flex-direction: column; align-items: center; padding: 4px 0; }
.tw-spire { width: 0; height: 0; border-left: 40px solid transparent; border-right: 40px solid transparent; border-bottom: 46px solid #6b5aa8; filter: drop-shadow(0 -4px 10px #b48cff66); }
.tw-fl { position: relative; width: 84px; height: 38px; margin-top: 3px; border-radius: 4px; display: flex; align-items: center; justify-content: center; gap: 4px;
  background: repeating-linear-gradient(90deg, #ffffff10 0 2px, transparent 2px 20px), linear-gradient(#3b3f6b, #262a4c); border: 1px solid #ffffff22; color: #cfd6f5; font-family: var(--display); font-size: 15px; }
.tw-fl i { font-style: normal; font-size: 12px; }
.tw-fl.done { opacity: .55; }
.tw-fl.rule { border-color: #2fbf9b; }
.tw-fl.boss { border-color: #ff7ad9; background: linear-gradient(#5a2f6a, #35204a); }
.tw-fl.cur { width: 96px; border: 2px solid var(--gold); color: #fff; box-shadow: 0 0 14px #ffd84a88; animation: twGlow 1.8s ease-in-out infinite; }
.tw-base { width: 104px; height: 14px; margin-top: 3px; border-radius: 4px 4px 10px 10px; background: linear-gradient(#2a2d4f, #151730); }
@keyframes twGlow { 50% { box-shadow: 0 0 22px #ffd84acc; } }
.reduce-motion .tw-fl.cur { animation: none; }
.tw-run { flex: 1; min-width: 0; padding: 12px; display: flex; flex-direction: column; gap: 6px; text-align: left; }
.tw-floor { font-family: var(--display); font-size: 24px; }
.tw-chips { display: flex; flex-wrap: wrap; gap: 4px; }
.tw-chip { margin: 0; font-size: 12px; }
.tw-next { display: flex; gap: 10px; align-items: flex-start; margin-top: 4px; }
.tw-next .grow { flex: 1; min-width: 0; }
.tw-next .t1 { font-family: var(--display); font-size: 17px; }
.tw-next .thumb { flex: none; }
.tw-badge { display: inline-block; border-radius: 999px; padding: 1px 8px; font-size: 11px; font-weight: 700; }
.tw-badge.rule { background: #2fbf9b33; color: #7ff0d2; border: 1px solid #2fbf9b; }
.tw-badge.boss { background: #ff7ad933; color: #ffb3ec; border: 1px solid #ff7ad9; }
.tw-rule { margin-top: 6px; font-size: 13px; white-space: normal; }
.tw-btns { margin-top: 6px; flex-wrap: wrap; }
.tw-boonhead { font-family: var(--display); font-size: 17px; margin-top: 6px; }
.tw-boons { display: flex; flex-direction: column; gap: 6px; width: 100%; }
.tw-boon { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; color: inherit; text-align: left; }
.tw-boon b { font-size: 15px; }
.tw-boon span { font-size: 12px; color: var(--muted); }
.tw-boon:hover, .tw-boon:focus-visible { border-color: var(--gold); }
.tw-lbrow.me { border: 1px solid var(--gold); }
.tw-rank { width: 34px; flex: none; color: var(--muted); font-size: 13px; }
.tw-lbfloor { flex: none; color: var(--gold); font-size: 14px; }
.tw-guest { text-align: center; }
.tw-rewards { padding-left: 18px; text-align: left; font-size: 14px; }
.tw-result { overflow-y: auto; padding-top: 50px; }
.tw-result .tw-big { font-size: 44px; text-align: center; }
.tw-result .tw-boonslot { width: 100%; max-width: 420px; }
.tw-reached { font-family: var(--display); font-size: 22px; }
.tw-newbest { font-size: 16px; color: var(--gold); border-color: var(--gold); }
.tw-actions { display: flex; gap: 10px; margin-top: auto; flex-wrap: wrap; justify-content: center; padding-top: 10px; }

/* ---------- Expansion 5 "Driftlight Isles" + ✦ ALT ART + Tower art (plan_exp5_altart.md) ---------- */
/* card art not drawn yet: a soft habitat-tinted glow and a faint ✦ instead of a black hole. It sits under .art
   (DOM order), so the real painting simply covers it once the file exists. */
.card .in { background: #0b1030; background: radial-gradient(circle at 50% 36%, color-mix(in srgb, var(--c) 42%, #0b1030) 0%, #0b1030 72%); }
.card .in::before { content: '✦'; position: absolute; left: 0; right: 0; top: 30%; text-align: center; font-size: 3.2em; line-height: 1; color: color-mix(in srgb, var(--c) 55%, #fff); opacity: .28; pointer-events: none; }
.card.mini .in::before { top: 34%; font-size: 2.2em; }
.card.back .in::before, .card.missing .in::before { content: none; }
/* ✦ ALT ART = the style guide's "Special Illustration Rare": the painting runs full-bleed inside a hairline rim of
   the card's own rarity material, the name/text plates thin out to let the art through, and a selective foil — a
   slow pastel rainbow in color-dodge, so it lights the painting's bright shapes and leaves the darks alone. */
.card.alt, .foil-v5 .card.alt:not(.back) { padding: .12em; }
.card.alt .in { inset: .12em; }
.foil-v5 .card.alt:not(.mini) .in { box-shadow: 0 0 0 .05em #0008; }
.card.alt:not(.mini) .nm, .foil-v5 .card.alt:not(.mini) .nm { background: linear-gradient(#0009, #0000); }
.card.alt:not(.mini) .txt, .foil-v5 .card.alt:not(.mini) .txt { background: linear-gradient(#0000, #0006 30%, #000b); }
.foil-v5 .card.alt:not(.mini) .txt::before { opacity: .12; }
.card.alt:not(.mini) .hab, .card.alt:not(.mini) .stg { background: #0006; }
.card.alt:not(.mini) .glit, .foil-v5 .card.alt:not(.mini) .glit { display: block; mix-blend-mode: color-dodge; opacity: calc(.22 + var(--hyp, 0) * .3); filter: none;
  background: linear-gradient(115deg, #0000 10%, #ff8ad866 28%, #ffe36e66 40%, #7af0ff66 52%, #b48cff66 64%, #0000 82%) var(--mx, 30%) var(--my, 30%) / 300% 300%; }
.card .altb { position: absolute; bottom: -.55em; left: 50%; transform: translateX(-50%); z-index: 3; white-space: nowrap; font-family: var(--display); font-size: .6em; padding: .2em .65em; border-radius: 1em;
  background: linear-gradient(90deg, #ffe36e, #ff8ad8, #7af0ff); color: #1a1030; box-shadow: 0 .2em .6em #000a; pointer-events: none; }
.card.mini .altb { bottom: auto; top: .15em; left: .3em; transform: none; font-size: .8em; padding: 0 .3em; line-height: 1.4; }
.altlbl { font-size: .7em; padding: .1em .5em; border-radius: 1em; background: linear-gradient(90deg, #ffe36e, #ff8ad8, #7af0ff); color: #1a1030; vertical-align: middle; }
.fx-banner.alt span { color: #fff; text-shadow: 0 0 12px #ff8ad8, 0 0 22px #7af0ff, 0 3px 0 #000; }
/* Tower boon badges (art/boons/*.png); a missing file is replaced by its emoji */
.tw-bi { width: 20px; height: 20px; border-radius: 50%; vertical-align: -5px; object-fit: cover; }
.tw-bi.big { width: 34px; height: 34px; vertical-align: -11px; margin-right: 2px; }
/* title poster (art/bg/title_p|l.jpg): the logo is painted in, so the HTML logo steps aside once it has loaded */
.title-scr.poster .logo { display: none; }
.title-scr.poster .bg-art::after { background: linear-gradient(#0a0e1f00 62%, #0a0e1fcc 92%); }
/* The ? help row is full-width for stacked (phone) layouts; in side-by-side layouts (wide/tab Home etc.) it must not
   be a flex item or its 100% width squeezes the columns - pin it to the top-right corner of the screen body instead. */
.wide .body, .tab .body { position: relative; }
.wide .body > .help-row, .tab .body > .help-row { position: absolute; top: 6px; right: 14px; width: auto; margin: 0; z-index: 5; }
/* deck picker: fixed small hero thumb + readable row width on every layout */
.modal .mrow .card.thumb { flex: 0 0 auto; width: 64px; }
.modal .mrow { min-width: min(420px, 86vw); }
.pickerlist { width: min(520px, 92vw); margin: 0 auto; display: flex; flex-direction: column; gap: 8px; }
.pickerlist .mrow { width: 100%; min-width: 0; }
/* deck builder: in-deck cards get a − (tap card = +1), and the collection filters stay pinned while scrolling */
.dminus { position: absolute; top: -6px; left: -6px; z-index: 3; width: 26px; height: 26px; border-radius: 50%; border: 2px solid #fff; background: #e0445a; color: #fff; font-weight: 800; font-size: 18px; line-height: 1; display: grid; place-items: center; padding: 0; box-shadow: 0 2px 6px #000a; }
.wide .dminus { width: 32px; height: 32px; font-size: 22px; }
.deckfilters { position: sticky; top: -1px; z-index: 6; margin: 0 -10px; padding: 4px 10px 2px; background: linear-gradient(#0b1230f2 85%, #0b123000); backdrop-filter: blur(6px); }
.deckfilters h3 { margin: 6px 0 0; }
.deckfilters .filters { margin: 6px 0; }

/* ---------- Graphic Home: painted icon buttons (art/ui/*.png, sliced by tools/slice_ui.py) ---------- */
.gi { display: block; width: 100%; height: auto; aspect-ratio: 1; object-fit: contain; pointer-events: none; filter: drop-shadow(0 4px 5px #000a); }
.gtile { position: relative; overflow: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; min-height: 92px; padding: 8px 6px 10px; border: 1px solid #ffc93c55; border-radius: 18px; color: var(--text); font-family: var(--display); font-size: 15px; line-height: 1.1; text-align: center; background: radial-gradient(120% 90% at 50% 0%, #ffffff26, transparent 60%), linear-gradient(180deg, #2a3570ee, #131a3cf2); box-shadow: 0 4px 0 #05070f, 0 10px 22px #0009, inset 0 1px 0 #ffffff40; transition: transform .1s; }
.gtile .gi { width: 56px; transition: transform .2s; }
.gtile span { text-shadow: 0 2px 0 #000a; text-wrap: balance; }
.gtile:active { transform: translateY(3px); box-shadow: 0 1px 0 #05070f, 0 4px 10px #0009, inset 0 1px 0 #ffffff40; }
.gtile::after { content: ''; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(105deg, transparent 40%, #ffffff3a 50%, transparent 60%); transform: translateX(-120%); }
@media (hover: hover) { .gtile:hover::after { transform: translateX(120%); transition: transform .7s; } .gtile:hover .gi { transform: translateY(-3px) scale(1.06); } }
.gheroes { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 12px; }
.ggrid { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 10px; margin-top: 10px; }
.gtile.hero { min-height: 132px; font-size: 21px; gap: 6px; }
.gtile.hero .gi { width: 88px; animation: giBob 3.4s ease-in-out infinite; }
.gtile.hero.adv { border-color: #ffc93c; background: radial-gradient(110% 80% at 50% 0%, #ffc93c66, transparent 65%), linear-gradient(180deg, #5b3a1cf2, #1f1530f2); }
.gtile.hero.quick { border-color: #62e6ff; background: radial-gradient(110% 80% at 50% 0%, #62e6ff55, transparent 65%), linear-gradient(180deg, #1c4a68f2, #101a38f2); }
.gtile.hero::after { animation: giShine 5.5s ease-in-out 1s infinite; }
.gtile.hero.quick::after, .gtile.hero.quick .gi { animation-delay: 2.2s, 0s; }
.gtile.online { border-color: #ff8a1f; background: radial-gradient(110% 80% at 50% 0%, #ff8a1f55, transparent 65%), linear-gradient(180deg, #4a2c1ef2, #1a1530f2); }
.gtile.friends-btn { align-self: stretch; }
.gtile .frbadge { position: absolute; top: 6px; right: 8px; margin: 0; }
.gtile.packgo { flex: none; width: 88px; min-height: 88px; font-size: 13px; border-color: #ffc93c; }
.gtile.packgo .gi { width: 46px; }
.rbicon.gic { width: 48px; height: 48px; background: none; box-shadow: none; filter: drop-shadow(0 0 7px var(--c)); }
.gbtn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
.gbtn .gi { width: 36px; margin: -6px 0; }
.nav button b .gi { width: 30px; transition: transform .15s; }
.nav button:not(.on) .gi { opacity: .85; }
.nav button.on .gi { transform: translateY(-2px) scale(1.12); }
@keyframes giBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-4px); } }
@keyframes giShine { 0%, 70% { transform: translateX(-120%); } 100% { transform: translateX(120%); } }
.wide .gtile { min-height: 120px; font-size: 20px; }
.wide .gtile .gi { width: 76px; }
.wide .gheroes, .wide .ggrid { gap: 16px; }
.wide .gtile.hero { min-height: 190px; font-size: 30px; }
.wide .gtile.hero .gi { width: 128px; }
.wide .gtile.packgo { width: 130px; min-height: 120px; font-size: 16px; }
.wide .gtile.packgo .gi { width: 64px; }
.wide .nav button b .gi { width: 44px; }
.tab .nav button b .gi { width: 34px; }
@media (prefers-reduced-motion: reduce) { .gtile::after { display: none; } .gtile .gi, .nav button b .gi { animation: none; transition: none; } }
.reduce-motion .gtile::after { display: none; } .reduce-motion .gtile .gi, .reduce-motion .nav button b .gi { animation: none; transition: none; }

/* ---- touch targets (QA pass): the compact look stays, but every small control gets an invisible halo so its hit area is ~44px+.
   Tiny round/pill controls get a wide halo, the chunkier .btn.sm / chips / tabs a narrow one. ---- */
:where(.icon-btn, .help-fab, .vibetoggle, .qbtn, .duo-chip, .sw, .btn.sm, .filters button, .slottab, .seg button, .mtabs button) { position: relative; } /* :where = zero specificity, so it never overrides an existing position:absolute (e.g. .help-tag) */
.icon-btn::after, .help-fab::after, .vibetoggle::after, .dinfo::after, .dminus::after, .qbtn::after, .cinskip::after, .cinsound::after, .dlgskip::after, .duo-chip::after, .sw::after { content: ''; position: absolute; inset: -9px; }
.btn.sm::after, .slottab::after, .seg button::after { content: ''; position: absolute; inset: -5px; }
.stepper .btn.sm::after, .filters button::after { content: ''; position: absolute; inset: -7px; }
.dinfo::after, .dminus::after { inset: -14px; }

/* tapping a locked Story stage flashes the stage you can play now */
.advnode.flash, .advfinale.flash { animation: advFlash .45s ease-in-out 3; }
@keyframes advFlash { 50% { transform: translate(-50%, -50%) scale(1.25); box-shadow: 0 0 0 6px #fff, 0 0 28px var(--c); } }
.advfinale.flash { animation-name: advFlashF; }
@keyframes advFlashF { 50% { transform: scale(1.06); box-shadow: 0 0 0 4px #fff, 0 0 24px var(--gold); } }
@media (prefers-reduced-motion: reduce) { .advnode.flash, .advfinale.flash { animation: none; outline: 4px solid #fff; } }
/* ---- graphics tiers (Settings → Graphics; ui.js sets html.q-med / html.q-low, nothing = High) ----
   Medium: no backdrop blur, quieter idle decoration. Low: also a static foil (no looping shine/sparkles/shimmer), no looping aura or bokeh. */
.q-med .glass, .q-low .glass { backdrop-filter: none; background: rgba(21, 28, 56, .94); }
.q-med .deckfilters, .q-low .deckfilters { backdrop-filter: none; }
.q-med .pglayer, .q-low .pglayer, .q-med .pgmote, .q-low .pgmote,
.q-med .gtile.hero .gi, .q-low .gtile.hero .gi, .q-med .gtile.hero::after, .q-low .gtile.hero::after { animation: none !important; }
.q-med .pgmote, .q-low .pgmote { display: none; }
.q-low .card .sweep::after, .q-low .spk, .q-low .rvshim, .q-low .rvfx { display: none; }
.q-low .foil-v5 .slot.autoshim, .q-low .card.r-SEC::after, .q-low .pack .sheen, .q-low .pack .mote, .q-low .aura, .q-low .stack.glow::before,
.q-low .opening::before, .q-low .opening.rainbow::after, .q-low .rays, .q-low .advnode:not(.lock), .q-low .card.can, .q-low .slotx.can,
.q-low .radar i, .q-low .ltile.today, .q-low .tw-fl.cur, .q-low .trapz .tb { animation: none !important; }
.gfx-pick { display: flex; flex-direction: column; align-items: center; gap: 2px; position: relative; }
.gfx-pick small { font-size: 12px; font-weight: 600; opacity: .8; }
.gfx-rec { position: absolute; top: -9px; right: 10px; padding: 2px 9px; border-radius: 999px; background: #30d158; color: #04230f; font-size: 11px; font-style: normal; font-weight: 800; }
.gfxlag { position: absolute; left: 12px; right: 12px; bottom: calc(96px + env(safe-area-inset-bottom)); z-index: 62; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; font-size: 13px; box-shadow: 0 8px 24px #000a; }
.gfxlag > div { display: flex; gap: 6px; flex: none; }
/* ---- Packs screen: one-page grid of every set (no sideways scroll) + tap-for-detail modal ---- */
.packgrid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px 12px; width: 100%; padding: 8px 0 12px; }
.tab .packgrid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* grid tiles are .pack too, but the cut-swipe rule above (touch-action:none) made the whole grid unscrollable on phones
   and tablets; and seven looping overlay-blend sheens over full-size art were the Packs page's lag: tiles stay still */
.packgrid .pack { touch-action: pan-y; }
.packgrid .pack .sheen { display: none; }
.wide .packgrid { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 18px; }
.packtile { position: relative; cursor: pointer; justify-self: center; width: 100%; max-width: 190px; -webkit-tap-highlight-color: transparent; }
.packtile .packbox { width: 100%; height: auto; aspect-ratio: 150 / 214; transition: box-shadow .18s; }
.packtile .packbox.on { box-shadow: 0 0 0 3px var(--pc), 0 0 26px var(--pc); }
.packtile .ptitle { font-size: 17px; bottom: 24px; }
.packtile .psub { font-size: 10px; letter-spacing: 1px; }
.wide .packtile .ptitle { font-size: 22px; }
.wide .packtile .psub { font-size: 13px; }
.packqty { position: absolute; top: 8px; right: 8px; z-index: 2; min-width: 28px; padding: 3px 8px; border-radius: 999px; background: var(--gold); color: #3a1c00; font-family: var(--display); font-size: 15px; box-shadow: 0 2px 0 #a3530a; }
.packdetail { justify-content: center; }
.packdetail .pdbody { display: flex; flex-direction: column; align-items: center; gap: 14px; }
.packdetail .pack { box-shadow: 0 0 0 3px var(--pc), 0 0 34px var(--pc); }

/* Daily Arena Rule (plan_daily_rule.md): home tile, battle banner, HUD chip */
.drule-tile .drule-lbl { font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--gold); }
.drule-tile .rbend { text-align: right; line-height: 1.35; }
.rule-banner { position: absolute; left: 50%; top: 30%; z-index: 60; transform: translateX(-50%); max-width: 90%; padding: 10px 18px; border-radius: 14px; background: #05070fe6; border: 1px solid var(--gold); text-align: center; pointer-events: none; display: flex; flex-direction: column; gap: 4px; animation: ruleBanner 3.6s ease both; }
.rule-banner b { font-family: var(--display); font-size: 18px; color: var(--gold); }
.rule-banner span { font-size: 14px; }
@keyframes ruleBanner { 0% { opacity: 0; transform: translate(-50%, -12px); } 10%, 85% { opacity: 1; transform: translate(-50%, 0); } 100% { opacity: 0; transform: translate(-50%, 0); } }
.rule-chip { cursor: pointer; margin: -6px 0 -6px 4px; padding: 6px 6px 6px 8px; border-left: 1px solid var(--line); }
#stage:not(.wide) .rule-chip .rn { display: none; }
@media (prefers-reduced-motion: reduce) { .rule-banner { animation: none; } }
