:root {
  --bg: #0b0d12;
  --panel: #151922;
  --text: #f4f6fb;
  --muted: #9aa3b2;
  --accent: #4f8cff;
  --felony: #ff4d4d;
  --misdemeanor: #ffb020;
  --infraction: #7a8699;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  /* Reserved clearance for the Ezoic anchor ad, which floats fixed over the
     bottom of the viewport. Bottom-anchored UI is lifted by this much so the
     anchor never covers it. */
  --anchor-space: 100px;
  /* Width of the feed column. Phones use the full viewport (capped); the
     desktop media query below widens it. Every fixed overlay (topbar, gate,
     hint, sheets) shares this so they always align with the feed. */
  --col: 500px;
}

* { box-sizing: border-box; }

/* The hidden attribute must always win over the display rules below
   (.empty/.sheet/.loader set display, which otherwise overrides the
   user-agent [hidden] { display:none } and leaves them stuck visible). */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  height: 100%;
  background: #000;
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}

/* On wide screens, frame the feed as a centered phone-width column. */
body {
  max-width: var(--col);
  margin: 0 auto;
  position: relative;
  background: var(--bg);
}

#topbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  max-width: var(--col);
  margin: 0 auto;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(10px + var(--safe-top)) 16px 10px;
  background: linear-gradient(180deg, rgba(0,0,0,0.75), rgba(0,0,0,0));
  pointer-events: none;
}
#topbar .brand {
  font-weight: 800;
  font-size: 22px;
  letter-spacing: -0.5px;
  text-shadow: 0 1px 4px rgba(0,0,0,0.6);
}
#topbar .brand-logo {
  display: block;
  height: 28px;
  width: auto;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6));
}
.sheet-legal {
  margin-top: 14px;
  text-align: center;
  font-size: 12px;
  color: var(--muted);
}
.sheet-legal a { color: var(--muted); text-decoration: none; }
.sheet-legal a:hover { color: var(--accent); }
.area-btn {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: rgba(20,25,34,0.7);
  color: var(--text);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 999px;
  padding: 7px 12px;
  font-size: 13px;
  font-weight: 600;
  backdrop-filter: blur(8px);
  cursor: pointer;
  max-width: 60vw;
}
.area-btn .pin { color: var(--accent); font-size: 9px; }
.area-btn #areaLabel { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

#feed {
  height: 100dvh;
  overflow-y: scroll;
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
#feed::-webkit-scrollbar { display: none; }
/* While the swipe gate holds the visitor on a card, touch scrolling is frozen
   (desktop wheel is blocked in JS). Taps still fire, so the detail sheet and
   ad clicks keep working. */
#feed.locked { touch-action: none; }

.card {
  position: relative;
  height: 100dvh;
  scroll-snap-align: start;
  scroll-snap-stop: always;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: #0e1118;
}

/* The photo + details fill the top region; the ad lives in its own band below
   (see .card-ad-band) so it can never overlap the image or push the text up. */
.card-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow: hidden;
}

.card-media { position: absolute; inset: 0; z-index: 0; }
.card-media .mug { width: 100%; height: 100%; object-fit: cover; }
/* lowres: tiny source photos capped at 2.5× native (see fitMug in app.js /
   imaging/compose.go maxUpscale). Blurred cover fill behind a sharp contain. */
.card-media .mug-blur { display: none; }
.card-media.lowres { background: #000; }
.card-media.lowres .mug-blur {
  display: block;
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  filter: blur(16px) brightness(0.72);
  transform: scale(1.06); /* hide blur edge bleed */
}
.card-media.lowres .mug {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: var(--mug-w); height: var(--mug-h);
  object-fit: contain;
  /* The desktop layout pads .card-media .mug to clear the topbar/anchor;
     with border-box sizing that padding would eat this fixed content box,
     so it must be zeroed in the lowres state (the capped image is far
     smaller than the pane and never reaches those edges). */
  padding: 0;
  border-radius: 6px;
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.55);
}
.mug-placeholder {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(120% 80% at 50% 0%, #2a3445 0%, #141823 55%, #0b0d12 100%);
}
.mug-placeholder .initials {
  font-size: 34vw;
  font-weight: 800;
  color: rgba(255,255,255,0.08);
  letter-spacing: -2px;
  user-select: none;
}
.no-photo {
  position: absolute;
  top: calc(50% + 18vw);
  left: 0; right: 0;
  text-align: center;
  color: var(--muted);
  font-size: 13px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.card.has-photo .mug-placeholder,
.card.has-photo .no-photo { display: none; }

.card-overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  padding: 20px 18px 18px;
  background: linear-gradient(0deg, rgba(0,0,0,0.92) 12%, rgba(0,0,0,0.55) 55%, rgba(0,0,0,0) 100%);
  transition: opacity 0.25s ease;
}
/* Eye toggle hides the whole text overlay (gradient included) so the photo is
   fully visible; the button itself stays so it can be toggled back. */
.card.text-clear .card-overlay { opacity: 0; pointer-events: none; }

.eye-btn {
  position: absolute;
  top: calc(64px + var(--safe-top));
  right: 14px;
  z-index: 2;
  width: 42px;
  height: 42px;
  display: flex;
  align-items: center;
  justify-content: center;
  appearance: none;
  border: 1px solid rgba(255,255,255,0.18);
  border-radius: 50%;
  background: rgba(20,25,34,0.55);
  color: #fff;
  backdrop-filter: blur(8px);
  cursor: pointer;
}
.eye-btn svg { width: 21px; height: 21px; }
.eye-btn .i-eye-off { display: none; }
.card.text-clear .eye-btn .i-eye { display: none; }
.card.text-clear .eye-btn .i-eye-off { display: block; }
.eye-btn:active { transform: scale(0.94); }

/* Per-card 320x100 ad in its own solid band beneath the photo. The band always
   reserves the anchor-ad clearance at the very bottom; the ad slot itself
   collapses when nothing fills so empty cards show no dead space. */
.card-ad-band {
  flex: 0 0 auto;
  background: var(--bg);
  padding-bottom: calc(var(--anchor-space) + var(--safe-bottom));
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-ad {
  width: 100%;
  min-height: 100px;
  padding: 8px 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.card-ad.ad-empty { min-height: 0; padding: 0; }

/* Second in-card ad band at the top of the desktop details rail. Never shown
   on phones (JS also removes it there); the desktop height query enables it. */
.card-ad-top { display: none; }

/* Full-page interstitial slide (every FULLPAGE_EVERY cards): a 300x600 ad
   centered on its own snap card, clear of the bottom anchor ad. */
.ad-card {
  align-items: center;
  justify-content: center;
  padding: calc(24px + var(--safe-top)) 16px
           calc(var(--anchor-space) + var(--safe-bottom) + 16px);
}
.fullpage-ad-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.ad-tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.fullpage-ad {
  width: 300px;
  min-height: 600px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.fullpage-ad.ad-empty { min-height: 0; }

.sev-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  padding: 4px 9px;
  border-radius: 6px;
  margin-bottom: 10px;
  color: #0b0d12;
}
.sev-felony { background: var(--felony); color: #fff; }
.sev-misdemeanor { background: var(--misdemeanor); }
.sev-infraction { background: var(--infraction); color: #fff; }
.sev-none { display: none; }

.name {
  margin: 0 0 6px;
  font-size: 27px;
  line-height: 1.1;
  font-weight: 800;
  text-shadow: 0 1px 6px rgba(0,0,0,0.6);
}
.meta-line { color: var(--text); font-size: 15px; font-weight: 600; margin-bottom: 10px; }
.meta-line .dot { margin: 0 7px; color: var(--muted); }
.meta-line .loc { color: var(--muted); }

.charges {
  margin: 0 0 12px;
  font-size: 16px;
  line-height: 1.35;
  color: #e7ebf3;
}
.sub { display: flex; flex-wrap: wrap; gap: 6px 14px; font-size: 13px; color: var(--muted); margin-bottom: 12px; }
.sub .bail:not(:empty)::before { content: "Bail: "; }

/* Presumption-of-innocence line on the server-rendered person lead card. Kept
   subtle so the SSR card matches the JS cards; the county link in .meta-line
   inherits the muted .loc color and the browser-default underline. */
.card-disclaimer { margin: 0 0 12px; font-size: 11px; line-height: 1.3; color: var(--muted); }

.footer-line { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: var(--muted); }
.footer-line .ago { color: #cfd6e4; font-weight: 600; }
.footer-line .source { margin-left: auto; opacity: 0.8; }

/* Fast-flip gate indicator: a top loading bar plus a label just below the
   header. While the feed is held the bar fills over the dwell time and the
   label tells the visitor it is loading; both hide once swiping unlocks. Kept
   at the top so it never collides with the card's button or the anchor ad. */
.gate {
  position: fixed;
  top: 0; left: 0; right: 0;
  max-width: var(--col);
  margin: 0 auto;
  z-index: 30;
  pointer-events: none;
}
.gate-track {
  width: 100%;
  height: 4px;
  background: rgba(255,255,255,0.16);
  overflow: hidden;
}
.gate-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--accent);
}
.gate-label {
  width: fit-content;
  margin: calc(54px + var(--safe-top)) auto 0;
  padding: 6px 14px;
  border-radius: 999px;
  background: rgba(11,13,18,0.85);
  backdrop-filter: blur(8px);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #fff;
  box-shadow: 0 2px 10px rgba(0,0,0,0.45);
}
/* A sheet covers the feed, so the gate is hidden behind it. */
body.sheet-open .gate { display: none !important; }

/* Subtle swipe-up cue: a ghosted chevron + label bobbing at the bottom right
   of the first card (clear of the left-aligned "View full record" button and
   the ad band below), retired on the visitor's first swipe. */
.swipe-hint {
  position: fixed;
  left: 0; right: 0;
  bottom: calc(var(--anchor-space) + var(--safe-bottom) + 132px);
  max-width: var(--col);
  margin: 0 auto;
  display: flex;
  justify-content: flex-end;
  padding-right: 20px;
  z-index: 15;
  color: rgba(255,255,255,0.82);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-shadow: 0 1px 6px rgba(0,0,0,0.7);
  pointer-events: none;
  transition: opacity 0.4s ease;
}
.swipe-hint-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  animation: hint-bob 1.7s ease-in-out infinite;
}
.swipe-hint svg {
  width: 22px;
  height: 22px;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.7));
}
.swipe-hint.hint-out { opacity: 0; }
body.sheet-open .swipe-hint { display: none !important; }
@keyframes hint-bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-7px); }
}
@media (prefers-reduced-motion: reduce) {
  .swipe-hint-inner { animation: none; }
}

.empty {
  position: fixed;
  left: 0; right: 0;
  max-width: var(--col);
  margin: 0 auto;
  text-align: center;
}
/* Infinite-scroll fetch indicator, tucked bottom-right above the anchor ad so
   it stays clear of the card's "View full record" button on the left. */
.loader {
  position: fixed;
  right: 14px;
  bottom: calc(14px + var(--anchor-space) + var(--safe-bottom));
  z-index: 15;
  color: var(--muted);
  font-size: 13px;
  padding: 5px 12px;
  border-radius: 999px;
  background: rgba(11,13,18,0.8);
  backdrop-filter: blur(8px);
}
.empty {
  top: 0; bottom: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 24px; gap: 8px; z-index: 5;
}
.empty h2 { margin: 0; font-size: 20px; }
.empty p { margin: 0; color: var(--muted); }

.btn {
  appearance: none;
  border: 1px solid rgba(255,255,255,0.2);
  background: var(--panel);
  color: var(--text);
  border-radius: 10px;
  padding: 12px 16px;
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
}
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }

.sheet {
  position: fixed;
  inset: 0;
  max-width: var(--col);
  margin: 0 auto;
  z-index: 40;
  display: flex;
  align-items: flex-end;
  background: rgba(0,0,0,0.55);
}
.sheet-card {
  width: 100%;
  background: var(--panel);
  border-radius: 18px 18px 0 0;
  padding: 22px 18px calc(22px + var(--anchor-space) + var(--safe-bottom));
  display: flex;
  flex-direction: column;
  gap: 14px;
  /* Never taller than the phone: the card itself scrolls when the content
     (search results, place chips) outgrows a small screen. */
  max-height: calc(100dvh - 24px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.sheet-card h2 { margin: 0; font-size: 19px; }
.muted { color: var(--muted); font-size: 13px; margin: 0; }
.radius { display: flex; flex-direction: column; gap: 8px; font-size: 14px; color: var(--muted); }
.radius input[type=range] { width: 100%; accent-color: var(--accent); }
.sheet-actions { display: flex; gap: 10px; }
.sheet-actions .btn { flex: 1; }

/* Place search + picker */
.area-search { display: flex; gap: 8px; }
.area-search input[type=search] {
  flex: 1;
  appearance: none;
  background: #0e1118;
  color: var(--text);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 10px;
  padding: 12px 14px;
  font-size: 15px;
}
.area-search input[type=search]::placeholder { color: var(--muted); }
.area-search .btn { white-space: nowrap; }

.place-results {
  list-style: none;
  margin: 0;
  padding: 0;
  /* Tall enough for all 5 geocoder results (see geo.Geocoder.Search) without
     inner scrolling; capped viewport-relative on short phones, where the
     sheet-card's own scroll takes over. flex-shrink: 0 is load-bearing: the
     sheet-card is a column flex container, and without it the list is the
     only shrinkable item, so the browser crushes it to a sliver to fit the
     card on screen. */
  flex-shrink: 0;
  max-height: min(300px, 45vh);
  overflow-y: auto;
  border: 1px solid rgba(255,255,255,0.12);
  border-radius: 10px;
}
.place-result, .place-empty {
  padding: 14px;
  font-size: 14px;
  line-height: 1.35;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  cursor: pointer;
}
.place-result:last-child, .place-empty:last-child { border-bottom: none; }
.place-result:hover { background: rgba(79,140,255,0.12); }
.place-result.place-active { background: rgba(79,140,255,0.22); }
.place-empty { color: var(--muted); cursor: default; }

.data-places { display: flex; flex-direction: column; gap: 8px; }
.chips {
  display: flex; flex-wrap: wrap; gap: 8px;
  /* The covered-county list keeps growing; cap it at ~3 rows and scroll so it
     can't push the sheet's Apply button off a small phone. */
  max-height: 126px;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-right: 2px;
}
.chip {
  appearance: none;
  background: #0e1118;
  color: var(--text);
  border: 1px solid rgba(255,255,255,0.2);
  border-radius: 999px;
  padding: 8px 14px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.chip.chip-active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* "View full record" affordance — a real, finger-sized button. */
.details-cta {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 14px;
  padding: 13px 20px;
  min-height: 48px;
  border: none;
  border-radius: 12px;
  background: var(--accent);
  color: #fff;
  font-size: 15.5px;
  font-weight: 700;
  letter-spacing: 0.01em;
  cursor: pointer;
  box-shadow: 0 3px 12px rgba(0,0,0,0.4);
}
.details-cta:active { transform: scale(0.98); }

/* Person detail sheet: a scrollable, flexible box of whatever fields exist. */
.detail-card {
  position: relative;
  max-height: 88dvh;
  overflow-y: auto;
  gap: 16px;
  -webkit-overflow-scrolling: touch;
}
.sheet-close {
  position: absolute;
  top: 10px; right: 12px;
  z-index: 2;
  width: 34px; height: 34px;
  display: flex; align-items: center; justify-content: center;
  appearance: none;
  border: none;
  border-radius: 50%;
  background: rgba(255,255,255,0.08);
  color: var(--text);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
}
.detail-body { display: flex; flex-direction: column; gap: 16px; }
.detail-loading { padding: 24px 4px; text-align: center; }

.detail-head { display: flex; gap: 14px; align-items: center; padding-right: 36px; }
.detail-mug {
  width: 72px; height: 72px;
  flex: 0 0 auto;
  object-fit: cover;
  border-radius: 12px;
  background: #0e1118;
}
.detail-head-txt { min-width: 0; }
.detail-head-txt h2 { margin: 0 0 4px; font-size: 21px; line-height: 1.15; word-break: break-word; }

.detail-section { display: flex; flex-direction: column; gap: 8px; }
.detail-section h3 {
  margin: 0;
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.kv { display: flex; gap: 12px; font-size: 14.5px; line-height: 1.35; }
.kv .k { flex: 0 0 40%; color: var(--muted); }
.kv .v { flex: 1; color: var(--text); word-break: break-word; }

.charge-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.charge-item {
  background: #0e1118;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 10px;
  padding: 11px 13px;
}
.charge-desc { font-size: 15px; font-weight: 600; line-height: 1.3; }
.charge-meta { font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.charge-bail { font-size: 13px; color: #cfd6e4; margin-top: 5px; font-weight: 600; }

/* Server-rendered full booking record on person permalinks, rendered VISIBLE
   in the default (pre-JS) HTML as its own snap card right after the lead card
   (see the .record-card section in templates/home.html). No `hidden`, no
   display:none — crawlers and users see the complete record in real layout,
   matching every SERP winner in this niche. It is a `.card`, so it inherits
   the feed's scroll-snap-align/stop and the swipe feed treats it as one more
   snap section; these overrides let it grow past the viewport when a record is
   long (a taller-than-viewport snap area stays reachable per the scroll-snap
   spec — the user scrolls through it, then the next card snaps), while a short
   record fills exactly one viewport for a clean one-swipe-per-card feel.
   Explicit display rules on the reused detail-sheet classes keep this
   self-contained in the feed context (not dependent on the sheet). */
.record-card {
  display: block;
  height: auto;
  min-height: 100dvh;
  overflow: visible;
  background: var(--panel);
}
.record-inner {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 640px;
  margin: 0 auto;
  /* Clear the fixed topbar at the top and the floating anchor ad at the bottom
     so the first heading and last row are never covered. */
  padding: calc(64px + var(--safe-top)) 18px
           calc(var(--anchor-space) + var(--safe-bottom) + 24px);
}
.record-title {
  margin: 0;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.2px;
  color: var(--text);
}
.record-disclaimer {
  margin: 2px 0 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--muted);
}
/* Self-contained display for the reused sheet classes in the feed context. */
.record-card .detail-section { display: flex; flex-direction: column; gap: 8px; }
.record-card .charge-list { display: flex; flex-direction: column; gap: 10px; }
.record-card .kv { display: flex; }

.detail-source { margin: 0; padding-top: 2px; }

/* 300x250 (or similar) in the full record. Wrapper reserves height; collapses
   if the placeholder does not fill. */
.detail-ad {
  min-height: 250px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.detail-ad.ad-empty { min-height: 0; }

/* ---- Desktop (>=1100px) ----
   A real desktop layout, not a phone frame: the feed goes full-bleed and each
   card becomes a complete desktop screen — the photo fills a large left pane
   over a blurred ambient backdrop, and the record details live in a solid
   right rail with the per-card ad below them. Vertical snap between records
   is unchanged, arrow buttons + keyboard advance the feed, and the bottom
   sheets become centered modals. Everything is behind this media query;
   phones and tablets are untouched. */

/* Ambient backdrop: two stacked layers crossfaded by JS on card change. Hidden
   until the desktop query turns it on. */
#backdrop { display: none; }

/* Desktop arrow nav: hidden on touch layouts, shown by the media query. */
#feedNav { display: none; }

@media (min-width: 1100px) {
  :root {
    --rail: 480px; /* width of the right details rail */
  }

  /* Full-bleed: the phone column cap comes off. */
  body { max-width: none; }
  #topbar { max-width: none; padding: 16px 24px 12px; }
  .gate { max-width: none; }
  .empty { max-width: none; }

  #backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    background: radial-gradient(120% 90% at 50% 0%, #1a2130 0%, #0b0d12 60%, #000 100%);
  }
  #backdrop .backdrop-img {
    position: absolute;
    inset: -40px; /* bleed past the blur's soft edges */
    width: calc(100% + 80px);
    height: calc(100% + 80px);
    object-fit: cover;
    filter: blur(40px) brightness(0.6) saturate(1.3);
    opacity: 0;
    transition: opacity 0.6s ease;
  }
  /* Vignette over the backdrop so the sharp photo in front stays the focus. */
  #backdrop::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(90% 90% at 40% 50%, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.6) 100%);
  }
  #backdrop .backdrop-img.show { opacity: 1; }

  #feed { position: relative; z-index: 1; }

  /* Each card is a desktop screen: photo pane left, details rail right. The
     stage's children are promoted to grid items (display: contents) so the
     media and the overlay can be placed independently without touching the
     DOM the phone layout uses. */
  .card {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--rail);
    grid-template-rows: auto minmax(0, 1fr) auto;
    background: transparent;
  }
  .card-stage { display: contents; }

  /* The record card is not a photo/rail screen — cancel the grid so it stays a
     single centered, readable column on desktop too. */
  .card.record-card { display: block; background: var(--panel); }
  .record-inner { max-width: 720px; padding-top: calc(88px + var(--safe-top)); }

  /* Photo pane: the sharp mugshot is letterboxed (never cropped) over the
     ambient blurred backdrop behind it. */
  .card-media {
    grid-area: 1 / 1 / 4 / 2;
    position: relative;
    background: transparent;
  }
  .card-media .mug {
    object-fit: contain;
    padding: calc(64px + var(--safe-top)) 32px calc(var(--anchor-space) + 16px);
  }
  .mug-placeholder { background: transparent; }
  .mug-placeholder .initials { font-size: 220px; }
  .no-photo { top: calc(50% + 140px); }

  /* The photo isn't under the text on desktop, so the eye toggle is moot. */
  .eye-btn { display: none; }
  .card.text-clear .card-overlay { opacity: 1; pointer-events: auto; }

  /* Details rail: the overlay stops being an overlay and becomes a solid
     right-hand panel with the record laid out large. Its top padding clears
     the fixed topbar; when the top ad band is enabled (tall screens, below)
     the band provides that clearance instead. */
  .card-overlay {
    grid-area: 2 / 2 / 3 / 3;
    position: relative;
    inset: auto;
    min-height: 0;
    justify-content: center;
    padding: calc(72px + var(--safe-top)) 40px 24px;
    background: var(--panel);
    border-left: 1px solid rgba(255,255,255,0.07);
  }
  .name { font-size: 36px; }
  .charges { font-size: 18px; }
  .meta-line { font-size: 16px; }
  .sub { font-size: 14px; }
  .footer-line { font-size: 13px; }

  /* Desktop rail ads mount id-less: the .card-ad div is only the ANCHOR the
     resolver inserts the generated placeholder after, so it must not reserve
     space itself -- the bands do. Bands collapse via .ad-empty on no-fill. */
  .card-ad { min-height: 0; padding: 0; }

  /* Bottom rail ad: a 300x250 below the record details. */
  .card-ad-band {
    grid-area: 3 / 2 / 4 / 3;
    background: var(--panel);
    border-left: 1px solid rgba(255,255,255,0.07);
    padding-top: 8px;
    min-height: calc(258px + var(--anchor-space) + var(--safe-bottom));
  }
  .card-ad-band.ad-empty { min-height: 0; }

  /* Top rail ad: a second 300x250 above the details, enabled by the height
     query below only when the rail can fit both rectangles + the record. */
  .card-ad-top {
    grid-area: 1 / 2 / 2 / 3;
    background: var(--panel);
    border-left: 1px solid rgba(255,255,255,0.07);
    align-items: center;
    justify-content: center;
    padding: calc(58px + var(--safe-top)) 0 6px;
    min-height: calc(314px + var(--safe-top));
  }
  /* Collapsed top band still clears the fixed topbar for the details. */
  .card-ad-top.ad-empty {
    min-height: calc(58px + var(--safe-top));
    padding-bottom: 0;
  }

  /* Full-page ad slides keep their centered flex layout (no photo/rail). The
     wrap is an id-less anchor on desktop (ad renders as its sibling), so its
     phone-mode reserved height must not apply. */
  .card.ad-card { display: flex; }
  .fullpage-ad { min-height: 0; width: auto; }

  /* Same for the full-record modal's ad wrap. */
  .detail-ad { min-height: 0; }

  /* Floating next/prev arrows at the photo/rail boundary. */
  #feedNav {
    position: fixed;
    right: calc(var(--rail) + 28px);
    top: 50%;
    transform: translateY(-50%);
    z-index: 25;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }
  .feednav-btn {
    width: 52px;
    height: 52px;
    display: flex;
    align-items: center;
    justify-content: center;
    appearance: none;
    border: 1px solid rgba(255,255,255,0.16);
    border-radius: 50%;
    background: rgba(20,25,34,0.6);
    color: #fff;
    backdrop-filter: blur(10px);
    cursor: pointer;
    transition: background 0.15s ease, transform 0.1s ease, opacity 0.2s ease;
  }
  .feednav-btn:hover { background: rgba(79,140,255,0.35); }
  .feednav-btn:active { transform: scale(0.94); }
  .feednav-btn svg { width: 24px; height: 24px; }
  .feednav-btn[disabled] { opacity: 0.35; cursor: default; }
  .feednav-btn[disabled]:hover { background: rgba(20,25,34,0.6); }

  /* Keyboard shortcut legend under the arrows. */
  .feednav-keys {
    margin-top: 4px;
    text-align: center;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: rgba(255,255,255,0.45);
    user-select: none;
  }

  /* The scroll cue sits at the bottom center of the photo pane. */
  .swipe-hint {
    max-width: none;
    left: 0;
    right: var(--rail);
    justify-content: center;
    padding-right: 0;
  }

  /* Bottom sheets become centered modal dialogs. */
  .sheet {
    max-width: none;
    align-items: center;
    justify-content: center;
    padding: 32px;
    background: rgba(0,0,0,0.65);
  }
  .sheet-card {
    max-width: 520px;
    border-radius: 18px;
    padding: 26px 24px;
    max-height: calc(100dvh - 96px);
    box-shadow: 0 24px 80px rgba(0,0,0,0.7);
  }
  .detail-card { max-height: calc(100dvh - 96px); }

}

/* Tall desktop screens fit the second rectangle above the record details:
   topbar clearance + 250 ad + ~280px of details + 258px bottom band + anchor
   clearance needs ~960px. Below that only the bottom rectangle mounts (JS
   checks this band's computed display before requesting the ad). */
@media (min-width: 1100px) and (min-height: 960px) {
  .card-ad-top { display: flex; }
  .card-overlay { padding-top: 24px; }
}
