:root {
  --bg: #0f1115;
  --panel: #171a21;
  --panel-2: #1e222b;
  --border: #2a2f3a;
  --text: #e7e9ee;
  --text-dim: #9aa1ae;
  --accent: #4f9dff;
  --accent-dim: #2c5fa8;
  --good: #3ecf8e;
  --bad: #ff5c5c;
  --warn: #f5a623;
  --radius: 10px;
  font-size: 16px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.5;
}

/* Keep ordinary and visited content links readable on the dark background.
   Component-specific navigation and button colors below still take precedence. */
a:link, a:visited {
  color: var(--accent);
  text-decoration-color: currentColor;
  text-underline-offset: 0.15em;
}

a:hover { color: var(--text); }

.site-header {
  padding: 0 24px 18px;
  max-width: 1100px;
  margin: 0 auto;
}

.site-header-bar {
  min-height: 66px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  border-bottom: 1px solid var(--border);
}

.site-brand { display: inline-flex; align-items: center; }

.site-wordmark {
  color: var(--text);
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: -0.035em;
}

.site-wordmark span { color: var(--good); }

.site-logo {
  display: block;
  height: 34px;
  width: auto;
  margin: 0;
  border-radius: 4px;
}

.page-heading { padding-top: 18px; }

.context-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
  color: var(--text-dim);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.breadcrumb {
  margin: 0 0 8px;
  color: var(--text-dim);
  font-size: 0.8rem;
}

.breadcrumb a { color: var(--text-dim); }

.platform-switch {
  display: flex;
  gap: 6px;
  margin: 0;
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: normal;
}

.platform-switch a {
  color: var(--text-dim);
  text-decoration: none;
  padding: 4px 10px;
  border-radius: 999px;
  border: 1px solid transparent;
}

.platform-switch a:hover {
  color: var(--text);
  border-color: var(--border);
}

.platform-switch a.active {
  color: var(--accent);
  background: var(--panel-2);
  border-color: var(--border);
  font-weight: 600;
}

/* task #226: shared top navigation (Cameras / NAS / Guides / Compare /
   About), a slim utility bar placed above <header> (or above .doc/.chooser
   on pages with no <header>) so the same 5 destinations are reachable from
   every page across both site trees. Distinct from .platform-switch, which
   toggles Synology<->QNAP within the SAME catalog -- this is the global
   cross-site destination list. No shared include file in this codebase
   (see build_camera_detail_pages.py's header-template comment), so this
   class is duplicated into every style.css copy (root, qnap/,
   nas_synology/, nas_qnap/) the same way .platform-switch/.site-logo
   already are. */
.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 20px;
  margin: 0;
  padding: 0;
  font-size: 0.86rem;
}

.site-nav a {
  color: var(--text-dim);
  text-decoration: none;
}

.site-nav a:hover {
  color: var(--text);
}

.site-nav a.active {
  color: var(--accent);
  font-weight: 600;
}

/* 2026-09-18: long-form pages (guides/about/methodology/compare/disclosure)
   share the same header via site_chrome.render_doc_header(). Their body
   copy previously had NO link rule at all, so links rendered in the
   browser default #0000EE -- nearly invisible on --bg. */
.doc a { color: var(--accent); text-decoration-color: var(--accent-dim); }
.doc a:hover { color: var(--text); }
.doc { max-width: 760px; margin: 32px auto 60px; padding: 0 24px; }
.doc h2 { font-size: 1.1rem; margin-top: 32px; }
.doc p, .doc li { color: var(--text-dim); line-height: 1.6; }
.doc .dek { color: var(--text-dim); font-size: 0.95rem; margin-bottom: 32px; }
.doc .related { background: var(--panel); border: 1px solid var(--border); border-left: 3px solid var(--accent); border-radius: var(--radius); padding: 16px 20px; margin-top: 40px; }
.doc .related h2 { margin-top: 0; font-size: 1rem; }
.doc .related p { margin: 6px 0; }

.site-header h1 {
  margin: 0 0 6px;
  font-size: clamp(1.55rem, 3vw, 2rem);
  line-height: 1.2;
}

.site-header p.tagline {
  color: var(--text-dim);
  margin: 0;
  max-width: 640px;
}

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 24px 64px;
}

.section-tabs {
  display: flex;
  gap: 8px;
  margin: 24px 0 8px;
}

.section-tabs button {
  background: var(--panel);
  border: 1px solid var(--border);
  color: var(--text-dim);
  padding: 10px 18px;
  border-radius: 999px;
  cursor: pointer;
  font-size: 0.9rem;
  font-weight: 600;
}

.section-tabs button.active {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: #fff;
}

.section-blurb {
  color: var(--text-dim);
  font-size: 0.85rem;
  margin: 0 0 8px;
  max-width: 720px;
}

.filter-group {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 14px 10px;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  align-items: center;
}

.filter-group legend {
  font-size: 0.78rem;
  color: var(--text-dim);
  padding: 0 6px;
}

.filter-group label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 0.82rem;
  color: var(--text);
  margin: 4px 0;
  white-space: nowrap;
}

.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin: 16px 0;
}

.filter-shell {
  position: sticky;
  top: 8px;
  z-index: 10;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.24);
}

.controls label {
  font-size: 0.8rem;
  color: var(--text-dim);
  display: block;
  margin-bottom: 4px;
}

.controls select,
.controls input[type="search"] {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 0.9rem;
  min-width: 180px;
}

.controls .field { display: flex; flex-direction: column; }

.advanced-filters {
  flex: 1 0 100%;
  border-top: 1px solid var(--border);
  padding-top: 10px;
}

.advanced-filters summary,
.info-disclosure summary,
.calc-panel summary {
  cursor: pointer;
  color: var(--text);
  font-weight: 600;
}

.advanced-filter-grid {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  padding-top: 12px;
}

.info-disclosure {
  margin: 24px 0;
  padding: 14px 16px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text-dim);
}

.info-disclosure p { margin: 10px 0 0; }
.secondary-links { color: var(--text-dim); font-size: 0.84rem; }

.controls .checks {
  display: flex;
  gap: 14px;
  align-items: center;
}

.controls .checks label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 0.82rem;
  color: var(--text);
  margin: 0;
  white-space: nowrap;
}

.view-toggle {
  display: flex;
  gap: 4px;
  margin-left: auto;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 3px;
}

.view-toggle button {
  background: transparent;
  border: none;
  color: var(--text-dim);
  padding: 6px 12px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.85rem;
}

.view-toggle button.active {
  background: var(--accent-dim);
  color: #fff;
}

/* --- List view: cards --- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 16px;
}

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.card-thumb {
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: 8px;
  overflow: hidden;
  background: var(--panel-2);
  border: 1px solid var(--border);
}

.card-thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #fff;
}

/* NAS detail-page hero photo (build_nas_pages.py) -- reuses .card-thumb's
   contained/white-background treatment, just capped narrower than a full
   card-grid tile so it doesn't dominate the top of a spec page. */
.detail-hero {
  max-width: 360px;
  margin: 0 0 20px;
}

.card .brand {
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--accent);
  font-weight: 600;
}

.card h3 {
  margin: 0;
  font-size: 1.1rem;
}
/* 2026-11-06: card title + photo link to the camera's model/ page (static
   browse cards already did; main.js cards now do too). Heading keeps its
   own color so the grid doesn't turn into a wall of blue. */
.card h3 a { color: inherit; text-decoration: none; }
.card h3 a:hover, .card h3 a:focus-visible { color: var(--accent); text-decoration: underline; }
.card-thumb a { display: block; }
/* Task #297, 2026-08-24 (Josh's review: "Can we make the 'value' a bar
   with a green, yellow and red zone... with a little arrow over where the
   value stands on the scale?"): replaces the old flat .value-score pill
   (kept below, still used as a fallback wherever a plain-text render is
   simpler) with a 3-zone gauge -- red "Fair" (score < 40), amber "Good"
   (40-69), green "Great" (70+), thresholds intentionally independent of
   the stricter greatValue boolean (build_value_scores.py's score>=80 AND
   confidence>=65 AND coverage>=0.33 AND cohort>=5 gate) since that flag
   answers a different question ("confident this is a standout deal") than
   this bar's ("roughly where does this score sit on the 1-100 scale").
   valueGaugeHtml()/valueBand() in main.js and qnap/main.js (and the
   Python-side twins in build_camera_pages.py) build the markup this
   targets; kept in one place here so both catalogs' generated HTML stays
   visually consistent without duplicating the CSS. */
.value-score { display:inline-block; margin:0 0 9px; padding:4px 8px; border-radius:999px; background:#173f2d; color:#75e2a7; font-size:.76rem; font-weight:700; }
.value-gauge { margin: 0 0 10px; }
.value-gauge-bar { position: relative; height: 8px; border-radius: 999px; display: flex; overflow: visible; }
.value-gauge-zone { height: 100%; }
.value-gauge-zone.fair { width: 40%; background: var(--bad); border-radius: 999px 0 0 999px; }
.value-gauge-zone.good { width: 30%; background: var(--warn); }
.value-gauge-zone.great { width: 30%; background: var(--good); border-radius: 0 999px 999px 0; }
.value-gauge-arrow {
  position: absolute; top: -8px; transform: translateX(-50%);
  font-size: 9px; line-height: 1; color: var(--text);
  text-shadow: 0 0 2px #000, 0 0 2px #000;
}
.value-gauge-meta { display: flex; justify-content: space-between; align-items: baseline; margin-top: 4px; }
.value-gauge-label { font-size: .74rem; font-weight: 700; }
.value-gauge-label.fair { color: var(--bad); }
.value-gauge-label.good { color: var(--warn); }
.value-gauge-label.great { color: var(--good); }
.value-gauge-score { font-size: .72rem; font-weight: 600; color: var(--text-dim); }

.card .specs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 0.78rem;
  color: var(--text-dim);
}

.card .specs span {
  background: var(--panel-2);
  border: 1px solid var(--border);
  padding: 2px 8px;
  border-radius: 999px;
}

/* Browse cards stay scannable; full evidence remains available on the
   dedicated model page. */
#results .card:not(.camera-detail-card) .specs:nth-of-type(2) span:nth-child(n + 6),
.card-grid .card:not(.camera-detail-card) .specs:nth-of-type(2) span:nth-child(n + 6) {
  display: none;
}

.camera-detail-card {
  display: grid;
  grid-template-columns: minmax(260px, 0.9fr) minmax(300px, 1.1fr);
  grid-auto-flow: row;
  align-items: start;
  column-gap: 32px;
  padding: 24px;
}

.camera-detail-card > .card-thumb {
  grid-column: 1;
  grid-row: 1 / span 6;
  max-height: 500px;
  aspect-ratio: 1 / 1;
}

.camera-detail-card > :not(.card-thumb) { grid-column: 2; }
.camera-detail-card .brand { font-size: 0.82rem; }
.camera-detail-card .specs span { display: inline-block !important; }
.camera-detail-card .cta { margin-top: 12px; flex-wrap: wrap; }

.detail-section {
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  max-width: 780px;
}

.detail-section h2 { margin: 0 0 8px; font-size: 1.15rem; }

.nas-detail-hero {
  display: grid;
  grid-template-columns: minmax(260px, 0.85fr) minmax(320px, 1.15fr);
  gap: 32px;
  align-items: start;
  margin-bottom: 28px;
}

.nas-detail-hero .detail-hero { max-width: 460px; margin: 0; }
.nas-detail-summary { min-width: 0; }
.nas-detail-summary .reference-table { margin-top: 12px; }
.nas-detail-summary .cta { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }

.card .notes {
  font-size: 0.88rem;
  color: var(--text-dim);
  border-left: 2px solid var(--accent-dim);
  padding-left: 10px;
}

.card .cta {
  margin-top: auto;
  display: flex;
  gap: 8px;
  align-items: center;
}

.btn-amazon {
  background: #ff9900;
  color: #111;
  font-weight: 600;
  text-decoration: none;
  padding: 8px 14px;
  border-radius: 6px;
  font-size: 0.85rem;
  display: inline-block;
}

.btn-amazon:hover { filter: brightness(1.08); }

.btn-ebay {
  background: #0064d2;
  color: #fff;
  font-weight: 600;
  text-decoration: none;
  padding: 8px 14px;
  border-radius: 6px;
  font-size: 0.85rem;
  display: inline-block;
}

.btn-ebay:hover { filter: brightness(1.12); }

.btn-ebay .ebay-price {
  font-weight: 400;
  opacity: 0.85;
  font-size: 0.78rem;
}

.btn-secondary {
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
}

/* CTA buttons are <a> tags, and the global `a:link, a:visited { color:
   var(--accent) }` rule above (line 29) has HIGHER CSS specificity (0,1,1 --
   element + pseudo-class) than a plain class selector like `.btn-amazon`
   (0,1,0), so it was silently winning and painting every button's text
   light blue (--accent, #4f9dff) instead of its intended color -- exactly
   the "light blue text on the buttons, hard to see" contrast bug Josh
   reported 2026-09-28. Restated here at higher specificity
   (element + class + pseudo-class = 0,2,1) so the intended colors actually
   render; values match .btn-amazon/.btn-ebay/.btn-secondary above exactly,
   nothing else about them changes. Applies identically to every style.css
   in the project (root, qnap/, nas_synology/, nas_qnap/, nas_drives/) since
   all five carry the same global link rule and the same button classes. */
a.btn-amazon:link, a.btn-amazon:visited { color: #111; }
a.btn-ebay:link, a.btn-ebay:visited { color: #fff; }
a.btn-secondary:link, a.btn-secondary:visited { color: var(--text); }

.verified-badge {
  font-size: 0.72rem;
  color: var(--good);
  font-weight: 600;
  white-space: nowrap;
}

.empty-state {
  text-align: center;
  color: var(--text-dim);
  padding: 48px 0;
}

.specs span.off { opacity: 0.35; }

.callout {
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin: 20px 0;
  font-size: 0.9rem;
  color: var(--text-dim);
}

.callout strong { color: var(--text); }

/* Task #225: a prominent cross-promo to the NAS database, distinct from
   .callout's info-box styling (that one explains a fact about this page;
   this one's job is to get clicked). Reused as-is on the QNAP camera page
   too, same treatment both sides. */
.nas-callout {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  background: linear-gradient(135deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin: 20px 0;
}

.nas-callout strong {
  color: var(--text);
  font-size: 0.98rem;
}

.nas-callout p {
  margin: 4px 0 0;
  color: var(--text-dim);
  font-size: 0.85rem;
}

.nas-callout .btn-amazon {
  flex-shrink: 0;
  white-space: nowrap;
}

#pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 24px 0;
  color: var(--text-dim);
  font-size: 0.85rem;
}

#pagination button {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 14px;
  border-radius: 6px;
  cursor: pointer;
}

#pagination button:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Real <a href> prev/next pagination on the static browse/*.html pages
   built by build_camera_pages.py (task #139) -- same visual treatment as
   the JS-driven #pagination buttons above, just anchor tags instead of
   <button>s since these need to work with JS off. .pagination-disabled is
   the boundary state (no prev on page 1 / no next on the last page) --
   plain text, not a link, mirroring #pagination button:disabled's look. */
.pagination-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 24px 0;
  color: var(--text-dim);
  font-size: 0.85rem;
}

.pagination-nav a {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 6px 14px;
  border-radius: 6px;
  text-decoration: none;
}

.pagination-nav .pagination-disabled {
  opacity: 0.4;
}

/* Task #319: bounded set of direct page-number links (first/last, a window
   around the current page, evenly-spaced anchors) beneath the prev/next bar
   -- see build_camera_pages.py's _pagination_jump_targets() docstring for
   the crawl-budget rationale. Deliberately understated (small text, no
   background) so it reads as secondary wayfinding, not a second nav bar. */
.pagination-jump {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: -12px 0 24px;
  font-size: 0.8rem;
}

.pagination-jump a {
  color: var(--text-dim);
  text-decoration: none;
  padding: 2px 6px;
  border-radius: 4px;
}

.pagination-jump a:hover {
  color: var(--text);
  background: var(--panel-2);
}

.pagination-ellipsis {
  color: var(--text-dim);
  opacity: 0.6;
}

.reference-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
  margin-top: 12px;
}

.reference-table th, .reference-table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  text-align: left;
}

.reference-table th { color: var(--text-dim); font-weight: 600; }
.reference-table a { font-weight: 600; }

/* --- Bandwidth/storage recommendation calculator (task #146) --- */
.calc-panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin: 16px 0 20px;
}

.calc-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: flex-end;
}

.calc-field { display: flex; flex-direction: column; }

.calc-field label {
  font-size: 0.8rem;
  color: var(--text-dim);
  margin-bottom: 4px;
}

.calc-field select,
.calc-field input[type="number"] {
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 6px;
  font-size: 0.9rem;
  min-width: 140px;
}

.calc-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin-top: 18px;
}

.calc-stat {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
}

.calc-stat .calc-stat-label {
  font-size: 0.75rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.calc-stat .calc-stat-value {
  font-size: 1.35rem;
  font-weight: 700;
  margin-top: 4px;
}

.calc-stat .calc-stat-sub {
  font-size: 0.78rem;
  color: var(--text-dim);
  margin-top: 2px;
}

.calc-note {
  font-size: 0.8rem;
  color: var(--text-dim);
  margin-top: 14px;
  line-height: 1.5;
}

.calc-fit-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-top: 14px;
}

.calc-fit-table th, .calc-fit-table td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  text-align: left;
}

.calc-fit-table th { color: var(--text-dim); font-weight: 600; }
.calc-fit-table .fit-yes { color: var(--good); font-weight: 600; }
.calc-fit-table .fit-no { color: var(--text-dim); }

/* --- Matrix view: table --- */
.matrix-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
}

table.matrix {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.85rem;
}

table.matrix th,
table.matrix td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  white-space: nowrap;
}

table.matrix thead th {
  position: sticky;
  top: 0;
  background: var(--panel-2);
  color: var(--text-dim);
  font-weight: 600;
}

table.matrix td.camera-name { font-weight: 600; }

.dot {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}

.dot.yes { background: var(--good); }
.dot.no { background: var(--border); }

/* --- Tooltips ---------------------------------------------------------
   CSS-only (no JS beyond setting the data-tooltip attribute itself, which
   main.js already does once per render from TOOLTIP_COPY -- see
   tooltip-copy.js) so this doesn't add any real cost to page generation.
   A [data-tooltip] element gets a floating explanation panel above it on
   hover/keyboard-focus via ::after with content: attr(data-tooltip) --
   the same technique used across both filter-dropdown labels and feature
   badges, just with different trigger-affordance styling for each (see
   below). content: attr() only supports plain text, not markup/line
   breaks, which is why tooltip-copy.json's entries are single paragraphs
   rather than multi-line/rich text. */
[data-tooltip] {
  position: relative;
}

[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 9px);
  transform: translateX(-50%) translateY(4px);
  width: max-content;
  max-width: 280px;
  background: var(--panel-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 9px 11px;
  font-size: 0.76rem;
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
  z-index: 60;
  display: none;
}

/* Small arrow pointing from the panel down to the element it describes. */
[data-tooltip]::before {
  content: "";
  position: absolute;
  left: 50%;
  bottom: calc(100% + 3px);
  transform: translateX(-50%) translateY(4px);
  border: 6px solid transparent;
  border-top-color: var(--border);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease, transform 0.12s ease;
  z-index: 60;
  display: none;
}

[data-tooltip]:hover::after, [data-tooltip]:hover::before,
[data-tooltip]:focus-visible::after, [data-tooltip]:focus-visible::before {
  display: block;
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

/* Filter labels: dotted underline + help cursor signal "hover me" up
   front, since there are only a handful and each is worth noticing. */
.filter-group label[data-tooltip],
.controls label[data-tooltip] {
  border-bottom: 1px dotted var(--text-dim);
  cursor: help;
}

/* Feature badges: help cursor only, no underline -- a card can have 15+
   badges, and underlining all of them would be visual noise rather than a
   useful affordance. */
.card .specs span[data-tooltip] {
  cursor: help;
}

/* A tooltip anchored to a badge near the left/right edge of a narrow card
   would otherwise get clipped by the card's own overflow -- nudge the
   panel to open from that edge instead of centering past it. */
.card .specs span[data-tooltip]:first-child::after,
.card .specs span[data-tooltip]:first-child::before {
  left: 0;
  transform: translateX(0) translateY(4px);
}
.card .specs span[data-tooltip]:first-child:hover::after,
.card .specs span[data-tooltip]:first-child:hover::before {
  transform: translateX(0) translateY(0);
}

footer {
  max-width: 1100px;
  margin: 0 auto;
  padding: 24px;
  color: var(--text-dim);
  font-size: 0.8rem;
  border-top: 1px solid var(--border);
}

/* task #206 (site-review follow-up): About/Methodology/Disclosure links,
   now in every footer across all four site trees (camera SPAs, NAS pages,
   both chooser/landing pages). */
footer .footer-links {
  margin-bottom: 8px;
}
footer .footer-links a {
  color: var(--accent);
}

/* NAS component expansion: compact, crawlable drive-compatibility summary.
   The chart is plain HTML/CSS so it remains useful without JavaScript; JS is
   limited to filtering the already-rendered official HCL table. */
.drive-summary-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 10px;
  margin: 14px 0;
}
.drive-summary-grid > div {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}
.drive-summary-grid strong { color: var(--text); font-size: 1.05rem; }
.drive-summary-grid span { color: var(--text-dim); font-size: 0.78rem; }
.drive-capacity-chart {
  margin: 14px 0 18px;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
}
.drive-chart-title { margin: 0 0 10px; font-weight: 650; }
.drive-chart-row {
  display: grid;
  grid-template-columns: 58px minmax(90px, 1fr) 34px;
  align-items: center;
  gap: 9px;
  margin: 5px 0;
  color: var(--text-dim);
  font-size: 0.78rem;
}
.drive-chart-track {
  display: block;
  height: 8px;
  overflow: hidden;
  border-radius: 999px;
  background: var(--border);
}
.drive-chart-bar {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
}
.drive-filter-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: end;
  gap: 10px 14px;
  margin-top: 14px;
  color: var(--text-dim);
  font-size: 0.8rem;
}
.drive-filter-controls label { display: grid; gap: 4px; }
.drive-filter-controls input,
.drive-filter-controls select {
  min-height: 36px;
  padding: 6px 9px;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel-2);
}
.drive-filter-controls > span { margin-left: auto; padding-bottom: 9px; }
.spec-source { margin-left: 4px; font-size: 0.75rem; white-space: nowrap; }

@media (max-width: 760px) {
  .site-header { padding: 0 16px 14px; }
  .site-header-bar { min-height: 56px; gap: 12px; }
  .site-logo { height: 28px; }
  .site-nav { gap: 6px 12px; font-size: 0.78rem; }
  .site-nav a:nth-last-child(-n + 2) { display: none; }
  .page-heading { padding-top: 14px; }
  .context-row { align-items: flex-start; flex-direction: column; gap: 6px; }
  .site-header h1 { font-size: 1.45rem; }
  main { padding: 0 16px 48px; }
  .section-tabs { overflow-x: auto; margin-top: 14px; }
  .section-tabs button { white-space: nowrap; padding: 8px 14px; }
  .filter-shell { position: static; padding: 12px; }
  .controls .field { flex: 1 1 140px; }
  .controls select, .controls input[type="search"] { width: 100%; min-width: 0; }
  .advanced-filter-grid { align-items: stretch; }
  .filter-group { flex: 1 1 100%; }
  .card-grid { grid-template-columns: 1fr; }
  .camera-detail-card,
  .nas-detail-hero { grid-template-columns: 1fr; gap: 18px; }
  .camera-detail-card > .card-thumb {
    grid-column: 1;
    grid-row: auto;
    max-height: min(70vh, 460px);
  }
  .camera-detail-card > :not(.card-thumb) { grid-column: 1; }
  .nas-callout { align-items: flex-start; }
  .reference-table { display: block; overflow-x: auto; }
  .drive-filter-controls > span { width: 100%; margin-left: 0; padding-bottom: 0; }
  .info-disclosure { overflow: hidden; }
  .info-disclosure:not([open]) > :not(summary),
  .advanced-filters:not([open]) > :not(summary) { display: none; }
}

@media (max-width: 390px) {
  .site-header-bar { align-items: flex-start; padding: 12px 0; }
  .site-nav { justify-content: flex-end; }
  .site-nav a { padding: 2px 0; }
  .platform-switch a { padding: 3px 8px; }
  .camera-detail-card { padding: 14px; }
}

/* 2026-10-27: buy links in the Compatible Memory / card tables (nas_parts.py) */
.part-buy { white-space: nowrap; margin-right: 8px; font-weight: 600; }

/* 2026-10-29: drive "Great Value" badge (drive_value.py). Price-free; links to
   the Great Value section of /nas/drives/best-value.html. */
.value-badge-row { margin: 0 0 12px; display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.value-badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: .78rem; font-weight: 700; text-decoration: none; }
.value-badge.great { background: var(--good); color: #0b1a12; }
.value-badge-row .section-blurb { margin: 0; }

/* 2026-11-06: "Other ... compatible cameras" links on model/ pages */
.related-list { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 6px 16px; }
.related-list li { font-size: 0.95rem; }
.related-list .related-meta { opacity: 0.7; font-size: 0.85rem; }

/* 2026-11-07: buying assessment above the spec table (assessments.py) */
.assessment { border-left: 3px solid var(--accent); padding-left: 16px; margin: 12px 0 20px; }
.assessment h2 { font-size: 1.25rem; }
.assessment h3 { font-size: 1rem; margin: 14px 0 4px; }
.assessment .assessment-verdict { font-size: 1.05rem; margin: 4px 0 10px; }
.assessment ul { margin: 4px 0 8px 18px; padding: 0; }
.assessment li { margin: 2px 0; }
.assessment .assessment-foot { font-size: 0.85rem; opacity: 0.75; margin-top: 10px; }
