:root {
  color-scheme: dark light;
  --bg: #0c0e14;
  --bg-elev: #161922;
  --bg-card: #1d2130;
  --fg: #e9ecf1;
  --fg-muted: #8b93a7;
  --accent: #5cdb95;
  --accent-soft: #5cdb9533;
  --warn: #f5b14a;
  --border: #262b3a;
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.04), 0 8px 24px rgba(0, 0, 0, 0.35);
  --radius: 12px;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f8fb;
    --bg-elev: #ffffff;
    --bg-card: #ffffff;
    --fg: #111827;
    --fg-muted: #5b6478;
    --border: #e5e7ef;
    --accent-soft: #5cdb951a;
    --shadow: 0 1px 0 rgba(0, 0, 0, 0.02), 0 8px 24px rgba(15, 23, 42, 0.06);
  }
}

/* Manual theme override via brand-icon click (highest precedence) */
:root[data-theme='dark'] {
  color-scheme: dark;
  --bg: #0c0e14;
  --bg-elev: #161922;
  --bg-card: #1d2130;
  --fg: #e9ecf1;
  --fg-muted: #8b93a7;
  --accent: #5cdb95;
  --accent-soft: #5cdb9533;
  --warn: #f5b14a;
  --border: #262b3a;
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.04), 0 8px 24px rgba(0, 0, 0, 0.35);
}

:root[data-theme='light'] {
  color-scheme: light;
  --bg: #f7f8fb;
  --bg-elev: #ffffff;
  --bg-card: #ffffff;
  --fg: #111827;
  --fg-muted: #5b6478;
  --border: #e5e7ef;
  --accent-soft: #5cdb951a;
  --shadow: 0 1px 0 rgba(0, 0, 0, 0.02), 0 8px 24px rgba(15, 23, 42, 0.06);
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  font-family:
    ui-sans-serif,
    system-ui,
    -apple-system,
    'Segoe UI',
    Roboto,
    Inter,
    sans-serif;
  background: var(--bg);
  color: var(--fg);
  line-height: 1.5;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.topbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 16px;
  padding: 12px 20px;
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  justify-self: start;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  cursor: pointer;
}

.brand-icon {
  width: 56px;
  height: 32px;
  display: block;
  flex-shrink: 0;
}

.brand-word,
.brand-name {
  font-weight: 700;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.last-updated {
  color: var(--fg-muted);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.ticker {
  font-variant-numeric: tabular-nums;
  display: flex;
  gap: 8px;
  align-items: baseline;
  justify-self: end;
}

/* Symbol + price form a single inline block; the meta column (status +
   timestamp) sits beside it. The wrapper exists so on narrow phones we
   can stack `quote` and `meta` vertically instead of cramming all four
   pieces onto one flex row that wraps mid-word ("FRI 4:00 / PM"). */
.ticker-quote {
  display: flex;
  gap: 8px;
  align-items: baseline;
}

.ticker-symbol {
  color: var(--fg-muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.ticker-price {
  font-size: 20px;
  font-weight: 700;
}

/* Status + price-feed timestamp stack vertically to the right of the price.
   align-items: baseline on .ticker aligns the first line of this column
   with the price baseline, so the layout reads as "$EBAY $21.18 STATUS /
   TIME" without the meta column pushing the row taller. Empty children
   (e.g. an empty status when the market is open) collapse so the live
   case shows just the clock. */
.ticker-meta {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  text-align: left;
}

.ticker-status,
.ticker-as-of {
  display: block;
  /* Don't break "MARKET CLOSED" into "MARKET / CLOSED" or "FRI 4:00 PM"
     into "FRI 4:00 / PM" if the column happens to be narrow — better to
     overflow predictably (which won't happen at our current widths since
     the mobile rule below moves meta to its own row). */
  white-space: nowrap;
}
.ticker-status:empty,
.ticker-as-of:empty {
  display: none;
}

/* Stale = "no fresh snapshot in 2+ minutes". The warning attaches to the
   meta wrapper rather than ticker-status so it still shows in the
   market-open case when status is empty. */
.ticker.is-stale .ticker-meta::before {
  content: '⚠ ';
  color: var(--warn);
}

@media (max-width: 720px) {
  /* Keep the left / center / right structure intact on portrait — the
     previous "brand | ticker / updated full-width" rearrangement was
     visually disorganised. Each cell stacks its contents vertically
     instead, so a narrow screen reads as three short columns. */
  .topbar {
    grid-template-columns: auto 1fr auto;
    grid-template-areas: 'brand updated ticker';
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
  }

  /* Brand: Half Cash / icon / Half Stock vertical. Smaller icon so the
     stacked-column height stays reasonable. */
  .brand {
    grid-area: brand;
    flex-direction: column;
    gap: 2px;
    align-items: center;
  }
  .brand-word { font-size: 12px; line-height: 1.1; }
  .brand-icon { width: 36px; height: 21px; }

  /* Updated: three-line stack. Spans were rendered with text nodes
     between them so display:block here gives one line per span; desktop
     keeps them inline. */
  .last-updated {
    grid-area: updated;
    text-align: center;
    line-height: 1.25;
  }
  .last-updated > span {
    display: block;
  }

  /* Ticker: keep "$GME $21.18" as the inline headline; meta stays in its
     existing vertical-column form below. Discard the temporary horizontal
     meta + bullet rule from the previous attempt. */
  .ticker {
    grid-area: ticker;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
  }
  .ticker-price { font-size: 17px; }
  .ticker-meta { flex-direction: column; }
}

main {
  flex: 1;
  max-width: 1100px;
  width: 100%;
  margin: 0 auto;
  padding: 24px 20px 40px;
}

.degraded-banner {
  margin: 0 0 16px;
  padding: 8px 14px;
  border-radius: var(--radius);
  border: 1px solid var(--warn, #d9a441);
  background: rgba(217, 164, 65, 0.12);
  color: var(--warn, #d9a441);
  font-size: 13px;
  font-weight: 500;
}

.intro {
  color: var(--fg-muted);
  margin: 0 0 20px;
}

.intro > summary {
  cursor: pointer;
  list-style: none;
  color: var(--fg);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.4;
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.intro > summary::-webkit-details-marker {
  display: none;
}

.intro > summary::after {
  content: 'About ▸';
  color: var(--fg-muted);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.intro[open] > summary::after {
  content: 'About ▾';
}

.intro-body {
  margin-top: 10px;
}

.intro-body p {
  margin: 0 0 10px;
}

.intro-body p:last-child {
  margin-bottom: 0;
}

.intro a {
  color: var(--fg);
}

.totals-heading {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--fg-muted);
  margin: 0 0 8px;
}

.totals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-bottom: 24px;
}

.totals .stat {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow);
}

.totals .stat-label {
  color: var(--fg-muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.totals .stat-value {
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

.most-recent-bid {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  box-shadow: var(--shadow);
  margin-bottom: 24px;
  font-size: 13px;
}

.most-recent-bid-empty {
  color: var(--fg-muted);
}

.most-recent-bid-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

.most-recent-bid-info {
  flex: 1;
  min-width: 0;
}

.most-recent-bid-thumb {
  width: 52px;
  height: 52px;
  border-radius: 8px;
  border: 1px solid var(--border);
  object-fit: cover;
  flex-shrink: 0;
  background: var(--bg-elev);
}

.most-recent-bid-thumb-placeholder {
  background: var(--bg-elev);
}

.most-recent-bid-label {
  color: var(--fg-muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  display: block;
  margin-bottom: 2px;
}

.most-recent-bid-title,
.most-recent-bid-title-link {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--fg);
  text-decoration: none;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.most-recent-bid-title-link:hover {
  text-decoration: underline;
}

.most-recent-bid-amount {
  font-size: 18px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
}

.most-recent-bid-time {
  color: var(--fg-muted);
  font-size: 12px;
  margin-top: 4px;
  cursor: help;
}

.most-recent-bid-link {
  color: var(--accent);
  text-decoration: none;
  padding: 6px 12px;
  border: 1px solid var(--accent);
  border-radius: 4px;
  font-size: 12px;
  cursor: pointer;
  transition: background 0.2s;
}

.most-recent-bid-link:hover {
  background: var(--accent-soft);
}

.items {
  display: grid;
  gap: 16px;
  /* Fallback for first paint before the view-mode class is applied. */
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}

.items.view-grid-sm {
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
}

.items.view-grid-md {
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}

.items.view-grid-lg {
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

/* Smaller cards get a shorter image so the whole card fits the fold, and a
   slightly smaller price so the price + bids/time line stays on one row. */
.items.view-grid-sm .item-image,
.items.view-grid-sm .item-gallery {
  aspect-ratio: 3 / 2;
}
.items.view-grid-sm .item-bid {
  font-size: 18px;
}

/* List mode: full-width compact rows — small square thumbnail beside the
   body, mirroring the most-recent-bid card. */
.items.view-list {
  grid-template-columns: 1fr;
  gap: 10px;
}

/* List mode: one full-width row per item laid out as a grid —
   [ image | title+meta | price / cash+stock split ].
   The card body uses display:contents so the title, meta, price line, and
   split become direct grid items of the card and can be placed into areas.
   Row 1: title (left) + price (right). Row 2: meta (left) + split (right). */
.items.view-list .item {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-areas:
    'img title price'
    'img meta  split';
  align-items: center;
  column-gap: 16px;
  row-gap: 4px;
  padding: 10px 16px 10px 0;
}

.items.view-list .item-body {
  display: contents;
}

.items.view-list .item-image,
.items.view-list .item-gallery {
  grid-area: img;
  width: 72px;
  min-width: 72px;
  height: 72px;
  aspect-ratio: 1;
  align-self: center;
  border-radius: 8px;
  margin-left: 12px;
  overflow: hidden;
}
/* List rows are too small for a carousel to be useful — hide the nav UI;
   the underlying scroll still works on touch if anyone wants to flip. */
.items.view-list .gallery-nav,
.items.view-list .gallery-dots {
  display: none;
}

.items.view-list .item-title {
  grid-area: title;
  align-self: end;
}

.items.view-list .item-meta {
  grid-area: meta;
  align-self: start;
}

/* Price block sits top-right. In list mode there's enough horizontal room
   to collapse the bid-row back onto one line: big price + stats inline +
   chart icon at the far right. display:contents on .item-bid-price flattens
   the price wrapper so price and icon become direct flex children of the
   row, letting us reorder via `order` (price → stats → icon). */
.items.view-list .item-bid-row {
  grid-area: price;
  justify-self: end;
  align-self: center;
  flex-direction: row;
  align-items: baseline;
  column-gap: 12px;
}

.items.view-list .item-bid-price {
  display: contents;
}

/* Visual order in list-mode is [stats · price · chart], not the source order
   (price · audit · stats — price first because .item-bid-price wraps it, audit
   second because it lives inside that wrapper, stats third). Stats sits to the
   left of the price so the eye reads price→split as one column on the right. */
.items.view-list .item-bid-row .item-bid-stats {
  order: 0;
}

.items.view-list .item-bid-row .item-bid {
  order: 1;
}

.items.view-list .item-bid-row .item-audit-link {
  order: 2;
}

/* Cash/stock split sits at the right of the list row, stacked label→value
   per half (same markup as grid mode). */
.items.view-list .item-split {
  grid-area: split;
  justify-self: end;
  align-self: start;
}

/* --- Phone portrait --- */
@media (max-width: 600px) {
  /* The largest grid is redundant on a phone: both grid-md (210px min) and
     grid-lg (300px min) collapse to a single column at this width, so they
     render identically. Drop the grid-lg toggle — list / 2-up / 1-up are the
     three distinct, useful densities here. */
  .view-btn[data-view='grid-lg'] {
    display: none;
  }

  /* List mode at desktop is a 3-column row [img | title+meta | price+split],
     which crams on a phone. Restack to [thumb | everything else], with the
     right column flowing title → meta → price → split top-to-bottom. */
  .items.view-list .item {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      'img title'
      'img meta'
      'img price'
      'img split';
    align-items: start;
    column-gap: 12px;
    row-gap: 6px;
    padding: 10px 14px 12px 0;
  }
  .items.view-list .item-image,
  .items.view-list .item-gallery {
    align-self: start;
    margin-top: 2px;
  }
  /* Undo the desktop right-alignment so the stacked right column reads
     left-aligned like a normal mobile list item. */
  .items.view-list .item-title,
  .items.view-list .item-bid-row,
  .items.view-list .item-split {
    justify-self: start;
    align-self: start;
  }
  .items.view-list .item-bid-row {
    align-items: baseline;
  }
}

.item {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
}

.item-image {
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--bg-elev);
  object-fit: cover;
  display: block;
}

/* Carousel: a horizontal scroll-snap track holding one img per gallery
   entry. Touch users swipe; pointer users get prev/next buttons + dot
   indicators that update on scroll via the existing IntersectionObserver
   wiring in app.js. Falls back gracefully to "primary image only" when
   additionalImages is empty (the buttons + dots hide). */
.item-gallery {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  background: var(--bg-elev);
  /* Clip the track to the 4/3 box. Belt to the absolute-positioned track
     below: even if a browser resolves the track height oddly, nothing
     spills past the gallery frame. */
  overflow: hidden;
}
.gallery-track {
  /* Fill the aspect-ratio box exactly. Using absolute inset:0 (rather than
     height:100%) so the track's height is always definite — height:100%
     against an aspect-ratio parent doesn't resolve reliably across browsers,
     which let portrait images render at their intrinsic (taller) height and
     made some tiles' pictures taller than others. */
  position: absolute;
  inset: 0;
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
.gallery-track::-webkit-scrollbar {
  display: none;
}
.gallery-track img {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  object-fit: cover;
  scroll-snap-align: start;
  /* Force the scroll to stop at each slide so a fast swipe can't fly past
     several photos at once — one swipe advances exactly one image. */
  scroll-snap-stop: always;
}
/* Prev/next buttons sit over the image edges. On touch devices they're
   always visible (you can't hover); on pointer devices they fade in on
   card hover. The :hover-fade scoped to (hover: hover) prevents the
   classic "first tap activates :hover, second tap clicks" mobile trap. */
.gallery-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: white;
  font-size: 16px;
  line-height: 1;
  cursor: pointer;
  opacity: 1;
  transition: opacity 0.15s, background 0.15s;
  display: grid;
  place-items: center;
  padding: 0;
}
.gallery-nav.gallery-prev { left: 6px; }
.gallery-nav.gallery-next { right: 6px; }
.gallery-nav:hover {
  background: rgba(0, 0, 0, 0.65);
}
@media (hover: hover) {
  .gallery-nav {
    opacity: 0;
  }
  .item:hover .gallery-nav,
  .gallery-nav:focus-visible {
    opacity: 1;
  }
}
.item-gallery.single-image .gallery-nav {
  display: none;
}

/* Compact "i / N" indicator used in place of dots when there are too many
   images for the dots to fit. Same bottom-centre position, fixed footprint. */
.gallery-counter {
  position: absolute;
  bottom: 6px;
  left: 50%;
  transform: translateX(-50%);
  padding: 1px 6px;
  border-radius: 8px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 10px;
  line-height: 14px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.item-gallery.single-image .gallery-counter {
  display: none;
}

/* --- Lightbox ---
   Fullscreen image carousel for the click-to-zoom flow. Same scroll-snap
   mechanics as the inline gallery (so touch swipe works for free) but
   sized to the viewport with object-fit: contain so the full uploaded
   image is visible, not cropped. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, 0.88);
  display: grid;
  place-items: center;
}
.lightbox[hidden] { display: none; }
.lightbox-track {
  width: 100vw;
  height: 100vh;
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
.lightbox-track::-webkit-scrollbar { display: none; }
.lightbox-track img {
  flex: 0 0 100%;
  width: 100%;
  height: 100%;
  object-fit: contain;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  user-select: none;
  -webkit-user-drag: none;
}
.lightbox-close,
.lightbox-nav {
  position: absolute;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  border: 0;
  cursor: pointer;
  padding: 0;
  display: grid;
  place-items: center;
  border-radius: 50%;
  transition: background 0.15s;
}
.lightbox-close:hover,
.lightbox-nav:hover {
  background: rgba(0, 0, 0, 0.85);
}
.lightbox-close {
  top: max(16px, env(safe-area-inset-top));
  right: max(16px, env(safe-area-inset-right));
  width: 40px;
  height: 40px;
  font-size: 22px;
  line-height: 1;
}
.lightbox-nav {
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  font-size: 28px;
  line-height: 1;
}
.lightbox-prev { left: max(12px, env(safe-area-inset-left)); }
.lightbox-next { right: max(12px, env(safe-area-inset-right)); }
.lightbox-counter {
  position: absolute;
  bottom: max(16px, env(safe-area-inset-bottom));
  left: 50%;
  transform: translateX(-50%);
  padding: 4px 12px;
  border-radius: 12px;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.lightbox.lightbox-single .lightbox-nav,
.lightbox.lightbox-single .lightbox-counter {
  display: none;
}

.item-body {
  padding: 12px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}

.item-title {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.35;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin: 0;
}

.item-title a {
  color: inherit;
  text-decoration: none;
}

.item-title a:hover {
  text-decoration: underline;
}

.item-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  font-size: 12px;
  color: var(--fg-muted);
}

.item-audit-link {
  color: var(--fg-muted);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  border-radius: 6px;
  padding: 2px;
  transition: color 0.15s, background 0.15s;
}

.item-audit-link:hover {
  color: var(--fg);
  background: var(--accent-soft);
}

.item-bid {
  font-size: 22px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Vertical stack: price-line (price + history icon) on row 1, stats on
   row 2. Each row holds at most one large atomic unit (the price) or a
   collection of small ones (the stat chunks), so no row depends on
   shoehorning multiple competing widths onto a single line — the
   geometric failure mode of the previous one-row layout at narrow widths. */
.item-bid-row {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* Price + history icon. The icon is tiny (~20px) so it shares this line
   comfortably even on the narrowest tile; margin-left:auto pins it right. */
.item-bid-price {
  display: flex;
  align-items: center;
  gap: 8px;
}

.item-bid-price .item-audit-link {
  margin-left: auto;
}

/* Bid count + (time remaining | ended date) as atomic flex items. Each
   chunk is white-space:nowrap, so "1d 20h left" stays one piece — it
   either fits beside "5 bids" with a column-gap or wraps as a whole to
   its own line. Never breaks mid-phrase. */
.item-bid-stats {
  display: flex;
  flex-wrap: wrap;
  column-gap: 10px;
  row-gap: 2px;
  font-size: 12px;
  line-height: 1.2;
  color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}

.item-bid-stat {
  white-space: nowrap;
}

.item-split {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--accent-soft);
  border-radius: 8px;
  padding: 10px 12px;
  font-variant-numeric: tabular-nums;
  /* Pin to card bottom — combined with grid's align-items:stretch on .items,
     the split box aligns across every card in a row even when titles or
     bid-rows above differ in content. */
  margin-top: auto;
}

/* On the largest grid tile (≥300px) and in list mode there's enough room
   to put the two halves side by side as a compact "receipt" strip. Each
   half still reflows label→value internally if it ever gets pinched. */
.items.view-grid-lg .item-split,
.items.view-list .item-split {
  flex-direction: row;
  column-gap: 16px;
}

.items.view-grid-lg .item-split .split-half,
.items.view-list .item-split .split-half {
  flex: 1;
  min-width: 0;
}

/* One half per line: label + value. flex-wrap lets the (atomic, nowrap)
   value drop to its own line beneath the label on narrow tiles instead of
   colliding with anything. margin-left:auto right-aligns the value so the
   numbers form a clean column whether they sit beside the label or wrap. */
.item-split .split-half {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  column-gap: 8px;
  row-gap: 1px;
}

.item-split .label {
  color: var(--fg-muted);
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.item-split .value {
  font-size: 15px;
  font-weight: 600;
  /* Atomic unit: the number (and its ticker logo) never split. It wraps as a
     whole to its own line when it can't fit beside the label — never
     overflowing into the other value or past the tile edge. */
  white-space: nowrap;
  margin-left: auto;
}

/* Spelled-out "shares" unit — used only when no ticker logo is configured
   (logo.dev token unset) or the image fails to load. Rendered smaller +
   muted so the prominent number doesn't get bullied by the unit text. */
.unit {
  font-size: 0.72em;
  font-weight: 500;
  color: var(--fg-muted);
  margin-left: 0.3em;
}

/* Ticker logo as the shares "unit" — sized to one line of the surrounding
   text so it scales naturally between tile values (15px) and totals values
   (22px). Vertical-align nudges it onto the text baseline. */
.ticker-logo {
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  margin-left: 0.3em;
  border-radius: 3px;
  object-fit: contain;
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 6px;
  border-radius: 999px;
  border: 1px solid var(--border);
  color: var(--fg-muted);
}

.tag-auction {
  border-color: var(--accent);
  color: var(--accent);
}

.tag-ended {
  border-color: #888;
  color: #aaa;
  background: rgba(0, 0, 0, 0.15);
}

/* Sale-mode tag: spell it out on large grid + list (room to spare), shrink
   to an icon-only pill on the smaller grids where the text overwhelms. */
.tag-icon { display: none; }
.tag-text { display: inline; }
.items.view-grid-sm .tag-icon,
.items.view-grid-md .tag-icon {
  display: block;
}
.items.view-grid-sm .tag-text,
.items.view-grid-md .tag-text {
  display: none;
}
/* Icon-only pill tightens its padding so it stays a neat circle. */
.items.view-grid-sm .tag,
.items.view-grid-md .tag {
  padding: 3px;
}

.ended-section {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border);
}

.ended-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 0.5rem;
}

.ended-header h2 {
  margin: 0;
  font-size: 1.1rem;
  opacity: 0.85;
}

.ended-toggle {
  background: none;
  border: 1px solid var(--border);
  color: var(--fg-muted);
  cursor: pointer;
  padding: 0.3rem 0.7rem;
  border-radius: 4px;
  font-size: 0.85rem;
}

.ended-toggle:hover {
  color: var(--fg);
  border-color: var(--fg-muted);
}

.ended-help {
  font-size: 0.85rem;
  opacity: 0.7;
  margin: 0 0 1rem;
}

.ended-section.is-collapsed .ended-totals-grid,
.ended-section.is-collapsed .ended-items,
.ended-section.is-collapsed .ended-help {
  display: none;
}

.ended-item {
  opacity: 0.85;
}

.ended-item .item-bid {
  color: var(--fg-muted);
}

.empty {
  padding: 40px 0;
  text-align: center;
  color: var(--fg-muted);
}

.error {
  padding: 14px 16px;
  border: 1px solid var(--warn);
  border-radius: var(--radius);
  color: var(--warn);
  background: var(--bg-card);
}

footer {
  border-top: 1px solid var(--border);
  padding: 14px 20px;
  color: var(--fg-muted);
  font-size: 12px;
  text-align: center;
}

footer p {
  margin: 0;
}

footer p + p {
  margin-top: 6px;
}

footer .disclaimer {
  font-size: 11px;
  opacity: 0.7;
}
/* Controls bar: stock left, sort right, same line */
/* Filter controls live below the totals + most-recent-bid, just above the
   grid. Two rows: (seller filter + stock) and (sort + view). Each row holds
   two groups that sit side by side on desktop and wrap on narrow screens. */
.controls {
  margin-bottom: 20px;
}

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

.controls-row + .controls-row {
  margin-top: 10px;
}

.stock-bar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}

.stock-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  padding: 4px 12px;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.stock-btn:hover {
  border-color: var(--fg-muted);
  color: var(--fg);
}

.stock-btn.is-active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

.ticker-input {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-muted);
  font-family: inherit;
  font-size: 13px;
  padding: 4px 12px;
  transition: border-color 0.15s, color 0.15s;
  width: 110px;
  max-width: 100%;
}

.ticker-input::placeholder {
  color: var(--fg-muted);
  opacity: 0.6;
}

.ticker-input:hover {
  border-color: var(--fg-muted);
  color: var(--fg);
}

.ticker-input:focus {
  outline: none;
  border-color: var(--accent);
  color: var(--fg);
}

.ticker-input.is-validating {
  border-color: var(--accent);
  color: var(--fg);
  animation: ticker-pulse 1s ease-in-out infinite;
}

.ticker-input.is-invalid {
  border-color: #d9534f;
  color: #d9534f;
  animation: ticker-shake 0.4s ease-in-out;
}

@keyframes ticker-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}

@keyframes ticker-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

.sort-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.sort-label {
  color: var(--fg-muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-right: 2px;
}

.sort-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  padding: 4px 12px;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.sort-btn:hover,
.view-btn:hover {
  border-color: var(--fg-muted);
  color: var(--fg);
}

.sort-btn.is-active,
.view-btn.is-active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

.view-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.view-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-muted);
  cursor: pointer;
  font-family: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 5px 9px;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.view-btn svg {
  display: block;
}

.seller-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.seller-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  padding: 4px 12px;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.seller-btn:hover {
  border-color: var(--fg-muted);
  color: var(--fg);
}

.seller-btn.is-active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

.seller-badge {
  display: inline-block;
  font-size: 11px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  padding: 2px 6px;
  border-radius: 999px;
  border: 1px solid currentColor;
  opacity: 0.85;
}

.seller-badge-mine {
  color: #6ba6c4;
}

.seller-badge-ryan {
  color: #c4956b;
}

.ended-price-mode,
.ended-window {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.ended-mode-btn,
.ended-window-btn {
  background: none;
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--fg-muted);
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  padding: 4px 12px;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.ended-mode-btn:hover,
.ended-window-btn:hover {
  border-color: var(--fg-muted);
  color: var(--fg);
}

.ended-mode-btn.is-active,
.ended-window-btn.is-active {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

/* Bid-update flash */
@keyframes bid-flash {
  0%   { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(92, 219, 149, 0.3), var(--shadow); }
  70%  { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(92, 219, 149, 0.3), var(--shadow); }
  100% { border-color: var(--border); box-shadow: var(--shadow); }
}

.bid-updated {
  animation: bid-flash 1.4s ease-out;
}

/* FLIP move transition */
.item.is-moving {
  transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

/* Highlight on click */
@keyframes highlight-pulse {
  0%, 100% { box-shadow: var(--shadow); }
  50% { box-shadow: 0 0 0 3px rgba(92, 219, 149, 0.4), var(--shadow); }
}

.item.highlight {
  animation: highlight-pulse 2s ease-in-out;
}