/* ==========================================================================
   Field Guide — Design System
   A clean, editorial design for Card Vault / MTG Collection.
   System fonts, white background, slate grays, single blue accent.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. CUSTOM PROPERTIES (Design Tokens)
   -------------------------------------------------------------------------- */
:root {
  --white: #ffffff;
  --gray-50: #f8fafc;
  --gray-100: #f1f5f9;
  --gray-200: #e2e8f0;
  --gray-300: #cbd5e1;
  --gray-400: #94a3b8;
  --gray-500: #64748b;
  --gray-600: #475569;
  --gray-700: #334155;
  --gray-800: #1e293b;
  --gray-900: #0f172a;
  --blue: #3b82f6;
  --blue-light: #eff6ff;
  --blue-dark: #1d4ed8;
  --red: #ef4444;
  --green-bg: #dcfce7;
  --green-text: #166534;
  --yellow-bg: #fef9c3;
  --yellow-text: #854d0e;
  --lime-bg: #ecfccb;
  --lime-text: #3f6212;
  --orange-bg: #ffedd5;
  --orange-text: #9a3412;
  --amber: #f59e0b;
  --on-accent: #ffffff;
  --red-bg: #fee2e2;
  --red-text: #991b1b;
  --accent-alt: #7c3aed;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --sidebar-width: 220px;
  --sidebar-width-collapsed: 60px;
  --content-max: none;
  --radius: 6px;
  --radius-sm: 4px;
}

/* --------------------------------------------------------------------------
   1b. DARK THEME
   -------------------------------------------------------------------------- */
[data-theme="dark"] {
  --white: #0f172a;
  --gray-50: #1e293b;
  --gray-100: #1e293b;
  --gray-200: #334155;
  --gray-300: #475569;
  --gray-400: #94a3b8;
  --gray-500: #94a3b8;
  --gray-600: #cbd5e1;
  --gray-700: #e2e8f0;
  --gray-800: #f1f5f9;
  --gray-900: #f8fafc;
  --blue: #60a5fa;
  --blue-light: #1e3a5f;
  --blue-dark: #93bbfd;
  --red: #f87171;
  --green-bg: #14532d;
  --green-text: #86efac;
  --yellow-bg: #422006;
  --yellow-text: #fde68a;
  --lime-bg: #365314;
  --lime-text: #bef264;
  --orange-bg: #431407;
  --orange-text: #fdba74;
  --amber: #fbbf24;
  --on-accent: #0f172a;
  --red-bg: #450a0a;
  --red-text: #fca5a5;
  --accent-alt: #a78bfa;
  color-scheme: dark;
}

[data-theme="dark"] img:not(.no-dim) {
  filter: brightness(0.95);
}

[data-theme="dark"] .search-suggestions {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

/* --------------------------------------------------------------------------
   2. RESET & BASE
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: var(--font);
  background: var(--white);
  color: var(--gray-900);
  font-size: 14px;
  line-height: 1.55;
}

a {
  color: var(--blue);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

::selection {
  background: var(--blue-light);
}

img {
  max-width: 100%;
  height: auto;
}

/* Alpine.js cloak */
[x-cloak] {
  display: none !important;
}

/* --------------------------------------------------------------------------
   2b. SKIP LINK
   Visually hidden off-canvas until keyboard focus lands on it (issue #244).
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  z-index: 1000;
  padding: 0.5rem 1rem;
  background: var(--blue);
  color: var(--on-accent);
  border-radius: 0 0 var(--radius-sm) 0;
  font-size: 13px;
  font-weight: 600;
}

.skip-link:focus-visible {
  top: 0;
}

/* --------------------------------------------------------------------------
   3. LAYOUT
   -------------------------------------------------------------------------- */
.layout {
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  min-height: 100vh;
}

.main {
  padding: 0;
  min-width: 0;
}

.content {
  padding: 1.5rem 2rem;
  max-width: var(--content-max);
}

.content h1 {
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.content .subtitle {
  font-size: 13px;
  color: var(--gray-500);
  margin-bottom: 1.5rem;
}

.narrow-form {
  max-width: 480px;
}

/* Shared page-header row: h1 (+ optional subtitle) on the left, actions
   (buttons/links) on the right. Wraps + gaps on narrow viewports instead of
   the actions crowding the title (issue #258). */
.page-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 0.25rem;
}

/* --------------------------------------------------------------------------
   4. SIDEBAR
   -------------------------------------------------------------------------- */
.sidebar {
  background: var(--gray-50);
  border-right: 1px solid var(--gray-200);
  padding: 1.25rem 0;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.sidebar-brand {
  padding: 0 1.25rem 1.25rem;
  border-bottom: 1px solid var(--gray-200);
  margin-bottom: 0.75rem;
  font-weight: 700;
  font-size: 16px;
  color: var(--gray-900);
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}

.sidebar-brand:hover {
  text-decoration: none;
}

.sidebar-brand .dot {
  width: 10px;
  height: 10px;
  background: var(--blue);
  border-radius: 2px;
  flex-shrink: 0;
}

.sidebar-section-label {
  padding: 0.5rem 1.25rem 0.25rem;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--gray-500);
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  position: relative;
  padding: 0.35rem 1.25rem;
  font-size: 13px;
  color: var(--gray-600);
  text-decoration: none;
  transition: all 0.1s;
}

/* .sidebar-link is normally an <a>; the theme toggle (issue #259) is a
   <button> so it's a real control instead of a link with no href. Reset the
   browser's button chrome so it matches the anchor styling above. */
button.sidebar-link {
  background: none;
  border: 0;
  width: 100%;
  font: inherit;
  cursor: pointer;
  text-align: left;
}

.sidebar-link .icon { flex-shrink: 0; }
/* Starred-deck cover thumbnail — sized to match the 18px sidebar icons so the
   Starred entries line up with the rest of the rail. art_crop is a wide strip;
   object-fit crops a centered square of it. */
.sidebar-deck-thumb {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: 3px;
  object-fit: cover;
}
.sidebar-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.sidebar-link:hover {
  background: var(--gray-100);
  color: var(--gray-900);
  text-decoration: none;
}

.sidebar-link:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
}

.sidebar-link.active {
  background: var(--blue-light);
  color: var(--blue-dark);
  font-weight: 500;
}

.sidebar-link .badge {
  margin-left: auto;
  background: var(--red-bg);
  color: var(--red-text);
  font-size: 10px;
  font-weight: 600;
  padding: 0 5px;
  border-radius: 99px;
  line-height: 1.6;
}

.sidebar-link .badge.badge--gray { background: var(--gray-200); color: var(--gray-800); }

.sidebar-spacer {
  flex: 1;
}

.sidebar-footer {
  padding: 0.75rem 1.25rem 0;
  border-top: 1px solid var(--gray-200);
  font-size: 12px;
  color: var(--gray-400);
}

/* Theme toggle (issue #259): both icons render, CSS shows the one matching
   the current theme — no JS icon swap needed. */
#theme-toggle .icon-sun { display: none; }
[data-theme="dark"] #theme-toggle .icon-moon { display: none; }
[data-theme="dark"] #theme-toggle .icon-sun { display: block; }

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 1.25rem 1.25rem;
  border-bottom: 1px solid var(--gray-200);
  margin-bottom: 0.75rem;
}

.sidebar-header .sidebar-brand {
  padding: 0;
  border: none;
  margin: 0;
}

.sidebar-pin {
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--gray-500);
  padding: 4px;
  border-radius: var(--radius);
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.sidebar-pin:hover { color: var(--gray-900); background: var(--gray-100); }
.sidebar-pin:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.sidebar-pin .icon { transition: transform 0.2s; }

.sidebar-close {
  display: none;
  background: transparent;
  border: none;
  cursor: pointer;
  color: var(--gray-500);
  font-size: 28px;
  line-height: 1;
  padding: 0 6px;
  border-radius: var(--radius);
}
.sidebar-close:hover { color: var(--gray-900); background: var(--gray-100); }
.sidebar-close:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

.collection-initial {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  flex-shrink: 0;
}

.sidebar-toggle {
  display: none;
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 100;
  width: 36px;
  height: 36px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: var(--white);
  color: var(--gray-700);
  font-size: 18px;
  cursor: pointer;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

.sidebar-toggle:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* Sidebar overlay for mobile (hidden by default) */
.sidebar-overlay {
  display: none;
}

/* --------------------------------------------------------------------------
   5. TOPBAR
   -------------------------------------------------------------------------- */
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 2rem;
  border-bottom: 1px solid var(--gray-200);
  position: sticky;
  top: 0;
  background: var(--white);
  z-index: 10;
  gap: 1rem;
}

.topbar-search {
  display: flex;
  align-items: center;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
  width: 320px;
  flex-shrink: 0;
  transition: border-color 0.1s;
}

.topbar-search:focus-within {
  border-color: var(--blue);
}

.topbar-search input {
  flex: 1;
  padding: 0.4rem 0.75rem;
  font-size: 13px;
  border: none;
  outline: none;
  font-family: var(--font);
  color: var(--gray-900);
  background: transparent;
}

.topbar-search input::placeholder {
  color: var(--gray-400);
}

.search-shortcut {
  padding: 0.25rem 0.5rem;
  margin-right: 0.4rem;
  font-size: 11px;
  color: var(--gray-400);
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  font-family: var(--font);
  flex-shrink: 0;
}

/* Positioning context so the dropdown anchors to the input wrapper (not the
   sticky topbar), and stays the width of the field rather than the full bar. */
.search-suggest {
  position: relative;
  flex-shrink: 0;
}

.search-suggestions {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  margin-top: 4px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  z-index: 20;
}

/* No results → no border/shadow ghost box (issue #236: the 2px topbar hairline). */
.search-suggestions:empty {
  display: none;
}

.search-suggestions-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.search-suggestion {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.45rem 0.75rem;
  font-size: 13px;
  color: var(--gray-700);
  text-decoration: none;
  border-bottom: 1px solid var(--gray-100);
  cursor: pointer;
}

.search-suggestions-list li:last-child .search-suggestion {
  border-bottom: none;
}

.search-suggestion:hover,
.search-suggestion.is-active {
  background: var(--gray-50);
  color: var(--gray-900);
}

.search-suggestion-meta {
  font-size: 11px;
  color: var(--gray-400);
  flex-shrink: 0;
}

/* --------------------------------------------------------------------------
   6. BREADCRUMB
   -------------------------------------------------------------------------- */
.breadcrumb {
  font-size: 13px;
  color: var(--gray-400);
  /* Keep the trail on a single line. In the flex topbar min-width:0 lets it
     shrink below its content width so a long trail scrolls horizontally
     (scrollbar hidden) instead of wrapping onto several lines on mobile. */
  white-space: nowrap;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none; /* Firefox */
}
.breadcrumb::-webkit-scrollbar {
  display: none; /* WebKit/Blink */
}

.breadcrumb a {
  color: var(--gray-500);
}

.breadcrumb a:hover {
  color: var(--blue);
}

.breadcrumb a:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

.breadcrumb .sep {
  margin: 0 6px;
  color: var(--gray-300);
}

.breadcrumb .current {
  color: var(--gray-900);
  font-weight: 500;
}

/* --------------------------------------------------------------------------
   6b. TABS
   -------------------------------------------------------------------------- */
.tab-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  border-bottom: 2px solid var(--gray-200);
  margin-bottom: 1.25rem;
}

.tab {
  padding: 0.5rem 1rem;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-500);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -2px;
  white-space: nowrap;
  transition: color 0.1s, border-color 0.1s;
}

.tab:hover {
  color: var(--gray-700);
  text-decoration: none;
}

.tab.active {
  color: var(--blue);
  border-bottom-color: var(--blue);
}

/* --------------------------------------------------------------------------
   7. BUTTONS
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  font-family: var(--font);
  font-size: 13px;
  font-weight: 500;
  padding: 0.4rem 1rem;
  border-radius: var(--radius);
  border: 1px solid var(--gray-200);
  background: var(--white);
  color: var(--gray-700);
  cursor: pointer;
  transition: all 0.1s;
  text-decoration: none;
  line-height: 1.55;
  white-space: nowrap;
}

.btn:hover {
  background: var(--gray-50);
  border-color: var(--gray-300);
  text-decoration: none;
}

.btn:active {
  background: var(--gray-100);
}

.btn:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

.btn-primary {
  background: var(--blue);
  color: var(--on-accent);
  border-color: var(--blue);
}

.btn-primary:hover {
  background: var(--blue-dark);
  border-color: var(--blue-dark);
}

.btn-primary:active {
  background: var(--blue-dark);
  border-color: var(--blue-dark);
}

/* Red outline button for destructive actions (issue #258). */
.btn--danger {
  background: transparent;
  border-color: var(--red);
  color: var(--red-text);
}

.btn--danger:hover {
  background: var(--red-bg);
  border-color: var(--red);
}

/* --------------------------------------------------------------------------
   8. FORMS
   -------------------------------------------------------------------------- */
.form-group {
  margin-bottom: 1rem;
}

.form-label {
  display: block;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-700);
  margin-bottom: 0.35rem;
}

/* Field validation message, used below a form-group's widget (issue #258). */
.form-error {
  margin-top: 4px;
  font-size: 12px;
  color: var(--red-text);
}

/* Inline checkbox row — the label wraps the checkbox + its text so the
   whole row is one click target, instead of the block label sitting above
   a lone, tiny checkbox that the generic form loop otherwise renders
   (issue #261). */
.form-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 13px;
  font-weight: 500;
  color: var(--gray-700);
  cursor: pointer;
}

.form-check input[type="checkbox"] {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  accent-color: var(--blue);
}

.form-input,
.form-group input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]),
.form-group select,
.form-group textarea {
  display: block;
  width: 100%;
  padding: 0.45rem 0.75rem;
  font-size: 14px;
  font-family: var(--font);
  color: var(--gray-900);
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  transition: border-color 0.1s;
  line-height: 1.55;
}

.form-input:focus,
.form-group input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):focus,
.form-group select:focus,
.form-group textarea:focus {
  outline: 2px solid var(--blue);
  outline-offset: -1px;
  border-color: var(--blue);
}

.form-input::placeholder,
.form-group input::placeholder,
.form-group textarea::placeholder {
  color: var(--gray-400);
}

input[type="number"].form-input,
.form-group input[type="number"] {
  -moz-appearance: textfield;
  appearance: textfield;
}

input[type="number"].form-input::-webkit-inner-spin-button,
input[type="number"].form-input::-webkit-outer-spin-button,
.form-group input[type="number"]::-webkit-inner-spin-button,
.form-group input[type="number"]::-webkit-outer-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

select.form-input,
.form-group select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2394a3b8' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  padding-right: 2rem;
}

textarea.form-input,
.form-group textarea {
  min-height: 80px;
  resize: vertical;
}

/* --------------------------------------------------------------------------
   9. TABLES
   -------------------------------------------------------------------------- */
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

thead th {
  text-align: left;
  padding: 0.5rem 0.75rem;
  font-size: 12px;
  font-weight: 500;
  color: var(--gray-500);
  border-bottom: 1px solid var(--gray-200);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

thead th:hover {
  color: var(--gray-900);
}

thead th .arr {
  font-size: 10px;
  margin-left: 2px;
}

tbody td {
  padding: 0.45rem 0.75rem;
  border-bottom: 1px solid var(--gray-100);
  vertical-align: middle;
}

tbody tr {
  transition: background 0.08s;
}

tbody tr:hover {
  background: var(--blue-light);
}

/* Table cell helpers */
.t-thumb {
  width: 24px;
  height: 34px;
  object-fit: cover;
  border-radius: 2px;
  display: block;
}

.t-name {
  font-weight: 500;
  color: var(--gray-900);
}

.t-name a {
  color: inherit;
  text-decoration: none;
}

.t-name a:hover {
  color: var(--blue);
  text-decoration: none;
}

.t-set {
  font-size: 12px;
  color: var(--gray-500);
}

.t-set .ss {
  font-size: 26px;
  vertical-align: middle;
}

.grid-card-meta .ss {
  font-size: 16px;
  vertical-align: middle;
}

/* On the dark theme's slate-900 background, each rarity gets a stroke
   tuned to its fill: light slate for the near-black common, dark steel
   for the silver uncommon, deep gold for the rare, and rust for the
   mythic. .ss (no rarity) gets a neutral mid-slate fallback. */
[data-theme="dark"] .ss {
  -webkit-text-stroke: 1px #94a3b8;
}
[data-theme="dark"] .ss-common {
  -webkit-text-stroke: 1px #cbd5e1;
}
[data-theme="dark"] .ss-uncommon {
  -webkit-text-stroke: 1px #475569;
}
[data-theme="dark"] .ss-rare {
  -webkit-text-stroke: 1px #7a5a14;
}
[data-theme="dark"] .ss-mythic {
  -webkit-text-stroke: 1px #7a2b0b;
}

.t-qty {
  font-weight: 600;
  text-align: center;
}

.t-price {
  font-variant-numeric: tabular-nums;
}

/* Table scroll wrapper for mobile */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* --------------------------------------------------------------------------
   10. CARDS / GRID
   -------------------------------------------------------------------------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--card-min-width, 130px), 1fr));
  gap: 12px;
}

/* Stack view: each type/category group is one vertical stack where the
   top 30px (name strip) of every underlying card peeks above the next.
   The peek must be CONSTANT regardless of slider size — to guarantee
   that, we pin each stack item's height to image-only (--card-min-width
   × MTG card aspect 88/63 ≈ 1.4) and hide the info/qty-select rows that
   sit below the image in the cards-view layout. Margin-bottom then
   makes the next sibling start 30px below this one's top. The last
   card in each stack still shows fully because we zero out its margin. */
.card-stack-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 16px;
}
.card-stack {
  display: flex;
  flex-direction: column;
  width: var(--card-min-width, 220px);
  max-width: 100%;
  /* Peek is a fraction of card width so the *visible portion* of each
     stacked card stays at the same proportion at every slider size.
     With cards in MTG aspect (height ≈ width × 1.4) a peek of 0.15 ×
     width works out to ~10.7% of card height — title + just enough
     room above to read the mana cost row at all sizes. */
  --stack-peek: calc(var(--card-min-width, 220px) * 0.15);
}
/* Stacks view: lay the type/category piles out side by side, wrapping to use
   the full width — on every screen size, including mobile. Each pile is one
   card wide, so the card-size slider controls how many fit per row (a pile is
   capped to the row width, so a single wide pile never overflows). Cards/text
   views are untouched (the wrapper is a plain block there). */
.deck-groups--stacks {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0 22px;
}
.deck-groups--stacks > .deck-type-group {
  width: var(--card-min-width, 220px);
  max-width: 100%;
  margin: 10px 0 0;
}
.card-stack > .grid-card-wrap {
  width: 100%;
  height: calc(var(--card-min-width, 220px) * 1.4);
  margin-bottom: calc(var(--stack-peek) - var(--card-min-width, 220px) * 1.4);
  overflow: hidden;
  /* Explicit base so the delayed z-index drop below is integer→integer (the
     resting order is unchanged: equal z-index falls back to DOM order). */
  z-index: 0;
  /* Animate the reveal: changing this card's margin slides the cards stacked
     on top of it down smoothly (see the .is-hovered/.revealed rule below).
     z-index has a transition DELAY equal to the slide so that when a card
     stops being hovered it keeps its raised stacking until the slide-back
     finishes. Without this, moving the cursor to an EARLIER card in the stack
     (painted under the current one by DOM order) snaps it above the previous
     card instantly while the margins are still animating — a jarring flip. */
  transition: margin-bottom 0.16s ease, z-index 0s linear 0.16s;
}
.card-stack > .grid-card-wrap > .grid-card,
.card-stack > .grid-card-wrap > .grid-card img {
  height: 100%;
  width: 100%;
}
.card-stack > .grid-card-wrap > .grid-card img {
  object-fit: cover;
  object-position: top;
}
/* In stacks view the info row, qty select, AND the action buttons are hidden
   by default — the cards overlap, so buttons would just collide with the
   cards on top. They appear only once a card is revealed (below). */
.card-stack .grid-card-info,
.card-stack .grid-card-qty-select,
.card-stack .grid-card-controls {
  display: none;
}
.card-stack > .grid-card-wrap:last-child {
  margin-bottom: 0;
}
.card-stack > .grid-card-wrap.filter-hidden {
  margin-bottom: 0;
}
/* Reveal a stacked card: drop its negative peek-margin so the cards stacked on
   top of it slide DOWN and it shows in full, lift it above its neighbours
   (overflow visible so the ⋯ menu isn't clipped), and only THEN surface its
   buttons. Driven by JS classes, NOT :hover — `.is-hovered` is applied after a
   short hover-intent delay (see the stack hover-intent script in
   _deck_controls_row.html) so sweeping the cursor across the pile doesn't
   cascade-reveal every card; `.revealed` is the touch (tap) equivalent. */
.card-stack > .grid-card-wrap.is-hovered,
.card-stack > .grid-card-wrap.revealed {
  margin-bottom: 10px;
  overflow: visible;
  z-index: 5;
  /* Raise the newly hovered card to the front immediately (no delay), so it
     reveals on top; the leaving card keeps its z-index for the slide (see the
     delayed z-index transition on the base rule above). */
  transition: margin-bottom 0.16s ease, z-index 0s;
}
.card-stack > .grid-card-wrap.is-hovered .grid-card-controls,
.card-stack > .grid-card-wrap.revealed .grid-card-controls {
  display: flex;
}

/* Deck-value "most expensive cards" list. */
.deck-value-top {
  list-style: none;
  margin: 0 0 4px;
  padding: 0;
  max-width: 420px;
}
.deck-value-top li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--gray-100);
  font-size: 14px;
}
.deck-value-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.deck-value-price {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.deck-value-mult {
  color: var(--gray-500);
  font-weight: 400;
  font-size: 12px;
}

/* Deck color-pip distribution as a single stacked horizontal bar. Each
   segment's width is set inline via flex-grow=<pip count>, so widths are
   proportional with no template-side division. Reusable for the upcoming
   mana-production bar so the two line up for comparison. */
.pip-bar {
  display: flex;
  width: 100%;
  height: 34px;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid var(--gray-200);
}
.pip-bar-seg {
  flex-basis: 0;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 700;
}
.pip-bar-seg-label {
  padding: 0 4px;
  white-space: nowrap;
}
.pip-bar-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-top: 8px;
  font-size: 12px;
  color: var(--gray-600);
}
.pip-bar-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}
.pip-bar-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 1px solid var(--gray-300);
}

/* Loading bar for full-area deck re-renders (filter chips, group switch,
   quick-add). htmx adds .htmx-request to the hx-indicator target while the
   request is in flight, so the bar is hidden until then. Lives outside
   #deck-card-area so a morph swap of the area doesn't disturb it. */
.deck-area-progress {
  display: none;
  height: 3px;
  margin: 0 0 8px;
  border-radius: 2px;
  background: var(--blue-light);
  position: relative;
  overflow: hidden;
}
.deck-area-progress.htmx-request {
  display: block;
}
.deck-area-progress::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 35%;
  border-radius: 2px;
  background: var(--blue);
  animation: deck-progress-slide 0.9s ease-in-out infinite;
}
@keyframes deck-progress-slide {
  0% { transform: translateX(-110%); }
  100% { transform: translateX(320%); }
}

.card-size-slider {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  color: var(--gray-500);
}

.card-size-slider input[type="range"] {
  width: 110px;
  accent-color: var(--blue);
}

/* Deck-card annotations: tag chips + notes indicator. Used by both the
   builder (editable) and the read-only deck detail page. */
.deck-tag-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}

.deck-tag-chip {
  display: inline-block;
  font-size: 10px;
  line-height: 1.6;
  padding: 0 6px;
  border-radius: 99px;
  background: var(--blue-light);
  color: var(--blue-dark);
  font-weight: 500;
  white-space: nowrap;
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
}

.deck-cat-chip {
  background: var(--blue-light);
  color: var(--blue-dark);
  border-color: var(--blue);
}

.card-tag-chip {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 0 1px rgba(0, 0, 0, 0.4);
  white-space: nowrap;
  vertical-align: middle;
}
.card-tag-chip--sm {
  font-size: 10px;
  padding: 0 6px;
}

.grid-card-notes {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 1;
  font-size: 14px;
  line-height: 1;
  padding: 2px 4px;
  background: rgba(0, 0, 0, 0.6);
  border-radius: var(--radius);
  pointer-events: auto;
  cursor: help;
}

/* When both the owned-count badge and the notes indicator are present,
   shift the notes icon down so they don't overlap. */
.grid-card-own + .grid-card-notes { top: 32px; }

/* One-line strip combining the group picker, tag chips, and the
   name-search input above the card list. Wraps on narrow viewports
   so each control keeps its own intrinsic width. */
.deck-controls-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin: 8px 0;
}
.deck-controls-row > * { margin: 0; }

/* Vertical divider between the view/group/sort pickers and the by-name
   filter in the toolbar row. */
.deck-toolbar-sep {
  width: 1px;
  align-self: stretch;
  background: var(--gray-200);
}
.deck-controls-row .deck-name-filter { width: 200px; }
/* Space between the collapsed/expanded view-controls and the card area
   (tag-filter chips or the cards). */
#deck-card-area { margin-top: 10px; }
@media (max-width: 768px) {
  .deck-controls-row .deck-name-filter { width: 100%; }
  .deck-toolbar-sep { display: none; }
}

/* Collapsible "View & filters" tools (shared by builder + detail). On
   desktop the body is always shown and the toggle is hidden; on mobile the
   body collapses behind the toggle so the card list isn't buried under the
   control rows. */
.deck-tools-toggle {
  display: none;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin: 12px 0 0;
  padding: 9px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
  color: var(--gray-700);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.deck-tools-toggle .chevron { transition: transform 0.15s ease; }
.deck-tools.is-open .deck-tools-toggle .chevron { transform: rotate(180deg); }

@media (max-width: 768px) {
  .deck-tools-toggle { display: flex; }
  .deck-tools-body { display: none; }
  .deck-tools.is-open .deck-tools-body { display: block; }
  .deck-tools-body .deck-controls-row { margin-top: 10px; gap: 10px 12px; }
  /* The tag-filter chip strips render inside #deck-card-area (their active
     states re-render with the area), but on mobile they should collapse
     with the "View & filters" toggle (issue #161). The detail page has
     .deck-tools as a direct preceding sibling of the area; the builder
     wraps it in the sticky .builder-toolbar, hence the :has() variant. */
  .deck-tools:not(.is-open) ~ #deck-card-area .deck-tag-filter,
  .builder-toolbar:has(.deck-tools:not(.is-open)) ~ #deck-card-area .deck-tag-filter {
    display: none;
  }
}

/* Deck tag-filter strip above the card area. Toggleable chips + Clear. */
.deck-tag-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin: 0 0 6px;
}

.deck-tag-filter-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--gray-500);
  margin-right: 4px;
}

.deck-filter-chip {
  display: inline-block;
  padding: 3px 10px;
  border: 1px solid var(--gray-300);
  border-radius: 99px;
  font-size: 12px;
  color: var(--gray-700);
  background: var(--white);
  text-decoration: none;
  white-space: nowrap;
}

.deck-filter-chip:hover { border-color: var(--gray-400); text-decoration: none; }
.deck-filter-chip.is-active {
  background: var(--blue);
  border-color: var(--blue);
  color: var(--on-accent);
}
.deck-filter-chip.is-untagged { font-style: italic; }

.deck-filter-clear {
  font-size: 12px;
  color: var(--gray-500);
  text-decoration: underline;
  margin-left: 4px;
}

.deck-group-picker {
  margin: 8px 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.deck-group-label {
  font-size: 13px;
  color: var(--gray-700);
}

/* Builder title + a "⋯" actions dropdown (Import / Statistics) next to the
   deck name, so those actions don't take space in the add bar. */
.builder-title {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.builder-title h1 { margin: 0; overflow: hidden; text-overflow: ellipsis; }
.deck-actions-menu { position: relative; }
.deck-actions-summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 32px;
  padding: 0 8px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
  color: var(--gray-700);
  font-size: 18px;
  line-height: 1;
}
.deck-actions-summary::-webkit-details-marker { display: none; }
.deck-actions-summary:hover { background: var(--gray-100); }
.deck-actions-items {
  position: absolute;
  left: 0;
  top: 100%;
  margin-top: 4px;
  /* Above the sticky .builder-toolbar (z-index:40) — they share the page-root
     stacking context, so an open menu would otherwise be painted over by the
     quick-add bar below it. Stays under the nav (100) and modals (150+). */
  z-index: 50;
  min-width: 170px;
  background: var(--white);
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.deck-actions-item {
  background: none;
  border: 0;
  text-align: left;
  padding: 7px 10px;
  font-size: 14px;
  color: var(--gray-900);
  border-radius: var(--radius);
  cursor: pointer;
}
.deck-actions-item:hover { background: var(--gray-100); }

/* Quick-add bar: a unified search box with the MB/SB target toggle living
   inside it (looks like one input), plus a compact skip toggle. The board
   value is a hidden input the toggle flips. Full-width search on mobile. */
.deck-add-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin: 10px 0 0;
}
.deck-add-input-group {
  position: relative;
  display: flex;
  align-items: stretch;
  flex: 1 1 280px;
  min-width: 0;
  max-width: 520px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
}
.deck-add-input-group:focus-within {
  border-color: var(--blue);
  box-shadow: 0 0 0 1px var(--blue);
}
.deck-add-input-group input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 8px 12px;
  font-size: 14px;
  color: var(--gray-900);
  border-radius: var(--radius);
}
.deck-add-input-group input[type="text"]:focus { outline: none; }
/* Main/Side segmented control inside the quick-add box (issue #165):
   states the target board instead of encoding it in a lone "MB" button. */
.deck-add-board-seg {
  flex: 0 0 auto;
  display: flex;
  align-items: stretch;
  border-left: 1px solid var(--gray-300);
}
.deck-add-board-seg button {
  border: 0;
  background: var(--gray-100);
  color: var(--gray-500);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.02em;
  padding: 0 10px;
  cursor: pointer;
}
.deck-add-board-seg button:last-child { border-radius: 0 var(--radius) var(--radius) 0; }
.deck-add-board-seg button.is-active {
  background: var(--blue-dark);
  color: var(--on-accent);
}
.deck-add-input-group #suggest-dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  z-index: 10;
  background: var(--white);
}
@media (max-width: 768px) {
  .deck-add-input-group { flex: 1 1 100%; max-width: none; }
}

/* Horizontal strip of card-printing thumbnails inside the card-edit
   modal. Each thumb is a button; the current printing is disabled and
   ringed in blue. */
.printing-strip {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  max-width: 100%;
  padding: 4px 2px;
  margin: 0 -2px;
}

.printing-thumb {
  flex: 0 0 auto;
  width: 76px;
  background: transparent;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border-radius: var(--radius);
}

.printing-thumb-img {
  position: relative;
  display: block;
}

.printing-thumb img {
  width: 76px;
  height: 106px;
  object-fit: cover;
  border-radius: var(--radius);
  border: 2px solid transparent;
  transition: border-color 0.1s;
}

.printing-thumb:hover img { border-color: var(--gray-400); }
.printing-thumb.is-current { cursor: default; }
.printing-thumb.is-current img { border-color: var(--blue); }

/* "N owned" badge on a printing thumbnail (market card-detail picker only —
   see mtg/services.py build_printings_picker_context owned_by_uuid). */
.printing-thumb-owned {
  position: absolute;
  top: 3px;
  right: 3px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--amber);
  color: #0f172a;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
}

.printing-thumb-set {
  font-size: 10px;
  color: var(--gray-500);
  letter-spacing: 0.04em;
}

.printing-thumb.is-current .printing-thumb-set { color: var(--blue); font-weight: 600; }

/* Lazy printings picker (deck/collection/scan modals). */
.printings-collapsible > summary {
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  color: var(--gray-700);
  padding: 6px 0;
  list-style: none;
}
.printings-collapsible > summary::-webkit-details-marker { display: none; }
.printings-collapsible > summary::before { content: "▸ "; color: var(--gray-500); }
.printings-collapsible[open] > summary::before { content: "▾ "; }
.printings-picker { margin-top: 8px; }
.printings-filter { width: 100%; margin-bottom: 8px; }
.printings-more-note {
  font-size: 12px;
  color: var(--gray-500);
  margin: 8px 0 0;
}

/* Tag-chip toggle pills used inside the card-edit modal. */
.tag-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: 1px solid var(--gray-300);
  border-radius: 99px;
  font-size: 12px;
  cursor: pointer;
  user-select: none;
  background: var(--white);
  color: var(--gray-700);
}

.tag-chip:hover { border-color: var(--gray-400); }
.tag-chip input { margin: 0; cursor: pointer; }
.tag-chip:has(input:checked) {
  background: var(--blue-light);
  border-color: var(--blue);
  color: var(--blue-dark);
}

.tag-chip.category-chip {
  background: var(--blue-light);
  color: var(--blue-dark);
  border-color: var(--blue);
}
.tag-chip.category-chip input:checked ~ span {
  background: var(--blue);
  color: var(--white);
  padding: 1px 8px;
  border-radius: 99px;
}

/* --------------------------------------------------------------------------
   Card-edit modal layout primitives.
   Shared by the editable and read-only branches of _card_edit_form.html;
   factored out of repeated inline styles to keep the htmx-swapped payload
   small and restyling in one place.
   -------------------------------------------------------------------------- */
/* The card-edit form swaps into <dialog id="deck-modal" class="paste-dialog">,
   so `dialog.paste-dialog h2` / `p.hint` (above) would otherwise win on
   specificity over a plain class and shift the heading/subtitle margins.
   Scope these to the same dialog so they reproduce the original inline
   values exactly (h2 margin 4px, sub margin 14px) instead of the
   paste-dialog defaults (8px / 10px). */
dialog.paste-dialog h2.card-edit-title { font-size: 16px; margin: 0 0 4px; }
dialog.paste-dialog p.hint.card-edit-sub { font-size: 12px; color: var(--gray-500); margin: 0 0 14px; }

.card-edit-fieldset { border: 0; padding: 0; margin: 0 0 14px; }
/* Last fieldset before the footer gets a touch more breathing room. */
.card-edit-fieldset--last { margin-bottom: 16px; }

.card-edit-legend {
  font-size: 12px;
  font-weight: 600;
  color: var(--gray-700);
  margin-bottom: 6px;
}

/* Wrapping row of tag/category chips. */
.card-edit-chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 8px;
}
/* Read-only branch: no add-row below, so no bottom margin. */
.card-edit-chip-group--flush { margin-bottom: 0; }

/* "Add new tag/category" input + button row. */
.card-edit-add-row {
  display: flex;
  gap: 6px;
  align-items: center;
}
.card-edit-add-row-input {
  flex: 1;
  padding: 6px 8px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font-size: 13px;
}
.card-edit-add-row-btn { padding: 6px 12px; font-size: 13px; }

/* Footer: market link on the left, action buttons on the right. */
.card-edit-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}
.card-edit-footer-link {
  font-size: 12px;
  color: var(--gray-600);
  text-decoration: underline;
}
.card-edit-footer-actions { display: flex; gap: 8px; }

/* --------------------------------------------------------------------------
   Modal dialogs (shared by paste-decklist and per-card edit dialogs).
   -------------------------------------------------------------------------- */
dialog.paste-dialog {
  /* Native <dialog> centers in modal mode, but only when no positioning
     props are overridden. Setting `width` knocks `margin: auto` off-track
     in some engines, so we pin the dialog ourselves: fixed + inset: 0 +
     margin: auto centers horizontally and vertically at any viewport. */
  position: fixed;
  inset: 0;
  margin: auto;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  padding: 20px;
  width: min(560px, calc(100vw - 2rem));
  max-height: calc(100vh - 2rem);
  overflow: auto;
  /* Keep scrolling inside the dialog — don't chain to the page behind it
     when you hit the top/bottom (background cards scrolling on mobile). */
  overscroll-behavior: contain;
  background: var(--white);
  color: var(--gray-900);
}
dialog.paste-dialog::backdrop { background: rgba(0,0,0,0.4); }

/* Sticky close bar for modal content that has no other close affordance
   (the statistics modal) — essential on touch, where there's no Esc key. */
.deck-stats-topbar {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  justify-content: flex-end;
  margin-bottom: 4px;
  background: var(--white);
}
.deck-modal-close {
  border: 0;
  background: transparent;
  cursor: pointer;
  font-size: 26px;
  line-height: 1;
  color: var(--gray-500);
  padding: 0 4px;
}
.deck-modal-close:hover { color: var(--gray-900); }
dialog.paste-dialog h2 { font-size: 16px; margin: 0 0 8px; }
dialog.paste-dialog p.hint { font-size: 12px; color: var(--gray-500); margin: 0 0 10px; }
dialog.paste-dialog textarea {
  width: 100%; min-height: 220px;
  padding: 8px 10px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  font: 13px/1.4 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--gray-900);
  background: var(--white);
  resize: vertical;
}
dialog.paste-dialog .actions {
  display: flex; gap: 8px; justify-content: flex-end;
  margin-top: 12px;
}

.grid-card {
  cursor: pointer;
  transition: transform 0.1s;
  text-decoration: none;
  color: inherit;
}

.grid-card:hover {
  transform: translateY(-2px);
  text-decoration: none;
}

.grid-card img {
  width: 100%;
  display: block;
  /* MTG card ratio (63×88mm): reserves the image box before the lazy-loaded
     image arrives, so the grid doesn't reflow / shift layout (issue #147).
     The stacks view overrides this with an explicit height. */
  aspect-ratio: 63 / 88;
  /* Proportional radius (~ the real MTG card corner) so it scales with the
     card-size slider; soft shadow gives the raised, framed look. */
  border-radius: 4.7% / 3.35%;
  border: 1px solid rgba(0, 0, 0, 0.18);
  box-shadow: 0 2px 7px rgba(0, 0, 0, 0.28);
}

.grid-card-info {
  padding-top: 4px;
}

.grid-card-name {
  font-size: 11px;
  font-weight: 500;
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.grid-card-meta {
  font-size: 10px;
  color: var(--gray-500);
}

/* --------------------------------------------------------------------------
   11. BADGES
   -------------------------------------------------------------------------- */
.badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  padding: 1px 6px;
  border-radius: 99px;
  background: var(--gray-100);
  color: var(--gray-600);
  line-height: 1.6;
}

.badge--red {
  background: var(--red-bg);
  color: var(--red-text);
}

.badge--gray {
  background: var(--gray-200);
  color: var(--gray-800);
}

.badge--blue {
  background: var(--blue-light);
  color: var(--blue-dark);
}

.cond-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 500;
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--gray-100);
  color: var(--gray-600);
}

.cond-badge--nm {
  background: var(--green-bg);
  color: var(--green-text);
}

.cond-badge--lp {
  background: var(--lime-bg);
  color: var(--lime-text);
}

.cond-badge--mp {
  background: var(--yellow-bg);
  color: var(--yellow-text);
}

.cond-badge--hp {
  background: var(--orange-bg);
  color: var(--orange-text);
}

.cond-badge--dmg {
  background: var(--red-bg);
  color: var(--red-text);
}

/* Badge rendered as a <button> (editable rows): strip UA button chrome so it
   looks identical to the span badge; tap reveals the condition select. */
.cond-badge-btn {
  border: 0;
  cursor: pointer;
  font-family: inherit;
}

/* --------------------------------------------------------------------------
   12. FILTERS & VIEW TOGGLE
   -------------------------------------------------------------------------- */
.controls {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1rem;
  gap: 1rem;
}

.controls-left {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}

.filter-btn {
  padding: 0.3rem 0.7rem;
  font-size: 12px;
  font-family: var(--font);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--gray-600);
  cursor: pointer;
  transition: all 0.1s;
}

.filter-btn:hover {
  background: var(--gray-50);
  border-color: var(--gray-300);
}

.filter-btn:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

.filter-btn.active {
  background: var(--blue-light);
  color: var(--blue-dark);
  border-color: var(--blue);
}

.view-toggle {
  display: flex;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.view-toggle button {
  padding: 0.3rem 0.6rem;
  font-size: 12px;
  font-family: var(--font);
  border: none;
  background: var(--white);
  color: var(--gray-500);
  cursor: pointer;
  border-right: 1px solid var(--gray-200);
  transition: all 0.1s;
}

.view-toggle button:last-child {
  border-right: none;
}

.view-toggle button.active {
  background: var(--gray-900);
  color: var(--white);
}

.view-toggle button:hover:not(.active) {
  background: var(--gray-50);
}

.view-toggle button:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: -2px;
}

/* --------------------------------------------------------------------------
   13. CARD DETAIL
   -------------------------------------------------------------------------- */
.card-detail {
  display: grid;
  grid-template-columns: 300px 1fr;
  gap: 2rem;
}

.card-detail img {
  width: 100%;
  border-radius: 8px;
  display: block;
  border: 1px solid var(--gray-200);
}

.card-foil {
  position: relative;
  overflow: hidden;
  border-radius: 8px;
}

.card-foil img {
  border-radius: 0;
}

.card-foil::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    rgba(255, 40, 140, 0.30) 0%,
    rgba(0, 200, 255, 0.35) 20%,
    rgba(180, 100, 255, 0.30) 35%,
    rgba(255, 220, 0, 0.25) 50%,
    rgba(0, 255, 160, 0.35) 65%,
    rgba(80, 80, 255, 0.30) 80%,
    rgba(255, 40, 140, 0.30) 100%
  );
  mix-blend-mode: color;
  pointer-events: none;
  z-index: 1;
}

.card-foil::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    220deg,
    transparent 10%,
    rgba(255, 255, 255, 0.18) 25%,
    rgba(255, 255, 255, 0.40) 42%,
    rgba(255, 255, 255, 0.18) 58%,
    transparent 75%
  );
  pointer-events: none;
  z-index: 2;
}

.card-detail h2 {
  font-size: 20px;
  font-weight: 600;
  margin-bottom: 0.15rem;
}

.card-detail .type-line {
  font-size: 13px;
  color: var(--gray-500);
  margin-bottom: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--gray-200);
}

.card-detail .oracle {
  font-size: 14px;
  line-height: 1.65;
  margin-bottom: 1.25rem;
  color: var(--gray-700);
}

.detail-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
}

.detail-field {
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--gray-100);
  display: flex;
  gap: 1rem;
}

.detail-field:nth-child(odd) {
  padding-right: 1rem;
  border-right: 1px solid var(--gray-100);
}

.detail-field:nth-child(even) {
  padding-left: 1rem;
}

.detail-field .label {
  font-size: 12px;
  color: var(--gray-500);
  min-width: 80px;
}

.detail-field .val {
  font-weight: 500;
  font-size: 13px;
}

.detail-actions {
  display: flex;
  gap: 8px;
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--gray-200);
}

.detail-divider {
  border-top: 1px solid var(--gray-200);
  margin: 2rem 0 1.5rem;
  padding-top: 1.5rem;
}

/* Uppercase eyebrow label above a card-detail section (issue #258). */
.section-label {
  font-size: 11px;
  text-transform: uppercase;
  color: var(--gray-500);
  letter-spacing: 0.05em;
}

/* Flavor-text blockquote styling, shared by the two card-detail pages
   (issue #258). */
.flavor-text {
  font-style: italic;
  color: var(--gray-500);
  border-left: 3px solid var(--gray-200);
  padding-left: 0.75rem;
}

/* components/card_prices.html — one row per vendor (issue #252). Replaces
   19 inline style= attrs that were previously copy-pasted per vendor. */
.price-caption {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  color: var(--gray-400);
}

.price-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 1rem;
}

.price-row {
  display: flex;
  justify-content: space-between;
  font-size: 13px;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--gray-100);
}

.price-row-name {
  color: var(--gray-500);
}

.price-row-name a {
  color: inherit;
  text-decoration: none;
}

.price-row-name a:hover {
  text-decoration: underline;
}

.price-row-value {
  font-weight: 500;
}

.price-row-foil {
  color: var(--gray-400);
  font-weight: 400;
}

/* --------------------------------------------------------------------------
   14. INLINE STATS
   -------------------------------------------------------------------------- */
.inline-stats {
  font-size: 13px;
  color: var(--gray-500);
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--gray-200);
}

.inline-stats strong {
  color: var(--gray-900);
  font-weight: 600;
}

.inline-stats .sep {
  margin: 0 8px;
  color: var(--gray-300);
}

/* --------------------------------------------------------------------------
   15. PAGINATION
   -------------------------------------------------------------------------- */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 0;
  font-size: 13px;
  color: var(--gray-500);
  border-top: 1px solid var(--gray-200);
  margin-top: 0.5rem;
}

.pagination-info {
  font-size: 13px;
  color: var(--gray-500);
}

.pagination-pages {
  display: flex;
  gap: 2px;
}

.pagination-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  font-size: 12px;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  color: var(--gray-600);
  text-decoration: none;
  transition: all 0.1s;
}

.pagination-link:hover {
  background: var(--gray-50);
  text-decoration: none;
}

.pagination-link:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

.pagination-link.current {
  background: var(--gray-900);
  color: var(--on-accent);
  border-color: var(--gray-900);
}

.pagination-ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  font-size: 12px;
  padding: 0.3rem 0.4rem;
  color: var(--gray-400);
}

/* --------------------------------------------------------------------------
   16. EMPTY STATE
   -------------------------------------------------------------------------- */
.empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--gray-500);
  font-size: 14px;
}

.empty-state h2 {
  font-size: 18px;
  font-weight: 600;
  color: var(--gray-600);
  margin-bottom: 0.5rem;
}

.empty-state p {
  margin-bottom: 1.25rem;
  color: var(--gray-500);
}

/* --------------------------------------------------------------------------
   17. DJANGO MESSAGES
   -------------------------------------------------------------------------- */
.django-message {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.65rem 1rem;
  margin-bottom: 1rem;
  font-size: 13px;
  border-radius: var(--radius);
  border: 1px solid var(--gray-200);
  background: var(--gray-50);
  color: var(--gray-700);
}

.django-message--success {
  background: var(--green-bg);
  color: var(--green-text);
  border-color: var(--green-bg);
}

.django-message--error {
  background: var(--red-bg);
  color: var(--red-text);
  border-color: var(--red-bg);
}

.django-message--warning {
  background: var(--yellow-bg);
  color: var(--yellow-text);
  border-color: var(--yellow-bg);
}

.django-message .close-btn {
  position: relative;
  background: none;
  border: none;
  font-size: 16px;
  cursor: pointer;
  color: inherit;
  opacity: 0.6;
  padding: 0 0.25rem;
  line-height: 1;
}

.django-message .close-btn:hover {
  opacity: 1;
}

/* --------------------------------------------------------------------------
   18. LANDING PAGE
   -------------------------------------------------------------------------- */
.landing-hero {
  text-align: center;
  padding: 4rem 2rem 3rem;
  max-width: 600px;
  margin: 0 auto;
}

.landing-hero h1 {
  font-size: 28px;
  font-weight: 700;
  margin-bottom: 0.5rem;
}

.landing-hero p {
  font-size: 15px;
  color: var(--gray-500);
  margin-bottom: 1.5rem;
  line-height: 1.6;
}

.landing-search {
  display: flex;
  align-items: center;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
  max-width: 440px;
  margin: 0 auto 1.5rem;
}

.landing-search input {
  flex: 1;
  padding: 0.55rem 1rem;
  font-size: 14px;
  border: none;
  outline: none;
  font-family: var(--font);
  color: var(--gray-900);
}

.landing-search input::placeholder {
  color: var(--gray-400);
}

.landing-search button {
  padding: 0.55rem 1.25rem;
  font-size: 13px;
  font-weight: 500;
  font-family: var(--font);
  background: var(--blue);
  color: var(--on-accent);
  border: none;
  cursor: pointer;
  transition: background 0.1s;
}

.landing-search button:hover {
  background: var(--blue-dark);
}

.landing-actions {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Fanned strip of popular card art above the search box (#256). Decorative:
   a "hand of cards" read via slight per-card rotation + overlap. The hover
   lift is a transition, so the global reduced-motion rule already tames it. */
.landing-hero-strip {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  min-height: 200px;
  margin: 0 auto 2rem;
  padding-top: 0.5rem;
}

.landing-hero-card {
  position: relative;
  width: 94px;
  height: auto;
  aspect-ratio: 146 / 204;
  object-fit: cover;
  margin-inline: -13px;
  border-radius: var(--radius);
  border: 1px solid var(--gray-200);
  background: var(--gray-100);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
  transform-origin: bottom center;
  transition: transform 140ms ease, box-shadow 140ms ease;
}

.landing-hero-card:nth-child(1) { transform: rotate(-9deg) translateY(6px); }
.landing-hero-card:nth-child(2) { transform: rotate(-5deg) translateY(1px); }
.landing-hero-card:nth-child(3) { transform: rotate(-1.5deg); }
.landing-hero-card:nth-child(4) { transform: rotate(1.5deg); }
.landing-hero-card:nth-child(5) { transform: rotate(5deg) translateY(1px); }
.landing-hero-card:nth-child(6) { transform: rotate(9deg) translateY(6px); }

.landing-hero-card:hover {
  transform: translateY(-10px) rotate(0deg);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.16);
  z-index: 2;
}

.landing-features {
  list-style: none;
  margin: 1.75rem auto 0;
  padding: 0;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  text-align: left;
}

.landing-feature {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  font-size: 14px;
  color: var(--gray-600);
}

.landing-feature .icon {
  flex-shrink: 0;
  color: var(--blue);
}

@media (max-width: 768px) {
  .landing-hero-strip { min-height: 150px; }
  .landing-hero-card { width: 68px; margin-inline: -10px; }
}

/* Market blank-browse zero-state: set shortcuts + a random sample (#256). */
.market-discovery {
  margin-top: 1.5rem;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.market-discovery-heading {
  font-size: 14px;
  font-weight: 600;
  color: var(--gray-700);
  margin-bottom: 0.75rem;
}

.market-discovery-headrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.75rem;
}

.market-discovery-headrow .market-discovery-heading { margin-bottom: 0; }

.market-set-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.market-set-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.4rem 0.7rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: var(--gray-50);
  font-size: 13px;
  color: var(--gray-700);
  text-decoration: none;
  transition: border-color 120ms ease, background 120ms ease;
}

.market-set-chip:hover {
  border-color: var(--blue);
  background: var(--blue-light);
}

.market-set-chip-name { font-weight: 500; }

.market-set-chip-code {
  font-size: 11px;
  color: var(--gray-400);
  font-family: var(--mono, ui-monospace, monospace);
}

.market-sample-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 12px;
}

/* --------------------------------------------------------------------------
   20. UTILITIES
   -------------------------------------------------------------------------- */
.text-dim {
  color: var(--gray-500);
}

/* Screen-reader-only text — visually hidden, still exposed to assistive
   tech (issue #248's loading-bar status text needs this and field-guide.css
   is the one stylesheet loaded on every page; filter-bar.css keeps its own
   copy too since not every page loads it). */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

/* --------------------------------------------------------------------------
   21. FOCUS & KEYBOARD ACCESSIBILITY (global)
   -------------------------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* Remove default outline for mouse clicks */
:focus:not(:focus-visible) {
  outline: none;
}

/* --------------------------------------------------------------------------
   21b. TOUCH TARGETS (pointer: coarse)
   Bumps hit areas toward the ~44px mobile touch-target guideline on coarse
   pointers only, leaving the compact desktop/mouse density untouched
   (issue #244).
   -------------------------------------------------------------------------- */
@media (pointer: coarse) {
  .sidebar-link {
    padding-top: 0.85rem;
    padding-bottom: 0.85rem;
  }

  .pagination-link {
    padding-top: 0.85rem;
    padding-bottom: 0.85rem;
    min-width: 44px;
  }

  /* Invisible ::after hit-area expansion — keeps the visible close glyph
     compact while enlarging the tappable region. */
  .django-message .close-btn::after {
    content: "";
    position: absolute;
    inset: -14px;
  }

  /* The "/" keyboard shortcut hint has no meaning on a touch device — there
     is no physical "/" key to press (issue #261). */
  .search-shortcut {
    display: none;
  }
}

/* --------------------------------------------------------------------------
   X. DECK + COLLECTION CARDS (shared by My Decks, dashboard, etc.)
   .deck-* and .collection-* are visually identical card/grid primitives —
   merged via :is() (issue #258). Genuinely deck-only (star toggle, mosaic)
   or collection-only (summary panel) declarations stay separate below.
   -------------------------------------------------------------------------- */
:is(.deck-grid, .collection-grid) {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 18px;
  margin: 8px 0 24px;
}

:is(.deck-card, .collection-card) {
  display: block;
  text-decoration: none;
  color: inherit;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 120ms ease, box-shadow 120ms ease;
}
:is(.deck-card, .collection-card):hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

/* Favorite (star) toggle overlaid on a deck card. It's a sibling of the card
   anchor (a button can't nest in an <a>); .deck-card-wrap anchors its position.
   Always visible so it's tappable on touch. */
.deck-card-wrap { position: relative; }
.deck-star {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease, transform 120ms ease;
}
.deck-star:hover { background: rgba(0, 0, 0, 0.6); transform: scale(1.05); }
.deck-star:focus-visible { outline: 2px solid var(--blue, #2563eb); outline-offset: 2px; }
.deck-star .icon { width: 18px; height: 18px; }
/* Active = filled gold star; the icon's fill="none" presentation attr loses to this CSS. */
.deck-star.is-active { color: #f5c518; }
.deck-star.is-active .icon { fill: #f5c518; }

/* Clone action overlaid on a public-browse deck card — same sibling-of-anchor
   technique as .deck-star (a <form>/<button> can't nest in the card's <a>).
   Bottom-right so it never collides with a top-right star (the two never
   render on the same card today, but keep them non-overlapping regardless). */
.deck-card-clone {
  position: absolute;
  bottom: 8px;
  right: 8px;
  z-index: 2;
  margin: 0;
}
.deck-card-clone-btn {
  padding: 4px 12px;
  font-size: 12px;
  font-weight: 600;
  border: none;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  cursor: pointer;
  transition: background 120ms ease;
}
.deck-card-clone-btn:hover { background: rgba(0, 0, 0, 0.72); }
.deck-card-clone-btn:focus-visible { outline: 2px solid var(--blue, #2563eb); outline-offset: 2px; }

.deck-mosaic {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  aspect-ratio: 1;
  gap: 1px;
  background: var(--gray-200);
}
.deck-mosaic-tile {
  overflow: hidden;
  background: var(--gray-100);
}
.deck-mosaic-tile img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.deck-mosaic-tile--empty { background: var(--gray-100); }
.deck-mosaic-placeholder {
  grid-column: 1 / -1; grid-row: 1 / -1;
  display: flex; align-items: center; justify-content: center;
  font-size: 64px; font-weight: 300;
  color: var(--gray-400);
  background: var(--gray-100);
}
.deck-mosaic--composite { display: block; }
.deck-mosaic--composite img {
  display: block; width: 100%; height: 100%; object-fit: cover;
}

:is(.deck-card-meta, .collection-card-meta) { padding: 12px 14px; }
:is(.deck-card-name-row, .collection-card-name-row) {
  display: flex; align-items: baseline; gap: 8px;
  justify-content: space-between;
}
:is(.deck-card-name, .collection-card-name) {
  margin: 0;
  font-size: 16px; font-weight: 600; color: var(--gray-900);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
:is(.deck-card-sub, .collection-card-sub) {
  margin: 4px 0 0;
  font-size: 13px; color: var(--gray-500);
}
/* Author byline — public browse only (My Decks omits it; every card there
   is already yours or a collaborator's). */
.deck-card-owner {
  margin: 2px 0 0;
  font-size: 12px; color: var(--gray-500);
}

:is(.format-chip, .game-chip) {
  display: inline-block;
  padding: 2px 8px;
  font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.4px;
  border-radius: 999px;
  background: var(--gray-100);
  color: var(--gray-700);
  flex-shrink: 0;
}
.format-chip--pauper    { background: var(--blue-light); color: var(--blue-dark); }
.format-chip--commander { background: var(--yellow-bg);  color: var(--yellow-text); }

/* --------------------------------------------------------------------------
   4b-pre. COLLECTION CARDS + DASHBOARD
   -------------------------------------------------------------------------- */
.collection-summary {
  aspect-ratio: 1;
  background: var(--gray-50);
  padding: 18px 22px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 10px;
}
.collection-set-row {
  display: grid;
  grid-template-columns: 24px 1fr auto;
  align-items: center;
  gap: 12px;
  font-size: 14px;
  color: var(--gray-700);
}
.collection-set-row .ss { font-size: 22px; line-height: 1; }
.collection-set-row .set-code {
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--gray-900);
}
.collection-set-row .set-count {
  font-variant-numeric: tabular-nums;
  color: var(--gray-500);
  font-size: 13px;
}
.collection-set-row--empty {
  color: var(--gray-300);
  text-align: center;
  grid-template-columns: 1fr;
}
.collection-summary-placeholder {
  flex: 1;
  display: flex; align-items: center; justify-content: center;
  font-size: 64px; font-weight: 300;
  color: var(--gray-400);
}

.dashboard-section { margin-bottom: 28px; }
.dashboard-section-header {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 4px 0 12px;
}
.dashboard-section-header h2 {
  margin: 0; font-size: 18px; font-weight: 600;
}
.dashboard-view-all {
  font-size: 13px; color: var(--gray-500); text-decoration: none;
}
.dashboard-view-all:hover { color: var(--gray-900); }
.dashboard-empty {
  grid-column: 1 / -1;
  padding: 32px; text-align: center;
  border: 1px dashed var(--gray-300); border-radius: var(--radius);
  color: var(--gray-500);
}
.dashboard-empty .btn {
  display: inline-block; margin-top: 10px;
  padding: 8px 18px; background: var(--blue); color: var(--on-accent);
  text-decoration: none; border-radius: var(--radius);
}

/* --------------------------------------------------------------------------
   4b. SIDEBAR — COLLAPSED RAIL (desktop only)
   -------------------------------------------------------------------------- */
@media (min-width: 769px) {
  html.sidebar-collapsed .layout {
    grid-template-columns: var(--sidebar-width-collapsed) 1fr;
  }
  /* Keep labels in the DOM + accessibility tree but visually hide them.
     Screen readers still announce each link's name when collapsed. */
  html.sidebar-collapsed .sidebar-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  html.sidebar-collapsed .sidebar-footer > span {
    display: none;
  }
  html.sidebar-collapsed .sidebar-link {
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
  }
  html.sidebar-collapsed .sidebar-section-label {
    font-size: 0;
    height: 1px;
    padding: 0;
    margin: 8px 12px;
    background: var(--gray-200);
    color: transparent;
  }
  html.sidebar-collapsed .sidebar-header {
    justify-content: center;
    padding-left: 0;
    padding-right: 0;
  }
  html.sidebar-collapsed .sidebar-pin .icon { transform: rotate(180deg); }

  /* Unread badge shrinks to a corner dot when collapsed */
  html.sidebar-collapsed .sidebar-link .badge {
    position: absolute;
    top: 4px;
    right: 8px;
    min-width: 8px;
    height: 8px;
    padding: 0;
    font-size: 0;
    border-radius: 50%;
    margin-left: 0;
  }

  /* CSS-only tooltip — only active while collapsed */
  html.sidebar-collapsed .sidebar-link[data-tooltip]:hover::after,
  html.sidebar-collapsed .sidebar-link[data-tooltip]:focus-visible::after,
  html.sidebar-collapsed .sidebar-pin[data-tooltip]:hover::after,
  html.sidebar-collapsed .sidebar-pin[data-tooltip]:focus-visible::after {
    content: attr(data-tooltip);
    position: absolute;
    left: calc(100% + 8px);
    top: 50%;
    transform: translateY(-50%);
    /* Hardcoded dark-pill colors: var(--gray-900) flips to near-white in dark mode. */
    background: #1e293b;
    color: #f8fafc;
    padding: 4px 8px;
    border-radius: var(--radius);
    font-size: 12px;
    white-space: nowrap;
    z-index: 300;
    pointer-events: none;
  }

  .layout { transition: grid-template-columns 0.18s; }
}

/* --------------------------------------------------------------------------
   22. MOBILE (max-width: 768px)
   -------------------------------------------------------------------------- */
@media (max-width: 768px) {
  /* Reset layout to single column */
  .layout {
    grid-template-columns: 1fr;
  }

  /* On mobile, ignore desktop collapsed state — drawer always shows full sidebar */
  html.sidebar-collapsed .layout {
    grid-template-columns: 1fr;
  }
  html.sidebar-collapsed .sidebar-label,
  html.sidebar-collapsed .sidebar-section-label,
  html.sidebar-collapsed .sidebar-header .sidebar-brand .sidebar-label,
  html.sidebar-collapsed .sidebar-footer > span {
    display: revert;
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0;
    overflow: visible;
    clip: auto;
    white-space: normal;
  }
  html.sidebar-collapsed .sidebar-section-label {
    font-size: 11px;
    height: auto;
    background: transparent;
    color: var(--gray-500);
    padding: 0.5rem 1.25rem 0.25rem;
    margin: 0;
  }
  html.sidebar-collapsed .sidebar-link {
    justify-content: flex-start;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
  }
  html.sidebar-collapsed .sidebar-pin { display: none; }

  /* Sidebar: off-screen by default, slides in with .open */
  .sidebar {
    position: fixed;
    top: 0;
    left: calc(-1 * var(--sidebar-width));
    width: var(--sidebar-width);
    height: 100vh;
    z-index: 200;
    transition: left 0.2s;
  }

  .sidebar.open {
    left: 0;
  }

  /* Show hamburger toggle */
  .sidebar-toggle {
    display: flex;
  }

  /* Drawer mode: swap desktop collapse-pin for a close button */
  .sidebar-pin { display: none; }
  .sidebar-close { display: inline-flex; align-items: center; justify-content: center; }

  /* Dark overlay behind sidebar */
  .sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.3);
    z-index: 150;
  }

  .sidebar.open ~ .sidebar-overlay,
  .sidebar-overlay.active {
    display: block;
  }

  /* Topbar adjustments */
  .topbar {
    padding: 0.75rem 1rem;
    padding-left: 3.5rem; /* room for hamburger */
    /* Let the breadcrumb keep a full row of its own so it has room to show
       on one line; the search wraps onto the next row instead of crushing
       the trail into a sliver. */
    flex-wrap: wrap;
  }

  .topbar .breadcrumb {
    flex-basis: 100%;
  }

  .topbar-search {
    width: 100%;
    max-width: 280px;
  }

  /* Content padding */
  .content {
    padding: 1rem;
  }

  /* Table scroll on mobile */
  .table-scroll {
    margin: 0 -1rem;
    padding: 0 1rem;
  }

  /* Card detail stacks vertically */
  .card-detail {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }

  /* Cap the foil wrapper together with the img: its ::before/::after
     overlay layers size to the wrapper (inset: 0), so a full-width
     wrapper paints the foil sheen past the card onto the page. */
  .card-detail img,
  .card-detail .card-foil {
    max-width: 300px;
  }

  /* Detail fields single column */
  .detail-fields {
    grid-template-columns: 1fr;
  }

  .detail-field:nth-child(odd) {
    padding-right: 0;
    border-right: none;
  }

  .detail-field:nth-child(even) {
    padding-left: 0;
  }

  /* Controls stack on small screens */
  .controls {
    flex-direction: column;
    align-items: flex-start;
  }

  /* Pagination stacks */
  .pagination {
    flex-direction: column;
    gap: 0.5rem;
    align-items: center;
  }

  /* Card grid: smaller columns */
  .card-grid {
    grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
    gap: 8px;
  }

  /* Narrow form full width on mobile */
  .narrow-form {
    max-width: 100%;
  }
}

/* --------------------------------------------------------------------------
   Deck cover picker
   -------------------------------------------------------------------------- */
.cover-picker { margin-top: 1.5rem; max-width: 720px; }
.cover-picker__header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 0.75rem;
}
.cover-picker__title { font-size: 16px; font-weight: 600; margin: 0; }
.cover-picker__meta { display: flex; align-items: center; gap: 0.75rem; }
.cover-picker__counter { font-size: 12px; color: var(--gray-500); }
.cover-picker__reset {
  background: transparent; border: 1px solid var(--gray-300);
  border-radius: var(--radius); padding: 4px 10px; font-size: 12px;
  cursor: pointer; color: var(--gray-700);
}
.cover-picker__reset:hover { background: var(--gray-100); }
.cover-picker__hint {
  font-size: 12px; color: var(--amber); margin: 0.25rem 0 0.75rem;
}
.cover-picker__preview {
  width: 240px; height: 240px; margin-bottom: 1rem;
  border: 1px solid var(--gray-200); border-radius: var(--radius);
  overflow: hidden;
}
.cover-picker__auto-label {
  position: absolute; left: 4px; top: 2px;
  font-size: 10px; color: var(--white); background: rgba(0, 0, 0, 0.5);
  padding: 1px 4px; border-radius: 3px;
}
.cover-picker__board {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--gray-500);
  margin: 1rem 0 0.5rem;
}
.cover-picker__gallery {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
  gap: 4px;
  max-height: 260px; overflow-y: auto;
  padding: 4px; margin: 0 -4px;
  border: 1px solid var(--gray-200); border-radius: var(--radius);
}
.cover-picker__card {
  position: relative; padding: 0; border: 1px solid var(--gray-200);
  border-radius: var(--radius); background: var(--white);
  cursor: pointer; overflow: hidden; text-align: left;
}
.cover-picker__card:hover { border-color: var(--blue); }
.cover-picker__card--picked { border-color: var(--blue); box-shadow: 0 0 0 2px var(--blue-light); }
.cover-picker__card img { display: block; width: 100%; height: 40px; object-fit: cover; }
.cover-picker__name {
  display: block; padding: 2px 4px;
  font-size: 10px; color: var(--gray-700); line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cover-picker__badge {
  position: absolute; top: 2px; right: 2px;
  width: 16px; height: 16px;
  background: var(--blue); color: var(--on-accent);
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 600;
  z-index: 1;
}
.cover-picker__intro {
  margin: 0 0 0.5rem;
  font-size: 12px;
  color: var(--gray-600);
}
.cover-picker__slot-wrap {
  position: relative;
  background: var(--gray-100);
  overflow: hidden;
  border-radius: var(--radius-sm);
}
.cover-picker__slot {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--radius-sm);
  transition: opacity 0.15s ease;
}
.cover-picker__slot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.cover-picker__preview:not(.cover-picker__preview--has-armed) .cover-picker__slot--auto img {
  opacity: 0.55;
}
.cover-picker__preview--has-armed .cover-picker__slot:not(.is-armed) {
  opacity: 0.35;
}
.cover-picker__slot.is-armed {
  box-shadow: inset 0 0 0 4px var(--blue);
}
.cover-picker__clear {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 28px;
  height: 28px;
  padding: 0;
  border-radius: 50%;
  border: 2px solid var(--white, #fff);
  background: rgba(15, 23, 42, 0.92);
  color: #fff;
  font-size: 18px;
  font-weight: 700;
  line-height: 1;
  text-align: center;
  cursor: pointer;
  z-index: 2;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
}
.cover-picker__clear:hover {
  background: var(--red, #ef4444);
}
.cover-picker__clear:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}
.cover-picker__hint--transient {
  background: var(--blue-light);
  border: 1px solid var(--blue);
  color: var(--gray-900);
  border-radius: var(--radius);
  padding: 6px 10px;
  font-size: 13px;
  margin: 0 0 0.5rem;
}

/* --------------------------------------------------------------------------
   Toast (issue #259)
   Global feedback for htmx errors + non-error notices (deck-card skip/
   remove-undo, brainstorm checkout). Positioning/shadow/z-index are
   theme-agnostic; .toast--error / .toast--info supply the token color pair
   so the JS only ever toggles classes, never touches inline style.
   -------------------------------------------------------------------------- */
.toast {
  display: none;
  position: fixed;
  bottom: 20px;
  left: 50%;
  transform: translateX(-50%);
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 14px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .25);
  z-index: 1000;
  max-width: min(420px, calc(100vw - 2rem));
}

.toast.is-visible {
  display: block;
}

.toast--error {
  background: var(--red-bg);
  color: var(--red-text);
}

.toast--info {
  background: var(--gray-700);
  color: var(--white);
}

/* The optional Undo action (issue #167) inherits the toast's own text
   color, which is already a verified-readable pair against that toast's
   background in both variants and both themes; var(--blue) is not (it
   fails contrast against --gray-700 in light mode and against --red-bg /
   --gray-700 in dark mode — checked against both toast--error and
   toast--info in both themes). Opacity + underline supply the "it's a
   button" affordance instead. */
.toast-action {
  margin-left: 12px;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
  text-decoration: underline;
  padding: 0;
  opacity: .85;
}

.toast-action:hover {
  opacity: 1;
}

/* --------------------------------------------------------------------------
   24. REDUCED MOTION (issue #244, WCAG 2.3.3)
   Applies site-wide, across every stylesheet: collapses CSS animations/
   transitions to effectively instant for users who've asked the OS for
   reduced motion. The JS-driven fly-to-cart clone (brainstorm.js) isn't a
   CSS animation, so it has its own matchMedia guard.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Move-quantity slider (#314 Phase B) — the split interstitial. Compact inline
   in a collection-row cell; a proper modal on the card-detail page. */
.move-slider__title { font-size: 16px; font-weight: 600; margin: 0 0 4px; }
.move-slider__hint { font-size: 12px; color: var(--gray-500); margin: 0 0 16px; }
.move-slider__control { display: flex; align-items: center; gap: 12px; }
.move-slider__range { flex: 1; min-width: 120px; accent-color: var(--blue); }
.move-slider__count { font-size: 13px; white-space: nowrap; }
.move-slider--modal .move-slider__count { font-size: 20px; }
.move-slider--modal .move-slider__control { margin-bottom: 18px; }
.move-slider__actions { display: flex; gap: 8px; align-items: center; margin-top: 6px; }
.move-slider--modal .move-slider__actions { justify-content: flex-end; margin-top: 0; }

/* Pull-confirm ("move to deck X?") — the same modal treatment on detail. */
.pull-confirm--modal .pull-confirm__q { font-size: 14px; margin: 0 0 16px; }
.pull-confirm--modal .pull-confirm__actions { display: flex; gap: 8px; justify-content: flex-end; }

/* Switch — visual-only restyle for a LONE boolean checkbox (spec 2026-07-19).
   Apply class="switch" directly to the <input type="checkbox">; markup,
   native semantics (keyboard, form posting, htmx change triggers) and the
   accessible checkbox role stay untouched. Checkbox GROUPS (tags, filters,
   column lists) deliberately keep the plain checkbox idiom. */
input[type="checkbox"].switch {
  appearance: none;
  -webkit-appearance: none;
  flex: none;
  width: 36px;
  height: 20px;
  margin: 0;
  position: relative;
  border: 1px solid var(--gray-300);
  border-radius: 999px;
  background: var(--gray-200);
  cursor: pointer;
  vertical-align: middle;
  transition: background-color 150ms ease, border-color 150ms ease;
}
input[type="checkbox"].switch::before {
  content: "";
  position: absolute;
  top: 1px;
  left: 1px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--gray-50);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
  transition: transform 150ms ease, background-color 150ms ease;
}
/* Dark theme: the flipped grays leave an unchecked thumb nearly invisible on
   its track — lift it to the mid-gray. Scoped to :not(:checked) so it can
   never fight the checked thumb color, regardless of rule order. */
[data-theme="dark"] input[type="checkbox"].switch:not(:checked)::before {
  background: var(--gray-400);
}
input[type="checkbox"].switch:checked {
  background: var(--blue);
  border-color: transparent;
}
input[type="checkbox"].switch:checked::before {
  transform: translateX(16px);
  background: var(--on-accent);
}
input[type="checkbox"].switch:hover:not(:checked):not(:disabled) {
  border-color: var(--gray-400);
}
input[type="checkbox"].switch:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}
input[type="checkbox"].switch:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
@media (prefers-reduced-motion: reduce) {
  input[type="checkbox"].switch,
  input[type="checkbox"].switch::before {
    transition: none;
  }
}
