/* Dark is the default look (bare :root, no data-theme needed) - this is a
   personal card-browsing tool, not a public site, so "default dark, opt
   into light" beats following the OS preference. */
:root {
  --ink: #14151A;
  --panel: #1C1E24;
  --panel-raised: #21232B;
  --border: #2A2D35;
  --text: #E7E7EA;
  --text-muted: #8B8F9A;
  --accent: #C9A227;
  --accent-dim: #8A701C;
  --row-hover: #1E2027;
  --row-alt: #191A20;
  --error: #D97A6C;
}

:root[data-theme="light"] {
  --ink: #F4F3F0;
  --panel: #FFFFFF;
  --panel-raised: #EFEEEA;
  --border: #DCDAD3;
  --text: #23241F;
  --text-muted: #6B6A63;
  --accent: #A8790E;
  --accent-dim: #C9A227;
  --row-hover: #F0EFEA;
  --row-alt: #FAFAF7;
  --error: #C0392B;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: 'Inter', -apple-system, sans-serif;
  font-size: 14px;
}

h1, h2, h3 {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 500;
  margin: 0 0 20px 0;
  letter-spacing: 0.2px;
}

h1 { font-size: 24px; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

.app-shell {
  display: grid;
  grid-template-columns: 220px 1fr;
  grid-template-rows: auto 1fr;
  height: 100vh;
  overflow: hidden;
}

.sidebar {
  grid-row: 1 / 3;
  grid-column: 1;
  background: var(--panel);
  border-right: 1px solid var(--border);
  overflow-y: auto;
  padding: 24px 0;
}

.sidebar .brand {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 16px;
  padding: 0 22px 20px 22px;
  color: var(--text);
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}

.sidebar a {
  display: block;
  padding: 10px 22px;
  color: var(--text-muted);
  font-size: 13.5px;
  border-left: 2px solid transparent;
}

.sidebar a:hover {
  color: var(--text);
  text-decoration: none;
  background: rgba(255,255,255,0.02);
}

.sidebar a.active {
  color: var(--text);
  border-left: 2px solid var(--accent);
  background: rgba(201,162,39,0.06);
}

header {
  grid-row: 1;
  grid-column: 2;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px;
  transition: padding 0.18s ease;
  z-index: 10;
}

header.compact {
  padding: 10px 28px;
}

header .page-label {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 15px;
  color: var(--text-muted);
  transition: font-size 0.18s ease;
}

header.compact .page-label {
  font-size: 13px;
}

.header-left { display: flex; align-items: center; gap: 14px; min-width: 0; }

/* Off-canvas nav toggle - hidden above the mobile breakpoint (see the
   .app-shell media query below), where the sidebar is always visible as a
   fixed column instead. */
.hamburger-btn {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 34px;
  height: 34px;
  padding: 0;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  flex-shrink: 0;
}
.hamburger-btn span { display: block; width: 16px; height: 2px; margin: 0 auto; background: var(--text); border-radius: 1px; }
.hamburger-btn:hover { background: var(--panel-raised); }

.sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  z-index: 90;
}

.user-info {
  display: flex;
  align-items: center;
  gap: 10px;
}

.theme-toggle {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text);
  border-radius: 50%;
  width: 30px;
  height: 30px;
  padding: 0;
  font-size: 14px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.theme-toggle:hover { background: var(--panel-raised); }

.user-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent-dim);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 600;
  flex-shrink: 0;
  transition: width 0.18s ease, height 0.18s ease;
}

header.compact .user-avatar {
  width: 26px;
  height: 26px;
  font-size: 11px;
}

.user-name {
  font-size: 13px;
  color: var(--text-muted);
}

main {
  grid-row: 2;
  grid-column: 2;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 28px;
  min-width: 0; /* let a wide child (the results table) scroll instead of stretching this 1fr grid track */
}

/* Phone layout: the sidebar stops being a permanent grid column and becomes
   an off-canvas drawer (toggled by the hamburger button, see base.html's
   inline script) that slides in over the content instead of squeezing it -
   a fixed 220px column left no room at all for content below ~760px. */
@media (max-width: 760px) {
  .app-shell { grid-template-columns: 1fr; }
  header, main { grid-column: 1; }

  .sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: 240px;
    max-width: 80vw;
    grid-row: auto;
    grid-column: auto;
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    z-index: 100;
  }
  .sidebar.open { transform: translateX(0); }
  .sidebar-backdrop:not(.hidden) { display: block; }

  .hamburger-btn { display: flex; }

  header { padding: 14px 16px; }
  header.compact { padding: 8px 16px; }
  main { padding: 16px; }

  /* Username text is the least essential part of the header on a phone -
     the avatar (which links to /profile) and level badge already identify
     the account; dropping the text keeps the page title from being
     squeezed into a couple of words. */
  .user-name { display: none; }
}

@media (max-width: 420px) {
  .header-left .page-label { font-size: 13px; }
}

form { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 20px; }

input, select {
  padding: 7px 10px;
  background: var(--panel-raised);
  color: var(--text);
  border: 1px solid var(--border);
  font-family: inherit;
  font-size: 13px;
}

input:focus, select:focus { outline: 1px solid var(--accent); }

button {
  padding: 7px 16px;
  background: var(--accent);
  color: var(--ink);
  border: none;
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
}

button:hover { background: #D9B23C; }

/* Touch targets - the desktop sizes above assume a mouse pointer and are
   smaller than comfortable to hit reliably with a finger. */
@media (max-width: 760px) {
  button, .btn-ghost { min-height: 38px; padding: 9px 16px; }
  input, select { min-height: 38px; }
  .theme-toggle { width: 38px; height: 38px; }
}

.count { color: var(--text-muted); font-size: 12.5px; }

.count-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 12px;
  gap: 12px;
  flex-wrap: wrap;
}

.count-row-controls {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.columns-menu { position: relative; }

.columns-menu-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 10px;
  min-width: 150px;
  z-index: 20;
  box-shadow: 0 8px 20px rgba(0,0,0,0.3);
}

.columns-menu-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--text);
  padding: 5px 2px;
  white-space: nowrap;
}

.columns-menu-reset {
  display: block;
  width: 100%;
  margin-top: 6px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
  border-left: none; border-right: none; border-bottom: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 11.5px;
  text-align: left;
  cursor: pointer;
}
.columns-menu-reset:hover { color: var(--text); }

.ms-filter { position: relative; }

.ms-filter-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.ms-filter-caret { font-size: 10px; color: var(--text-muted); }

.ms-filter-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 10px;
  min-width: 170px;
  max-height: 260px;
  overflow-y: auto;
  z-index: 20;
  box-shadow: 0 8px 20px rgba(0,0,0,0.3);
}

.ms-filter-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  color: var(--text);
  padding: 5px 2px;
  white-space: nowrap;
}

.ms-filter-empty {
  font-size: 12px;
  color: var(--text-muted);
  padding: 4px 2px;
}

.search-sidebar .filter-field { margin-bottom: 10px; }

.search-sidebar .filter-field-label {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 4px;
}

.search-sidebar .filter-field .ms-filter-toggle { width: 100%; }
.search-sidebar .filter-field .ms-filter-panel { min-width: 100%; }

.page-size-control {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--text-muted);
  font-size: 12.5px;
  white-space: nowrap;
}

.page-size-control select { padding: 4px 8px; font-size: 12.5px; }

.btn-ghost {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  font-weight: 500;
}
.btn-ghost:hover { background: rgba(255,255,255,0.04); color: var(--text); }

.filter-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
  min-height: 1px;
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--panel-raised);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 12px;
  padding: 4px 6px 4px 10px;
  border-radius: 100px;
}

.pill-remove {
  background: transparent;
  color: var(--text-muted);
  border: none;
  border-radius: 50%;
  width: 16px;
  height: 16px;
  line-height: 1;
  padding: 0;
  font-size: 13px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pill-remove:hover { background: rgba(255,255,255,0.1); color: var(--text); }

.search-layout {
  display: grid;
  grid-template-columns: 1fr 260px;
  gap: 24px;
  align-items: start;
}

.search-main {
  min-width: 0; /* same grid-blowout fix as main{} - a grid item's default min-width:auto would otherwise force this column to grow to fit a wide table instead of letting it scroll */
}

.search-sidebar {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 18px;
  position: sticky;
  top: 0;
  /* With ~20 filter fields across 3 fieldsets, the sidebar's natural height
     now regularly exceeds the viewport - sticky alone doesn't cap that, so
     its bottom (and the last fieldset's fields) ran off the bottom of the
     window with no border/edge ever showing. Capping height and scrolling
     internally keeps the whole box - and its bottom edge - always visible. */
  max-height: calc(100vh - 170px);
  overflow-y: auto;
}

.search-sidebar h3 {
  font-size: 14px;
  margin-bottom: 14px;
}

.search-sidebar fieldset {
  border: none;
  border-top: 1px solid var(--border);
  padding: 12px 0;
  margin: 0;
}
.search-sidebar fieldset:first-child { border-top: none; padding-top: 0; }

.search-sidebar legend {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  padding: 0;
  margin-bottom: 8px;
}

/* :not(.ms-filter-item) - a checkbox-panel row (label.ms-filter-item,
   see the checkbox_filter macro in cards.html) is also a `label` inside
   .search-sidebar, but needs its own flex layout, not this block/stacked
   one - .ms-filter-item's rules would otherwise lose to this on
   specificity (0,1,0 < 0,1,1) regardless of source order. */
.search-sidebar label:not(.ms-filter-item) {
  display: block;
  font-size: 12px;
  color: var(--text-muted);
  margin-bottom: 10px;
}

.search-sidebar label:not(.ms-filter-item) input,
.search-sidebar label:not(.ms-filter-item) select {
  display: block;
  width: 100%;
  margin-top: 4px;
}

label.checkbox-label {
  display: flex;
  align-items: center;
  gap: 7px;
}

label.checkbox-label input {
  display: inline;
  width: auto;
  margin-top: 0;
  flex-shrink: 0;
}

.search-sidebar .range-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}

@media (max-width: 900px) {
  .search-layout { grid-template-columns: 1fr; }
  .search-sidebar { position: static; }
}

.table-wrap {
  overflow: auto;
  border: 1px solid var(--border);
  max-height: calc(100vh - 220px);
  transition: opacity 0.15s ease;
}

.table-wrap.is-loading { opacity: 0.5; pointer-events: none; }

#pagination {
  display: flex;
  justify-content: center;
  margin-top: 16px;
}

.page-bar {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 100px;
  padding: 5px;
}

.page-nav, .page-num {
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 7px 13px;
  border-radius: 100px;
  cursor: pointer;
  min-width: 34px;
  text-align: center;
}

.page-nav:hover:not(:disabled),
.page-num:hover:not(.active) {
  background: rgba(255,255,255,0.06);
  color: var(--text);
}

.page-nav:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}

.page-num.active {
  background: var(--accent);
  color: var(--ink);
  font-weight: 700;
}

.page-ellipsis {
  color: var(--text-muted);
  padding: 0 6px;
  font-size: 13px;
  user-select: none;
}

/* table-layout: fixed + no width:100% - column widths come strictly from
   #results-colgroup (see table-columns.js), so resizing actually sticks
   instead of the browser re-flowing everything to fit the container. */
/* min-width (not width): fills the container when few columns are visible
   (was looking sparse/too-small), but still grows past it - scrolling
   inside .table-wrap - when many columns' widths add up to more. */
table { border-collapse: collapse; table-layout: fixed; min-width: 100%; font-variant-numeric: tabular-nums; }

th, td {
  text-align: left;
  padding: 9px 14px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

th {
  background: var(--panel-raised);
  position: sticky;
  top: 0;
  z-index: 2;
  font-weight: 600;
  font-size: 12.5px;
  color: var(--text-muted);
}

th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--text); }
th.sortable.sorted { color: var(--accent); }
.sort-indicator { font-size: 10px; color: var(--border); }
th.sortable:hover .sort-indicator { color: var(--text-muted); }
th.sortable.sorted .sort-indicator { color: var(--accent); }

/* Column reorder (native drag-and-drop) and resize (mousedown-drag handle) */
th[draggable="true"] { cursor: grab; }
th.dragging { opacity: 0.4; }
th.drag-over { box-shadow: inset 2px 0 0 var(--accent); }

.col-resize-handle {
  position: absolute;
  top: 0;
  right: 0;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 4;
}
.col-resize-handle:hover, .col-resize-handle:active { background: var(--accent); opacity: 0.5; }

/* Header filter caret - opens that column's filter control (moved from the
   sidebar/top bar, not duplicated) in #col-filter-popover. See
   openColumnFilterPopoverFor() in search.js. */
.col-filter-btn {
  background: transparent;
  border: none;
  /* var(--border) (the sort-indicator's faint default) was too close to the
     header background to read as a clickable affordance - text-muted
     matches the header's own text color instead, so the caret reads as
     part of the label rather than disappearing into the background. */
  color: var(--text-muted);
  font-size: 11px;
  line-height: 1;
  cursor: pointer;
  padding: 2px;
  margin-left: 3px;
  border-radius: 2px;
}
.col-filter-btn:hover { color: var(--text); background: rgba(255,255,255,0.08); }
.col-filter-btn.active { color: var(--accent); }

/* Floating home for a popped-out column filter control. Reuses .search-sidebar
   (background/border/padding + its label/.filter-field/.ms-filter descendant
   rules) for identical styling to the real sidebar - only position/sizing
   differ, via higher-specificity #id rules below. */
#col-filter-popover {
  position: fixed;
  z-index: 50;
  width: max-content;
  max-width: 280px;
  /* Explicitly undoes .search-sidebar's own max-height/overflow-y (shared
     via that class, see above) - a checkbox field's .ms-filter-panel is
     position:absolute (so it doesn't contribute to this box's own content
     size) and already scrolls internally past its own 260px cap, so
     clipping here as well cut the panel off almost entirely: this box
     sized itself to fit only the in-flow toggle button. */
  max-height: none;
  overflow-y: visible;
  box-shadow: 0 8px 20px rgba(0,0,0,0.3);
}

/* HP is the only numeric column - right-aligned per usual table convention.
   By class, not position - the actions column (logged-in only) and column
   reordering both shift a column's actual nth-child position around. */
th.col-hp, td.col-hp { text-align: right; }

td:first-child, th:first-child {
  position: sticky;
  left: 0;
  background: var(--ink);
  z-index: 1;
}

th:first-child { z-index: 3; background: var(--panel-raised); }

/* Actions column is always last (see renderActionsHeader/renderRows in
   table-columns.js) and pinned to the right edge instead of the left, so it
   stays visible without a left-sticky claim on it too - by class rather
   than :last-child since it's the only column allowed to make that claim. */
td.col-actions, th.col-actions {
  position: sticky;
  right: 0;
  background: var(--ink);
  z-index: 1;
  border-left: 1px solid var(--border);
}

th.col-actions { z-index: 3; background: var(--panel-raised); }

/* A phone's viewport isn't wide enough to give both the left-sticky first
   column and the right-sticky Actions column their own claimed space
   without the two overlapping each other (found by actually clicking the
   first column's filter caret at 375px - it was unreachable, covered by
   the sticky Actions column sitting right on top of it). Dropping sticky
   behavior for a plain horizontally-scrolling table is the standard mobile
   tradeoff here - there's no width to spare for an anchored column. */
@media (max-width: 760px) {
  td:first-child, th:first-child, td.col-actions, th.col-actions {
    position: static;
  }
}

/* Zebra striping for scannability across a 100-row page. tr:hover is
   declared after this so it always wins the equal-specificity tie on
   the sticky first/actions columns. */
#results-body tr:nth-child(even) td { background: var(--row-alt); }
#results-body tr:nth-child(even) td:first-child,
#results-body tr:nth-child(even) td.col-actions { background: var(--row-alt); }

tr:hover td { background: var(--row-hover); }
tr:hover td:first-child,
tr:hover td.col-actions { background: var(--row-hover); }

/* Quick-actions column (favorite/own/want toggle + star rating), prepended
   to the results table only for a logged-in session - see
   renderActionsHeader/buildActionsCell in table-columns.js. */
.actions-cell { white-space: nowrap; cursor: default; }
.quick-actions { display: inline-flex; gap: 3px; margin-right: 8px; vertical-align: middle; }
.action-icon-btn {
  width: 22px;
  height: 22px;
  padding: 0;
  line-height: 1;
  font-size: 12px;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
}
.action-icon-btn:hover { background: rgba(255,255,255,0.06); color: var(--text); }
.action-icon-btn.active { color: var(--accent); border-color: var(--accent); }

.star-rating { display: inline-flex; gap: 1px; vertical-align: middle; }
.star-rating .star { font-size: 13px; color: var(--border); cursor: pointer; }
.star-rating .star.filled { color: #E3B341; }
.star-rating-lg .star { font-size: 20px; }

@media (max-width: 760px) {
  .quick-actions { gap: 8px; }
  .action-icon-btn { width: 32px; height: 32px; font-size: 15px; }
  .star-rating { gap: 4px; }
  .star-rating .star { font-size: 18px; padding: 2px; }
}

.placeholder-note { color: var(--text-muted); font-size: 14px; margin-top: 40px; }

.card-row { cursor: pointer; }

/* --- Card detail modal --- */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(10, 10, 12, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  /* Above every other overlay in the app, including the Pack Simulator's
     full-screen reveal (z-index 300) - clicking a card there needs the
     detail modal to actually appear on top of it, not underneath. */
  z-index: 1000;
}

.modal-overlay.hidden { display: none; }

.modal-box {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  max-width: 920px;
  width: 100%;
  max-height: calc(100vh - 48px);
  overflow-y: auto;
  /* Less top padding than the sides/bottom - the nav lives at the bottom
     now, so the top only needs to clear the close button, not much more. */
  padding: 16px 28px 28px;
}

/* Centered, and always the same shape - a fixed-height logo slot (whether
   or not this card's set has a logo, or it fails to load) so the name/
   number/rarity below land in the same spot on every card. */
.modal-set-banner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  padding-bottom: 16px;
  margin-bottom: 20px;
  border-bottom: 1px solid var(--border);
}

.modal-set-logo-wrap {
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-set-logo {
  max-height: 36px;
  max-width: 220px;
  object-fit: contain;
}

.modal-set-name {
  font-family: 'Fraunces', Georgia, serif;
  font-size: 17px;
  color: var(--text);
}

.modal-set-number-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}

.modal-set-number {
  font-size: 22px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.modal-rarity-badge { font-size: 12.5px; padding: 5px 12px; flex-shrink: 0; }

.modal-close {
  position: absolute;
  top: 14px;
  right: 14px;
  background: transparent;
  color: var(--text-muted);
  font-size: 22px;
  line-height: 1;
  padding: 4px 8px;
}

.modal-close:hover { background: rgba(255,255,255,0.06); color: var(--text); }

.modal-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.modal-nav-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 50%;
  width: 26px;
  height: 26px;
  padding: 0;
  font-size: 16px;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal-nav-btn:hover:not(:disabled) { background: var(--panel-raised); color: var(--text); }
.modal-nav-btn:disabled { opacity: 0.3; cursor: not-allowed; }

/* The global mobile min-height (button, .btn-ghost { min-height: 38px })
   stretched this into a 26x38 oval instead of a circle, since it only sets
   a fixed 26px width - give both dimensions the same bump here instead. */
@media (max-width: 760px) {
  .modal-nav-btn { width: 38px; height: 38px; }
  .modal-close { min-width: 38px; }
}

#modal-nav-position { font-size: 11.5px; color: var(--text-muted); white-space: nowrap; }

.modal-loading, .modal-error { color: var(--text-muted); font-size: 14px; }
.modal-error { color: var(--error); }
.hidden { display: none; }

.modal-card {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 28px;
}

.modal-image-wrap { display: flex; justify-content: center; align-items: flex-start; }

.modal-image {
  width: 100%;
  max-width: 320px;
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.4);
}

.modal-image-missing {
  width: 100%;
  aspect-ratio: 5 / 7;
  border: 1px dashed var(--border);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 12.5px;
  text-align: center;
  padding: 12px;
}

.modal-name { margin-bottom: 4px; }

.modal-subtitle { color: var(--text-muted); font-size: 13px; margin-bottom: 14px; }

.modal-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }

.badge {
  background: var(--panel-raised);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 11.5px;
  padding: 3px 9px;
  border-radius: 100px;
}

/* Rarity tiers - shared between the modal badge (.badge-rarity) and the
   table's compact chip (.rarity-chip); see rarity.js. Ordered roughly
   low -> high rarity. */
.badge-rarity.tier-common,   .rarity-chip.tier-common   { background: rgba(139,143,154,0.12); border-color: #8B8F9A; color: #C4C7CE; }
.badge-rarity.tier-uncommon, .rarity-chip.tier-uncommon { background: rgba(95,174,124,0.14); border-color: #5FAE7C; color: #8FD4AA; }
.badge-rarity.tier-rare,     .rarity-chip.tier-rare     { background: rgba(108,147,224,0.14); border-color: #6C93E0; color: #A9C1F0; }
.badge-rarity.tier-ultra,    .rarity-chip.tier-ultra    { background: rgba(164,114,217,0.16); border-color: #A472D9; color: #CBA6EE; }
.badge-rarity.tier-secret,   .rarity-chip.tier-secret   { background: rgba(227,179,65,0.18); border-color: #E3B341; color: #F0CC6E; }
.badge-rarity.tier-promo,    .rarity-chip.tier-promo    { background: rgba(193,122,84,0.16); border-color: #C17A54; color: #E0A47F; }
.badge-rarity.tier-none,     .rarity-chip.tier-none     { background: transparent; border-style: dashed; color: var(--text-muted); }

/* The tier text colors above are pastel-on-dark; darkened here so they
   stay readable against a light panel instead of washing out. */
:root[data-theme="light"] .badge-rarity.tier-common,   :root[data-theme="light"] .rarity-chip.tier-common   { color: #5B5F68; }
:root[data-theme="light"] .badge-rarity.tier-uncommon, :root[data-theme="light"] .rarity-chip.tier-uncommon { color: #2F7A4E; }
:root[data-theme="light"] .badge-rarity.tier-rare,     :root[data-theme="light"] .rarity-chip.tier-rare     { color: #3A5FA8; }
:root[data-theme="light"] .badge-rarity.tier-ultra,    :root[data-theme="light"] .rarity-chip.tier-ultra    { color: #7A45AD; }
:root[data-theme="light"] .badge-rarity.tier-secret,   :root[data-theme="light"] .rarity-chip.tier-secret   { color: #8A6415; }
:root[data-theme="light"] .badge-rarity.tier-promo,    :root[data-theme="light"] .rarity-chip.tier-promo    { color: #8F5432; }

/* Compact version of the badge for a table cell - just enough padding to
   read as "surrounded by color" without inflating row height. */
.rarity-chip {
  display: inline-block;
  border: 1px solid;
  border-radius: 4px;
  padding: 1px 7px;
  font-size: 12px;
  line-height: 1.6;
}

.modal-stats {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}

.modal-stat { display: flex; flex-direction: column; gap: 2px; }
.modal-stat-label { font-size: 11px; color: var(--text-muted); }
.modal-stat-value { font-size: 14px; font-variant-numeric: tabular-nums; }

.modal-description {
  font-size: 13px;
  font-style: italic;
  color: var(--text-muted);
  line-height: 1.5;
  margin: 0;
}

.modal-section {
  border-top: 1px solid var(--border);
  padding-top: 10px;
  margin-top: 10px;
}

.modal-section-title { font-weight: 600; font-size: 13.5px; margin-bottom: 3px; }
.modal-section-meta { font-size: 12.5px; color: var(--text-muted); margin-bottom: 2px; }
.modal-section-body { font-size: 13px; line-height: 1.45; }

.modal-matchups {
  font-size: 12.5px;
  color: var(--text-muted);
  border-top: 1px solid var(--border);
  padding-top: 10px;
  margin-top: 10px;
}

.modal-pricing .modal-section-title { color: var(--accent); }
.modal-legality .modal-stats { margin-bottom: 0; }
.modal-flavor { border-top: none; padding-top: 4px; margin-top: 4px; }

.modal-buy-links { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 2px; }
.modal-buy-link {
  background: var(--panel-raised);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 12.5px;
  padding: 5px 12px;
  border-radius: 100px;
  text-decoration: none;
  transition: border-color 0.15s, color 0.15s;
}
.modal-buy-link:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

@media (max-width: 700px) {
  .modal-card { grid-template-columns: 1fr; }
  .modal-image-wrap { justify-content: flex-start; }
  .modal-image, .modal-image-missing { max-width: 200px; }
}

/* --- Accounts: auth forms, profile, binders --- */

img.user-avatar { object-fit: cover; }

.logout-form { margin: 0; }
.logout-btn { padding: 5px 10px; font-size: 12px; }

.auth-form-wrap { max-width: 320px; }
.auth-form-wrap form { flex-direction: column; align-items: stretch; }
.auth-switch { color: var(--text-muted); font-size: 13px; }

.profile-summary {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-bottom: 28px;
}

.profile-avatar-large {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  background: var(--accent-dim);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 24px;
  font-weight: 600;
  flex-shrink: 0;
  overflow: hidden;
}
.profile-avatar-large img { width: 100%; height: 100%; object-fit: cover; }

.profile-level { font-family: 'Fraunces', Georgia, serif; font-size: 18px; margin-bottom: 4px; }

#avatar-search { margin: 12px 0 16px; width: 100%; max-width: 320px; }

.grid { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 24px; }
.thumb { width: 100px; text-align: center; }
.thumb-img {
  width: 92px; height: 92px; border-radius: 50%; border: 2px solid var(--border);
  object-fit: cover; display: block; margin: 0 auto 6px; background: var(--panel-raised);
}
.thumb-img-missing { background: var(--panel-raised); }
.thumb-name { font-size: 10.5px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 6px; }

.avatar-pick-form { width: 100px; text-align: center; }
.avatar-pick-btn { font-size: 10.5px; padding: 4px 8px; width: 100%; }

.new-binder-form { margin-bottom: 20px; }

.binder-list { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 24px; }
.binder-tile {
  display: block;
  width: 160px;
  padding: 16px;
  background: var(--panel-raised);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
}
.binder-tile:hover { border-color: var(--accent); text-decoration: none; }
.binder-tile-name { font-family: 'Fraunces', Georgia, serif; font-size: 15px; margin-bottom: 4px; }

.lang-toggle { display: flex; gap: 4px; margin-bottom: 16px; }
.lang-toggle a {
  padding: 5px 12px; border: 1px solid var(--border); border-radius: 5px;
  font-size: 13px; color: var(--text-muted);
}
.lang-toggle a:hover { text-decoration: none; color: var(--text); }
.lang-toggle a.active { background: var(--panel-raised); color: var(--accent); border-color: var(--accent-dim); }

.progress-bar {
  width: 100%; height: 6px; border-radius: 3px; background: var(--panel-raised);
  border: 1px solid var(--border); overflow: hidden; margin: 6px 0 4px;
}
.progress-bar-fill { height: 100%; background: var(--accent); }
.progress-bar-lg { height: 10px; max-width: 320px; }

.set-grid { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 24px; }
.set-tile {
  position: relative; width: 180px; padding: 16px 16px 32px;
  background: var(--panel-raised); border: 1px solid var(--border); border-radius: 6px; color: var(--text);
}
.set-tile-link { display: block; color: inherit; }
.set-tile-link:hover { text-decoration: none; }
.set-tile:has(.set-tile-link:hover) { border-color: var(--accent); }
.set-tile-logo { width: 100%; height: 56px; object-fit: contain; margin-bottom: 10px; }
.set-tile-logo-missing {
  background: var(--panel); border-radius: 4px; display: flex; align-items: center; justify-content: center;
  font-family: 'Fraunces', Georgia, serif; font-size: 18px; color: var(--text-muted);
}
.set-tile-name { font-family: 'Fraunces', Georgia, serif; font-size: 14px; margin-bottom: 4px; }
.set-tag {
  display: inline-block; font-family: 'Inter', sans-serif; font-size: 10.5px; font-weight: 600;
  letter-spacing: 0.03em; color: var(--text-muted); background: var(--panel);
  border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px; white-space: nowrap;
  vertical-align: middle;
}
.set-detail-title-row .set-tag { font-size: 12px; }
.set-tag-corner { position: absolute; bottom: 10px; right: 10px; }

.sets-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }
#set-search {
  padding: 7px 12px; border: 1px solid var(--border); border-radius: 5px; background: var(--panel-raised);
  color: var(--text); font-size: 13.5px; min-width: 220px;
}
.sets-toolbar .lang-toggle { margin-bottom: 0; }
.sets-section-title { font-family: 'Fraunces', Georgia, serif; font-size: 17px; margin: 0 0 12px; }
.sets-subsection-title {
  font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-muted); margin: 0 0 8px; font-weight: 600;
}

.quick-add-panel {
  background: var(--panel-raised); border: 1px solid var(--border); border-radius: 6px;
  padding: 16px; margin-bottom: 24px;
}
.quick-add-panel .sets-section-title { margin-bottom: 6px; }
.quick-add-hint { color: var(--text-muted); font-size: 12.5px; margin: 0 0 10px; }
.quick-add-hint code {
  background: var(--panel); border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; font-size: 12px;
}
.quick-add-row { display: flex; gap: 10px; align-items: flex-start; }
#tag-input {
  flex: 1; min-height: 60px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 5px;
  background: var(--panel); color: var(--text); font-family: 'Inter', sans-serif; font-size: 13px; resize: vertical;
}
.tag-results { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; }
.tag-result-row {
  display: flex; align-items: center; gap: 10px; padding: 6px 8px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 5px;
}
.tag-result-row.tag-result-error { border-color: var(--error); }
.tag-result-row.tag-result-added { opacity: 0.7; }
.tag-result-thumb {
  width: 36px; height: 36px; border-radius: 50%; object-fit: cover; flex-shrink: 0; background: var(--panel-raised);
}
.tag-result-info { flex: 1; min-width: 0; }
.tag-result-name, .tag-result-tag { font-size: 13.5px; }
.tag-result-set { font-size: 12px; color: var(--text-muted); }
.tag-result-error-msg { font-size: 12px; color: var(--error); }
.tag-result-check { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--text-muted); white-space: nowrap; }
.tag-result-added-label { font-size: 12.5px; color: var(--accent); white-space: nowrap; }
.quick-add-submit { margin-top: 4px; align-self: flex-start; }

.set-detail-header { display: flex; align-items: center; gap: 20px; margin-bottom: 20px; }
.set-detail-logo { max-width: 220px; max-height: 90px; object-fit: contain; }
.set-detail-header .set-tile-logo-missing { width: 90px; height: 90px; font-size: 26px; }
.set-detail-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.set-detail-title-row h1 { margin: 0; }

@media (max-width: 560px) {
  .set-detail-header { flex-direction: column; align-items: flex-start; gap: 12px; }
  .set-detail-logo, .set-detail-header .set-tile-logo-missing { max-width: 160px; }
}

.track-star-btn {
  background: none; border: none; cursor: pointer; font-size: 17px; line-height: 1;
  color: var(--border); padding: 2px;
}
.track-star-btn:hover { color: var(--text-muted); }
.track-star-btn.active { color: var(--accent); }
.set-tile .track-star-btn { position: absolute; top: 10px; right: 10px; z-index: 1; }

.set-card-thumb { width: 110px; text-align: center; cursor: pointer; position: relative; }
.card-thumb-img {
  width: 110px; height: 153px; border-radius: 6px; border: 1px solid var(--border);
  object-fit: cover; display: block; margin: 0 auto 6px; background: var(--panel-raised);
}
.card-thumb-img-missing { background: var(--panel-raised); }
.set-card-thumb.unowned .card-thumb-img { opacity: 0.35; filter: grayscale(70%); }

.owned-toggle-btn {
  position: absolute; top: 4px; right: 4px; width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--panel); color: transparent;
  font-size: 12px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.owned-toggle-btn:hover { border-color: var(--accent); }
.owned-toggle-btn.active { background: var(--accent); border-color: var(--accent); color: var(--ink); }

/* At-a-glance rarity indicator on a set-grid card, reusing the same tier
   colors as .rarity-chip/.badge-rarity (rarity.js) - a small filled dot
   rather than a text chip since these thumbnails are only ~110px wide. */
.rarity-dot {
  position: absolute; bottom: 4px; left: 4px; width: 12px; height: 12px;
  border-radius: 3px; border: 1px solid rgba(0,0,0,0.3);
}
.rarity-dot.tier-common { background: #8B8F9A; }
.rarity-dot.tier-uncommon { background: #5FAE7C; }
.rarity-dot.tier-rare { background: #6C93E0; }
.rarity-dot.tier-ultra { background: #A472D9; }
.rarity-dot.tier-secret { background: #E3B341; }
.rarity-dot.tier-promo { background: #C17A54; }
.rarity-dot.tier-none { background: var(--border); }

.modal-collection-row { display: flex; align-items: center; gap: 10px; }
.modal-collection-status { font-size: 13px; min-width: 90px; }
.collection-btn { width: 28px; height: 28px; padding: 0; line-height: 1; font-size: 15px; }

.modal-toggle-btn { padding: 5px 10px; font-size: 13px; }

.pack-open-panel { display: flex; align-items: center; gap: 16px; margin: 24px 0; flex-wrap: wrap; }

/* A phone has no Space bar to press - this tip is only relevant with a
   physical keyboard. */
@media (max-width: 760px) {
  .keyboard-tip { display: none; }
}
#open-pack-btn { font-size: 15px; padding: 10px 22px; }

.pack-stats-panel {
  background: var(--panel-raised); border: 1px solid var(--border); border-radius: 6px;
  padding: 10px 14px; margin-top: 12px; font-size: 13px; color: var(--text-muted);
}
.pack-stats-row { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
.pack-stats-row + .pack-stats-row { margin-top: 8px; }
.pack-stats-value { font-weight: 600; color: var(--text); }

.dashboard-summary { margin-bottom: 24px; }

.dashboard-bars { display: flex; flex-direction: column; gap: 8px; margin-bottom: 24px; }
.dashboard-bar-row { display: grid; grid-template-columns: 90px 1fr auto; align-items: center; gap: 10px; }
.dashboard-bar-label { font-size: 12.5px; color: var(--text-muted); }
.dashboard-bar { margin: 0; }

.dashboard-table { width: 100%; border-collapse: collapse; margin-bottom: 24px; font-size: 13px; }
.dashboard-table th, .dashboard-table td {
  text-align: left; padding: 6px 10px; border-bottom: 1px solid var(--border);
}
.dashboard-table th { color: var(--text-muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; }

.pack-reveal-grid { margin-top: 8px; }

/* "Cards Pulled" grid (pack_open.html) - same dimming treatment as Set
   Completion's .unowned, but a distinct class since this tracks virtual
   pulls (user_pack_pulls), never real ownership. */
.set-card-thumb.unpulled .card-thumb-img { opacity: 0.35; filter: grayscale(70%); }
.pull-qty-badge {
  position: absolute; top: 4px; right: 4px; background: var(--panel-raised); color: var(--text);
  border: 1px solid var(--border); font-size: 10.5px; font-weight: 700; padding: 1px 5px;
  border-radius: 3px; z-index: 1;
}

.pack-pull-new-badge {
  position: absolute; top: 4px; left: 4px; background: #e0393e; color: #fff;
  font-size: 11.5px; font-weight: 800; letter-spacing: 0.04em; padding: 3px 7px; border-radius: 4px; z-index: 1;
  border: 1.5px solid #fff; box-shadow: 0 1px 4px rgba(0,0,0,0.5);
}
.pack-pull-price {
  font-size: 12.5px; font-weight: 600; color: #7CC98A; margin-bottom: 2px;
}

.god-pack-banner {
  font-family: 'Fraunces', Georgia, serif; font-size: clamp(18px, 3vw, 26px); text-align: center;
  color: #F0CC6E; border: 1px solid #E3B341; background: rgba(227,179,65,0.1);
  border-radius: 10px; padding: 14px 20px; margin-bottom: 24px; animation: god-pack-banner-in 0.5s ease-out;
}
@keyframes god-pack-banner-in {
  from { opacity: 0; transform: scale(0.9); }
  to { opacity: 1; transform: none; }
}

/* --- Full-screen reveal overlay: the pack-opening moment fills the whole
   viewport rather than staying inline, like a real pack-opening screen. --- */
.pack-fullscreen-overlay {
  position: fixed; inset: 0; z-index: 300; background: rgba(6,6,10,0.94);
  display: flex; flex-direction: column; align-items: center;
  /* flex-start (not center) + overflow-y: auto - a pack big enough to
     overflow the viewport (e.g. an 11-card Base Set pack on a laptop
     screen) must stay scrollable so the reveal grid and the Reveal
     All/Open Another Pack buttons below it are always reachable. Centered
     content that overflows a fixed-position container clips at both ends
     with no way to scroll to the clipped part - flex-start avoids that. */
  justify-content: flex-start;
  overflow-y: auto;
  padding: 60px 20px 32px;
}
.pack-fullscreen-overlay.hidden { display: none; }
.pack-fullscreen-close {
  position: fixed; top: 18px; right: 22px; background: none; border: none;
  color: var(--text-muted); font-size: 36px; line-height: 1; cursor: pointer; padding: 4px 10px; z-index: 301;
}
.pack-fullscreen-close:hover { color: var(--text); }
.pack-fullscreen-content { width: 100%; max-width: 1300px; display: flex; align-items: center; justify-content: center; margin: auto 0; }
.pack-fullscreen-actions { display: flex; gap: 12px; margin-top: 28px; }
.pack-reveal { display: flex; flex-direction: column; align-items: center; width: 100%; }

/* Explicit CSS grid, not flex-wrap - grid-template-columns/gap are set in
   JS (sizePackGrid() in pack_open.html) to a fixed column count and card
   width computed from the actual pack size and viewport, then corrected
   against the real rendered height so the whole pack fits on screen with
   no scrolling in the common case. A fixed column count also makes that
   correction pass predictable - flex-wrap can silently re-wrap into a
   different number of columns when width changes, which a plain
   width-scaling correction can't account for. */
.pack-reveal-grid { display: grid; justify-content: center; width: 100%; }

/* A ragged last row (pack size not a multiple of the column count) - see
   groupRaggedLastRow() in pack_open.html for why this can't just be a grid
   justify-content trick. Spans every column of its row, then centers its
   (fewer than a full row's worth of) children as a flex line - width/gap
   on the children and this wrapper are set in JS to match whatever the
   real grid columns are currently sized to. */
.pack-reveal-lastrow { grid-column: 1 / -1; display: flex; justify-content: center; }
.pack-reveal-lastrow .pack-card-slot { flex-shrink: 0; }

/* --- Card-back flip mechanic: each pull starts face-down, click to reveal one at a time --- */
.pack-card-slot {
  width: 100%; /* fills its grid column track - grid-template-columns is the sizing source of truth */
  perspective: 900px; cursor: pointer;
  animation: pack-card-slot-in 0.3s ease-out backwards;
}
/* animation-delay is set inline per slot in buildCardSlot() (index * 0.05s)
   rather than nth-child CSS, so the stagger scales to any pack size. */
@keyframes pack-card-slot-in {
  from { opacity: 0; transform: translateY(14px) scale(0.92); }
  to { opacity: 1; transform: none; }
}

.pack-card-flipper {
  position: relative; width: 100%; aspect-ratio: 110 / 175; transition: transform 0.6s cubic-bezier(0.4, 0.2, 0.2, 1);
  transform-style: preserve-3d;
}
.pack-card-slot.revealed { cursor: default; }
.pack-card-slot.revealed .pack-card-flipper { transform: rotateY(180deg); }

.pack-card-face { position: absolute; inset: 0; backface-visibility: hidden; }
.pack-card-back {
  display: flex; align-items: center; justify-content: center; border-radius: 10px;
  background: linear-gradient(135deg, var(--panel-raised), var(--panel));
  border: 1px solid var(--border); font-size: clamp(28px, 7vw, 48px); color: var(--text-muted);
}
.pack-card-slot:not(.revealed) .pack-card-back:hover { border-color: var(--accent); }
.pack-card-front { transform: rotateY(180deg); pointer-events: none; }
.pack-card-slot.revealed .pack-card-front { pointer-events: auto; }
.pack-card-slot.revealed .pack-card-back { pointer-events: none; }
.pack-card-front .set-card-thumb { width: 100%; }
.pack-card-front .card-thumb-img { width: 100%; height: auto; aspect-ratio: 110 / 153; }

/* --- Value-tier effects, applied to the front face once revealed --- */
.pack-card-slot.revealed.effect-shine .pack-card-front .card-thumb-img { box-shadow: 0 0 8px rgba(108,147,224,0.5); }

.pack-card-slot.revealed.effect-sparkle .pack-card-front .card-thumb-img {
  border-color: #A472D9; box-shadow: 0 0 14px rgba(164,114,217,0.6); animation: pack-card-pulse 1.4s ease-in-out;
}

.pack-card-slot.revealed.effect-gold .pack-card-front .card-thumb-img {
  border-color: #E3B341; box-shadow: 0 0 20px rgba(227,179,65,0.75); animation: pack-card-gold-burst 0.9s ease-out;
}
.pack-card-slot.revealed.effect-gold .pack-card-front { animation: pack-card-shake 0.5s ease-out; }

@keyframes pack-card-pulse {
  0% { transform: scale(1); } 30% { transform: scale(1.08); } 100% { transform: scale(1); }
}
@keyframes pack-card-gold-burst {
  0% { transform: scale(1); box-shadow: 0 0 0 rgba(227,179,65,0); }
  40% { transform: scale(1.15); box-shadow: 0 0 30px rgba(227,179,65,0.9); }
  100% { transform: scale(1); box-shadow: 0 0 20px rgba(227,179,65,0.75); }
}
@keyframes pack-card-shake {
  0%, 100% { transform: translateX(0) rotateY(180deg); }
  20% { transform: translateX(-4px) rotateY(180deg); }
  40% { transform: translateX(4px) rotateY(180deg); }
  60% { transform: translateX(-3px) rotateY(180deg); }
  80% { transform: translateX(3px) rotateY(180deg); }
}


.pack-size-badge {
  font-size: 12px; color: var(--text-muted); border: 1px solid var(--border);
  border-radius: 999px; padding: 3px 10px; white-space: nowrap;
}

/* --- Binder redesign: creation form, page grid, drag-and-drop, themes --- */

.form-row { margin-bottom: 12px; }
.form-row-grid { display: flex; flex-wrap: wrap; gap: 14px; align-items: end; }
.form-field {
  display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; color: var(--text-muted);
}
.form-field input, .form-field select {
  font-size: 13.5px; padding: 6px 8px; background: var(--panel-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: 5px;
}
.new-binder-form-full { max-width: 640px; }
.binder-tile-meta { font-size: 11px; color: var(--text-muted); margin-top: 4px; }

.binder-meta-line { color: var(--text-muted); font-size: 13px; margin: -12px 0 16px; }

.binder-edit {
  margin-bottom: 20px; padding: 12px 14px; background: var(--panel-raised);
  border: 1px solid var(--border); border-radius: 6px;
}
.binder-edit summary { cursor: pointer; font-size: 13px; color: var(--text-muted); }
.binder-edit form.form-row-grid { margin-top: 12px; }
.binder-autofill-form { margin-top: 10px; }

.binder-page-nav { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.page-nav-btn {
  padding: 5px 12px; border: 1px solid var(--border); border-radius: 5px; font-size: 13px; color: var(--text-muted);
}
.page-nav-btn:hover { color: var(--text); text-decoration: none; }
.page-nav-btn.disabled { opacity: 0.35; pointer-events: none; }
.binder-page-indicator { font-size: 13px; color: var(--text-muted); }
.binder-add-page-form { margin-left: auto; }

/* Two facing pages side by side, like an open physical binder - together
   they fill the available width (or scroll, on a screen too narrow to fit
   both at a readable size), rather than one page filling the width alone. */
.binder-spread {
  display: flex;
  gap: 16px;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  margin-bottom: 24px;
  align-items: stretch;
}
.binder-page {
  flex: 1 1 0;
  min-width: 0;
  padding: 16px;
  border-radius: 10px;
  border: 1px solid var(--border);
}
.binder-page-end {
  display: flex;
  align-items: center;
  justify-content: center;
  border-style: dashed;
}

/* Two facing pages side by side need real width each - a binder page's
   grid tracks have a hard 68px floor (.binder-page-grid's inline
   minmax(68px, 1fr)), so squeezing both pages into a phone's width doesn't
   shrink them further, it overflows each page's box and visually bleeds
   into its neighbor. Stacking them removes the squeeze entirely - one full-
   width page at a time reads better on a phone than a cramped spread
   anyway. */
@media (max-width: 700px) {
  .binder-spread { flex-direction: column; overflow-x: visible; }
}
.binder-page-grid {
  display: grid;
  gap: 8px;
  width: 100%;
}

/* Every cell keeps a real card's aspect ratio, filled or not - an empty
   binder page should look exactly as "full sized" as a filled one, not
   collapse to a flat minimum-height strip because there's no image yet to
   force the row taller. */
.binder-cell {
  min-width: 56px;
  aspect-ratio: 5 / 7;
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 5px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  position: relative;
  background: rgba(0,0,0,0.08);
}
.binder-cell-empty { cursor: pointer; }
.binder-cell-empty:hover { border-color: var(--accent-dim); background: rgba(201,162,39,0.06); }
.binder-cell-filled { border-style: solid; background: var(--panel-raised); cursor: grab; padding: 4px; }
.binder-cell.drag-over { border-color: var(--accent); background: rgba(201,162,39,0.14); }

/* Card art, not the circular profile-style avatar used elsewhere in the app
   (.thumb-img) - corners rounded but rectangular. Inside a binder cell the
   image fills whatever space the cell's own aspect-ratio leaves after the
   name label (flex: 1, not a second fixed aspect-ratio nested inside the
   cell's); in the tray, where there's no outer aspect-ratio box, it sets
   its own via .binder-thumb-img. */
.binder-cell-img {
  width: 100%;
  flex: 1;
  min-height: 0;
  border-radius: 5px;
  object-fit: cover;
  background: var(--panel-raised);
  margin-bottom: 2px;
}
.binder-thumb-img, .binder-img-missing {
  width: 100%;
  aspect-ratio: 5 / 7;
  border-radius: 5px;
  object-fit: cover;
  background: var(--panel-raised);
}
.binder-img-missing { border: 1px dashed var(--border); }
.binder-cell-name { font-size: 9.5px; color: var(--text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.binder-cell-plus { font-size: 22px; color: var(--text-muted); opacity: 0.5; }
.binder-cell-remove {
  position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; line-height: 16px;
  border: none; border-radius: 50%; background: var(--panel); color: var(--text-muted);
  font-size: 13px; cursor: pointer; padding: 0;
}
.binder-cell-remove:hover { color: var(--error); }

.binder-tray-controls { display: flex; gap: 8px; flex-wrap: wrap; margin: 4px 0 10px; }
.binder-tray-controls select, .binder-tray-controls input {
  padding: 7px 10px; font-size: 13px; background: var(--panel-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: 6px;
}
.binder-tray-controls input { flex: 1; min-width: 160px; }

.binder-tray { margin-top: 12px; }
.binder-tray-item { cursor: grab; width: 110px; }

.binder-picker-box { max-width: 640px; }
.binder-picker-controls { display: flex; gap: 8px; margin: 10px 0 6px; }
#binder-picker-search {
  flex: 1; padding: 8px 10px; font-size: 13.5px;
  background: var(--panel-raised); color: var(--text); border: 1px solid var(--border); border-radius: 6px;
}
#binder-picker-set {
  padding: 8px 10px; font-size: 13.5px; max-width: 180px;
  background: var(--panel-raised); color: var(--text); border: 1px solid var(--border); border-radius: 6px;
}

/* The set <select> doesn't shrink below its own content width, so on a
   phone the two controls side by side (search input + select) don't both
   fit and the select spills out of the modal box - wrapping them onto
   their own lines, each full width, is simpler than fighting flex-shrink
   on a native select. */
@media (max-width: 480px) {
  .binder-picker-controls { flex-wrap: wrap; }
  #binder-picker-search, #binder-picker-set { flex: 1 1 100%; max-width: none; }
}
.binder-picker-item .binder-thumb-img { cursor: pointer; }
.binder-picker-item:hover .binder-thumb-img { border: 1px solid var(--accent); }
.binder-picker-add-btn { width: 100%; margin-top: 4px; font-size: 11px; padding: 3px 0; }

/* Aesthetic skins - independent of the binder's content theme. Kept as
   background/border treatments only, layered on top of the same .binder-cell
   structure so the grid layout never changes per-skin. */
.binder-skin-classic { background: linear-gradient(160deg, #2a1d12, #1c130b); border-color: #4a3520; }
.binder-skin-holo {
  background: linear-gradient(135deg, #2a1a3a, #16202f, #2a2a12, #1a2a24);
  background-size: 300% 300%;
  border-color: #5a4a7a;
}
.binder-skin-pokeball { background: linear-gradient(180deg, #3a1414 0%, #3a1414 46%, #1a1a1a 46%, #1a1a1a 54%, #f4f3f0 54%, #f4f3f0 100%); border-color: #1a1a1a; }
.binder-skin-forest { background: linear-gradient(160deg, #16281a, #0f1a10); border-color: #2f5c37; }
.binder-skin-inferno { background: linear-gradient(160deg, #3a1710, #2a0d08); border-color: #7a3016; }
.binder-skin-tidal { background: linear-gradient(160deg, #0f2436, #0a1620); border-color: #1e5478; }
.binder-skin-vault { background: linear-gradient(160deg, #23262d, #1a1c21); border-color: #3a3f4a; }
.binder-skin-retro { background: linear-gradient(160deg, #2e2a18, #f4ecd0 0%, #2e2a18 100%); background: #ece2bd; border-color: #a8935a; }
.binder-skin-retro .binder-cell { background: rgba(255,255,255,0.55); border-color: #a8935a; color: #2e2a18; }
.binder-skin-retro .binder-cell-name { color: #5a5236; }

.binder-tile.binder-skin-classic, .binder-tile.binder-skin-holo, .binder-tile.binder-skin-pokeball,
.binder-tile.binder-skin-forest, .binder-tile.binder-skin-inferno, .binder-tile.binder-skin-tidal,
.binder-tile.binder-skin-vault, .binder-tile.binder-skin-retro { color: #E7E7EA; }
.binder-tile.binder-skin-retro { color: #2e2a18; }
.modal-toggle-btn.active { color: var(--accent); border-color: var(--accent); }
