/* Discount Verdict
   ------------------------------------------------------------------------------
   Editorial, evidential, quiet. The look is inherited from the original design work
   because that work was right: this should read like a broadsheet investigation, not
   a deals blog and not a tech demo. Serif display, mono for anything measured, deep
   green to affirm, muted red to flag, and nothing shouts.

   Zero dependencies and zero framework, deliberately. The whole site is data tables
   and prose; every kilobyte of JavaScript here would be paid for on 671 pages and
   bought back nothing. Animation is CSS, with one ~15-line IntersectionObserver in
   the layout for scroll reveals, and all of it collapses under prefers-reduced-motion.
*/

:root {
  --ground:      #EDF0EE;
  --surface:     #FFFFFF;
  --surface-alt: #F5F7F5;
  --ink:         #141917;
  --ink-soft:    #444E49;
  --ink-faint:   #78827C;
  --rule:        #D6DCD8;
  --rule-soft:   #E4E9E6;
  --accent:      #0F5C4A;
  --accent-soft: #DFEDE7;
  --bad:         #A8382C;
  --bad-soft:    #F4DFDC;
  --warn:        #8A6410;
  --warn-soft:   #F2E9D2;

  --shadow: 0 1px 2px rgba(20,25,23,.05), 0 10px 30px -20px rgba(20,25,23,.28);
  --shadow-lift: 0 2px 4px rgba(20,25,23,.06), 0 16px 40px -22px rgba(20,25,23,.34);

  --display: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

  --ease: cubic-bezier(.22,.61,.36,1);

  /* Text drawn ON the accent colour. It cannot be a fixed white: the dark theme flips
     --accent to a pale green, where white text falls to roughly 2:1 contrast and is
     unreadable. This flips with it. */
  --on-accent: #FFFFFF;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground:      #0D110F;
    --surface:     #151A18;
    --surface-alt: #1A201D;
    --ink:         #E7EDEA;
    --ink-soft:    #AAB5B0;
    --ink-faint:   #78837E;
    --rule:        #2A322E;
    --rule-soft:   #232A26;
    --accent:      #4FBF9A;
    --accent-soft: #172B25;
    --on-accent:   #0D110F;
    --bad:         #E58174;
    --bad-soft:    #2E1D1A;
    --warn:        #D9AE55;
    --warn-soft:   #2E2717;
    --shadow: 0 1px 2px rgba(0,0,0,.4), 0 10px 30px -20px rgba(0,0,0,.9);
    --shadow-lift: 0 2px 4px rgba(0,0,0,.5), 0 16px 40px -22px rgba(0,0,0,1);
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

.wrap { max-width: 780px; margin: 0 auto; padding: 0 26px; }
.wrap-wide { max-width: 1060px; margin: 0 auto; padding: 0 26px; }

.skip { position: absolute; left: -9999px; background: var(--accent); color: #fff; padding: 10px 16px; z-index: 20; }
.skip:focus { left: 12px; top: 12px; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }

/* ---------------------------------------------------------------- masthead */

.masthead {
  border-bottom: 2px solid var(--ink);
  background: var(--surface);
  position: sticky; top: 0; z-index: 10;
}
.masthead-inner {
  /* Wraps on purpose: below 620px the nav drops to its own line rather than overflowing
     the viewport, and the masthead goes static there so it is free to grow. */
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: 14px;
  padding-top: 20px; padding-bottom: 16px;
}
.wordmark {
  font-family: var(--display); font-size: 25px; font-weight: 600;
  letter-spacing: -.01em; color: var(--ink); text-decoration: none; line-height: 1;
}
.wordmark span { color: var(--accent); }
.masthead nav { display: flex; gap: 22px; font-size: 14.5px; }
.masthead nav a {
  color: var(--ink-soft); text-decoration: none; padding-bottom: 3px;
  border-bottom: 2px solid transparent; transition: color .18s var(--ease), border-color .18s var(--ease);
}
.masthead nav a:hover { color: var(--ink); border-bottom-color: var(--rule); }
.masthead nav a[aria-current] { color: var(--accent); border-bottom-color: var(--accent); }

/* -------------------------------------------------------------------- type */

h1 {
  font-family: var(--display);
  font-size: clamp(32px, 5.2vw, 46px); font-weight: 600;
  line-height: 1.06; letter-spacing: -.022em; text-wrap: balance;
  margin: 54px 0 20px;
}
h2 {
  font-family: var(--display);
  font-size: clamp(24px, 3.4vw, 29px); font-weight: 600;
  line-height: 1.18; letter-spacing: -.012em; text-wrap: balance;
  margin: 52px 0 15px;
}
h3 { font-size: 17px; font-weight: 660; line-height: 1.4; margin: 30px 0 8px; }

.standfirst { font-size: 19.5px; line-height: 1.55; color: var(--ink-soft); margin: 0 0 10px; text-wrap: pretty; }
.kicker {
  font-family: var(--mono); font-size: 11px; letter-spacing: .17em;
  text-transform: uppercase; color: var(--accent); margin: 48px 0 12px;
}
.kicker a { color: inherit; }
p { margin: 0 0 18px; text-wrap: pretty; }
strong { font-weight: 660; }
ul, ol { padding-left: 22px; }
li { margin-bottom: 9px; }
main { padding-bottom: 90px; }

.thesis {
  background: var(--surface); border-left: 3px solid var(--accent);
  padding: 24px 26px; margin: 30px 0;
  font-family: var(--display); font-size: 21px; line-height: 1.5;
  box-shadow: var(--shadow);
}

.panel {
  background: var(--surface); border: 1px solid var(--rule);
  border-top: 2px solid var(--accent);
  padding: 22px 24px; margin: 28px 0;
}
.panel.alarm { border-top-color: var(--bad); }
.panel > :last-child { margin-bottom: 0; }
.panel .h {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink-faint); margin: 0 0 10px;
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
code, .mono { font-family: var(--mono); font-size: .9em; }
pre {
  background: var(--surface); border: 1px solid var(--rule);
  padding: 16px 18px; overflow-x: auto; font-size: 13px; line-height: 1.6;
}
.updated { font-family: var(--mono); font-size: 12px; color: var(--ink-faint); margin-top: 44px; }

/* ------------------------------------------------------------------- stats */

.stat-row {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin: 28px 0;
}
.stat-row > div { background: var(--surface); padding: 19px 19px 17px; }
.stat-row .n {
  display: block; font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 27px; font-weight: 600; letter-spacing: -.035em; line-height: 1.1;
}
.stat-row .n.good { color: var(--accent); }
.stat-row .n.hot { color: var(--bad); }
.stat-row .c { display: block; font-size: 13px; line-height: 1.45; color: var(--ink-soft); margin-top: 7px; }

/* ----------------------------------------------------------- the exhibit
   The single most important component on the site: one product, the price the
   retailer says it used to be, and the price we actually recorded. The original
   design got this right and it deserves to be the first thing anyone sees. */

.exhibit {
  background: var(--surface); border: 1px solid var(--rule);
  box-shadow: var(--shadow); margin: 30px 0 34px; overflow: hidden;
}
.exhibit-head {
  display: flex; flex-wrap: wrap; gap: 18px; justify-content: space-between;
  align-items: flex-start; padding: 22px 26px; border-bottom: 1px solid var(--rule-soft);
}
.exhibit-head h3 { font-family: var(--display); font-size: 21px; font-weight: 600; margin: 0 0 4px; line-height: 1.25; }
.exhibit-head .retailer {
  font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-faint);
}
.stamp {
  font-family: var(--mono); font-size: 11.5px; font-weight: 600; letter-spacing: .12em;
  text-transform: uppercase; padding: 8px 13px; border: 1.5px solid currentColor;
  border-radius: 2px; white-space: nowrap;
}
.stamp.is-bad   { color: var(--bad);    background: var(--bad-soft); }
.stamp.is-warn  { color: var(--warn);   background: var(--warn-soft); }
.stamp.is-good  { color: var(--accent); background: var(--accent-soft); }
.stamp.is-muted { color: var(--ink-faint); background: var(--surface-alt); border-color: var(--rule); }

.exhibit-body { padding: 26px; }
.exhibit-foot {
  display: flex; flex-wrap: wrap; gap: 12px; align-items: baseline;
  background: var(--surface-alt); border-top: 1px solid var(--rule-soft);
  padding: 16px 26px; font-size: 14.5px; color: var(--ink-soft);
}
.exhibit-foot .mark {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* --------------------------------------------------------------- gap bar
   With one day of record there is no line to draw, and drawing one would imply a
   trend we have not observed. But the GAP between the claimed price and the
   recorded price is real on day one, and it is the whole story. So we draw that,
   and it grows into a real chart as the archive fills. */

.gap {
  /* Fallback only. The real figure arrives as a .p0-.p100 class below, NOT as an
     inline style attribute — see the note there before changing this. */
  --pct: 50%;
  position: relative; margin: 4px 0 0;
}

/* Gap width as a class per whole percent.
   ------------------------------------------------------------------------------
   This looks absurd next to `style="--pct: 40%"`, which is what it replaces, and the
   reason is the Content-Security-Policy. `style-src 'self'` forbids inline style
   ATTRIBUTES, and unlike inline <style> blocks they cannot be allowed by hash — the
   only ways to permit them are 'unsafe-inline' or 'unsafe-hashes', both of which
   loosen the policy across the whole site.

   It was not a theoretical objection. On the live site every one of these bars was
   drawing at the 50% fallback above instead of its real figure, because the browser
   was discarding the attribute exactly as instructed. 613 bars, silently wrong, on the
   one graphic that carries the site's entire argument — and invisible in local
   preview, where no CSP header is served. The security control was working; the markup
   was asking for something it was not allowed to have.

   Whole percents: at the width this bar is drawn, a fraction of a percent is well under
   one pixel, and every rule here gzips to almost nothing. */
.gap.p0{--pct:0%}.gap.p1{--pct:1%}.gap.p2{--pct:2%}.gap.p3{--pct:3%}.gap.p4{--pct:4%}
.gap.p5{--pct:5%}.gap.p6{--pct:6%}.gap.p7{--pct:7%}.gap.p8{--pct:8%}.gap.p9{--pct:9%}
.gap.p10{--pct:10%}.gap.p11{--pct:11%}.gap.p12{--pct:12%}.gap.p13{--pct:13%}.gap.p14{--pct:14%}
.gap.p15{--pct:15%}.gap.p16{--pct:16%}.gap.p17{--pct:17%}.gap.p18{--pct:18%}.gap.p19{--pct:19%}
.gap.p20{--pct:20%}.gap.p21{--pct:21%}.gap.p22{--pct:22%}.gap.p23{--pct:23%}.gap.p24{--pct:24%}
.gap.p25{--pct:25%}.gap.p26{--pct:26%}.gap.p27{--pct:27%}.gap.p28{--pct:28%}.gap.p29{--pct:29%}
.gap.p30{--pct:30%}.gap.p31{--pct:31%}.gap.p32{--pct:32%}.gap.p33{--pct:33%}.gap.p34{--pct:34%}
.gap.p35{--pct:35%}.gap.p36{--pct:36%}.gap.p37{--pct:37%}.gap.p38{--pct:38%}.gap.p39{--pct:39%}
.gap.p40{--pct:40%}.gap.p41{--pct:41%}.gap.p42{--pct:42%}.gap.p43{--pct:43%}.gap.p44{--pct:44%}
.gap.p45{--pct:45%}.gap.p46{--pct:46%}.gap.p47{--pct:47%}.gap.p48{--pct:48%}.gap.p49{--pct:49%}
.gap.p50{--pct:50%}.gap.p51{--pct:51%}.gap.p52{--pct:52%}.gap.p53{--pct:53%}.gap.p54{--pct:54%}
.gap.p55{--pct:55%}.gap.p56{--pct:56%}.gap.p57{--pct:57%}.gap.p58{--pct:58%}.gap.p59{--pct:59%}
.gap.p60{--pct:60%}.gap.p61{--pct:61%}.gap.p62{--pct:62%}.gap.p63{--pct:63%}.gap.p64{--pct:64%}
.gap.p65{--pct:65%}.gap.p66{--pct:66%}.gap.p67{--pct:67%}.gap.p68{--pct:68%}.gap.p69{--pct:69%}
.gap.p70{--pct:70%}.gap.p71{--pct:71%}.gap.p72{--pct:72%}.gap.p73{--pct:73%}.gap.p74{--pct:74%}
.gap.p75{--pct:75%}.gap.p76{--pct:76%}.gap.p77{--pct:77%}.gap.p78{--pct:78%}.gap.p79{--pct:79%}
.gap.p80{--pct:80%}.gap.p81{--pct:81%}.gap.p82{--pct:82%}.gap.p83{--pct:83%}.gap.p84{--pct:84%}
.gap.p85{--pct:85%}.gap.p86{--pct:86%}.gap.p87{--pct:87%}.gap.p88{--pct:88%}.gap.p89{--pct:89%}
.gap.p90{--pct:90%}.gap.p91{--pct:91%}.gap.p92{--pct:92%}.gap.p93{--pct:93%}.gap.p94{--pct:94%}
.gap.p95{--pct:95%}.gap.p96{--pct:96%}.gap.p97{--pct:97%}.gap.p98{--pct:98%}.gap.p99{--pct:99%}
.gap.p100{--pct:100%}
.gap-track {
  position: relative; height: 46px; background: var(--surface-alt);
  border: 1px solid var(--rule-soft);
}
.gap-fill {
  position: absolute; inset: 0 auto 0 0; width: var(--pct);
  background: linear-gradient(90deg, var(--accent-soft), var(--accent-soft));
  border-right: 2px solid var(--accent);
  transform-origin: left center; transform: scaleX(0);
  animation: gap-grow 1.1s var(--ease) .15s forwards;
}
.gap-claimed {
  position: absolute; top: -7px; bottom: -7px; right: 0;
  border-right: 2px dashed var(--bad);
}
.gap-labels {
  display: flex; justify-content: space-between; gap: 14px;
  margin-top: 12px; font-family: var(--mono); font-size: 12.5px;
}
.gap-labels .paid { color: var(--accent); font-weight: 600; }
.gap-labels .claimed { color: var(--bad); font-weight: 600; }
.gap-labels small { display: block; font-family: var(--sans); font-size: 11.5px; color: var(--ink-faint); font-weight: 400; letter-spacing: 0; }
.gap-labels .claimed-wrap { text-align: right; }

@keyframes gap-grow { to { transform: scaleX(1); } }

/* ----------------------------------------------------------------- call to action
   One button, directly under the exhibit. The exhibit shows a reader a single striking
   claim; this is the sentence that follows — "there are thousands more, here they are".
   Everything else on the site is a text link, which is why this can be the only button
   and still be obvious. */

.cta-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px;
  margin: 26px 0 6px;
}
.cta {
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--accent); color: var(--on-accent);
  border: 1px solid var(--accent);
  padding: 14px 24px; font-size: 16px; font-weight: 620;
  text-decoration: none; box-shadow: var(--shadow);
  transition: transform .18s var(--ease), box-shadow .18s var(--ease), filter .18s var(--ease);
}
.cta:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); filter: brightness(1.08); }
.cta:active { transform: none; }
.cta .arr { font-size: 18px; line-height: 1; transition: transform .18s var(--ease); }
.cta:hover .arr { transform: translateX(3px); }
.cta-aside { font-size: 14.5px; color: var(--ink-soft); }

/* ------------------------------------------------------------- deal cards */

.card-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px; margin: 28px 0;
}
.deal-card {
  background: var(--surface); border: 1px solid var(--rule); border-left: 3px solid var(--rule);
  padding: 18px 20px 16px; display: flex; flex-direction: column; gap: 13px;
  text-decoration: none; color: inherit;
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s var(--ease);
}
.deal-card:hover, .deal-card:focus-visible { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.deal-card.v-bad   { border-left-color: var(--bad); }
.deal-card.v-warn  { border-left-color: var(--warn); }
.deal-card.v-good  { border-left-color: var(--accent); }
.deal-card.v-muted { border-left-color: var(--rule); }
.dc-top { display: flex; gap: 12px; justify-content: space-between; align-items: flex-start; }
.dc-title { font-size: 15.5px; font-weight: 620; line-height: 1.35; margin: 0 0 3px; text-wrap: balance; }
.dc-retailer { font-family: var(--mono); font-size: 10.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint); }
.dc-prices { display: flex; align-items: baseline; gap: 10px; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.dc-now { font-size: 21px; font-weight: 600; letter-spacing: -.025em; }
.dc-was { font-size: 14px; color: var(--ink-faint); text-decoration: line-through; text-decoration-thickness: 1.5px; }
.dc-pct { margin-left: auto; font-family: var(--sans); font-size: 12.5px; font-weight: 600; color: var(--bad); }

/* ---------------------------------------------------------------- listings */

.retailer-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(235px, 1fr)); gap: 12px; margin: 26px 0; }
.retailer-card {
  display: block; background: var(--surface); border: 1px solid var(--rule);
  border-left: 3px solid var(--accent); padding: 16px 18px; text-decoration: none;
  transition: transform .18s var(--ease), box-shadow .18s var(--ease);
}
.retailer-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }
.rc-name { display: block; font-weight: 640; color: var(--ink); margin-bottom: 3px; }
.rc-meta { display: block; font-size: 13px; color: var(--ink-soft); }

.tbl-wrap { overflow-x: auto; margin: 28px 0; }
table { width: 100%; border-collapse: collapse; font-size: 14.5px; }
th, td { text-align: left; padding: 11px 14px 11px 0; border-bottom: 1px solid var(--rule-soft); vertical-align: top; }
th {
  font-family: var(--mono); font-size: 10.5px; letter-spacing: .11em;
  text-transform: uppercase; color: var(--ink-faint); font-weight: 500;
  border-bottom: 1px solid var(--rule); background: var(--ground);
  /* NOT sticky, and it cannot be.
     ---------------------------------------------------------------------------------
     This used to read `position: sticky; top: 62px`, meaning to pin the column labels
     under the masthead while a long table scrolled. It never worked, and what it did
     instead was visible corruption on every table on the site: the header row landed on
     top of the first row of data, slicing it in half.

     The cause is one line further up — `.tbl-wrap { overflow-x: auto }`. When one
     overflow axis is not `visible`, CSS computes the other to `auto` as well, so the
     wrapper becomes a scroll container, and a scroll container is the containing block
     for any sticky element inside it. The header was therefore sticking to the wrapper,
     a box only as tall as the table itself and one that never scrolls vertically — and
     `top: 62px` pushed it 62px DOWN from the wrapper's top edge, straight onto the data.

     There is no offset that fixes this; the containing block is wrong, not the number.
     The wrapper cannot go either — it is what lets a 640px-wide listing table scroll
     sideways on a phone instead of blowing out the layout. So the sticky header goes.
     It was decoration; the wrapper is function. */
}
table.listing { min-width: 640px; }
table.listing td { padding: 13px 14px 13px 0; }
table.listing tbody tr { transition: background .14s var(--ease); }
table.listing tbody tr:hover td { background: var(--surface); }
table.listing .name { font-weight: 560; line-height: 1.35; }
table.listing .name a { color: var(--ink); text-decoration-color: var(--rule); }
table.listing .name a:hover { color: var(--accent); text-decoration-color: currentColor; }
.row-retailer { display: block; font-family: var(--mono); font-size: 10.5px; letter-spacing: .06em; color: var(--ink-faint); margin-top: 3px; }
table.listing .num { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
table.listing .claim { white-space: nowrap; font-family: var(--mono); font-size: 13px; }
.claim .was { text-decoration: line-through; text-decoration-thickness: 1.5px; color: var(--ink-faint); }
.claim .pct { display: inline-block; margin-left: 8px; color: var(--bad); font-weight: 600; }
.claim .none { font-family: var(--sans); font-size: 13px; color: var(--ink-faint); }

.pill {
  display: inline-block; font-family: var(--mono); font-size: 10px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase; padding: 4px 8px; border-radius: 2px; white-space: nowrap;
}
.pill.is-bad   { color: var(--bad);    background: var(--bad-soft); }
.pill.is-warn  { color: var(--warn);   background: var(--warn-soft); }
.pill.is-good  { color: var(--accent); background: var(--accent-soft); }
.pill.is-muted { color: var(--ink-faint); background: var(--surface-alt); }

.pager { display: flex; align-items: center; gap: 20px; margin: 32px 0; font-size: 14.5px; }
.pager-at { font-family: var(--mono); font-size: 12px; color: var(--ink-faint); }

/* ----------------------------------------------------------- product page */

.verdict-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr));
  gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin: 28px 0;
}
.vcell { background: var(--surface); padding: 18px 20px; }
.vcell dt { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint); margin: 0 0 6px; }
.vcell dd { margin: 0; }
.vcell .fig { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 23px; font-weight: 600; letter-spacing: -.025em; }
.vcell dd.claim .fig { text-decoration: line-through; text-decoration-thickness: 1.5px; color: var(--bad); }
.vcell dd.actual .fig { color: var(--accent); }
.vcell small { display: block; font-size: 12.5px; color: var(--ink-soft); margin-top: 4px; }

.spark { display: block; width: 100%; height: auto; background: var(--surface); border: 1px solid var(--rule); }
.series-line {
  fill: none; stroke: var(--accent); stroke-width: 2.2;
  stroke-linejoin: round; stroke-linecap: round;
  stroke-dasharray: var(--len, 2000); stroke-dashoffset: var(--len, 2000);
  animation: draw 1.5s var(--ease) .2s forwards;
}
.claimed-line { stroke: var(--bad); stroke-width: 2; stroke-dasharray: 7 5; opacity: .85; }
.chart-empty {
  background: var(--surface); border: 1px dashed var(--rule);
  padding: 24px; text-align: center; font-size: 14px; color: var(--ink-faint);
}
@keyframes draw { to { stroke-dashoffset: 0; } }

/* ------------------------------------------------------------ scroll reveal */

.reveal { opacity: 0; transform: translateY(14px); }
.reveal.in { opacity: 1; transform: none; transition: opacity .55s var(--ease), transform .55s var(--ease); }

/* --------------------------------------------------------------------- foot */

.site-foot { border-top: 1px solid var(--rule); background: var(--surface); padding: 32px 0 50px; }
.foot-disclosure {
  font-size: 13.5px; color: var(--ink-soft); background: var(--surface-alt);
  border: 1px solid var(--rule-soft); padding: 14px 16px; margin: 0 0 18px;
}
.foot-nav { display: flex; flex-wrap: wrap; gap: 18px; font-size: 13.5px; margin-bottom: 14px; }
.foot-legal { font-size: 12.5px; color: var(--ink-faint); margin: 0; }

@media (max-width: 620px) {
  body { font-size: 16.5px; }
  .masthead { position: static; }
  /* th sticky was removed entirely - see the note on th above. */
  .exhibit-head, .exhibit-body, .exhibit-foot { padding-left: 18px; padding-right: 18px; }
  .pager { flex-wrap: wrap; gap: 12px; }
  .gap-labels { font-size: 11.5px; }
}

/* Motion is decoration here, never information. Everything above still reads with
   all of it switched off. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  .reveal { opacity: 1; transform: none; }
  .gap-fill { transform: scaleX(1); }
  .series-line { stroke-dashoffset: 0; }
}
