@charset "utf-8";
/* ==========================================================================
   LowTox Review, design system
   Consumer-testing publication. Cool ink on warm paper, one petrol accent.

   ACCENT LAW: the petrol teal is editorial only. It marks scores, verdicts,
   winner cells, links and the focus ring. It never sells anything:
   commercial calls to action are solid ink. If an element makes money, or
   discloses that we make money from it, it is ink.

   Order: 1 tokens, 2 reset, 3 base type, 4 layout, 5 focus,
          6..19 components, 20 utilities, 21 gallery, 22 print.
   ========================================================================== */

/* ==========================================================================
   1. TOKENS. Every declared token is referenced by a component.
   ========================================================================== */

:root {

  /* --- Type families ---------------------------------------------------- */
  --font-head: "Inter Tight", "Inter", -apple-system, BlinkMacSystemFont,
               "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-body: "Source Serif 4", "Source Serif Pro", Charter,
               "Bitstream Charter", "Iowan Old Style", "Palatino Linotype",
               Georgia, "Times New Roman", serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
               "Liberation Mono", monospace;

  /* --- Paper ramp (warm off-white) -------------------------------------- */
  --paper-000: #ffffff;   /* card and table faces                           */
  --paper-100: #fbf9f5;   /* the page itself                                */
  --paper-200: #f5f2ec;   /* quiet band, table head, callouts               */
  --paper-300: #ede8de;   /* footer band                                    */

  /* --- Ink ramp (cool near-black) --------------------------------------- */
  /* Measured on paper-100. 900..500 are safe for text at any size.
     400 and lighter FAIL AA as text: rules, tracks and marks only.         */
  --ink-900: #0e1013;     /* display, headlines            18.28:1          */
  --ink-800: #181b20;     /* body text                     16.42:1          */
  --ink-700: #2b3038;     /* strong secondary text         12.61:1          */
  --ink-600: #3e434b;     /* secondary text                 9.47:1          */
  --ink-500: #565c66;     /* muted text, meta, captions     6.40:1          */
  --ink-400: #7c828c;     /* NOT TEXT. Bar fills, marks.    3.87:1          */
  --ink-300: #b3b5b8;     /* strong hairline                                */
  --ink-200: #d3d2d1;     /* default hairline                               */
  --ink-100: #e5e3e0;     /* faint hairline, bar track                      */

  /* --- Accent ramp (deep petrol teal). EDITORIAL ONLY, never commercial.
     Built in OKLCH on one hue, H=201, which sits 53 degrees from Vove's
     sage #4E6A51 and 58 degrees from Vove's cobalt #3470cb: nearly
     equidistant, and far enough from both that neither can be read into it.
     It also carries 1.7x the chroma of the muted sage. ------------------- */
  --accent-050: #e6fbfc;  /* whisper tint: winner cells, TOC, how we test,
                             verdict wash                                  */
  --accent-100: #cdf6f8;  /* pick badge fill, cited source highlight        */
  --accent-200: #adedf1;  /* selection                                      */
  --accent-300: #87e3e9;  /* tag border                                     */
  --accent-400: #009fa7;  /* quiet underline               3.06:1           */
  --accent-600: #00787e;  /* core accent   5.01:1 on paper, 5.27:1 on white */
  --accent-700: #006267;  /* accent text and hover          6.79:1          */
  --accent-800: #005054;  /* accent text on accent-100      7.97:1          */

  /* --- Semantic aliases -------------------------------------------------- */
  --paper: var(--paper-100);
  --ink: var(--ink-800);
  --rule: var(--ink-200);
  --rule-faint: var(--ink-100);
  --rule-strong: var(--ink-300);

  /* --- Type scale (root 16px) -------------------------------------------- */
  --fs-3xs: 0.6875rem;  /* 11px  table micro labels        */
  --fs-2xs: 0.75rem;    /* 12px  tags, small caps          */
  --fs-xs:  0.8125rem;  /* 13px  disclosure, footnotes     */
  --fs-sm:  0.875rem;   /* 14px  table cells, meta         */
  --fs-base:1rem;       /* 16px  UI default                */
  --fs-md:  1.0625rem;  /* 17px  intro UI, list text       */
  --fs-lg:  1.125rem;   /* 18px  article body, mobile      */
  --fs-xl:  1.1875rem;  /* 19px  article body, desktop     */
  --fs-2xl: 1.375rem;   /* 22px  standfirst, card title    */
  --fs-3xl: 1.75rem;    /* 28px  section heading           */
  --fs-4xl: 2.25rem;    /* 36px  article h1, mobile        */
  --fs-5xl: 3rem;       /* 48px  article h1, desktop       */
  --fs-6xl: 3.75rem;    /* 60px  overall score display     */

  /* --- Leading and tracking ---------------------------------------------- */
  --lh-tight: 1.08;
  --lh-snug:  1.22;
  --lh-ui:    1.4;
  --lh-body:  1.68;     /* generous leading for the serif  */
  --tr-tight: -0.02em;
  --tr-head:  -0.011em;
  --tr-flat:   0;
  --tr-caps:   0.09em;
  --tr-caps-wide: 0.15em;

  /* --- Spacing (4px base). A complete scale, used as a scale. ------------ */
  --sp-1: 0.25rem;  --sp-2: 0.5rem;   --sp-3: 0.75rem;  --sp-4: 1rem;
  --sp-5: 1.25rem;  --sp-6: 1.5rem;   --sp-7: 1.75rem;  --sp-8: 2rem;
  --sp-10: 2.5rem;  --sp-12: 3rem;    --sp-16: 4rem;

  /* --- Radii. One scale, three roles, nothing ad hoc per component. ------ */
  --r-sm: 6px;     /* tags, badges, table outer container                   */
  --r-md: 8px;     /* controls: buttons, inputs, search, images             */
  --r-lg: 12px;    /* cards and modules                                     */
  --r-pill: 999px; /* score bars and delta rails                            */

  /* --- Shadows. A sticky column edge, and one small lift for hover. ------ */
  --shadow-sticky: 5px 0 7px -6px rgba(14, 16, 19, 0.22);
  --shadow-1: 0 1px 2px rgba(14, 16, 19, 0.07), 0 2px 6px rgba(14, 16, 19, 0.05);

  /* --- Measures ---------------------------------------------------------- */
  --wrap: 72rem;        /* full page container            */
  --measure: 40rem;     /* article column                 */
  --measure-ch: 68ch;   /* hard character cap on any prose column */
  --gutter: var(--sp-5);

  /* --- Controls ----------------------------------------------------------- */
  --control-h: 2.75rem; /* 44px. Every button, input and search box.      */

  /* --- Borders ----------------------------------------------------------- */
  --hair: 1px solid var(--rule);
  --hair-faint: 1px solid var(--rule-faint);
  --hair-strong: 1px solid var(--rule-strong);
  --border-heavy: 2px solid var(--ink-800);

  /* --- Motion ------------------------------------------------------------ */
  --t-fast: 90ms ease-out;
}

@media (min-width: 56rem) {
  :root { --gutter: var(--sp-8); }
}

/* ==========================================================================
   2. RESET
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: var(--sp-6);
}

body {
  margin: 0;
  /* clip, not hidden: clip does not create a scroll container, so the
     sticky first column inside .ctable-wrap keeps working. */
  overflow-x: clip;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-lg);
  line-height: var(--lh-body);
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-synthesis-weight: none;
}

img, svg, video { max-width: 100%; height: auto; display: block; }
figure { margin: 0; }

button, input, select, textarea { font: inherit; color: inherit; margin: 0; }

table { border-collapse: collapse; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ==========================================================================
   3. BASE TYPOGRAPHY
   ========================================================================== */

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-head);
  color: var(--ink-900);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  line-height: var(--lh-snug);
  margin: 0 0 var(--sp-3);
  text-wrap: balance;
}

h1 { font-size: var(--fs-4xl); line-height: var(--lh-tight); letter-spacing: var(--tr-tight); }
h2 { font-size: var(--fs-3xl); }
h3 { font-size: var(--fs-2xl); }
h4 { font-size: var(--fs-md); font-weight: 600; }
h5, h6 { font-size: var(--fs-sm); font-weight: 600; }

@media (min-width: 56rem) {
  h1 { font-size: var(--fs-5xl); }
}

p { margin: 0 0 var(--sp-5); }
p:last-child { margin-bottom: 0; }

a { color: var(--ink-800); }

strong, b { font-weight: 700; }

ul, ol { margin: 0 0 var(--sp-5); padding-left: 1.35em; }
li { margin-bottom: var(--sp-2); }
li:last-child { margin-bottom: 0; }

code, kbd, samp { font-family: var(--font-mono); font-size: 0.9em; }

::selection { background: var(--accent-200); color: var(--ink-900); }

/* ==========================================================================
   4. LAYOUT
   ========================================================================== */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Every column of running prose is capped in BOTH rem and ch, so a large
   root size cannot blow the measure. */
.article-body { max-width: min(var(--measure), var(--measure-ch)); }

.article-body > * + h2 { margin-top: var(--sp-12); }
.article-body > * + h3 { margin-top: var(--sp-8); }

@media (min-width: 56rem) {
  .article-body { font-size: var(--fs-xl); }
}

/* ==========================================================================
   5. FOCUS. Accent ring everywhere, never the browser default.
   ========================================================================== */

:where(a, button, input, select, textarea, summary, [tabindex]):focus {
  outline: 2px solid var(--accent-600);
  outline-offset: 2px;
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus:not(:focus-visible) {
  outline: none;
}

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent-600);
  outline-offset: 2px;
  border-radius: var(--r-md);
}

/* Filled controls get a paper ring inside the accent ring, so the focus
   state is legible against a dark fill as well as against the page. */
.btn--primary:focus-visible,
.skip-link:focus-visible {
  outline: 2px solid var(--accent-600);
  outline-offset: 0;
  box-shadow: inset 0 0 0 2px var(--paper-000);
}

.skip-link {
  position: absolute;
  left: var(--sp-3);
  top: var(--sp-3);
  z-index: 50;
  transform: translateY(-200%);
  background: var(--ink-900);
  color: var(--paper-000);
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-md);
  text-decoration: none;
}
.skip-link:focus-visible { transform: translateY(0); }

/* ==========================================================================
   6. SITE HEADER. Masthead, nav, standing facts and disclosure are ONE
      banner landmark, so the disclosure is never orphaned outside it.
   ========================================================================== */

.site-header { background: var(--paper); }

.masthead { padding-block: var(--sp-5) var(--sp-3); }

.masthead__top {
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* The wordmark IS the logo. Typographic, no device, no lockup. */
.wordmark {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-3);
  text-decoration: none;
  color: var(--ink-900);
  font-family: var(--font-head);
  line-height: 1;
}

.wordmark__mark {
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: -0.035em;
}

.wordmark__sep {
  width: 1px;
  align-self: stretch;
  background: var(--ink-300);
  margin-block: 0.1em;
}

.wordmark__sub {
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: var(--tr-caps-wide);
  text-transform: uppercase;
  color: var(--ink-600);
}

.wordmark:hover .wordmark__mark { color: var(--ink-800); }

.masthead__tagline {
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  line-height: var(--lh-ui);
  letter-spacing: var(--tr-flat);
  color: var(--ink-500);
  margin: var(--sp-2) 0 0;
  max-width: min(34rem, 62ch);
}

@media (min-width: 56rem) {
  .masthead { padding-block: var(--sp-8) var(--sp-4); }
  .masthead__top {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-8);
  }
  .wordmark__mark { font-size: 2.25rem; }
  .wordmark__sub { font-size: 0.875rem; }
}

/* --- Category nav --------------------------------------------------------- */

.nav {
  margin-top: var(--sp-4);
  padding-top: var(--sp-2);
  border-top: var(--hair-faint);
}

.nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  column-gap: var(--sp-5);
  row-gap: 0;
}

.nav__list li { margin: 0; }

.nav__link {
  display: inline-block;
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-700);
  text-decoration: none;
  padding-block: var(--sp-2);
  border-bottom: 2px solid transparent;
  transition: color var(--t-fast), border-color var(--t-fast);
}

.nav__link:hover { color: var(--ink-900); border-bottom-color: var(--ink-300); }

.nav__link[aria-current="page"] {
  color: var(--ink-900);
  border-bottom-color: var(--accent-600);
}

@media (min-width: 56rem) {
  .nav__link { font-size: var(--fs-xs); }
  .nav__list { column-gap: var(--sp-7); }
}

/* --- Search --------------------------------------------------------------- */

/* The hairline is an inset shadow, not a border, so the children stretch to
   the FULL --control-h instead of --control-h minus 2px of border. */
.searchbox {
  display: flex;
  align-items: stretch;
  min-height: var(--control-h);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--rule-strong);
  border-radius: var(--r-md);
  background: var(--paper-000);
  max-width: 22rem;
  width: 100%;
}

/* The wrapper ring is scoped to the INPUT, so focusing the button does not
   draw a second ring around the whole box. Where :has() is unsupported the
   input keeps its own global ring instead. */
@supports selector(:has(*)) {
  .searchbox__input:focus,
  .searchbox__input:focus-visible { outline: none; }

  .searchbox:has(.searchbox__input:focus-visible) {
    outline: 2px solid var(--accent-600);
    outline-offset: 1px;
    box-shadow: inset 0 0 0 1px var(--ink-600);
  }
}

.searchbox__input {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  background: transparent;
  padding: 0 var(--sp-3);
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  color: var(--ink-800);
}

.searchbox__input::placeholder { color: var(--ink-500); opacity: 1; }

.searchbox__btn {
  flex: 0 0 auto;
  min-height: var(--control-h);
  border: 0;
  border-left: var(--hair);
  background: var(--paper-200);
  color: var(--ink-800);
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  padding-inline: var(--sp-4);
  cursor: pointer;
  transition: background var(--t-fast);
}

.searchbox__btn:hover { background: var(--paper-300); box-shadow: var(--shadow-1); }

/* ==========================================================================
   7. TOP DISCLOSURE. Directly under the masthead, inside the banner
      landmark, hairline rule above and below, linked to the ownership
      block. Not dismissible.
   ========================================================================== */

.disclosure {
  background: var(--paper-200);
  border-top: var(--hair);
  border-bottom: var(--hair);
}

.disclosure__text {
  margin: 0;
  padding-block: var(--sp-3);
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  line-height: var(--lh-ui);
  letter-spacing: 0.01em;
  color: var(--ink-700);
}

.disclosure__mark {
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  font-size: var(--fs-3xs);
  color: var(--ink-900);
  margin-right: var(--sp-2);
}

.disclosure__link {
  color: var(--ink-700);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--accent-600);
}
.disclosure__link:hover { color: var(--ink-900); text-decoration-color: var(--accent-700); }

/* ==========================================================================
   8. STANDING FACTS BAND. Rule bound, tabular figures. Standing policy
      only: nothing here may imply a history the publication does not have.
   ========================================================================== */

.standing {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin: 0;
  border-bottom: var(--hair);
  font-variant-numeric: tabular-nums;
}

.standing__item {
  padding: var(--sp-3) var(--sp-4) var(--sp-3) 0;
  border-top: var(--hair-faint);
}

.standing__item:nth-child(-n + 2) { border-top: 0; }
.standing__item + .standing__item { border-left: var(--hair-faint); padding-left: var(--sp-4); }
.standing__item:nth-child(odd) { border-left: 0; padding-left: 0; }

.standing dt {
  font-family: var(--font-head);
  font-size: var(--fs-3xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-500);
  margin-bottom: var(--sp-1);
}

.standing dd {
  margin: 0;
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: var(--lh-ui);
  color: var(--ink-800);
}

@media (min-width: 48rem) {
  .standing { grid-template-columns: repeat(4, 1fr); }
  .standing__item { border-top: 0; }
  .standing__item + .standing__item { border-left: var(--hair-faint); padding-left: var(--sp-5); }
  .standing__item:nth-child(odd) { border-left: var(--hair-faint); padding-left: var(--sp-5); }
  .standing__item:first-child { border-left: 0; padding-left: 0; }
}

/* ==========================================================================
   9. BUTTONS AND LINKS
      Commercial calls to action are INK. The accent never sells.
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--control-h);
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.2;
  padding: var(--sp-2) var(--sp-5);
  border-radius: var(--r-md);
  border: 1px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--t-fast), color var(--t-fast),
              border-color var(--t-fast);
}

/* Primary = the commercial action. Solid ink, paper text. 18.28:1. */
.btn--primary {
  background: var(--ink-900);
  border-color: var(--ink-900);
  color: var(--paper-000);
}
.btn--primary:hover { background: var(--ink-700); border-color: var(--ink-700); box-shadow: var(--shadow-1); }
.btn--primary:active { background: var(--ink-800); border-color: var(--ink-800); }

.btn--secondary {
  background: transparent;
  border-color: var(--ink-800);
  color: var(--ink-800);
}
.btn--secondary:hover { background: var(--ink-900); border-color: var(--ink-900); color: var(--paper-000); box-shadow: var(--shadow-1); }

.btn--block { width: 100%; }
.btn--sm { font-size: var(--fs-2xs); padding-inline: var(--sp-4); }

.btn[aria-disabled="true"], .btn:disabled {
  background: var(--ink-100);
  border-color: var(--ink-300);
  color: var(--ink-600);
  cursor: not-allowed;
}

/* Text link. Ink with a real underline, accent only on hover.
   .link--quiet varies ONLY the underline: it never switches family or size,
   so it is safe in running serif and inherits the grotesk in UI modules. */
.link {
  color: var(--ink-800);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--accent-600);
  transition: color var(--t-fast), text-decoration-color var(--t-fast);
}
.link:hover { color: var(--accent-700); text-decoration-color: var(--accent-700); }

.link--quiet { text-decoration-color: var(--accent-400); }

.link--arrow::after { content: " \2192"; text-decoration: none; }

.article-body a:not(.btn):not(.cite) {
  color: var(--ink-800);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--accent-600);
}
.article-body a:not(.btn):not(.cite):hover {
  color: var(--accent-700);
  text-decoration-color: var(--accent-600);
}

/* ==========================================================================
   10. OWNERSHIP FLAG. Marks a product the publisher sells, inside the data.
       Ink, never accent: this is a disclosure, not an endorsement.
   ========================================================================== */

.owns-flag {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-family: var(--font-head);
  font-size: var(--fs-3xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-700);
  background: var(--paper-000);
  border: 1px solid var(--ink-300);
  border-radius: var(--r-sm);
  padding: 1px var(--sp-2);
  white-space: nowrap;
}

/* A drawn square, not a text glyph, so no screen reader announces a
   character before the words "We sell this". */
.owns-flag::before {
  content: "";
  flex: 0 0 auto;
  width: 0.55em;
  height: 0.55em;
  background: var(--ink-600);
}

/* ==========================================================================
   11. ARTICLE HEADER
   ========================================================================== */

.article-header { padding-block: var(--sp-8) var(--sp-6); }

.kicker {
  display: block;
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--accent-700);
  margin-bottom: var(--sp-3);
}

/* The class carries the type, not the tag, so the heading level can follow
   the document outline without changing how the page looks. */
.article-header__title {
  font-size: var(--fs-4xl);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-tight);
  margin-bottom: var(--sp-4);
  max-width: 22ch;
}

@media (min-width: 56rem) {
  .article-header__title { font-size: var(--fs-5xl); max-width: 20ch; }
}

.standfirst {
  font-size: var(--fs-2xl);
  line-height: 1.45;
  color: var(--ink-600);
  max-width: min(var(--measure), 56ch);
  margin: 0 0 var(--sp-6);
  text-wrap: pretty;
}

.byline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-4);
  padding-block: var(--sp-4);
  border-top: var(--hair);
  border-bottom: var(--hair);
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  color: var(--ink-500);
}

.byline__author { font-weight: 600; color: var(--ink-800); }
.byline__item { white-space: nowrap; }
.byline__item time { font-variant-numeric: tabular-nums; }

/* --- Table of contents ---------------------------------------------------- */

.toc {
  margin-top: var(--sp-6);
  padding: var(--sp-5);
  background: var(--accent-050);
  border: var(--hair);
  border-radius: var(--r-lg);
  max-width: min(var(--measure), var(--measure-ch));
}

.toc__title {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-600);
  margin: 0 0 var(--sp-3);
}

.toc__list {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: toc;
}

.toc__list li {
  counter-increment: toc;
  margin: 0;
  border-top: var(--hair-faint);
}
.toc__list li:first-child { border-top: 0; }

.toc__link {
  display: flex;
  gap: var(--sp-3);
  padding-block: var(--sp-2);
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  color: var(--ink-800);
  text-decoration: none;
}

.toc__link::before {
  content: counter(toc, decimal-leading-zero);
  flex: 0 0 auto;
  color: var(--ink-500);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xs);
  padding-top: 0.15em;
}

.toc__link:hover { color: var(--accent-700); text-decoration: underline; text-underline-offset: 0.18em; }

/* ==========================================================================
   12. VERDICT CARD. Top of every comparison.
   ========================================================================== */

/* Capped so the reason column and the aside stay in proportion instead of
   the sentence stranding a third of the card as dead air. */
.verdict {
  background: var(--accent-050);
  border: var(--hair);
  border-top: 3px solid var(--accent-600);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  padding: var(--sp-5);
  display: grid;
  gap: var(--sp-5);
  max-width: 56rem;
}

.verdict__label {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--accent-700);
  margin: 0 0 var(--sp-2);
}

.verdict__name {
  font-family: var(--font-head);
  font-size: var(--fs-2xl);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  line-height: var(--lh-snug);
  color: var(--ink-900);
  margin: 0 0 var(--sp-2);
}

.verdict__flag { margin-bottom: var(--sp-3); }

.verdict__reason {
  margin: 0;
  font-size: var(--fs-md);
  line-height: 1.55;
  color: var(--ink-700);
  max-width: 60ch;
  text-wrap: pretty;
}

/* Mobile: a single stacked column, score row then full width button then
   note. No side by side alignment below 44rem. */
.verdict__aside {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-4);
  padding-top: var(--sp-5);
  border-top: var(--hair-faint);
}

.verdict__score {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
}

.verdict__score .score__caption { margin-bottom: 0; }

.verdict__actions {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.verdict__note {
  font-family: var(--font-head);
  font-size: var(--fs-3xs);
  line-height: var(--lh-ui);
  color: var(--ink-500);
  margin: 0;
  max-width: 44ch;
}

@media (min-width: 44rem) {
  .verdict {
    grid-template-columns: minmax(0, 1fr) minmax(15rem, 20rem);
    align-items: start;
    padding: var(--sp-6) var(--sp-7);
    gap: var(--sp-8);
  }
  .verdict__aside {
    gap: var(--sp-5);
    padding-top: 0;
    border-top: 0;
    border-left: var(--hair-faint);
    padding-left: var(--sp-7);
  }
  .verdict__score { display: block; }
  .verdict__score .score__caption { margin-bottom: var(--sp-2); }
}

/* ==========================================================================
   13. SCORE DISPLAY
   ========================================================================== */

.score {
  display: flex;
  align-items: baseline;
  gap: 0.1em;
  margin: 0;
  font-family: var(--font-head);
  color: var(--accent-600);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

/* A score that is not the pick sits in ink, so the accent stays scarce. */
.score--neutral { color: var(--ink-800); }

.score__val {
  font-size: var(--fs-4xl);
  font-weight: 700;
  letter-spacing: -0.03em;
}

.score__den {
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--ink-500);
}

.score__caption {
  display: block;
  font-family: var(--font-head);
  font-size: var(--fs-3xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-500);
  margin-bottom: var(--sp-2);
}

.score--xl .score__val { font-size: var(--fs-6xl); }
.score--xl .score__den { font-size: var(--fs-2xl); }
.score--sm .score__val { font-size: var(--fs-2xl); }
.score--sm .score__den { font-size: var(--fs-sm); }

/* ==========================================================================
   14. SCORECARD
   ========================================================================== */

.scorecard {
  background: var(--paper-000);
  border: var(--hair);
  border-radius: var(--r-lg);
  padding: var(--sp-5);
  max-width: 34rem;
}

.scorecard__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-bottom: var(--sp-5);
  border-bottom: var(--border-heavy);
}

.scorecard__title {
  font-family: var(--font-head);
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  line-height: var(--lh-snug);
  color: var(--ink-900);
  margin: 0;
}

/* Siblings of the heading, not children of it: the accessible name of the
   h3 is the product name alone, not name plus date plus disclosure. */
.scorecard__sub {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 500;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-500);
  margin: var(--sp-2) 0 0;
}

.scorecard__flag { margin: var(--sp-3) 0 0; }

/* The overall block. The qualifier sits with the number, never apart from
   it, so the figure cannot be read without its caveat. */
.scorecard__overall {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-2);
}

.scorecard__overall .score__caption { margin-bottom: 0; }

/* A partly scored product is NOT ranked, so its overall drops out of the
   editorial accent into ink. The accent is reserved for a score that can
   be compared with the rest of the table. Put .scorecard--partly on the
   card and the number de-ranks itself, rather than relying on whoever
   renders it to remember. */
.scorecard--partly .score { color: var(--ink-700); }
.scorecard--partly .score__den { color: var(--ink-500); }

/* A criterion that could not be scored shows an empty track and n/a, so the
   gap in the weighting is visible in the same place as the scores. */
.criteria__row--unscored .criteria__label { color: var(--ink-600); }
.criteria__val--na { color: var(--ink-500); font-weight: 400; }

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

.criteria__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 4.5rem 2.75rem;
  align-items: center;
  gap: var(--sp-4);
  padding-block: var(--sp-3);
  border-bottom: var(--hair-faint);
  margin: 0;
}

@media (min-width: 30rem) {
  .criteria__row { grid-template-columns: minmax(0, 1fr) 9rem 3rem; }
}

.criteria__label {
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  color: var(--ink-700);
}

/* Every displayed row carries its own published weight, so the overall
   score can be recomputed from what is on the page. */
.criteria__weight {
  display: block;
  font-size: var(--fs-3xs);
  font-weight: 400;
  color: var(--ink-500);
  letter-spacing: var(--tr-flat);
  text-transform: none;
}

.criteria__bar {
  height: 6px;
  background: var(--ink-100);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.criteria__bar > i {
  display: block;
  height: 100%;
  width: var(--val, 0%);
  background: var(--accent-600);
}

.criteria__val {
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--ink-900);
  text-align: right;
}

.criteria__val span { color: var(--ink-500); font-weight: 400; }

.scorecard__foot {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  gap: var(--sp-4);
  padding-top: var(--sp-5);
  font-family: var(--font-head);
}

@media (min-width: 30rem) {
  .scorecard__foot { grid-template-columns: minmax(0, 1fr) auto; }
}

.scorecard__method {
  font-size: var(--fs-xs);
  line-height: var(--lh-ui);
  color: var(--ink-500);
  margin: 0;
}

/* ==========================================================================
   15. COMPARISON TABLE
   ========================================================================== */

.ctable-block { margin: 0; }

/* Caption sits OUTSIDE the scroll container so it never scrolls off. */
.ctable-block__cap {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-500);
  line-height: var(--lh-ui);
  margin: 0 0 var(--sp-3);
  padding: 0;
  border: 0;
}

.ctable-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: var(--hair);
  border-radius: var(--r-sm);
  background: var(--paper-000);
  scroll-snap-type: x proximity;
  scroll-padding-left: 7.5rem;
}

.ctable-wrap:focus-visible { outline-offset: 0; }

/* Right edge fade, driven BY the scroll position so it clears itself at the
   end of the scroll instead of permanently dimming the last column. A
   registered <number> is animated rather than the gradient itself, because a
   number interpolates reliably where a gradient may not.

   Gated on OVERFLOW, not on viewport. A scroll timeline whose scroller has
   nothing to scroll is inactive, so the animation contributes nothing and
   --edge-alpha rests at its initial value. That is why the initial value is
   the CLEAR end and the keyframes supply the fade: a ten column table gets
   the affordance at 1280, while a four column table that fits gets none at
   any width. Without scroll-driven animation support there is no fade
   anywhere and the scroll-snap points remain the affordance. */
@property --edge-alpha {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

@keyframes ctable-edge-fade {
  0%, 80% { --edge-alpha: 0.08; }
  100%    { --edge-alpha: 1; }
}

@supports (animation-timeline: scroll(self x)) {
  .ctable-wrap {
    mask-image: linear-gradient(to right,
                  rgb(0 0 0 / 1) calc(100% - 1.75rem),
                  rgb(0 0 0 / var(--edge-alpha)) 100%);
    animation: ctable-edge-fade 1s linear both;
    animation-timeline: scroll(self x);
  }
}

/* Fixed layout: every product column is ALWAYS exactly equal, so the
   publisher's own column can never claim extra width by carrying an extra
   mark in its header.

   COLUMN COUNT CONTRACT: whatever renders a table declares the number of
   product columns on the table element, style="--cols: 7". The default of 4
   only covers a table that forgot to declare it. --colmin is the floor per
   column: as soon as cols x colmin exceeds the container, the table grows
   past it and .ctable-wrap scrolls, instead of crushing 10 columns into
   45px of mid-word breaks. --rail is the sticky criteria column. */
.ctable {
  --rail: 7.5rem;
  --colmin: 8.5rem;
  border-collapse: separate;
  border-spacing: 0;
  table-layout: fixed;
  width: 100%;
  min-width: calc(var(--rail) + (var(--cols, 4) * var(--colmin)));
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-ui);
  color: var(--ink-800);
}

.ctable th, .ctable td {
  text-align: left;
  padding: var(--sp-3);
  vertical-align: middle;
  overflow-wrap: break-word;
}

/* Every product column: an equal share of what is left after the rail. */
.ctable thead th + th { width: calc((100% - var(--rail)) / var(--cols, 4)); }

/* Head. Top aligned so thumbs and brand names line up across columns even
   when one column carries extra flags. */
.ctable thead th {
  background: var(--paper-200);
  vertical-align: top;
  font-weight: 600;
  color: var(--ink-900);
  box-shadow: inset 0 -2px 0 var(--ink-800);
  padding-top: var(--sp-4);
  padding-bottom: var(--sp-4);
  scroll-snap-align: start;
}

.ctable thead th.is-winner-col { box-shadow: inset 0 -2px 0 var(--accent-600); }

.ctable thead th.is-owned {
  box-shadow: inset 0 -2px 0 var(--ink-800),
              inset 1px 0 0 var(--ink-300),
              inset -1px 0 0 var(--ink-300);
}

.ctable thead th.is-winner-col.is-owned {
  box-shadow: inset 0 -2px 0 var(--accent-600),
              inset 1px 0 0 var(--ink-300),
              inset -1px 0 0 var(--ink-300);
}

.ctable__brand {
  display: block;
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  color: var(--ink-900);
  margin-top: var(--sp-2);
}

.ctable__prod {
  display: block;
  font-size: var(--fs-3xs);
  font-weight: 400;
  color: var(--ink-500);
  margin-top: 2px;
}

/* Marks stack under the brand block and wrap freely, so they never set the
   column's min-content width. No inline style patches in the markup. */
.ctable__marks {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
}

/* Scoped to the table itself, not just to .ctable__marks, so ANY badge a
   page drops into a cell wraps inside the column instead of overhanging
   its neighbour. A two word badge fits 8.5rem on one line and a longer one
   takes two, either way inside its own cell. */
.ctable .tag,
.ctable .owns-flag {
  white-space: normal;
  max-width: 100%;
}

/* Honest placeholder for the product shot. No filters, no duotone. */
.thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  background: var(--paper-000);
  border: var(--hair);
  border-radius: var(--r-md);
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--ink-500);
  letter-spacing: var(--tr-flat);
}

.thumb img { width: 100%; height: 100%; object-fit: contain; }

/* Body */
.ctable tbody th, .ctable tbody td { border-bottom: var(--hair-faint); }
.ctable tbody tr:last-child th, .ctable tbody tr:last-child td { border-bottom: 0; }

.ctable tbody th[scope="row"] {
  font-weight: 600;
  color: var(--ink-700);
  font-size: var(--fs-sm);
}

.ctable__unit {
  display: block;
  font-size: var(--fs-3xs);
  font-weight: 400;
  color: var(--ink-500);
  margin-top: 1px;
}

/* Sticky first column. Opaque background is mandatory. */
.ctable th:first-child,
.ctable td:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  width: var(--rail);
  scroll-snap-align: none;
}

.ctable thead th:first-child {
  z-index: 3;
  background: var(--paper-200);
  box-shadow: inset 0 -2px 0 var(--ink-800),
              inset -1px 0 0 var(--rule),
              var(--shadow-sticky);
}

.ctable tbody th:first-child,
.ctable tbody td:first-child {
  background: var(--paper-000);
  box-shadow: inset -1px 0 0 var(--rule), var(--shadow-sticky);
}

/* Only the rail changes across breakpoints. The per-column share and the
   table minimum both derive from it, so they follow automatically. */
@media (min-width: 30rem) {
  .ctable { --rail: 9.5rem; }
  .ctable th, .ctable td { padding: var(--sp-3) var(--sp-4); }
  .ctable-wrap { scroll-padding-left: 9.5rem; }
}

@media (min-width: 44rem) {
  .ctable { --rail: 12rem; }
  .ctable-wrap { scroll-padding-left: 12rem; }
}

/* Owned column. Ink brackets, no tint, so the commercial relationship is
   readable down the column without competing with the score colour. */
.ctable tbody td.is-owned {
  box-shadow: inset 1px 0 0 var(--ink-300), inset -1px 0 0 var(--ink-300);
}

/* Winner cell. Accent tint plus weight. The delta rail carries the mark.
   The whisper step, not the stronger one: a table full of saturated blocks
   stops reading as data. The rail and the weight do the work. */
.ctable td.is-winner {
  background: var(--accent-050);
  color: var(--ink-900);
  font-weight: 700;
}

.ctable tbody td.is-owned.is-winner {
  background: var(--accent-050);
  box-shadow: inset 1px 0 0 var(--ink-300), inset -1px 0 0 var(--ink-300);
}

.ctable td.is-winner .ctable__unit { font-weight: 400; color: var(--ink-600); }

/* --- Delta rail. Each scored cell shows its value against the row best,
       measured from a fixed left rail, so the row reads as a spread and a
       competitor winning a row is structurally visible. ------------------- */
.delta {
  display: block;
  height: 4px;
  margin-top: var(--sp-2);
  background: var(--ink-100);
  border-radius: var(--r-pill);
  overflow: hidden;
}

.delta > i {
  display: block;
  height: 100%;
  width: var(--pct, 0%);
  background: var(--ink-400);
}

.is-winner .delta > i { background: var(--accent-600); }

/* Score row */
.ctable .row-score td { padding-block: var(--sp-4); }
.ctable .row-score .score__val { font-size: var(--fs-3xl); }
.ctable .row-score .score__den { font-size: var(--fs-2xs); }
.ctable .row-score td.is-winner { font-weight: 400; }

/* Yes / no cells. No colour coding, contrast only. */
.val-yes { font-weight: 700; color: var(--ink-900); }
.val-no  { font-weight: 400; color: var(--ink-500); }

.ctable__note {
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  line-height: var(--lh-ui);
  color: var(--ink-500);
  margin: var(--sp-3) 0 0;
  max-width: min(var(--measure), 76ch);
}

/* ==========================================================================
   16. PROS AND CONS
   ========================================================================== */

.proscons {
  display: grid;
  gap: 0;
  border: var(--hair);
  border-radius: var(--r-lg);
  background: var(--paper-000);
  overflow: hidden;
}

.proscons__col { padding: var(--sp-5); }
.proscons__col + .proscons__col { border-top: var(--hair); }

.proscons__title {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-600);
  margin: 0 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: var(--border-heavy);
}

.proscons__list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--ink-800);
}

.proscons__list li {
  position: relative;
  padding-left: var(--sp-6);
  padding-block: var(--sp-2);
  border-bottom: var(--hair-faint);
  margin: 0;
}
.proscons__list li:last-child { border-bottom: 0; padding-bottom: 0; }

.proscons__list li::before {
  position: absolute;
  left: 0;
  top: var(--sp-2);
  font-weight: 700;
  color: var(--ink-500);
}

.proscons__list--pro li::before { content: "+"; }
.proscons__list--con li::before { content: "\2212"; }

@media (min-width: 44rem) {
  .proscons { grid-template-columns: 1fr 1fr; }
  .proscons__col + .proscons__col { border-top: 0; border-left: var(--hair); }
}

/* ==========================================================================
   17. HOW WE TEST
   ========================================================================== */

.howwetest {
  background: var(--accent-050);
  border: var(--hair);
  border-top: var(--border-heavy);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
  padding: var(--sp-5);
  max-width: min(var(--measure), var(--measure-ch));
  font-family: var(--font-head);
}

.howwetest__title {
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-900);
  margin: 0 0 var(--sp-3);
}

.howwetest__body {
  font-size: var(--fs-sm);
  line-height: 1.6;
  color: var(--ink-700);
  margin: 0 0 var(--sp-4);
}

.howwetest__facts {
  list-style: none;
  margin: 0 0 var(--sp-4);
  padding: 0;
  display: grid;
  gap: 0;
  border-top: var(--hair);
}

.howwetest__facts li {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-block: var(--sp-2);
  border-bottom: var(--hair-faint);
  font-size: var(--fs-xs);
  margin: 0;
}

.howwetest__facts .k { color: var(--ink-500); }
.howwetest__facts .v { color: var(--ink-900); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ==========================================================================
   18. CITATIONS AND SOURCES
   ========================================================================== */

/* line-height 1, not 0: a zero height box makes the 50% offsets below
   resolve against nothing, which puts the hit area in the wrong place. At
   0.68em superscripted the marker still sits inside the existing leading,
   so the running serif baseline does not move. */
.cite {
  position: relative;
  font-family: var(--font-head);
  font-size: 0.68em;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  vertical-align: super;
  line-height: 1;
  color: var(--ink-600);
  text-decoration: none;
  padding-inline: 0 0.12em;
  white-space: nowrap;
}

/* Invisible hit area, centred on the marker and never smaller than 24x24,
   so the target clears the minimum without disturbing the line box. */
.cite::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 24px;
  height: 24px;
  translate: -50% -50%;
}

.cite:hover { color: var(--accent-700); text-decoration: underline; }

.sources {
  border-top: var(--border-heavy);
  padding-top: var(--sp-4);
  margin-top: var(--sp-10);
  max-width: min(var(--measure), var(--measure-ch));
}

.sources__title {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-600);
  margin: 0 0 var(--sp-4);
}

.sources__list {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: src;
}

.sources__item {
  counter-increment: src;
  position: relative;
  padding-left: var(--sp-8);
  padding-block: var(--sp-3);
  border-bottom: var(--hair-faint);
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  line-height: 1.55;
  color: var(--ink-700);
  margin: 0;
  max-width: 66ch;
}
.sources__item:last-child { border-bottom: 0; }

.sources__item::before {
  content: "[" counter(src) "]";
  position: absolute;
  left: 0;
  top: var(--sp-3);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--ink-500);
}

.sources__item a {
  display: inline-block;
  color: var(--ink-900);
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  text-decoration-color: var(--accent-600);
}
.sources__item a:hover { color: var(--accent-700); text-decoration-color: var(--accent-600); }

.sources__item cite { font-style: normal; }
.sources__item .sources__meta { display: block; color: var(--ink-500); margin-top: 2px; }

.sources__item:target { background: var(--accent-100); box-shadow: inset 2px 0 0 var(--accent-600); }

/* ==========================================================================
   19. EMAIL SIGNUP
   ========================================================================== */

.signup {
  background: var(--accent-050);
  border: var(--hair);
  border-radius: var(--r-lg);
  padding: var(--sp-6) var(--sp-5);
  max-width: 42rem;
}

.signup__title {
  font-family: var(--font-head);
  font-size: var(--fs-2xl);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  color: var(--ink-900);
  margin: 0 0 var(--sp-3);
}

.signup__body {
  font-size: var(--fs-md);
  line-height: 1.55;
  color: var(--ink-700);
  margin: 0 0 var(--sp-5);
  max-width: 58ch;
}

.signup__form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.field { display: flex; flex-direction: column; gap: var(--sp-2); }

.field__label {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-600);
}

.input {
  width: 100%;
  height: var(--control-h);
  background: var(--paper-000);
  border: var(--hair-strong);
  border-radius: var(--r-md);
  padding: 0 var(--sp-3);
  font-family: var(--font-head);
  font-size: var(--fs-base);
  line-height: 1.2;
  color: var(--ink-900);
}

.input::placeholder { color: var(--ink-500); opacity: 1; }
.input:hover { border-color: var(--ink-500); }

.signup__small {
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  line-height: var(--lh-ui);
  color: var(--ink-500);
  margin: var(--sp-3) 0 0;
  max-width: 66ch;
}

@media (min-width: 40rem) {
  .signup { padding: var(--sp-8) var(--sp-7); }
  .signup__form { flex-direction: row; align-items: flex-end; }
  .signup__form .field { flex: 1 1 auto; }
  .signup__form .btn { flex: 0 0 auto; }
}

/* ==========================================================================
   20. FOOTER AND OWNERSHIP BLOCK
   ========================================================================== */

.site-footer {
  background: var(--paper-300);
  border-top: var(--border-heavy);
  margin-top: var(--sp-16);
  padding-block: var(--sp-10) var(--sp-8);
}

.ownership { max-width: 46rem; scroll-margin-top: var(--sp-6); }

.ownership__title {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-900);
  margin: 0 0 var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: var(--hair-strong);
}

.ownership__body {
  font-size: var(--fs-base);
  line-height: 1.6;
  color: var(--ink-700);
}

.ownership__body p {
  margin-bottom: var(--sp-4);
  max-width: min(var(--measure), var(--measure-ch));
  text-wrap: pretty;
}

.ownership__body h3 {
  font-size: var(--fs-md);
  font-weight: 600;
  color: var(--ink-900);
  margin: var(--sp-6) 0 var(--sp-2);
}

.ownership__body a { color: var(--ink-800); text-decoration-color: var(--accent-600); }

.footer__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-5);
  margin-top: var(--sp-8);
  padding-top: var(--sp-4);
  border-top: var(--hair-strong);
  font-family: var(--font-head);
  font-size: var(--fs-xs);
  color: var(--ink-600);
}

.footer__meta a { color: var(--ink-700); }

/* ==========================================================================
   21. UTILITIES
   ========================================================================== */

.tag {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-head);
  font-size: var(--fs-3xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  border: 1px solid var(--ink-300);
  background: var(--paper-200);
  color: var(--ink-700);
  white-space: nowrap;
}

.tag--win {
  border-color: var(--accent-300);
  background: var(--accent-100);
  color: var(--accent-800);
}

/* Partly scored. Ink, never accent: an incomplete score is not a verdict.
   The dashed rule is the at-a-glance difference from the solid accent pick
   badge, and it is a difference of SHAPE, so it survives both a monochrome
   printer and a reader who cannot separate the two hues. */
.tag--partly {
  border-style: dashed;
  border-color: var(--ink-400);
  background: var(--ink-100);
  color: var(--ink-800);
}

.t-meta {
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  color: var(--ink-500);
}

.t-micro {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--tr-caps);
  text-transform: uppercase;
  color: var(--ink-500);
}

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   22. GALLERY SCAFFOLDING. Not part of the site, used by gallery.html only.
   ========================================================================== */

.gal { padding-block: var(--sp-10) var(--sp-8); }

.gal__intro {
  max-width: 42rem;
  padding-block: var(--sp-8);
  border-bottom: var(--border-heavy);
}

.gal__intro .t-meta { max-width: min(42rem, 74ch); }

.gal__section {
  padding-block: var(--sp-10);
  border-bottom: var(--hair);
}

.gal__section--last { border-bottom: 0; }

.gal__num {
  font-family: var(--font-head);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--tr-caps);
  color: var(--ink-500);
  font-variant-numeric: tabular-nums;
  display: block;
  margin-bottom: var(--sp-2);
}

.gal__h {
  font-family: var(--font-head);
  font-size: var(--fs-2xl);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  color: var(--ink-900);
  margin: 0 0 var(--sp-2);
}

.gal__note {
  font-family: var(--font-head);
  font-size: var(--fs-sm);
  line-height: var(--lh-ui);
  color: var(--ink-500);
  max-width: min(44rem, 78ch);
  margin: 0 0 var(--sp-6);
}

.gal__subhead {
  font-family: var(--font-head);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  color: var(--ink-900);
  margin: var(--sp-10) 0 var(--sp-2);
}

.gal__sublabel { margin: 0 0 var(--sp-3); }
.gal__sublabel--top { margin-top: var(--sp-8); }

.gal__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}

.gal__bodydemo { font-size: var(--fs-lg); max-width: min(var(--measure), var(--measure-ch)); }

.gal__swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: var(--sp-2);
  margin-bottom: var(--sp-6);
}

.gal__sw {
  border: var(--hair-faint);
  border-radius: var(--r-sm);
  overflow: hidden;
  background: var(--paper-000);
}

.gal__sw i { display: block; height: 3rem; }

.gal__sw b {
  display: block;
  font-family: var(--font-head);
  font-size: var(--fs-3xs);
  font-weight: 600;
  color: var(--ink-800);
  padding: var(--sp-2) var(--sp-2) 0;
}

.gal__sw span {
  display: block;
  font-family: var(--font-mono);
  font-size: 10px;
  color: var(--ink-500);
  padding: 0 var(--sp-2) var(--sp-2);
  text-transform: uppercase;
}

.gal__typerow {
  display: flex;
  align-items: baseline;
  gap: var(--sp-4);
  padding-block: var(--sp-3);
  border-bottom: var(--hair-faint);
}

.gal__typerow code {
  flex: 0 0 7.5rem;
  font-size: var(--fs-2xs);
  color: var(--ink-500);
}

.gal__spec {
  font-family: var(--font-head);
  font-weight: 700;
  letter-spacing: var(--tr-head);
  color: var(--ink-900);
}

.gal__spec--6xl { font-size: var(--fs-6xl); letter-spacing: -0.03em; line-height: 1; }
.gal__spec--5xl { font-size: var(--fs-5xl); letter-spacing: var(--tr-tight); line-height: 1.05; }
.gal__spec--3xl { font-size: var(--fs-3xl); }
.gal__spec--body { font-family: var(--font-body); font-weight: 400; font-size: var(--fs-xl); letter-spacing: var(--tr-flat); }
.gal__spec--sm { font-size: var(--fs-sm); font-weight: 400; }
.gal__spec--xs { font-size: var(--fs-xs); font-weight: 400; color: var(--ink-500); }

/* ==========================================================================
   23. PRINT. Backgrounds off, table unrolled, winners re-marked with a
       rule and a glyph, source URLs exposed.
   ========================================================================== */

@media print {

  html { scroll-behavior: auto; }

  body {
    background: #fff;
    color: #000;
    font-size: 10.5pt;
    line-height: 1.45;
    overflow-x: visible;
  }

  .skip-link,
  .searchbox,
  .nav,
  .signup,
  .gal__swatches,
  .gal__typerow { display: none; }

  .site-header, .disclosure, .standing, .verdict, .scorecard,
  .proscons, .howwetest, .toc, .ctable-block { break-inside: avoid; }

  .disclosure { background: transparent; border-block: 1pt solid #000; }
  .disclosure__text, .disclosure__link { color: #000; }

  /* Every tinted surface loses its fill: ink on white only. */
  .site-footer, .toc, .howwetest, .signup, .scorecard, .verdict,
  .proscons, .thumb, .standing { background: transparent; }

  .site-footer { border-top: 1.5pt solid #000; margin-top: var(--sp-8); }
  .toc, .howwetest, .scorecard, .verdict, .proscons { border-color: #000; }

  /* The accent IS the editorial colour, so it prints. Only the surface
     fills come off, never the marks that carry a judgement. */
  .verdict { border-top-color: var(--accent-600); }

  /* --- Table: no scroll container, no sticky column, auto layout -------- */
  .ctable-wrap {
    overflow: visible;
    border: 1pt solid #000;
    border-radius: 0;
    scroll-snap-type: none;
    -webkit-mask-image: none;
    mask-image: none;
  }

  .ctable {
    min-width: 0;
    width: 100%;
    table-layout: auto;
    font-size: 7.5pt;
  }

  .ctable th, .ctable td { padding: 4pt 5pt; background: transparent; }

  .ctable th:first-child,
  .ctable td:first-child {
    position: static;
    width: auto;
    min-width: 0;
    box-shadow: none;
    border-right: 0.75pt solid #000;
  }

  .ctable thead th {
    box-shadow: none;
    border-bottom: 1.25pt solid #000;
    background: transparent;
  }

  /* These selectors outrank the plain resets above, so they are listed
     explicitly. Without them the head band keeps #f5f2ec, the sticky column
     keeps its drop shadow, and the accent winner rule survives, leaving the
     publisher's own column as the only coloured thing on the paper. */
  .ctable thead th:first-child,
  .ctable tbody th:first-child,
  .ctable tbody td:first-child,
  .ctable thead th.is-winner-col,
  .ctable thead th.is-owned,
  .ctable thead th.is-winner-col.is-owned {
    background: transparent;
    box-shadow: none;
  }

  .ctable thead th:first-child { border-bottom: 1.25pt solid #000; }

  /* Winner cells lose their tint, so they are re-marked with a rule and a
     glyph that survives a monochrome printer. Both selectors are listed so
     the reset also beats the higher specificity owned-column rule. */
  .ctable td.is-winner,
  .ctable tbody td.is-owned.is-winner {
    background: transparent;
    box-shadow: none;
    border-left: 1.5pt solid #000;
    font-weight: 700;
  }

  .ctable td.is-winner::before { content: "\25B8\00A0"; }

  .ctable tbody td.is-owned { box-shadow: none; border-inline: 0.75pt solid #000; }
  .ctable tbody td.is-owned.is-winner { border-right: 0.75pt solid #000; }

  .delta { display: none; }

  /* \00A0 not a literal space: CSS eats the whitespace that terminates a
     hex escape, which would glue the glyph to the next word. */
  .ctable-block__cap::after {
    content: " (\25B8\00A0marks the best value in each row. The bracketed column is a product the publisher sells.)";
    font-weight: 400;
    text-transform: none;
    letter-spacing: 0;
  }

  .owns-flag { background: transparent; border-color: #000; color: #000; }
  .tag, .tag--win, .tag--partly { background: transparent; border-color: #000; color: #000; }

  /* Backgrounds are off, so the two badge states are told apart by rule
     style alone: solid for the pick, dashed for a partly scored product. */
  .tag--win { border-style: solid; }
  .tag--partly { border-style: dashed; }

  /* An unranked score must not look ranked on paper either. */
  .scorecard--partly .score, .scorecard--partly .score__den { color: #000; }
  .criteria__row--unscored .criteria__bar { border-style: dashed; }

  .criteria__bar { border: 0.5pt solid #000; background: transparent; }

  /* --- Expose the destination of every off-site link ------------------- */
  .sources__item a[href]::after,
  .article-body a[href^="http"]::after,
  .ownership__body a[href^="http"]::after,
  .ownership__body a[href^="mailto"]::after {
    content: " (" attr(href) ")";
    font-size: 7.5pt;
    font-weight: 400;
    color: #000;
    word-break: break-all;
  }

  .sources__item:target { background: transparent; box-shadow: none; }

  a, .link { color: #000; }
  .score, .kicker, .verdict__label { color: var(--accent-600); }
  .scorecard--partly .score { color: #000; }

  /* The ink fill must go with the ink text, or the button prints black on
     black. Both are forced together. */
  .btn, .btn--primary, .btn--secondary {
    background: transparent !important;
    color: #000 !important;
    border: 0.75pt solid #000 !important;
    min-height: 0;
    padding: 3pt 6pt;
  }
}
