:root {
  /* Dark premium theme, near-black with a warm gold accent. Chosen to read
     as a technology comparison site for high-value purchases (projectors
     in the $800-$6,000 range), not a generic deal-aggregator look. */
  --bg: #0a0d13;
  --bg-alt: #0d1119;
  --surface: #12161f;
  --surface-alt: #171c27;
  --border: #232936;
  --border-soft: #1a1f2b;
  --text: #edeff2;
  --text-muted: #aab1c2;
  --text-faint: #838ba0;
  --accent: #d9ae5c;
  --accent-dark: #c4954a;
  --accent-soft: rgba(217, 174, 92, 0.12);
  --link: #d9ae5c;
  --estimate: #d9ae5c;
  --estimate-bg: rgba(217, 174, 92, 0.1);
  --up: #e0685f;
  --down: #4fae76;
  --radius: 10px;
  --radius-lg: 14px;
  --max-width: 1280px;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.35);
  /* Per-category accent colors. Both current categories reuse the sitewide
     gold accent since the theme is monochrome-accent by design, override
     per key here if a category should stand out. */
  --cat-ust: #d9ae5c;
  --cat-standard: #d9ae5c;
  /* Amazon product photos are shot on white/light backgrounds, near-
     universally. Putting them in a dark box (var(--bg-alt)) looked bad,
     real feedback from review: inconsistent photo aspect ratios plus each
     one's own baked-in white background clashing against dark made the
     thumbnails look like a bug, not a design choice. A dedicated white
     "photo card" (used for every product thumbnail, table/mobile card/
     homepage pick alike) embraces that instead of fighting it, a small
     shadow and border keep it feeling intentional against the dark page,
     not just a stray white rectangle. */
  --photo-bg: #ffffff;
  --photo-border: rgba(0, 0, 0, 0.08);
  --photo-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  font-size: 16px;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

html {
  overflow-y: scroll;
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 0.9375rem; /* 15px body */
  line-height: 1.55;
}

a { color: var(--link); }

.wrap {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 1.5rem;
}

/* Header */
header.site-header {
  border-bottom: 1px solid var(--border);
  background: var(--bg-alt);
}
header.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 1.1rem;
  padding-bottom: 1.1rem;
  flex-wrap: wrap;
  gap: 0.75rem;
}
.site-logo {
  font-size: 1.3rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--text);
  text-decoration: none;
}
.site-logo span { color: var(--accent); }
/* Split in two on purpose: .marketplace-badge (the actual flex item in
   .wrap) is what gets pushed onto its own mobile row, via flex-basis:100%,
   the only way to force a flex-wrap break with nothing else to naturally
   overflow the line. But flex-basis:100% also makes the ITEM's own box
   100% wide, and this element used to carry the pill's padding/border/
   border-radius directly, real bug found on a real phone: the pill
   itself stretched edge to edge instead of staying a small badge. The
   visual pill styling now lives on the nested .marketplace-badge-pill
   span instead, sized to its own content regardless of what the outer
   wrapper's flex-basis is doing. */
.marketplace-badge {
  order: 2;
  flex-basis: 100%;
}
.marketplace-badge-pill {
  display: inline-block;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.2rem 0.65rem;
  white-space: nowrap;
  cursor: help;
}

.desktop-nav { display: none; }
/* margin-left:auto is the real fix, not decoration: .wrap is display:flex
   with justify-content:space-between, and at narrow widths (logo +
   marketplace-badge + this toggle don't all fit on one line) this toggle
   wraps onto its own second line alone. space-between has nothing to
   distribute for a single item on a line, so it defaults to flex-start
   (left), a real bug found on a real phone: the toggle rendered on the
   LEFT instead of the right, and .mobile-nav-panel's right:0 (correct
   assuming the toggle sits at the right edge) then anchored a 230px-wide
   panel off a toggle near the left edge, pushing most of the panel off
   the left side of the viewport entirely. margin-left:auto pushes this
   item to the right edge of whatever line it ends up on, wrapped alone
   or not, which is what right:0 on the panel was already assuming. */
.mobile-nav-toggle { display: inline-block; margin-left: auto; }

@media (min-width: 860px) {
  .desktop-nav { display: flex; align-items: center; gap: 1.75rem; }
  .mobile-nav-toggle { display: none; }
  .marketplace-badge { order: 0; flex-basis: auto; }
}

.desktop-nav > a,
.nav-dropdown > summary {
  text-decoration: none;
  color: var(--text-muted);
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  list-style: none;
}
.nav-dropdown > summary::-webkit-details-marker { display: none; }
.nav-dropdown > summary::after { content: "\25BE"; font-size: 0.65em; margin-left: 0.3em; }
.desktop-nav > a:hover,
.desktop-nav > a.active,
.nav-dropdown > summary:hover,
.nav-dropdown > summary.active { color: var(--accent); }

.nav-dropdown { position: relative; }
.nav-dropdown-panel {
  position: absolute;
  top: calc(100% + 0.75rem);
  left: 0;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 0.4rem;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  z-index: 20;
}
.nav-dropdown-panel a {
  text-decoration: none;
  color: var(--text);
  font-size: 0.875rem;
  padding: 0.55rem 0.7rem;
  border-radius: 6px;
  white-space: nowrap;
}
.nav-dropdown-panel a:hover,
.nav-dropdown-panel a.active { background: var(--surface); color: var(--accent); }

.mobile-nav-toggle { position: relative; }
.mobile-nav-toggle > summary {
  list-style: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 1.15rem;
  color: var(--text);
}
.mobile-nav-toggle > summary::-webkit-details-marker { display: none; }
.mobile-nav-toggle[open] > summary { background: var(--surface); border-color: var(--accent); }
.mobile-nav-panel {
  position: absolute;
  top: calc(100% + 0.6rem);
  right: 0;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  padding: 0.4rem;
  min-width: 230px;
  display: flex;
  flex-direction: column;
  z-index: 20;
}
.mobile-nav-panel a {
  text-decoration: none;
  color: var(--text);
  font-size: 0.9375rem;
  padding: 0.6rem 0.7rem;
  border-radius: 6px;
}
.mobile-nav-panel a:hover,
.mobile-nav-panel a.active { background: var(--surface); color: var(--accent); font-weight: 600; }
.mobile-nav-panel hr { border: none; border-top: 1px solid var(--border); margin: 0.4rem 0.2rem; }

/* Main content */
main { padding: 0 0 3rem; }

h1 { font-size: 2.25rem; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 0.5rem; }
h2 { font-size: 1.625rem; font-weight: 700; letter-spacing: -0.01em; margin: 2.5rem 0 1rem; }
h3 { font-size: 1.1875rem; font-weight: 700; margin: 0 0 0.5rem; }
.lede { color: var(--text-muted); max-width: 65ch; font-size: 0.9375rem; }

/* Hero: full-bleed dark panel with an optional background photo, matching
   the "premium technology comparison site" reference design. Falls back to
   a plain dark gradient if no hero image is configured, never looks broken
   either way. */
.hero {
  position: relative;
  /* Breaks out of main.wrap's centered max-width so the hero background
     spans the full viewport edge to edge, while its own internal .wrap
     keeps the actual heading/text content aligned with the rest of the
     page. Standard full-bleed-inside-a-centered-container technique,
     avoids restructuring base.html's <main class="wrap"> used by every
     other page on the site. */
  width: 100vw;
  margin-left: calc(-50vw + 50%);
  margin-right: calc(-50vw + 50%);
  margin-bottom: 2rem;
  padding: 4.5rem 0;
  min-height: 460px;
  display: flex;
  align-items: center;
  background: linear-gradient(120deg, #0d1119 0%, #10141d 55%, #0d1119 100%);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
  box-sizing: border-box;
}
.hero.has-image {
  background-size: cover;
  background-position: right center;
  /* Real bug found in review: background-repeat was never set, so wherever
     the image doesn't fully fill the box (the category-hero override
     below scales by height, so at wide viewports the image is narrower
     than the box) it tiled itself across the empty space instead of
     showing once. */
  background-repeat: no-repeat;
}
.hero.has-image::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, rgba(10, 13, 19, 0.97) 0%, rgba(10, 13, 19, 0.88) 32%, rgba(10, 13, 19, 0.45) 62%, rgba(10, 13, 19, 0.15) 100%);
}
/* width:100% is the actual fix, real bug found in review: .hero is
   display:flex, and flex items default to shrink-to-fit (content-based)
   width instead of stretching to max-width like a normal block-level
   .wrap does everywhere else on the site. Without this, .wrap shrank to
   its text content's width and margin:auto then centered THAT smaller
   box, so hero text landed ~300px right of where every other page's
   left-aligned content starts, at wide viewports. */
.hero .wrap { position: relative; z-index: 1; width: 100%; }

/* Compact hero: same full-bleed treatment as .hero.has-image, sized to
   match .category-hero's height instead of the landing-page hero's taller
   460px. Used on content pages (guides index, guide articles, the
   calculator) that need a real title banner, not the full homepage-scale
   hero, and not the plain oversized gradient-only box they had before.
   Full-width background-image (not category-hero's right-side photo
   panel), safe from the "massive zoom" bug that pattern was built to
   avoid: these source images are already close to typical content width
   (1264px), so background-size:cover crops moderately instead of
   upscaling, confirmed visually after building this, not just assumed. */
.hero-compact {
  min-height: 285px;
  padding: 2.25rem 0;
  margin-bottom: 1.75rem;
}
.hero-compact h1 { font-size: 2.25rem; max-width: none; }
.hero-compact .lede { font-size: 0.9375rem; max-width: 60ch; margin-top: 0.6rem; }

/* Category pages now share the exact .hero-compact.has-image treatment
   guides/calculator use (full-width background-image, not a right-side
   panel), see .hero-compact above. Previously used a fixed-width <img>
   panel instead of a background-image, because the panel's own aspect
   ratio (~3.68:1) made object-fit:cover predictable while a full-viewport
   background-image on this short a box was assumed to force a "massive
   zoom" crop. Verified after switching that this doesn't actually happen
   with these source images (2.357:1, 3168px wide): cover only ever
   downscales to fit viewport width here, never upscales, so it just
   crops moderately, same as the guides/calculator pages already proved
   safe with a narrower-aspect source. Real feedback from review asked for
   this consolidation once the guides pages showed it working. */
.category-hero .related-guide { margin: 0.75rem 0 0; }
/* Real lesson from getting hero-ust.webp right: .hero.has-image's dark
   gradient overlay (::before) is nearly opaque on the LEFT side of the
   box (where the title/lede text sits) and fades to mostly transparent
   on the right, by design, for text legibility. A wide/short cover-crop
   box like this one usually has almost no horizontal crop headroom (the
   scaled image width barely exceeds the box width), so background-
   position-x has little to no room to actually shift what's visible,
   confirmed by computing the real cover-scale offset in the browser
   rather than guessing from a screenshot. The only real fix for a
   subject that lands under that dark overlay is compositional: generate
   the source image with its subject on the box's visible (right) side to
   begin with, matching every other image using this treatment. */
/* Vertical crop tweak for hero-ust.webp specifically: unlike horizontal
   position (no real headroom, see above), vertical DOES have crop room
   (box height 285px vs. the scaled image's ~340-380px), so shifting this
   pulls the visible window down toward the console/projector instead of
   centering on the ceiling. Specificity has to match or beat
   .hero.has-image (2 classes) or this silently loses the cascade, see
   the .category-hero--ust vs. .hero.has-image mixup this replaced. */
.hero.has-image.category-hero--ust { background-position: center 75%; }
.hero-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 0.9rem;
}
.hero h1 {
  font-size: 2.75rem;
  line-height: 1.12;
  max-width: 20ch;
}
.hero h1 .accent-line { color: var(--accent); display: block; }
.hero .lede { font-size: 1rem; max-width: 52ch; margin: 0.9rem 0 0; color: var(--text-muted); }
.hero-search {
  margin-top: 1.75rem;
  max-width: 480px;
  position: relative;
}
.hero-search input[type="search"] {
  width: 100%;
  padding: 0.85rem 1rem 0.85rem 2.6rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: 0.9375rem;
  font-family: inherit;
}
.hero-search input[type="search"]::placeholder { color: var(--text-faint); }
.hero-search .search-icon {
  position: absolute;
  left: 1rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-faint);
  pointer-events: none;
}
.hero-pills { display: flex; flex-wrap: wrap; gap: 0.55rem; margin-top: 1.25rem; }
.hero-pill {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0.4rem 0.85rem;
  text-decoration: none;
  transition: border-color 0.15s, color 0.15s;
}
.hero-pill:hover { border-color: var(--accent); color: var(--accent); }

/* Category grid on homepage. */
/* Grid, not flex-wrap+centering: with only 2 categories, a centered flex
   row of small fixed-max-width cards left a huge dead void on wide screens
   (real user feedback while reviewing this). A 2-column grid that fills
   the full content width, with generously-sized cards, reads as deliberate
   instead of sparse regardless of viewport width. */
.category-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 1.25rem;
  margin: 1.5rem 0 2.5rem;
}
.category-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-top: 3px solid var(--cat-color, var(--accent));
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.75rem 1.75rem 1.5rem;
  text-decoration: none;
  color: var(--text);
  display: block;
  transition: box-shadow 0.15s, transform 0.15s, border-color 0.15s;
}
.category-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); border-color: var(--accent); }
.category-card .cat-icon { color: var(--cat-color, var(--accent)); margin-bottom: 0.9rem; }
.category-card .cat-icon svg { width: 32px; height: 32px; }
.category-card h3 { margin: 0 0 0.5rem; font-size: 1.375rem; }
.category-card p { margin: 0; color: var(--text-muted); font-size: 0.875rem; max-width: 46ch; }
.category-card .count { display: inline-block; margin-top: 0.9rem; color: var(--accent); font-weight: 650; font-size: 0.875rem; }

/* Product cards (homepage highlight picks + any grid-style listing) */
.pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1.25rem;
}
.pick-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1rem;
  transition: box-shadow 0.15s, border-color 0.15s;
}
.pick-card:hover { box-shadow: var(--shadow-md); border-color: var(--border); }
.pick-badge {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 2;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: #16130a;
  background: var(--accent);
  border-radius: 5px;
  padding: 0.2rem 0.55rem;
}
.pick-card .pick-image {
  width: 100%;
  height: 150px;
  background: var(--photo-bg);
  border: 1px solid var(--photo-border);
  box-shadow: var(--photo-shadow);
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0.85rem;
  overflow: hidden;
}
.pick-card img { max-width: 88%; max-height: 88%; object-fit: contain; }
.pick-card .pick-title {
  font-size: 1rem;
  font-weight: 650;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-bottom: 0.5rem;
  min-height: 2.5em;
}
.pick-tags { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-bottom: 0.75rem; }
.pick-card .pick-value { display: block; color: var(--accent); font-weight: 700; font-size: 1.25rem; margin-bottom: 0.6rem; }
.pick-card .pick-link a {
  display: block;
  text-align: center;
  background: var(--accent);
  color: #16130a;
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 650;
  padding: 0.55rem;
  border-radius: 7px;
}
.pick-card .pick-link a:hover { background: var(--accent-dark); }

/* Spec tag chips (used on product cards / rows / compare-at-a-glance) */
.spec-tag {
  display: inline-flex;
  align-items: center;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 0.15rem 0.45rem;
  white-space: nowrap;
}
.spec-tag.laser { color: var(--accent); border-color: rgba(217, 174, 92, 0.35); }

/* Filter bar (category pages). Deliberately not a sidebar, see
   category.html's comment above .filter-bar: 2 checkboxes didn't justify
   permanently giving up ~280px of the table's width. */
.category-main { margin-top: 1.5rem; }

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  margin: 1rem 0;
  padding: 0.85rem 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.filter-bar input[type="search"] {
  flex: 1 1 220px;
  padding: 0.5rem 0.7rem;
  border: 1px solid var(--border);
  background: var(--bg-alt);
  color: var(--text);
  border-radius: 6px;
  font-size: 0.875rem;
  font-family: inherit;
}
.filter-bar label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
  white-space: nowrap;
  cursor: pointer;
}
.filter-bar input[type="checkbox"] { accent-color: var(--accent); }
.filter-count {
  font-size: 0.8125rem;
  color: var(--text-faint);
  margin: 0.5rem 0 0.75rem;
}

.table-scroll { position: relative; overflow-x: auto; border-radius: var(--radius); background: var(--surface); border: 1px solid var(--border); }

.scroll-fade {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 28px;
  background: linear-gradient(to right, rgba(18, 22, 31, 0), var(--surface));
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.scroll-fade.visible { opacity: 1; }
/* table-layout:fixed with explicit per-column widths, so the table always
   fits the container width instead of growing to fit unwrapped content
   (real bug found in review: 9 columns of auto-sized content, especially
   the "Check price on Amazon" button and long titles, forced horizontal
   scroll even on normal desktop widths). Product gets the most room so
   long titles wrap into 2-3 readable lines instead of a squeezed stack. */
table.product-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
table.product-table col.col-image { width: 16%; }
table.product-table col.col-product { width: 20%; }
table.product-table col.col-resolution { width: 7%; }
table.product-table col.col-brightness { width: 6%; }
table.product-table col.col-contrast { width: 8%; }
table.product-table col.col-price { width: 15%; }
table.product-table col.col-rating { width: 8%; }
table.product-table col.col-bought { width: 8%; }
table.product-table col.col-cta { width: 12%; }
table.product-table th, table.product-table td {
  padding: 0.85rem 0.9rem;
  text-align: left;
  border-bottom: 1px solid var(--border-soft);
  font-size: 0.8125rem;
  font-weight: 500;
  vertical-align: middle;
  overflow-wrap: break-word;
}
table.product-table th {
  background: var(--surface-alt);
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  white-space: nowrap;
}
table.product-table th.sortable { cursor: pointer; user-select: none; }
table.product-table th.sortable:hover { color: var(--accent); }
table.product-table th .arrow { opacity: 0.5; font-size: 0.7rem; margin-left: 0.2rem; }
table.product-table th.sort-active .arrow { opacity: 1; color: var(--accent); }
table.product-table tbody tr:hover { background: var(--surface-alt); }
table.product-table img.thumb {
  /* Rectangular, not square, projectors are horizontal objects and a
     square crop wasted space / looked odd, real feedback from review. */
  width: 100%;
  max-width: 190px;
  height: 122px;
  object-fit: contain;
  display: block;
  background: var(--photo-bg);
  border: 1px solid var(--photo-border);
  box-shadow: var(--photo-shadow);
  border-radius: 8px;
  padding: 0.6rem;
}
.product-title { font-weight: 650; font-size: 1rem; text-decoration: none; color: var(--text); }
.product-title:hover { color: var(--accent); }
.brand { display: block; color: var(--accent); font-weight: 700; font-size: 0.8125rem; margin-top: 0.2rem; letter-spacing: 0.01em; }
.cell-note { color: var(--text-muted); font-size: 0.78rem; }
.badge { display: inline-block; font-size: 0.6875rem; font-weight: 600; padding: 0.12rem 0.4rem; border-radius: 4px; margin-right: 0.25rem; margin-top: 0.35rem; }
.badge.short-throw { background: var(--surface-alt); color: var(--text-muted); border: 1px solid var(--border); }
.badge.portable { background: var(--surface-alt); color: var(--text-muted); border: 1px solid var(--border); }
.unit-price { font-weight: 700; font-size: 1rem; color: var(--accent); }
.unit-price.estimated { color: var(--estimate); }
.est-flag {
  display: inline-block;
  font-size: 0.6875rem;
  background: var(--estimate-bg);
  color: var(--estimate);
  border-radius: 3px;
  padding: 0 0.3rem;
  margin-left: 0.25rem;
  cursor: help;
}
.no-value { color: var(--text-faint); font-style: italic; font-size: 0.85rem; }
.amazon-link {
  display: inline-block;
  background: var(--accent);
  color: #16130a;
  text-decoration: none;
  padding: 0.5rem 0.7rem;
  border-radius: 7px;
  font-size: 0.8125rem;
  font-weight: 650;
  text-align: center;
}
.amazon-link:hover { background: var(--accent-dark); }
th[data-tooltip] { position: relative; }
.th-tooltip {
  visibility: hidden;
  opacity: 0;
  transition: opacity 0.15s;
  position: absolute;
  bottom: 125%;
  /* Anchored from the right edge, not the left: a left:0 tooltip on a
     column near the table's right side extends 220px further right,
     past the table boundary. Real bug found in review, that overflow is
     invisible (visibility:hidden) but still counts toward the table
     container's scrollWidth, forcing a horizontal scrollbar to appear
     even though nothing visible was actually overflowing. Anchoring from
     the right instead keeps it within the table for every column we
     attach it to (none of them are the table's leftmost column). */
  right: 0;
  background: #1c212c;
  border: 1px solid var(--border);
  color: var(--text);
  padding: 0.5rem 0.65rem;
  border-radius: 6px;
  font-size: 0.75rem;
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  width: 220px;
  white-space: normal;
  z-index: 5;
}
th[data-tooltip]:hover .th-tooltip { visibility: visible; opacity: 1; }

/* Footer */
footer.site-footer {
  border-top: 1px solid var(--border);
  padding: 1.75rem 0;
  color: var(--text-faint);
  font-size: 0.8125rem;
  margin-top: 2rem;
}
footer.site-footer .wrap { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem 1.5rem; }
.last-updated { margin: 0; }
.footer-nav { display: flex; gap: 1rem; }
.footer-nav a { color: var(--text-faint); text-decoration: none; }
.footer-nav a:hover { color: var(--accent); }

/* Static content pages (About/Privacy/Contact) */
.page-content { max-width: 68ch; margin: 0 auto; }
.page-content h2 { font-size: 1.375rem; }
.page-content p, .page-content ul, .page-content ol { color: var(--text-muted); }
.page-content ul, .page-content ol { padding-left: 1.25rem; }
.page-content li { margin-bottom: 0.5rem; }

/* main has no top padding (see main { padding: 0 0 3rem }), same gap
   issue and same fix as .guide-breadcrumb below: these pages open
   straight into an h1 with no hero/breadcrumb above it, flush against
   the header's border-bottom. */
.page-content-top-gap > h1:first-child { margin-top: 2rem; }

/* Guide articles */
.guide-content { max-width: 46rem; }
.guide-content .lede { max-width: none; }
/* main has no top padding (see main { padding: 0 0 3rem }), fine for
   pages that open with a large h1 (its own font size reads as enough
   breathing room) but the breadcrumb's small text sat flush against the
   header's border-bottom with no gap at all. */
.guide-breadcrumb { margin: 2rem 0 0.75rem; font-size: 0.8125rem; }
.guide-breadcrumb a { color: var(--text-faint); }
.guide-breadcrumb a:hover { color: var(--accent); }
.guide-breadcrumb .sep { color: var(--text-faint); margin: 0 0.4rem; }
.guide-breadcrumb .current { color: var(--text-muted); }
.guide-content h1 { font-size: 1.75rem; }
.guide-hero-image { width: 100%; max-height: 380px; object-fit: cover; border-radius: var(--radius-lg); margin: 1rem 0 0.4rem; display: block; }
.guide-credit { font-size: 0.75rem; color: var(--text-faint); margin: 0 0 1.5rem; }
.guide-sources { font-size: 0.8125rem; color: var(--text-faint); margin: 1.5rem 0 0; padding-top: 0.75rem; border-top: 1px solid var(--border); }

/* Calculator/tool pages */
.calc-content { max-width: 46rem; }
.calc-content .lede { max-width: none; }
.calculator-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.4rem 1.6rem 1.6rem;
  margin: 1.5rem 0;
}
.calculator-section h2 { margin-top: 0; }
.calc-row { display: flex; gap: 1rem; flex-wrap: wrap; }
.calc-field { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 1rem; flex: 1 1 160px; }
.calc-field label { font-size: 0.8125rem; font-weight: 600; color: var(--text-muted); }
.calc-field input, .calc-field select {
  padding: 0.55rem 0.65rem;
  border: 1px solid var(--border);
  background: var(--bg-alt);
  color: var(--text);
  border-radius: 6px;
  font-size: 0.9375rem;
  font-family: inherit;
  line-height: 1.3;
  box-sizing: border-box;
  height: 2.75rem;
  /* Flex's default align-items:stretch does NOT reliably stretch a
     <select> to fill its flex parent on every mobile browser, some
     size the closed box to fit its widest OPTION text instead (this
     field's own "Screen size, from my available distance" option is
     long enough to overflow a narrow phone container that way), a real
     bug reported on a real device that a height-only fix didn't touch.
     width/max-width:100% forces the container to win regardless. */
  width: 100%;
  max-width: 100%;
}
/* Real bug on a real phone: without appearance:none, Android Chrome's
   native <select> chrome adds its own internal padding on top of ours,
   rendering noticeably taller than the text input right next to it (same
   declared padding, visibly different heights). appearance:none hands
   full box-sizing control back to our own padding/height, at the cost of
   removing the native dropdown arrow, replaced with a custom chevron
   (baked-in hex, a background-image data URI can't reference CSS custom
   properties, matches --text-muted #aab1c2). */
.calc-field select {
  -webkit-appearance: none;
  appearance: none;
  padding-right: 2.25rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23aab1c2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.65rem center;
}
.calc-result {
  background: var(--estimate-bg);
  color: var(--estimate);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  margin: 0.5rem 0 1rem;
  font-size: 0.9375rem;
}
.calc-result p { margin: 0.25rem 0; }
.calc-result strong { color: var(--accent); }
.calc-button {
  background: var(--accent);
  color: #16130a;
  border: none;
  padding: 0.6rem 1.1rem;
  border-radius: 7px;
  font-size: 0.8125rem;
  font-weight: 650;
  cursor: pointer;
}
.calc-button:hover { background: var(--accent-dark); }
.calc-button:disabled { background: var(--border); color: var(--text-faint); cursor: not-allowed; }
.calc-disclaimer { font-size: 0.85rem; color: var(--text-faint); margin: 1.5rem 0; }

.embed-snippet textarea {
  width: 100%;
  box-sizing: border-box;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-alt);
  color: var(--text);
  resize: vertical;
}
.embed-copy-status { font-size: 0.85rem; color: var(--accent); min-height: 1.2em; margin: 0.4rem 0 0; }
.embed-preview { margin-top: 1.25rem; }
.embed-preview > summary {
  cursor: pointer;
  list-style: none;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent);
  margin-bottom: 0.4rem;
}
.embed-preview > summary::-webkit-details-marker { display: none; }
.embed-preview > summary::before { content: "\25B8  "; }
.embed-preview[open] > summary::before { content: "\25BE  "; }
.embed-preview iframe { width: 100%; height: 900px; border: 1px solid var(--border); border-radius: var(--radius); display: block; margin-top: 0.5rem; }

/* Price index trend chart */
.trend-chart { width: 100%; height: auto; margin: 0.75rem 0; }
.trend-band { fill: var(--estimate-bg); stroke: none; }
.trend-line { stroke: var(--accent); stroke-width: 2; }
.trend-dot { fill: var(--accent); }
.trend-axis-label { fill: var(--text-faint); font-size: 9px; }
.price-index-stat { font-size: 0.9375rem; margin: 0.25rem 0 0.75rem; color: var(--text-muted); }
.price-index-stat .up { color: var(--up); }
.price-index-stat .down { color: var(--down); }
.price-index-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.price-index-scroll-hint { display: none; font-size: 0.8rem; color: var(--text-faint); margin: 0.5rem 0 0.25rem; }
.price-index-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; margin: 0.5rem 0 1.5rem; }
.price-index-table th, .price-index-table td { padding: 0.4rem 0.6rem; text-align: right; border-bottom: 1px solid var(--border-soft); white-space: nowrap; }
.price-index-table th:first-child, .price-index-table td:first-child { text-align: left; }

@media (max-width: 640px) {
  .price-index-scroll-hint { display: block; }
  .price-index-table { font-size: 0.78rem; }
  .price-index-table th, .price-index-table td { padding: 0.35rem 0.5rem; }
}
.price-index-empty { color: var(--text-faint); font-size: 0.9rem; font-style: italic; }
.csv-download { display: inline-block; margin: 0.5rem 0 1.5rem; font-weight: 600; }
.guide-content h3 { font-size: 1.0625rem; margin: 1.25rem 0 0.5rem; }
.guide-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.guide-content table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.9rem; }
/* No nowrap here on purpose, unlike .price-index-table's short numeric
   cells: guide comparison tables have long prose cells ("Sits on a low
   console just inches from the wall..."), nowrap forced every row onto
   one unbroken line, making the table far wider than its container and
   triggering horizontal scroll even at generous desktop widths. Letting
   cells wrap keeps the table inside guide-content's own width, no scroll
   needed at all in the normal case. */
.guide-content th, .guide-content td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border-soft); word-wrap: break-word; }
.guide-content th { background: var(--surface-alt); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.03em; color: var(--text-faint); }

.guide-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 340px));
  justify-content: center;
  gap: 1rem;
  margin: 1.5rem 0;
}
.guide-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 1.2rem 1.3rem;
  text-decoration: none;
  color: var(--text);
  transition: box-shadow 0.15s, transform 0.15s, border-color 0.15s;
}
.guide-card:hover { box-shadow: var(--shadow-md); transform: translateY(-2px); border-color: var(--accent); }
.guide-card h3 { margin: 0 0 0.4rem; font-size: 1.0625rem; color: var(--text); }
.guide-card p { margin: 0; color: var(--text-muted); font-size: 0.8125rem; }

.related-guide { margin: 0 0 1rem; font-size: 0.875rem; color: var(--text-muted); }

.data-freshness-note {
  background: var(--estimate-bg);
  color: var(--estimate);
  border-radius: 6px;
  padding: 0.6rem 0.85rem;
  font-size: 0.8125rem;
  margin: 0 0 1rem;
}
.data-freshness-note time { font-weight: 600; }

#cookie-banner {
  position: fixed;
  bottom: 1rem;
  left: 50%;
  transform: translateX(-50%);
  width: calc(100% - 2.5rem);
  max-width: var(--max-width);
  background: #171c27;
  border: 1px solid var(--border);
  color: var(--text);
  padding: 1rem 1.25rem;
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  z-index: 100;
  font-size: 0.85rem;
}
#cookie-banner p { margin: 0; max-width: 60ch; color: var(--text-muted); }
#cookie-banner a { color: var(--accent); text-decoration: underline; }
#cookie-banner .cookie-actions { display: flex; gap: 0.5rem; flex-shrink: 0; }
#cookie-banner button {
  border: none;
  border-radius: 6px;
  padding: 0.45rem 0.9rem;
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
  font-family: inherit;
}
#cookie-accept { background: var(--accent); color: #16130a; }
#cookie-reject { background: transparent; color: var(--text-muted); border: 1px solid var(--border); }

/* Mobile product cards */
.mobile-sort-bar, .mobile-cards { display: none; }
.mobile-sort-bar {
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1rem;
  font-size: 0.8125rem;
  color: var(--text-muted);
}
.mobile-sort-bar select {
  flex: 1;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.875rem;
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
}
.product-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.9rem;
  margin-bottom: 0.75rem;
}
.product-card-top { display: flex; gap: 0.75rem; margin-bottom: 0.65rem; }
.product-card-top img.thumb {
  width: 100px;
  height: 68px;
  object-fit: contain;
  flex-shrink: 0;
  background: var(--photo-bg);
  border: 1px solid var(--photo-border);
  box-shadow: var(--photo-shadow);
  border-radius: 8px;
  padding: 0.35rem;
}
.product-card-top .product-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 0.875rem;
}
.product-card-stats { display: flex; gap: 0.5rem; margin-bottom: 0.65rem; }
.product-card-stats .stat { flex: 1; background: var(--bg-alt); border-radius: 8px; padding: 0.45rem 0.5rem; text-align: center; }
.product-card-stats .stat-label { display: block; font-size: 0.6875rem; color: var(--text-faint); text-transform: uppercase; letter-spacing: 0.02em; }
.product-card-stats .stat-value { display: block; font-size: 0.9375rem; font-weight: 700; }
.product-card-stats .stat-value.unit-price { color: var(--accent); }
.product-card-stats .stat-value.estimated { color: var(--estimate); }
.card-cta { display: block; text-align: center; width: 100%; padding: 0.65rem; }

/* Table-vs-cards toggle uses its own, wider breakpoint (900px) than the
   typography scale-down below (640px, genuine phone widths). Real problem
   found in review: the product-table's 9 columns (image, title, resolution,
   brightness, contrast, price, rating, bought, CTA) need real room, at
   700-900px wide they were technically not overflowing (table-layout:fixed
   prevents that) but numbers were wrapping mid-value ("1,500,000" breaking
   across lines) and were basically illegible. Below 900px, stacked cards
   read far better than a cramped table at any width. */
@media (max-width: 900px) {
  .table-scroll { display: none; }
  .mobile-sort-bar { display: flex; }
  .mobile-cards { display: block; }
}

@media (max-width: 640px) {
  h1 { font-size: 1.875rem; }
  h2 { font-size: 1.4375rem; }
  h3 { font-size: 1.125rem; }
  body { font-size: 0.875rem; }
  .hero h1 { font-size: 1.875rem; }
  .hero { padding: 2.5rem 0 2rem; }
}
