/* The site's own chrome.

   The report body brings its own stylesheet (templates/vendor/_report_css.html,
   vendored from axoba_worker) and injects it into the middle of the page, so on
   /r/<slug> its `:root` block is what the whole document reads its variables
   from. That is the design system: the report is the instrument readout, this
   file is the case around it, and the two share a palette because they share the
   variable names rather than because somebody kept two lists in step.

   Every token below is copied from that file, values and names both. Anything
   this file needs that the report has no opinion on (--display, --r) is named
   differently and survives the override. `--wrap` is the one that stopped being
   in that list: the report publishes `--report-wrap`, this file reads it back
   on a report page, and the two measures step together rather than separately. Class names are still this file's
   own: nothing here targets one of the report's, so a change on that side is a
   change to the report and not to the page around it.

   The report gained a dark mode; this follows it, off the same tokens. */

:root {
  color-scheme: light dark;

  --paper:#F6F8F8; --surface:#FFFFFF; --sunk:#EDF1F1;
  --ink:#14191A; --ink-2:#566163; --ink-3:#7C8789;
  --line:#DFE6E6; --line-2:#C9D4D4;
  --accent:#0E6B75; --accent-ink:#0A5159; --accent-wash:#E2F0F1;
  --dead:#B03024; --unknown:#96690A; --live:#15715A; --live-wash:#E3F2ED;
  /* The fourth verdict colour, and the only token here no public page reads:
     the operator list uses it, and nothing else does. The three above are what
     a scan found - working, doubtful, dead. This one is a different kind of
     fact: a door that is shut rather than a door that is missing, which on
     that list is an address YouTube is holding behind its sign-in. It is blue
     because green is already the answer next to it on the same row, and two
     teals a shade apart is a distinction nobody makes at a glance. */
  --held:#1D5DB8;
  --verdict-bg:#101617; --verdict-fg:#F2F6F6;
  --verdict-dim:#8B9A9C; --verdict-line:#2A3436; --on-fill:#FFFFFF; --edge:rgba(20,25,26,0.16);

  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI Variable Text","Segoe UI",system-ui,"Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,"Cascadia Mono","SF Mono","JetBrains Mono",Menlo,Consolas,"Liberation Mono",monospace;

  --step--2:0.6875rem; --step--1:0.8125rem; --step-0:1.0625rem; --step-1:1.1875rem;
  --step-2:clamp(1.15rem,1.8vw,1.3rem);
  --step-3:clamp(1.7rem,3.4vw,2.25rem);
  --step-4:clamp(2.2rem,6.2vw,3.8rem);

  /* This file's own, and deliberately not one of the report's step names: the
     landing headline is bigger than anything in a report and must not become a
     third opinion about --step-4. */
  --display:clamp(2rem,3.6vw,2.7rem);

  /* The reading measure, for the pages that hold their prose to one. Here
     rather than on the document classes that read it because the scale it
     belongs to is here: a measure in `ch` and the type it is counted in have to
     step in one block or they step apart. Nothing outside `body.report-doc` and
     `body.prose-doc` reads either one.

     --measure-lede is DERIVED and not chosen: it is --measure scaled by
     step-0/step-1, which is the character count that makes the hero exactly as
     wide on the paper as the sections under it. A lede is one step of type
     larger than the prose it introduces, so an equal count of characters would
     have been a wider column, and the same count either side is the only
     version of "line them up" that survives all four steps. It was 44ch, which
     was a 375px ribbon down a 948px sheet on a 13" laptop and a 620px one down
     1344px at 1920 - right about the line length, wrong about the page, and
     worse the day the gated hero grew a second paragraph. */
  --measure:68ch; --measure-lede:60ch;

  /* And the size the scale has no step for. Every label, control, small aside
     and form field on the site is drawn at 0.95rem, which is 0.894 of a
     1.0625rem body - so it is written as that ratio and steps with it. A
     literal here is the one line on a page that stays 15px while everything
     around it grows, which is the bug this whole pass exists to fix. */
  --ui:calc(var(--step-0) * 0.894);

  --gutter:clamp(1.25rem,5vw,3.5rem); --stack:clamp(3rem,7vw,5rem);
  --wrap:66.25rem;   /* 1060px — the report's .wrap, so the chrome lines up with it */
  --narrow:44rem;
  --r:3px; --r-2:2px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper:#0D1112; --surface:#151B1C; --sunk:#1B2223;
    --ink:#E7ECEC; --ink-2:#9DAAAB; --ink-3:#778384;
    --line:#242D2E; --line-2:#33403F;
    --accent:#3FC3CE; --accent-ink:#7FDDE4; --accent-wash:#12292C;
    --dead:#FF7062; --unknown:#E3AC46; --live:#48CC9F; --live-wash:#10261F;
    --held:#79B8FF;
    --verdict-bg:#060909; --verdict-fg:#EDF3F3;
    --verdict-dim:#7E8D8F; --verdict-line:#222C2D; --on-fill:#0B1516; --edge:rgba(0,0,0,0.55);
  }
}

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: var(--step-0)/1.62 var(--sans);
  -webkit-font-smoothing: antialiased;
  /* The one string on these pages this file did not write is the channel
     somebody pasted, and it is printed on /scan/<id> and on the account list.
     A 90-character URL with nothing in it to break on is a phone scrolling
     sideways; `break-word` breaks one only when it would otherwise overflow,
     so no line anybody wrote is affected. */
  overflow-wrap: break-word;
}

/* Layout */

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

/* Wide monitors. 1060px is the report's width and the reason this file has one
   at all; away from /r/<slug> there is no report to line up with, and a 27"
   monitor showing a 1060px column in the middle of 2560px of paper is a measure
   nobody chose. Steps rather than a vw expression because every paragraph on
   the site is capped in `ch` already: what the extra width buys is the hero,
   the card grid and the footer columns, and each of those wants a container
   that stops somewhere known. `.narrow` is left alone here rather than
   untouched: the pages that need their prose column to move say so themselves
   below, and the two that do it do it differently, because a sheet with tables
   on it and a sheet with nothing but sentences on it do not want the same
   container. */
@media (min-width: 90rem)  { :root { --wrap: 76rem; } }   /* ≥1440px viewport */
@media (min-width: 120rem) { :root { --wrap: 84rem; } }   /* ≥1920px viewport */
@media (min-width: 160rem) { :root { --wrap: 92rem; } }   /* ≥2560px viewport */

/* On the findings page the report decides, and this line is how it says so.
   The report's stylesheet is included from further down the document than this
   file can reach; it now publishes its own measure as `--report-wrap` and takes
   the same two steps this file does, so deferring to it lines the chrome up
   with the body at every width instead of at one. Widening it is a change to
   the worker's _report_css.html, which is vendored here and not ours to edit
   (CONTEXT.md, "What is vendored, not owned").

   THE FALLBACK IS THE LOAD-BEARING HALF. A report published before that
   stylesheet existed is served from its stored HTML, which carries the old
   `max-width:1060px` written into `.wrap` as a literal and defines no
   `--report-wrap` at all — and most published findings pages are still in that
   state until the worker republishes them (app/mirror.py, MIN_CONTEXT_VERSION).
   Missing variable, 66.25rem, chrome pinned to a body that cannot follow it.
   Present variable, the report's own steps. Nothing to keep in step by hand and
   nothing to remember on the day the last legacy page is republished. */
body.report-page { --wrap: var(--report-wrap, 66.25rem); }

/* And the two published reports this repo renders itself. `clean` and `gated`
   were built as prose pages - `wrap narrow`, 44rem, at every width there is -
   which was right about the line length and wrong about the page: 44rem on a
   2560px monitor is a 704px ribbon down the middle of about a quarter of the
   screen, and the one element on either page that genuinely wants room, the
   table of dead links, was being squeezed into a third of that ribbon.

   `--narrow` is remapped rather than the templates being re-classed, because
   the hero, the "that's yours now" banner, the main and the removal band are
   four separate `wrap narrow` elements: move one and its left edge stops
   lining up with the other three. One declaration moves all four, and every
   `.narrow` on every other page is untouched.

   The caps under it are the other half and are not optional. The narrow
   container WAS the prose measure on these pages - nothing else capped a
   paragraph - so widening it without them hands a big monitor a 170-character
   line. This is how the findings report has always worked: the container steps
   and the line length is held in `ch`, which is a cap on characters and not on
   pixels - see the scale below, which is what makes that distinction pay. */
body.report-doc { --narrow: var(--wrap); }
body.report-doc .page > section > p,
body.report-doc .page > section > ul { max-width: var(--measure); }
body.report-doc .page > section > h2 { max-width: 32ch; }
body.report-doc .hero .lede { max-width: var(--measure-lede); }

/* The headings, and the trap `/accuracy` hit from the other side: a `ch` cap
   and the container step at the same rate, so a cap drawn on a narrow page
   binds just as hard at 2560px and a rule aimed at a big monitor can make the
   big monitor worse. 24ch put "Is this your channel, and you'd rather this page
   didn't exist?" on three lines at every width there is; 22ch - which `.hero
   h1` sets for the LANDING hero, where the headline shares its row with the
   readout panel and has about 640px to live in - put "6 of your videos point
   at a page that isn't there." on three. These two pages have the whole sheet
   and nothing beside it, so they cap where display type actually stops reading
   well rather than where the landing grid runs out. Both are still counts of
   characters: on a phone the viewport binds first and neither is reached, so
   this is a wide-screen change that cannot move a narrow one. */
body.report-doc .hero h1 { max-width: 30ch; }

/* And `/accuracy`, which is the same problem with the other half missing. It is
   the page the whole "unlisted reports about people who didn't ask" position
   rests on - linked from every report and from the footer, and written to be
   found by someone annoyed at the moment they are annoyed - and on a 27" monitor
   it was a 592px ribbon of 17px type down the middle of 2560px of paper. The one
   argument nobody is obliged to read, set smaller than everything around it.

   It takes the scale the two report documents take (the media blocks below name
   both) and a container it does not share with them, because the content is not
   the same. Those pages have a table and a CTA panel that genuinely want the
   whole sheet; this one has nothing on it but sentences. Handing it `--wrap`
   would have parked an 80-character column against the left edge of a 92rem
   sheet with 600px of nothing beside it - wider paper, same ribbon, now
   off-centre.

   So here the container IS the measure. `.wrap` is border-box and pads by
   `--gutter`, so a max-width of measure-plus-two-gutters makes the content box
   exactly `--measure` across and leaves the column centred, which is what a page
   of prose wants. Derived rather than written out, and that is the whole point
   of the calc: the measure takes four steps and a container written as a number
   would have to be kept in step with it by hand at three widths at once.

   Below 90rem this lands on about the 44rem this page has always been - that
   container was already roughly 68 characters wide, which is where the base
   measure came from - so laptops, tablets and phones are the page they were.

   Two declarations and no per-element caps, which is the other thing that falls
   out of the container being the measure. `clean` and `gated` need a cap on every
   paragraph because their container is the whole sheet and the sheet is much
   wider than a line should be; here there is nothing between the measure and
   the text to cap. Capping the headings on top of it was tried and taken back
   out: a `ch` cap and the container step at the same rate, so it binds at every
   width rather than only on a big monitor, and 24ch turned the one heading long
   enough to matter - "What the numbers are, and are not" - back into two lines
   at 2560px, where the wider column had just given it one. `text-wrap: balance`
   is already on every heading and is the right tool for that job. */
body.prose-doc { --narrow: calc(var(--measure) + var(--gutter) * 2); }

/* And the text steps with the sheet. Every page, one scale, `:root`.

   It got here in three passes and the first two were scoped, so the reasoning
   is worth writing down rather than the result.

   Pass one stepped only the scale, on the reasoning that every cap on a
   document page is in `ch` and a `ch` resolves against the font size it is set
   on: grow the type and the column grows with it, without a single line getting
   longer. True, and not enough - the type grew 12% while the sheet grew 27%, so
   the prose got RELATIVELY NARROWER on every screen it was supposed to be
   adapting to. Pass two stepped the measure beside it.

   Pass three is this one, and it is the opposite kind of fix: not a page that
   was wrong but a SITE that was three sizes. The scale was scoped to
   `body.report-doc` because of one real obstacle - the landing page's readout
   panel was built out of literal `rem` sizes no token could reach, so stepping
   `:root` would have grown the copy around a panel that stayed still. That
   obstacle is gone: the panel's three literals, and every other one in this
   file, are ratios of `--step-0` now (see `--ui` above and the panel below).
   With nothing left that cannot step, the scope has no reason to be narrower
   than the site, and a reader moving from the landing page to a report to
   `/accuracy` was meeting 17px, 22px and 22px on the same monitor.

   The ramp is deliberately gentler than the one it replaces: 17px -> 18 -> 19
   -> 20, where the document pages alone used to go 17 -> 19 -> 21 -> 22. That
   was sized for a page you sit down to read; it is now also the size of the
   landing page's chrome and of every form on the site, and 22px body copy on a
   sign-up form reads as a zoomed browser rather than as a design. Medium, and
   the same medium everywhere.

   The measure steps with it (68ch -> 72 -> 76 -> 80), which is what keeps the
   column at about the same share of the sheet at all four widths instead of
   thinning out as the paper grows. 80 characters is the end of what a line
   reads well at, and filling the sheet with text is not the goal: a column is
   allowed to stop, and what is left over is margin.

   Two things this file cannot reach, and both are by design:

   - The findings page takes its scale from the report's own stylesheet, which
     is also the standalone report.html the worker writes to disk - a creator
     may be sent both, and they must not be two documents (CONTEXT.md, "What is
     vendored, not owned"). That file carries this same ramp, edited in the
     worker and re-vendored, and `test_web.py` compares the two token for token.
   - A report published before that stylesheet existed is served from its stored
     HTML, whose own `:root` is later in the document than this one and wins.
     Those pages keep the scale they were published with - 17px at every width -
     until the worker republishes them (app/mirror.py, MIN_CONTEXT_VERSION).
     Nothing here can change that and nothing here should try.

   --step-4 is stepped here and used by nothing this file draws. It is in the
   list because this file's scale is the report's scale, name for name and value
   for value (see the top of this file), and a token that quietly disagrees is
   how two files that must match stop matching. */
@media (min-width: 90rem) {
  :root {
    --step--2: 0.75rem; --step--1: 0.875rem; --step-0: 1.125rem; --step-1: 1.25rem;
    --step-2: 1.375rem; --step-3: 2.4rem; --step-4: 4rem;
    --display: 2.875rem;
    --measure: 72ch; --measure-lede: 64ch;
  }
}
@media (min-width: 120rem) {
  :root {
    --step--2: 0.8125rem; --step--1: 0.9375rem; --step-0: 1.1875rem; --step-1: 1.3125rem;
    --step-2: 1.4375rem; --step-3: 2.55rem; --step-4: 4.25rem;
    --display: 3rem;
    --measure: 76ch; --measure-lede: 68ch;
  }
}
/* --step--2 stops growing here, in step with the report's own stylesheet: it is
   the mono label in a nowrap column there, and the two files carry one scale. */
@media (min-width: 160rem) {
  :root {
    --step--2: 0.8125rem; --step--1: 1rem; --step-0: 1.25rem; --step-1: 1.375rem;
    --step-2: 1.5rem; --step-3: 2.7rem; --step-4: 4.5rem;
    --display: 3.15rem;
    --measure: 80ch; --measure-lede: 72ch;
  }
}

/* Every band on the page is separated by a hairline rather than by a colour
   change, which is how the report separates its own sections. */
.band { padding-block: var(--stack); border-top: 1px solid var(--line); }
.band.sunk { background: var(--sunk); }

/* Type */

h1, h2, h3 { text-wrap: balance; }
/* --display and not --step-4: the report body's own headline is the biggest
   type on the site and every headline this file sets sits under it. */
h1 { font-size: var(--display); line-height: 1.06; letter-spacing: -0.032em; font-weight: 800; margin: 0 0 1rem; }
h2 { font-size: var(--step-3); line-height: 1.14; letter-spacing: -0.024em; font-weight: 700; margin: 2.75rem 0 0.75rem; }
h3 { font-size: var(--step-2); line-height: 1.3; letter-spacing: -0.015em; font-weight: 650; margin: 2rem 0 0.4rem; }
p { margin: 0 0 1rem; }

.lede { font-size: var(--step-1); color: var(--ink-2); max-width: 46ch; line-height: 1.55; }
.lede b, .lede strong { color: var(--ink); font-weight: 650; }

/* The mono uppercase label is the report's data voice. It names a section; it
   never carries a fact the sentence under it does not repeat. */
.eyebrow {
  font-family: var(--mono); font-size: var(--step--2); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.15em; color: var(--ink-3);
  margin: 0 0 0.75rem;
}
.fineprint { color: var(--ink-2); font-size: var(--step--1); }
.aside { color: var(--ink-2); font-size: var(--ui); }
.quote { border-left: 2px solid var(--line-2); padding-left: 1rem; color: var(--ink-2); }
.num { font-variant-numeric: tabular-nums; }

code {
  font-family: var(--mono); font-size: 0.88em;
  background: var(--sunk); border: 1px solid var(--line);
  padding: 0.08em 0.35em; border-radius: var(--r-2);
}

a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { text-decoration: none; }
:is(a, button, input, textarea, summary):focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r-2);
}

.sec-title { display: flex; flex-direction: column; margin-bottom: 2rem; }
/* On the heading and not on the block around it: `ch` resolves against the
   element's own font-size, and a measure set on the wrapper is a measure in
   body-copy characters applied to display type. */
.sec-title h2 { margin: 0; max-width: 22ch; }
.sec-title p { margin: 0.6rem 0 0; color: var(--ink-2); max-width: 58ch; }
.sec-note { margin-top: 1.5rem; }
.prose { max-width: 66ch; }

/* Every page that is only text. Its own class rather than `main`, because on
   /r/<slug> the report body brings a <main> of its own and it is not this. */
.page { padding-block: clamp(2.25rem, 5vw, 3.25rem) 0; }

/* The line above a headline that says which channel, and when. Mono because it
   is data, and not `.eyebrow` because a channel's name is not ours to shout. */
.meta {
  display: flex; flex-wrap: wrap; gap: 0.45rem 1.1rem; align-items: baseline;
  font-family: var(--mono); font-size: var(--step--1); color: var(--ink-3);
  margin: 0 0 1.15rem;
}
.meta b { color: var(--ink); font-weight: 600; letter-spacing: 0.02em; }

/* A verdict, in one word. Dead unless it says otherwise. */
.tag {
  display: inline-flex; align-items: center;
  font-family: var(--mono); font-size: var(--step--2); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.11em;
  padding: 0.28rem 0.55rem; border-radius: var(--r-2);
  background: var(--dead); color: var(--on-fill);
}
.tag.ok { background: var(--live); }
.tag.q { background: var(--unknown); }

ul.plain { list-style: none; padding: 0; margin: 1rem 0; }
ul.plain li { padding: 0.85rem 0; border-bottom: 1px solid var(--line); }
ul.plain li:last-child { border-bottom: 0; }
.reasons { padding-left: 1.15rem; }
.reasons li { margin-bottom: 0.7rem; }

.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;
}
.skip {
  position: fixed; left: 0.75rem; top: -3rem; z-index: 10;
  background: var(--ink); color: var(--paper); border-radius: var(--r);
  padding: 0.6rem 0.9rem; font-size: var(--step--1); text-decoration: none;
  transition: top 0.15s ease;
}
.skip:focus { top: 0.75rem; }

/* Nav and footer */

.site-head { border-bottom: 1px solid var(--line); background: var(--paper); }
.site-nav {
  display: flex; align-items: center; gap: 0.5rem 1.35rem; flex-wrap: wrap;
  max-width: var(--wrap); margin-inline: auto;
  padding: 0.9rem var(--gutter);
}
.site-nav .spacer { flex: 1; }
.site-nav .brand {
  display: inline-flex; align-items: center; gap: 0.55rem;
  font-family: var(--mono); font-weight: 700; font-size: var(--ui);
  letter-spacing: 0.16em; color: var(--ink); text-decoration: none;
}
.brand-mark {
  width: 0.85rem; height: 0.85rem; border-radius: var(--r-2); flex: none;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
.site-nav .nav-link {
  color: var(--ink-2); text-decoration: none; font-size: var(--ui);
}
.site-nav .nav-link:hover { color: var(--ink); }
/* Where the row would otherwise wrap and put the one action on a line of its
   own. The link is in the footer of every page as well. */
@media (max-width: 34rem) { .site-nav .nav-aside { display: none; } }
/* A 360px phone fits the brand, the one link and the button on a single row
   only just, and the gap is what it comes up short by. Narrower than that it
   wraps, which is the layout at that width rather than a failure of it. */
@media (max-width: 30rem) { .site-nav { gap: 0.5rem 0.9rem; } }

.site-foot {
  border-top: 1px solid var(--line);
  margin-top: var(--stack); padding: 2.75rem 0 3.5rem;
  color: var(--ink-2); font-size: var(--step--1);
}
/* A page that ends on the dark band has already drawn its closing edge; the
   footer's own rule and the gap above it would be a second one. Browsers
   without :has() get the gap, which is the layout this replaces. */
#main:has(.close-band) + .site-foot { margin-top: 0; border-top: 0; padding-top: 3.25rem; }
.site-foot a { color: var(--ink-2); text-decoration: none; }
.site-foot a:hover { color: var(--ink); text-decoration: underline; }
.site-foot p { margin: 0 0 0.6rem; }
.foot-cols {
  display: grid; gap: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.foot-cols h2 {
  font-family: var(--mono); font-size: var(--step--2); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.15em; color: var(--ink-3);
  margin: 0 0 0.9rem;
}
.foot-cols ul { list-style: none; padding: 0; margin: 0; }
.foot-cols li { margin-bottom: 0.5rem; }
.foot-say { max-width: 30ch; }
.foot-say b { color: var(--ink); font-family: var(--mono); letter-spacing: 0.16em; }
.foot-rule {
  margin-top: 2.5rem; padding-top: 1.25rem; border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: var(--step--2); color: var(--ink-3);
  letter-spacing: 0.04em;
}

/* Buttons and forms */

.cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  padding: 0.8rem 1.35rem;
  background: var(--accent); color: var(--on-fill);
  border: 1px solid var(--accent); border-radius: var(--r);
  font: inherit; font-weight: 650; font-size: calc(var(--step-0) * 0.941); line-height: 1.2;
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
.cta:hover { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--on-fill); }
.cta:active { transform: translateY(1px); }
.cta.sm { padding: 0.5rem 0.9rem; font-size: calc(var(--step-0) * 0.847); }

/* The same button with the fill taken off, for the second thing a page asks
   for. Two filled buttons are two calls to action, and on the clean and gated
   reports only one of them is the point: the download is the thing to do now,
   the rescan is the thing to do next month. This keeps the rescan a target a
   thumb can hit and stops it competing for the eye. Used only where there is a
   filled button above it to defer to - see report_clean.html and
   report_gated.html, which drop the class on a page with nothing to download.*/
.cta.quiet {
  background: transparent; color: var(--ink-2);
  border-color: var(--line-2); font-weight: 500;
}
.cta.quiet:hover { background: transparent; color: var(--ink); border-color: var(--ink-3); }

.scanform {
  display: flex; flex-wrap: wrap; gap: 0.6rem;
  margin: 1.75rem 0 0.75rem; max-width: 34rem;
}
.scanform input {
  flex: 1 1 15rem; min-width: 0;
  padding: 0.8rem 0.95rem;
  border: 1px solid var(--line-2); border-radius: var(--r);
  font-family: var(--mono); font-size: var(--ui);
  background: var(--surface); color: var(--ink);
}
.scanform input::placeholder { color: var(--ink-3); }
.scanform input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-color: transparent; }
/* Wrapped onto its own line the button keeps its own width and reads as
   something left over rather than as the action. Same button, filling the line
   it has already taken. */
@media (max-width: 30rem) { .scanform .cta { width: 100%; } }

.formerror { color: var(--dead); font-size: var(--ui); font-weight: 600; }

main form { max-width: 26rem; margin: 1.5rem 0; }
main form label { display: block; margin: 1.1rem 0 0.35rem; font-size: var(--ui); color: var(--ink-2); }
main form :is(input[type=email], input[type=password], input[type=text], textarea) {
  width: 100%; padding: 0.7rem 0.9rem;
  border: 1px solid var(--line-2); border-radius: var(--r);
  font: inherit; background: var(--surface); color: var(--ink);
}
main form button { margin-top: 1.25rem; }

/* The feedback form. Everything here is a control `main form` above does not
   already style: a file input, and the honeypot. */
main form input[type=file] {
  width: 100%; font: inherit; font-size: var(--ui); color: var(--ink-2);
}
main form input[type=file]::file-selector-button {
  margin-right: 0.75rem; padding: 0.5rem 0.85rem;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--surface); color: var(--ink); font: inherit; cursor: pointer;
}
.opt { color: var(--ink-3); font-weight: 400; }
/* The honeypot. Off the layout rather than `display:none`: a form filler that
   skips hidden inputs is exactly the one this is meant to catch. Nothing here
   is reachable by tab, by a screen reader (aria-hidden on the wrapper) or by a
   pointer. */
.nofill {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}
/* Cloudflare's widget, when it is configured at all. */
.cf-turnstile { margin-top: 1.4rem; }

form.inline { display: inline; margin: 0; max-width: none; }
form.inline button { margin: 0; }
.linkish {
  background: none; border: 0; padding: 0; color: var(--ink-2);
  font: inherit; font-size: var(--ui); cursor: pointer;
}
.linkish:hover { color: var(--ink); }

/* Sign in with Google / Facebook, above the email form on /login and /signup.
   Sized to the same 26rem as `main form` so the two stack as one column. */

.oauth-list { display: grid; gap: 0.6rem; max-width: 26rem; margin: 1.5rem 0 0; }
.oauth-btn {
  display: flex; align-items: center; justify-content: center; gap: 0.65rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--surface); color: var(--ink); text-decoration: none;
  font: inherit; font-weight: 600; font-size: calc(var(--step-0) * 0.922);
}
.oauth-btn:hover { border-color: var(--ink-3); background: var(--sunk); }
.oauth-mark { width: 1.15rem; height: 1.15rem; flex: none; }

/* "or", with a rule either side of it. */
.oauth-or {
  display: flex; align-items: center; gap: 0.75rem;
  max-width: 26rem; margin: 1.5rem 0 0;
  color: var(--ink-3); font-family: var(--mono); font-size: var(--step--2);
  text-transform: uppercase; letter-spacing: 0.15em;
}
.oauth-or::before, .oauth-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }

/* Landing: the hero */

.hero { padding-block: clamp(2.5rem, 6vw, 4rem) clamp(2.5rem, 6vw, 4rem); }
.hero-grid { display: grid; gap: clamp(2.25rem, 5vw, 3.5rem); align-items: center; }
@media (min-width: 62rem) {
  .hero-grid { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
}
.hero h1 { max-width: 22ch; }
/* A hero already spends the space above the first heading of the page under it. */
.hero + main.page { padding-top: 0; }
main.page section:first-of-type > h2:first-child { margin-top: 0; }
.hero .lede { max-width: 44ch; }
#scan { scroll-margin-top: 5rem; }

/* The scan-readout panel: the one dark surface on the page, and a faithful
   miniature of what the report's own verdict band looks like. It is the product
   rather than a picture of it, which is why it is markup and not a screenshot -
   it reflows, it is selectable, and it cannot go stale against the real one. */

.readout {
  position: relative; overflow: hidden;
  margin: 0;
  background: var(--verdict-bg); color: var(--verdict-fg);
  border: 1px solid var(--verdict-line); border-radius: var(--r);
  border-bottom: 3px solid var(--dead);
}
.readout-bar {
  display: flex; align-items: center; gap: 0.6rem 1rem; flex-wrap: wrap;
  padding: 0.7rem 1.1rem; border-bottom: 1px solid var(--verdict-line);
  font-family: var(--mono); font-size: var(--step--2);
  text-transform: uppercase; letter-spacing: 0.15em; color: var(--verdict-dim);
}
.readout-bar b { color: var(--verdict-fg); font-weight: 700; }
.readout-bar .spacer { flex: 1; }
.readout-body { padding: 1.4rem 1.1rem 1.5rem; }
.readout-what {
  font-family: var(--mono); font-size: var(--step--2); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--verdict-dim);
  margin: 0 0 0.7rem;
}
.readout-url {
  font-family: var(--mono); font-weight: 600;
  /* Both ends of the clamp are ratios of the body size, so the URL grows with
     the panel; the vw in the middle is untouched and is still what sizes it on
     a phone. 0.988 and 1.412 of 1.0625rem are the 1.05rem and 1.5rem this was
     drawn at. */
  font-size: clamp(calc(var(--step-0) * 0.988), 2.6vw, calc(var(--step-0) * 1.412));
  letter-spacing: -0.01em; word-break: break-all; margin: 0 0 0.9rem;
  text-decoration: line-through; text-decoration-color: var(--dead); text-decoration-thickness: 2px;
}
.readout-meta {
  display: flex; align-items: center; gap: 0.6rem 0.9rem; flex-wrap: wrap;
  margin: 0 0 1.4rem;
  font-family: var(--mono); font-size: var(--step--1); color: var(--verdict-dim);
}
.readout-stats {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); row-gap: 1.15rem;
  margin: 0; padding-top: 1.15rem; border-top: 1px solid var(--verdict-line);
}
/* Value above label, so a label that wraps to two lines cannot knock the three
   figures off a common baseline. column-reverse rather than a reordered <dl>:
   the pairing has to stay dt-then-dd for anything reading the markup. */
.readout-stats > div {
  display: flex; flex-direction: column-reverse; justify-content: flex-end; gap: 0.35rem;
}
.readout-stats > div:not(:last-child) { padding-right: 0.9rem; }
.readout-stats > div + div { border-left: 1px solid var(--verdict-line); padding-left: 0.9rem; }
.readout-stats dt {
  font-family: var(--mono); font-size: calc(var(--step-0) * 0.588); text-transform: uppercase;
  letter-spacing: 0.12em; color: var(--verdict-dim);
}
.readout-stats dd {
  margin: 0; font-family: var(--mono); font-size: calc(var(--step-0) * 1.224); font-weight: 700;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
.readout-stats .zero { color: var(--dead); }
/* Three columns on a 375px phone is three four-word labels in about 90px each,
   which is a three-line label over every figure. Two columns is the same three
   figures with room to be read; the third drops to a second row and gives up
   the rule down its left, which on a row of one would be a divider standing at
   the start of a line rather than between two things. */
@media (max-width: 30rem) {
  .readout-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .readout-stats > div:nth-child(odd) { border-left: 0; padding-left: 0; }
}
.readout figcaption {
  padding: 0.85rem 1.1rem; border-top: 1px solid var(--verdict-line);
  font-family: var(--mono); font-size: var(--step--2);
  letter-spacing: 0.08em; color: var(--verdict-dim);
}

/* The one moving thing on the site: a single pass of the check across the
   panel, on load, and never again. */
.readout::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 42%, rgba(63, 195, 206, 0.16) 50%, transparent 58%);
  transform: translateX(-110%);
  animation: sweep 1.5s cubic-bezier(0.4, 0, 0.2, 1) 0.35s 1 both;
}
@keyframes sweep { to { transform: translateX(110%); } }

/* The claim strip under the hero. Five things we do, in the data voice, because
   each one is a fact about the scan rather than a benefit. */
.spec {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.6rem;
  list-style: none; margin: clamp(2rem, 4vw, 2.75rem) 0 0; padding: 1.1rem 0 0;
  border-top: 1px solid var(--line);
  font-family: var(--mono); font-size: var(--step--2);
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--ink-3);
}
.spec li { display: flex; align-items: center; gap: 0.5rem; }
.spec li::before {
  content: ""; width: 0.3rem; height: 0.3rem; flex: none;
  background: var(--accent); border-radius: var(--r-2);
}

/* Three real findings, in the shape a finding actually has. */

/* Three findings are a set, and the third one on a row of its own reads as an
   afterthought rather than as the third. `auto-fit, minmax(12rem, 1fr)` was
   aimed at that and did not reach it: two columns fit from about 430px, three
   only from about 680px, so every phone in landscape and every small tablet got
   exactly the 2+1 the measure was chosen to avoid. Stated as the intent instead
   — three across, or one down, and nothing in between. */
.case-grid { display: grid; gap: 1rem; }
@media (min-width: 44rem) { .case-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.case {
  display: flex; flex-direction: column; gap: 0.45rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.15rem 1.2rem 1.25rem;
}
.case-who {
  font-family: var(--mono); font-size: var(--step--2); text-transform: uppercase;
  letter-spacing: 0.14em; color: var(--ink-3); margin: 0;
}
.case-n {
  font-size: calc(var(--step-0) * 1.788); font-weight: 800; letter-spacing: -0.035em; line-height: 1;
  font-variant-numeric: tabular-nums; margin: 0.15rem 0 0.1rem;
}
.case-n span { font-size: 0.5em; font-weight: 500; letter-spacing: 0; color: var(--ink-2); margin-left: 0.4rem; }
.case-what {
  font-family: var(--mono); font-size: calc(var(--step-0) * 0.8); color: var(--ink-2); margin: 0;
  text-decoration: line-through; text-decoration-color: var(--dead); text-decoration-thickness: 1px;
}
.case-tag { margin: 0.5rem 0 0; }
.case-tag .tag { background: none; color: var(--dead); border: 1px solid currentColor; }

/* Free against paid, side by side. The one place on the site that draws the
   line, and it is drawn as two boxes rather than as a sentence with a "but" in
   it: the free half is genuinely complete and genuinely not enough, and neither
   of those reads as true unless both are in view at once. Same 44rem as
   `.case-grid` - two boxes and three cards break to one column together. */
.split { display: grid; gap: 1rem; }
@media (min-width: 44rem) { .split { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.split-col {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.3rem 1.4rem 1.4rem;
}
/* The same left rule the buy panel carries, so the paid half is marked the same
   way in both places a reader meets it. */
.split-col.paid { border-left: 3px solid var(--accent); }
.split-h {
  font-family: var(--mono); font-size: var(--step--2); text-transform: uppercase;
  letter-spacing: 0.14em; color: var(--ink-3); margin: 0 0 0.9rem;
}
.split-col ul { margin: 0; padding-left: 1.15rem; }
.split-col li { margin-bottom: 0.5rem; }
.split-col li:last-child { margin-bottom: 0; }
.split-note {
  margin: 1rem 0 0; padding-top: 0.9rem; border-top: 1px solid var(--line);
  color: var(--ink-2); font-size: var(--ui);
}

/* The three steps. A real sequence, so it is numbered; the numbering is the
   report's own step marker. */

.stepline { display: grid; gap: 1.5rem; list-style: none; padding: 0; margin: 0; }
/* 50rem and not 52: a tablet held upright is 820px, and three short steps
   stacked down the middle of it is a screen of white on either side. The same
   number is on `.qa` below, for the same tablet. */
@media (min-width: 50rem) { .stepline { grid-template-columns: repeat(3, 1fr); gap: 2rem; } }
.stepline li { display: flex; flex-direction: column; gap: 0.5rem; }
.stepline .n {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem; flex: none;
  border: 1px solid var(--accent); border-radius: 50%;
  font-family: var(--mono); font-size: var(--step--2); font-weight: 700;
  color: var(--accent-ink); margin-bottom: 0.35rem;
}
.stepline b { font-weight: 650; }
.stepline p { margin: 0; color: var(--ink-2); }

/* The one number the whole ranking argument rests on. */

.bigstat {
  display: grid; gap: 1.25rem 2.5rem; align-items: center;
  margin: 2rem 0 0; padding: 1.75rem 0 0; border-top: 1px solid var(--line);
}
@media (min-width: 46rem) { .bigstat { grid-template-columns: auto minmax(0, 1fr); } }
.bigstat-n {
  font-family: var(--mono); font-weight: 700;
  font-size: clamp(calc(var(--step-0) * 3.059), 11vw, calc(var(--step-0) * 5.176));
  letter-spacing: -0.05em; line-height: 0.9; color: var(--dead);
  font-variant-numeric: tabular-nums; margin: 0;
}
.bigstat p { margin: 0; max-width: 42ch; color: var(--ink-2); }
.bigstat p b { color: var(--ink); }

/* Questions, as rows rather than as an accordion: everything on this list is
   something somebody hesitates over, and hiding it behind a click is the wrong
   place to save space. */

.qa { border-top: 1px solid var(--line); margin: 0; }
.qa > div { padding: 1.4rem 0; border-bottom: 1px solid var(--line); }
@media (min-width: 50rem) {
  .qa > div { display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); gap: 1rem 2.5rem; }
}
.qa dt { font-weight: 650; letter-spacing: -0.01em; }
.qa dd { margin: 0.4rem 0 0; color: var(--ink-2); max-width: 60ch; }
@media (min-width: 50rem) { .qa dd { margin-top: 0; } }

/* The close, on the same dark surface as the hero panel, so the page opens and
   shuts on the instrument rather than on a second idea. */

/* Centred, and the only thing on the page that is: the argument has been made
   by here and what is left is the one action. */
.close-band {
  background: var(--verdict-bg); color: var(--verdict-fg);
  border-top: 1px solid var(--verdict-line);
  padding-block: var(--stack);
  text-align: center;
}
.close-band h2 { color: var(--verdict-fg); margin: 0 auto 0.75rem; max-width: 22ch; }
.close-band p { color: var(--verdict-dim); max-width: 52ch; margin-inline: auto; }
.close-band .scanform { margin-inline: auto; justify-content: center; }
.close-band .scanform input {
  background: rgba(255, 255, 255, 0.06); border-color: var(--verdict-line); color: var(--verdict-fg);
}
.close-band .scanform input::placeholder { color: var(--verdict-dim); }
.close-band .fineprint { color: var(--verdict-dim); }
.close-band .fineprint a, .close-band p a { color: var(--verdict-fg); }
.close-band .eyebrow { color: var(--verdict-dim); }
/* The teal has nowhere to go against #101617, so the button on this band is the
   paper colour instead. Same button, inverted, rather than a second style. */
.close-band .cta {
  background: var(--verdict-fg); border-color: var(--verdict-fg); color: var(--verdict-bg);
}
.close-band .cta:hover { background: #fff; border-color: #fff; color: var(--verdict-bg); }

/* The report page's own chrome, around the vendored report body.

   Everything below is styled through a class rather than an element, because on
   /r/<slug> the report's stylesheet is later in the document and its bare `p`,
   `h2` and `section` rules would otherwise win here. */

.banner-ok {
  background: var(--accent-wash); border: 1px solid var(--accent);
  border-radius: var(--r);
  padding: 1.15rem 1.3rem; margin: 1.25rem auto 0;
}
.banner-ok p { margin: 0 0 0.75rem; max-width: 68ch; }
.banner-ok p:last-child { margin-bottom: 0; }

/* The neutral sibling of .banner-ok. Nothing good just happened - the reader
   asked for a scan and is being shown the one they already have - so it takes
   the sunk surface rather than the accent wash, which is reserved for a thing
   that went right. */
.banner-note {
  background: var(--sunk); border: 1px solid var(--line-2);
  border-radius: var(--r);
  padding: 1.15rem 1.3rem; margin: 1.25rem auto 0;
}
.banner-note p { margin: 0 0 0.5rem; max-width: 68ch; }
.banner-note p:last-child { margin-bottom: 0; }

/* The buy block, injected into the report body under the fix-list table.
   Sized to sit inside that template's .wrap without fighting it. */
.cta-block {
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--surface);
  padding: 1.6rem; margin: 2rem 0 0;
  border-left: 3px solid var(--accent);
}
.cta-block .cta-title {
  font-size: var(--step-1); font-weight: 700; letter-spacing: -0.015em;
  margin: 0 0 0.5rem;
}
.cta-block p { margin: 0 0 1rem; max-width: 54ch; }
/* The panel is as wide as the sheet, so its copy has to travel with the sheet
   or the box reads as two thirds empty on a wide monitor. Written out at each
   step rather than reading `--measure`, because this block is injected into the
   report body as well and a page served from an older report's stored HTML
   defines no measure at all - it would have taken the fallback, silently, on
   the pages where the panel is the only thing being sold. */
@media (min-width: 90rem)  { .cta-block p { max-width: 60ch; } }
@media (min-width: 120rem) { .cta-block p { max-width: 64ch; } }
@media (min-width: 160rem) { .cta-block p { max-width: 68ch; } }
.cta-block p:last-child { margin-bottom: 0; }
.cta-block .cta-fine { color: var(--ink-2); font-size: var(--step--1); margin-bottom: 0; }

/* The second offer inside the same panel: the file above is the work written
   down, this is the work done.

   It was a hairline rule and a ghost button until 2026-09-06, on the reasoning
   that the download is the thing to do now and this is the thing to consider.
   That reasoning produced a section the eye skips: the whole point of the offer
   is to count who wants it, and it was being asked in the typographic register
   of a footnote. It is a real offer and it is now dressed as one.

   Still not a second box, which is the part of the old reasoning that holds -
   two detached panels read as two unrelated asks. It is a tinted *region* of
   the same panel instead, bled out to the block's padding on three sides so it
   finishes the card rather than floating inside it. The negative margins are
   the mirror of .cta-block's 1.6rem padding and have to move with it. */
.fix-offer {
  background: var(--accent-wash);
  border-top: 1px solid var(--line);
  margin: 1.6rem -1.6rem -1.6rem; padding: 1.4rem 1.6rem 1.5rem;
  border-radius: 0 0 var(--r) 0;
}
.fix-offer p:last-child { margin-bottom: 0; }
/* The copy cap on .cta-block p is set against the white panel; inside the tint
   the measure is the same but the padding is not, so the last line does not
   run to the edge. */
.fix-offer .cta-fine { max-width: 58ch; }

.removal {
  border-top: 1px solid var(--line);
  margin-top: var(--stack); padding-block: 2rem 3.5rem;
}
.removal h2 { margin: 0 0 0.6rem; font-size: var(--step-2); }
/* 0.9 of body copy, not a fixed 0.95rem. This was the first rule written this
   way and the argument it was written on - a literal is the one line on the
   page that stays 15px while the rest grows - turned out to apply to every
   literal in the file. Left at 0.9 rather than folded into `--ui` (0.894)
   because the two are a tenth of a pixel apart and this one says what it
   means: nine tenths of the body copy above it. */
.removal p {
  margin: 0 0 0.75rem; color: var(--ink-2);
  font-size: calc(var(--step-0) * 0.9); max-width: 60ch;
}
.removal p:last-child { margin-bottom: 0; }

/* Where a scan has got to, on /scan/<id>. Three steps and a marker, above the
   headline, because a scan takes hours and the page is reloaded by someone who
   wants that one fact. `.past` and `.now` never carry the state alone: the
   heading under this says the same thing in words, so a printed page, a screen
   reader and a reader who cannot tell the two greys apart all still get it. */

.track {
  display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem;
  list-style: none; padding: 0 0 1.5rem; margin: 0 0 1.75rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono); font-size: var(--step--2);
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--ink-3);
}
.track li { display: flex; align-items: center; gap: 0.5rem; }
.track li::before {
  content: ""; flex: none;
  width: 0.55rem; height: 0.55rem; border-radius: var(--r-2);
  border: 1px solid var(--line-2); background: var(--surface);
}
.track li.past { color: var(--ink-2); }
.track li.past::before { background: var(--live); border-color: var(--live); }
.track li.now { color: var(--ink); font-weight: 700; }
.track li.now::before { background: var(--accent); border-color: var(--accent); }

/* The dead-link table on the clean and gated pages. Those two carry no report
   body, so they do not get the report's own stylesheet and the one table they
   now show has to be styled here.

   THE URLS USED TO BE PLAIN TEXT, deliberately: a page whose whole subject is
   that these links are dead has no business asking anyone to click one, and
   several of them are lapsed domains that may now belong to somebody else.
   That reasoning protected the reader from a page they were being told not to
   trust, and cost them the only thing the report is really selling - "every
   claim here is one click from proof" (report-spec.md, The one rule). The
   findings report has always made its dead link clickable and always will;
   these two were the odd ones out, and the same page is now the same page in
   all three states.

   The original worry is answered in the markup rather than by refusing:
   `target="_blank"` keeps the report open behind it, `noopener`/`noreferrer`
   keep whatever is there now away from this page, and `nofollow ugc` says we
   are naming the URL as evidence rather than pointing at it.

   One line, not four. `max-width: 34ch` with `overflow-wrap: anywhere` broke a
   60-character URL - the longest there is, since the worker truncates
   `display_url` at 60 - across three or four lines of a three-column table.
   Nothing wraps now; a screen too narrow to hold the row scrolls the frame,
   which is what the frame is for. */

/* The frame the table scrolls inside on a narrow screen. Focusable because an
   overflow box with nothing focusable in it cannot be scrolled from a keyboard,
   and the two shadow layers are how it admits there is more table off the edge:
   the `local` gradients scroll with the content and cover the shadow at
   whichever end is already in view, so a table that fits shows neither. The
   report body's own stylesheet draws its frames the same way and off the same
   `--edge` token - one more name copied across, like every other one in this
   file's palette, because the clean and gated pages never include that
   stylesheet and still have a table that can overflow. */
.tablewrap {
  overflow-x: auto; margin: 1.25rem 0;
  background:
    linear-gradient(to right, var(--paper), transparent) 0 0/1.75rem 100% no-repeat local,
    linear-gradient(to left, var(--paper), transparent) 100% 0/1.75rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, var(--edge), transparent)
      0 0/0.6rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, var(--edge), transparent)
      100% 0/0.6rem 100% no-repeat scroll;
}
.tablewrap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.deadlinks {
  width: 100%; border-collapse: collapse;
  font-size: var(--step--1);
}
.deadlinks th, .deadlinks td {
  text-align: left; padding: 0.55rem 0.75rem 0.55rem 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.deadlinks th {
  font-family: var(--mono); font-size: var(--step--2); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.12em; color: var(--ink-3);
  border-bottom-color: var(--line-2); white-space: nowrap;
}
.deadlinks td.url {
  font-family: var(--mono);
  white-space: nowrap;
}
.deadlinks td.dim { color: var(--ink-2); }
.deadlinks td.num, .deadlinks th.num {
  text-align: right; padding-right: 0;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.deadlinks tr:last-child th, .deadlinks tr:last-child td { border-bottom: none; }

/* Touch. Keyed on the pointer rather than on the width, because the case is a
   1024px tablet as much as a phone: nothing here is small enough to miss with a
   mouse, and a 0.95rem link in a row of them is with a thumb. Padding rather
   than a font size, so the same page is the same page. */

@media (pointer: coarse) {
  .site-nav .nav-link, .site-nav .linkish { padding-block: 0.4rem; }
  .foot-cols li a { display: inline-block; padding-block: 0.2rem; }
}

/* Motion. One staggered entrance on the hero and the single sweep above; the
   site has no script, so there is nothing that reveals on scroll and nothing
   that can be left half-animated by a slow connection. */

@media (prefers-reduced-motion: no-preference) {
  /* Short on purpose: `backwards` holds the hero at opacity 0 until its turn
     comes, so the whole sequence has to be over before it is the thing keeping
     the largest text on the page from being painted. */
  .rise { animation: rise 0.4s cubic-bezier(0.2, 0.7, 0.2, 1) backwards; }
  .rise-2 { animation-delay: 0.05s; }
  .rise-3 { animation-delay: 0.1s; }
  .rise-4 { animation-delay: 0.15s; }
}
@keyframes rise { from { opacity: 0; transform: translateY(0.6rem); } }

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