:root { --bg:#14161a; --panel:#1e2127; --ink:#f2f3f5; --muted:#a7adb8; --accent:#e8b34b; --line:#333842; }
* { box-sizing:border-box; }
body { margin:0; font-family:system-ui,-apple-system,"PingFang SC","Microsoft YaHei",sans-serif; background:var(--bg); color:var(--ink); }
/* Header is NOT sticky: filters and search scroll away with the page
   (user request, 2026-10-21); use the floating ↑ button to get back. */
header { background:var(--bg); padding:.75rem .9rem .4rem; border-bottom:1px solid var(--line); }
#toTop { position:fixed; right:1rem; bottom:1rem; z-index:10; width:3rem; height:3rem; border-radius:50%; font-size:1.4rem; line-height:1; opacity:0; pointer-events:none; transition:opacity .2s; }
/* Header bar (2026-10-23, user request): the site title and the
   section links share one row, RiftDecks/BoundRift-style. Links
   are plain text sized to their names — not full-width buttons —
   and the current page gets the accent color plus an underline. */
h1 { display:flex; align-items:center; gap:.5rem; flex-wrap:wrap; }
.siteBar { display:flex; align-items:baseline; gap:1.1rem; flex-wrap:wrap; margin-bottom:.55rem; }
.brand { font-size:1.25rem; font-weight:700; margin:0; }
.brand a { color:var(--ink); text-decoration:none; }
.topnav { display:flex; gap:1rem; margin-top:0; }
.topnav a { flex:none; padding:.15rem 0; border:none; border-bottom:2px solid transparent; background:transparent; color:var(--muted); text-decoration:none; font-size:.98rem; text-align:left; white-space:nowrap; }
.topnav a[aria-current="page"] { border-color:var(--accent); color:var(--accent); font-weight:700; }
/* Sort direction checkbox (2026-10-23): sits inline next to the
   sort dropdown in the filter row / builder filter bar. */
.sortDir { display:inline-flex; align-items:center; gap:.25rem; font-size:.9rem; color:var(--muted); white-space:nowrap; }
/* A link styled as a button (e.g. the lookup page's + New, which
   navigates to the Decks page instead of running page script). */
.btnA { display:inline-block; padding:.65rem .9rem; border-radius:.6rem; border:1px solid var(--line); background:var(--panel); color:var(--ink); font-size:.95rem; text-decoration:none; text-align:center; white-space:nowrap; }
/* How to play page. */
.howto { max-width:42rem; margin:0 auto; padding:.8rem .9rem 2rem; }
.howto h2 { margin:1.2rem 0 .4rem; font-size:1.15rem; }
.howto h3 { margin:1rem 0 .3rem; font-size:1rem; }
.howto p, .howto li { line-height:1.6; }
.howto ul, .howto ol { margin:.4rem 0; padding-left:1.3rem; }
.howto table { width:100%; border-collapse:collapse; margin:.5rem 0 1rem; font-size:.92rem; }
.howto th, .howto td { text-align:left; padding:.4rem .5rem; border-bottom:1px solid var(--line); vertical-align:top; }
.howto th { color:var(--muted); }
.howto .toc a { color:var(--accent); }
#toTop.show { opacity:1; pointer-events:auto; }
h1 { font-size:1.25rem; margin:.1rem 0 .6rem; } h1 span { color:var(--accent); font-size:.95rem; font-weight:600; }
.lookup { display:flex; gap:.5rem; }
.lookup input { flex:1; min-width:0; padding:.7rem .8rem; font-size:1rem; border-radius:.6rem; border:1px solid var(--line); background:var(--panel); color:var(--ink); }
button { padding:.65rem .9rem; border-radius:.6rem; border:1px solid var(--line); background:var(--panel); color:var(--ink); font-size:.95rem; }
button.active { border-color:var(--accent); color:var(--accent); }
.filters { display:flex; gap:.4rem; margin-top:.5rem; flex-wrap:wrap; }
.filters select { flex:1; min-width:6.5rem; padding:.5rem; border-radius:.5rem; background:var(--panel); color:var(--ink); border:1px solid var(--line); }
.tabs { display:flex; gap:.4rem; margin-top:.55rem; }
.count, .note, .empty { color:var(--muted); font-size:.88rem; }
/* Auto grid (2026-10-23, user report): the old 104px minimum let a
   wide monitor fit 22 columns, making each card unreadably small.
   The minimum is now the larger of 160px and one eighth of the
   row (accounting for 7 gaps), so Auto never exceeds 8 columns
   no matter how wide the screen gets, while phones still get 2
   columns. Explicit per-row choices (.cols-N) are unaffected. */
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(max(160px,calc((100% - 7 * .6rem) / 8)),1fr)); gap:.6rem; padding:.8rem; }
.tile { background:var(--panel); border:1px solid var(--line); border-radius:.7rem; padding:.45rem; text-align:left; }
.tile img { width:100%; aspect-ratio:5/7; object-fit:cover; border-radius:.45rem; background:#0c0d10; display:block; }
/* Card names always occupy exactly 2 lines: longer names are
   truncated with an ellipsis, 1-line names leave a blank second line,
   so tiles stay aligned. */
.tile .n { display:-webkit-box; margin-top:.4rem; font-size:.86rem; line-height:1.25; min-height:2.5em; -webkit-line-clamp:2; line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
.tile .num { display:block; color:var(--muted); font-size:.72rem; }
.tile .pin { float:right; }
/* dvh, not just vh: on iOS Chrome/Safari, 100vh is the height with
   the URL bar collapsed, so a 92vh dialog is taller than the visible
   viewport while the bar is fully shown and its top is pushed
   off-screen (user report, 2026-10-21). dvh tracks the visible
   height as the bar expands/collapses, so the dialog always fits;
   vh stays as a fallback for browsers without dvh. overflow:auto
   lets a tall dialog scroll internally instead of clipping. */
dialog { width:min(94vw,30rem); max-height:92vh; max-height:92dvh; overflow:auto; background:var(--panel); color:var(--ink); border:1px solid var(--line); border-radius:1rem; padding:1rem; }
dialog::backdrop { background:rgba(0,0,0,.65); }
/* NOTE: this must NOT be plain "dialog img" — that rule stretched
   inline element-cost icons (.elSym) in effect text to full width
   (user report, 2026-10-21). Only the main card image gets 100%. */
dialog img#dImg { width:100%; border-radius:.6rem; background:#0c0d10; }
dialog video { width:100%; border-radius:.6rem; background:#000; }
.close { float:right; }
/* Image toggle rows (main page and detail) always fit on one line
   (user request, 2026-10-21): no wrapping. Buttons are natural
   width, not stretched to fill the row — the stretched version
   left a lot of empty space around "Original" / "English" in the
   detail (user report, 2026-10-21), so flex is 0 0 auto and the
   padding is just enough for a comfortable tap target. The
   detail row's Flip/Pin icon buttons stay compact the same way. */
.imgToggle { display:flex; gap:.4rem; margin-bottom:.6rem; flex-wrap:nowrap; }
.imgToggle button { flex:0 0 auto; white-space:nowrap; padding:.45rem .65rem; font-size:.9rem; }
.imgToggle button#flipBtn, .imgToggle button#pinBtn { padding:.45rem .7rem; }
/* The detail dialog must not let a pull-down at its top scroll
   the page/list underneath (user report, 2026-10-21): contain
   overscroll inside the dialog, on top of the body scroll lock
   that openDetail sets. */
dialog#detail { overscroll-behavior:contain; }
.mainImgToggle { margin-top:.55rem; margin-bottom:0; }
/* Cards-per-row toggle (user request, 2026-10-21): a compact row
   under the image toggle. Fixed column counts override the
   auto-fill grid; Auto restores it. */
.colsControl { display:flex; gap:.3rem; margin-top:.55rem; align-items:center; flex-wrap:nowrap; }
.colsControl span { color:var(--muted); font-size:.8rem; flex:0 0 auto; margin-right:.1rem; }
.colsControl select { padding:.45rem .5rem; font-size:.85rem; border-radius:.5rem; background:var(--panel); color:var(--ink); border:1px solid var(--line); }
.grid.cols-2 { grid-template-columns:repeat(2,1fr); }
.grid.cols-3 { grid-template-columns:repeat(3,1fr); }
.grid.cols-4 { grid-template-columns:repeat(4,1fr); }
.grid.cols-5 { grid-template-columns:repeat(5,1fr); }
.grid.cols-6 { grid-template-columns:repeat(6,1fr); }
.effect { white-space:pre-wrap; line-height:1.55; background:#17191e; border:1px solid var(--line); border-radius:.6rem; padding:.7rem; }
.kw { font-weight:700; background:var(--accent); color:#14161a; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
/* Keyword spans are tappable (2026-10-21): they open a definition
   popover, so they get a pointer cursor and a visible focus ring. */
.kw[data-kw] { cursor:pointer; }
.kw[data-kw]:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
/* Fullscreen image (2026-10-21): the fullscreen view is a second
   modal dialog (see index.html): it must fill the viewport when
   open, and must not show at all when closed, so the flex layout
   only applies to [open]. Tapping the detail image opens it;
   there is no separate fullscreen button (removed 2026-10-21). */
#dImg { cursor:zoom-in; }
#imgFull { width:100vw; height:100dvh; max-width:100vw; max-height:100dvh; margin:0; padding:0; border:0; border-radius:0; background:rgba(0,0,0,.94); cursor:zoom-out; }
#imgFull[open] { display:flex; align-items:center; justify-content:center; }
#imgFull img { max-width:100vw; max-height:100dvh; object-fit:contain; }
#imgFullClose { position:absolute; top:.75rem; right:.75rem; background:rgba(0,0,0,.6); color:#fff; border:1px solid rgba(255,255,255,.35); padding:.5rem .7rem; border-radius:.5rem; font-size:1rem; }
.flavor { font-style:italic; color:#969ca6; }
.classif { color:var(--muted); }
/* Ambush line highlight (2026-10-21, user request, modelled on
   BP02-C-073's original): the whole Ambush line sits on a
   grey-brown bar (#817c73, sampled from that card) with white
   text; the gold Ambush pill keeps its own colors on top. Block
   display so the bar spans the line's full width. */
.ambush-line { display:block; background:#817c73; color:#fff; border-radius:.4rem; padding:.2rem .5rem; margin:.15rem 0; }
/* Compact stats (user request, 2026-10-21): smaller font and tighter
   rows, plus tighter dialog headings, so the effect text is reached
   with less scrolling on a phone. */
.stats { width:100%; border-collapse:collapse; margin:0 0 .4rem; }
.stats th { text-align:left; color:var(--muted); font-weight:600; font-size:.8rem; padding:.15rem .5rem .15rem 0; width:5.5rem; }
.stats td.val { font-weight:700; color:var(--accent); font-size:.88rem; padding:.15rem 0; }
/* Cost / ATK / Range on one line under the stats table (user
   request, 2026-10-21): labels muted and small like the table
   headers, values bold and accent-colored like the table values. */
.statLine { display:flex; gap:1.25rem; margin:0 0 .4rem; font-size:.88rem; }
.statLine b { color:var(--muted); font-weight:600; font-size:.8rem; margin-right:.3rem; }
.statLine span { font-weight:700; color:var(--accent); }
dialog h2 { margin:.6rem 0 .3rem; font-size:1.25rem; }
dialog h3 { margin:.5rem 0 .3rem; font-size:1rem; }
.elbadge { display:inline-block; font-weight:700; padding:.15em .55em; border-radius:.4em; white-space:nowrap; }
/* Element colors taken from the card art's element badge (below the cost
   circle), not invented: Wind gold, Fire red, Forest green, Mountain
   blue, Thunder pale, Yin dark gray. */
/* Element filter backgrounds (2026-10-23, user report): these now
   match the measured fill colors of the user-supplied element
   symbol PNGs. The old values were furthest off for Wind (was
   bright gold #c9a227, the symbol is ochre #a6752e) and Yin (was
   dark gray #4a4642, the symbol is actually warm brown #856655);
   the other four are updated to their measured fills too so the
   set stays consistent. Wind's label switches to white, which
   reads better on the darker ochre. */
.el-wind { background:#a6752e; color:#fff; }
.el-fire { background:#91332e; color:#fff; }
.el-forest { background:#4a7637; color:#fff; }
.el-mountain { background:#2f668e; color:#fff; }
.el-thunder { background:#d6d3d1; color:#14161a; }
.el-yin { background:#856655; color:#fff; }
.elFilter { display:flex; gap:.4rem; margin-top:.5rem; flex-wrap:wrap; }
/* Element filter buttons: symbol on top, much smaller label
   underneath (user request, 2026-10-21) — far narrower than the
   old side-by-side layout, while keeping the label for clarity. */
.elBtn { display:inline-flex; flex-direction:column; align-items:center; gap:.15rem; padding:.4rem .5rem; font-size:.6rem; opacity:.55; }
.elBtn img { width:26px; height:29px; object-fit:cover; border-radius:.3rem; }
.elBtn.active { opacity:1; outline:2px solid #fff; outline-offset:1px; }
.elBtn.elAll { justify-content:center; font-size:.85rem; padding:.65rem .7rem; }
/* Element-cost symbols inline in English effect text. width:auto is
   essential: without it, the dialog-image rule stretches them. */
.elSym { width:auto; height:1.15em; vertical-align:-0.2em; margin:0 1px; }
.flipSym { width:auto; height:1.15em; vertical-align:-0.2em; margin:0 1px; }
.restSym { width:auto; height:1.15em; vertical-align:-0.2em; margin:0 1px; }
/* Icon-only pin button in the detail's top row. */
.pinBtnTop { padding:.35rem .55rem; font-size:.95rem; }
.elBtn.elAll.active { opacity:1; }
/* Per-keyword highlight colors. Main blue, Response red, Ambush gold
   are the timing-pill colors on the original cards; the rest are a
   distinct site palette so each keyword reads differently. */
.kw-main { font-weight:700; background:#2f6fb2; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-response { font-weight:700; background:#b23a3a; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-ambush { font-weight:700; background:var(--accent); color:#14161a; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-intercept { font-weight:700; background:#4a9e6a; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-awaken { font-weight:700; background:#8b6fd8; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-rest { font-weight:700; background:#8a8f98; color:#14161a; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-ready { font-weight:700; background:#3aa99e; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-flip { font-weight:700; background:#d97b29; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-draw { font-weight:700; background:#4aa8c8; color:#14161a; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-breakthrough { font-weight:700; background:#8b2e2e; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-execute { font-weight:700; background:#3a3a3f; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-insight { font-weight:700; background:#7fb3d5; color:#14161a; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-elusive { font-weight:700; background:#7a6fd0; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-suppress { font-weight:700; background:#8a6d3b; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-swift { font-weight:700; background:#7ab648; color:#14161a; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
.kw-evasion { font-weight:700; background:#d06a9a; color:#fff; padding:.05em .3em; border-radius:.3em; white-space:nowrap; }
/* Smaller preview: capped width, centred, so scan results are
   visible without scrolling the dialog. */
.scanWrap { position:relative; max-width:280px; margin:0 auto; }
.scanWrap video { width:100%; height:auto; display:block; }
.scanOverlayBtns { position:absolute; left:50%; bottom:.6rem; transform:translateX(-50%); display:flex; gap:.4rem; }
.scanOverlayBtns button { padding:.4rem .7rem; font-size:.85rem; background:rgba(30,33,39,.88); }
.scanNote { white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Outline size is set in JS (sizeScanOutline): CSS width/aspect-ratio
   alone breaks on landscape video — width 80% with max-height caps
   the height but not the width, so the outline stretches wide. */
.scanOutline { position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); border:2px dashed #fff; border-radius:.5rem; pointer-events:none; opacity:.8; }
.fileInput { max-width:100%; }
.fileInput::file-selector-button { padding:.65rem .9rem; border-radius:.6rem; border:1px solid var(--line); background:var(--panel); color:var(--ink); font-size:.95rem; cursor:pointer; margin-right:.5rem; }
.scanResults { margin:.6rem 0; }
.matchCard { display:flex; align-items:center; gap:.6rem; width:100%; text-align:left; margin-bottom:.5rem; }
.matchCard img { width:52px; aspect-ratio:5/7; object-fit:cover; }
.matchCard small { color:var(--muted); }

/* Decklists (user request, 2026-10-23): a decklist scopes the grid
   to just that deck's cards, so the table can open a deck instead
   of scanning each card or fiddling with filters. In deck mode the
   filter rows and tabs hide (the deck defines the card set; only
   the search box still narrows within the deck) and the banner
   shows instead. */
.deckBar { display:flex; gap:.4rem; margin-top:.55rem; align-items:center; }
.deckBar label { color:var(--muted); font-size:.8rem; flex:0 0 auto; }
.deckBar select { flex:1; min-width:0; padding:.5rem; border-radius:.5rem; background:var(--panel); color:var(--ink); border:1px solid var(--line); }
.deckBar button { flex:0 0 auto; }
body.deck-mode .filters, body.deck-mode .elFilter, body.deck-mode .tabs { display:none; }
.deckBanner { margin-top:.55rem; padding:.6rem .7rem; background:var(--panel); border:1px solid var(--line); border-radius:.6rem; }
.deckBanner[hidden] { display:none; }
.deckBannerBtns { display:flex; gap:.4rem; flex-wrap:wrap; margin-top:.5rem; }
.deckBannerBtns button { padding:.45rem .65rem; font-size:.85rem; }
.deckShareLink { width:100%; margin-top:.5rem; padding:.5rem; border-radius:.5rem; background:var(--bg); color:var(--ink); border:1px solid var(--line); font-size:.85rem; }
.deckShareLink[hidden] { display:none; }
/* Decklist builder dialog. */
.deckField { display:block; margin:.5rem 0; color:var(--muted); font-size:.85rem; }
.deckField input { display:block; width:100%; margin-top:.3rem; padding:.6rem .7rem; font-size:1rem; border-radius:.6rem; border:1px solid var(--line); background:var(--bg); color:var(--ink); }
.deckAddResults { margin-bottom:.5rem; }
.deckCount { font-weight:700; color:var(--accent); font-size:.9rem; min-width:2rem; text-align:center; }
/* Deck tray tiles (2026-10-23, user request): the tray is no
   longer a vertical list of small rows — each deck entry is a
   grid tile (reusing .tile) with the art, the name, the card
   number, and the copy controls pinned to the tile's bottom via
   margin-top:auto (grid rows stretch tiles to equal height). */
.trayTile { display:flex; flex-direction:column; }
.trayCtrls { display:flex; gap:.25rem; align-items:center; justify-content:center; margin-top:auto; padding-top:.4rem; }
.trayCtrls button { padding:.3rem .55rem; font-size:.85rem; }
.deckAddRow { background:#17191e; border:1px solid var(--line); border-radius:.5rem; padding:.4rem .5rem; }
.deckEditBtns { display:flex; gap:.4rem; flex-wrap:wrap; margin:.8rem 0 .4rem; }
.deckExportBox { width:100%; min-height:8rem; padding:.6rem; border-radius:.6rem; background:var(--bg); color:var(--ink); border:1px solid var(--line); font-size:.8rem; font-family:monospace; }
.deckExportBox[hidden] { display:none; }

/* Decks page (2026-10-23): deck list cards, the builder tray, and
   the visual picker. The picker reuses .grid/.tile; a picked tile
   gets an accent border so the deck's contents read at a glance. */
.decksMain { padding:.8rem .9rem 2rem; }
.deckHead { display:flex; gap:.5rem; align-items:center; justify-content:space-between; margin:.6rem 0 .4rem; }
.deckHead h2 { margin:0; font-size:1.25rem; }
.decksMain h3 { margin:1rem 0 .4rem; font-size:1rem; }
.deckList { display:flex; flex-direction:column; gap:.5rem; }
.deckCard { background:var(--panel); border:1px solid var(--line); border-radius:.7rem; padding:.7rem; }
.deckCard h4 { margin:0 0 .25rem; font-size:1rem; }
.deckMeta { color:var(--muted); font-size:.85rem; margin:0; }
.deckCardBtns { display:flex; gap:.4rem; flex-wrap:wrap; margin-top:.5rem; }
.deckCardBtns button { padding:.45rem .65rem; font-size:.85rem; }
#bTray h3 { margin:.8rem 0 .3rem; font-size:1rem; }
.tile.picked { border-color:var(--accent); box-shadow:0 0 0 1px var(--accent); }

.actions { margin:.8rem 0 .2rem; }
footer { padding:1rem .9rem 2rem; color:var(--muted); font-size:.8rem; }
footer a { color:var(--accent); }
