/* Known Recalls, from the Figma comp.
 *
 * Ported from design/knownrecalls_design.zip. The comp shipped as a Vite +
 * Tailwind + React project; none of that survives here, and none of it needed
 * to. Its markup used semantic class names throughout and no utility classes,
 * so the whole design is this file plus matching element structure in
 * render.js.
 *
 * Two things were dropped deliberately:
 *   `@import 'tailwindcss'` — nothing referenced a utility class.
 *   `@theme { ... }` — a Tailwind construct. Its two font variables are
 *   declared in :root below, because the rest of the sheet reads them.
 *
 * The register is a printed public record: paper cream, ink brown, a serif for
 * anything a person reads and a sans for anything a machine counted, rules
 * instead of boxes, and a letterpress dot on the background. Colour means
 * exactly one thing — severity.
 *
 * Two rules survive from the brief and may not be traded away:
 *   .advisory is the loudest element on the page at every width. It is set
 *   from a federal field, not from editorial judgement.
 *   Tables scroll rather than drop a column. Hiding a number on a safety page
 *   is not a layout decision.
 */

/* ── the two typefaces, served from here ──────────────────────────────────
 *
 * These were on fonts.googleapis.com until the cost of that was measured. The
 * stylesheet lived on one Google origin and pointed at fonts on a second, so
 * before a single character could be drawn the browser had to resolve, connect
 * to and negotiate TLS with two hosts it had never spoken to. preconnect
 * shortens the handshakes; it does not remove a link from the chain. Served
 * from here, the connection is already open — the page itself came down it.
 *
 * The old argument for the CDN was a shared cache: the visitor already has
 * this font from some other site. Browsers have partitioned the HTTP cache by
 * top-level site since 2020, so that has not been true for years. Every
 * visitor downloaded these files fresh, from two extra hosts.
 *
 * It also settles a question the privacy page should not have to answer.
 * fonts.googleapis.com sees the address of everyone who opens a page here.
 *
 * Both families are SIL Open Font License, which permits this. The version in
 * each filename is Google's (v28, v26), so these bytes can never change under
 * a name a browser already holds — which is what makes the one-year immutable
 * cache on them honest rather than a bet.
 *
 * The subsets and unicode-ranges are Google's own, unaltered. latin-ext costs
 * nothing until a page actually contains a character in it.
 *
 * Newsreader is one variable file carrying both an optical-size and a weight
 * axis, which is why it is six times the size of a static cut and still the
 * cheaper choice: every heading weight comes out of that one download, and the
 * optical axis is what keeps a 5rem h1 from being a 22px h2 scaled up.
 */

@font-face {
  font-family: "Alegreya Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-400-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-400-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: italic; font-weight: 400; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-400i-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: italic; font-weight: 400; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-400i-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-500-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: normal; font-weight: 500; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-500-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-700-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Alegreya Sans"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/fonts/alegreya-sans-v28-700-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* One file, both weights the site asks for. Google declares it three times,
   once per weight, all pointing here; the range says the same thing once. */
@font-face {
  font-family: "Newsreader"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url(/fonts/newsreader-v26-latin.woff2) format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url(/fonts/newsreader-v26-latin-ext.woff2) format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  color-scheme: light dark;

  --font-serif: "Newsreader", Georgia, serif;
  --font-sans: "Alegreya Sans", Arial, sans-serif;

  --paper: #f4ecdc;
  --surface: #fffaf0;
  --ink: #302624;
  --muted: #675d52;
  --rule: #b7aa97;
  --link: #3e5148;
  --alert: #a62e21;
  --alert-ink: #581a14;
  --alert-paper: #fff0dc;
  --wash: #e9dfce;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #25201d;
    --surface: #302a26;
    --ink: #fff5e6;
    --muted: #d4c5b1;
    --rule: #86796b;
    --link: #bdd0bd;
    --alert: #ff927d;
    --alert-ink: #ffd9cf;
    --alert-paper: #45251f;
    --wash: #3a322d;
  }
}

:root[data-theme="dark"] {
  --paper: #25201d;
  --surface: #302a26;
  --ink: #fff5e6;
  --muted: #d4c5b1;
  --rule: #86796b;
  --link: #bdd0bd;
  --alert: #ff927d;
  --alert-ink: #ffd9cf;
  --alert-paper: #45251f;
  --wash: #3a322d;
}

* { box-sizing: border-box; }

html {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 17px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  min-width: 320px;
  background-color: var(--paper);
  /* The letterpress tooth. Two CSS values, no image request. */
  background-image: radial-gradient(rgba(82, 60, 43, .08) .55px, transparent .7px);
  background-size: 9px 9px;
  color: var(--ink);
}

a { color: var(--link); text-decoration-thickness: 1px; text-underline-offset: .19em; }
a:hover { text-decoration-thickness: 2px; }

/* Focus is the alert colour, which is the one place severity red is used for
   something that is not a safety advisory. It is the most visible thing in
   the palette, which is what a focus ring is for. */
a:focus-visible,
button:focus-visible,
input:focus-visible { outline: 3px solid var(--alert); outline-offset: 3px; }

button, input { font: inherit; }

/* ── masthead ─────────────────────────────────────────────────────────── */

.site-header {
  border-top: 6px solid var(--ink);
  border-bottom: 1px solid var(--rule);
  background: color-mix(in srgb, var(--paper) 88%, var(--wash));
}
.site-header__inner {
  max-width: 1180px;
  min-height: 76px;
  margin: auto;
  padding: 0 24px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}
.wordmark {
  color: var(--ink);
  font: 700 1.7rem/.9 var(--font-serif);
  text-decoration: none;
  letter-spacing: -.055em;
}
.wordmark span { color: var(--link); font-style: italic; }
.site-header nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px 20px;
  font-size: .92rem;
  font-weight: 700;
}
.site-header nav a { color: var(--ink); }

/* ── page frame ───────────────────────────────────────────────────────── */

.main {
  max-width: 1120px;
  min-height: calc(100vh - 196px);
  margin: 0 auto;
  padding: 38px 24px 80px;
}
.main--home { max-width: 1180px; }

.index-intro { max-width: 770px; padding: 56px 0 64px; }

/* The homepage heading is one sentence on two lines: the scope at full size,
   the question under it and smaller. Relative to the h1 rather than fixed, so
   it keeps its proportion through the whole clamp down to a phone. */
.index-intro h1 span {
  display: block;
  margin-top: .1em;
  font-size: .62em;
  /* The h1 tracks at -.062em, which is right at 5rem and far too tight two
     thirds of the way down. Tracking is optical, not proportional, so this is
     set on its own rather than inherited. */
  letter-spacing: -.028em;
}

.eyebrow {
  margin: 0 0 12px;
  color: var(--muted);
  font-size: .77rem;
  font-weight: 700;
  letter-spacing: .055em;
  line-height: 1.35;
  text-transform: uppercase;
}

h1, h2, h3, h4 { font-family: var(--font-serif); color: var(--ink); }
h1 {
  max-width: 920px;
  margin: 0 0 20px;
  font-size: clamp(2.55rem, 5.9vw, 5rem);
  font-weight: 500;
  line-height: .99;
  letter-spacing: -.062em;
}
h2 {
  margin: 0 0 10px;
  font-size: clamp(1.42rem, 2vw, 1.86rem);
  font-weight: 500;
  letter-spacing: -.035em;
}
h3 {
  margin: 10px 0 22px;
  font-size: clamp(1.55rem, 3vw, 2.35rem);
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -.045em;
}
h4 {
  margin: 0 0 5px;
  font: 700 .78rem/1.4 var(--font-sans);
  letter-spacing: .045em;
  text-transform: uppercase;
}

.index-intro > p:not(.eyebrow),
.record-heading > p:not(.eyebrow),
.not-found > p {
  max-width: 645px;
  margin: 0;
  color: var(--muted);
  font: 1.18rem/1.52 var(--font-serif);
}

.record-heading { padding: 25px 0 38px; border-bottom: 2px solid var(--ink); }
.record-heading--year { border-bottom-width: 1px; }

.lead { font-size: 1.34rem !important; color: var(--ink) !important; }
.lead strong { font-weight: 700; color: var(--ink); }

/* ── search ───────────────────────────────────────────────────────────── */

.search { margin-top: 33px; max-width: 690px; }
.search label { display: block; margin-bottom: 8px; font-size: .94rem; font-weight: 700; }
/* The field and the button do not touch.
   The focus ring is a 3px outline at a 3px offset — 6px of bleed on every
   side — so a button flush against the input sat underneath the ring the
   moment the field was focused. The gap is wider than the ring. */
.search > div { display: flex; gap: 10px; }
.search input {
  min-width: 0;                /* or the input overflows its flex row */
  flex: 1;
  border: 1.5px solid var(--ink);
  border-radius: 0;
  background: var(--surface);
  color: var(--ink);
  padding: 13px 15px;
}
.search button {
  border: 1.5px solid var(--ink);
  border-radius: 0;
  padding: 0 23px;
  color: var(--paper);
  background: var(--ink);
  font-weight: 700;
  cursor: pointer;
}
.search button:hover { background: var(--link); }
.search--compact { max-width: 520px; margin: 27px 0 42px; }

/* ── browse ───────────────────────────────────────────────────────────── */

.browse { padding: 26px 0 43px; border-top: 3px solid var(--ink); }
/* The masthead nav links here, and a jump that lands flush against the rule
   reads as though the heading were cut off. */
.browse[id] { scroll-margin-top: 24px; }
.browse--secondary { border-top-width: 1px; }
.quiet { margin: -3px 0 24px; color: var(--muted); font-size: .9rem; }

.brand-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}
.brand-grid a {
  min-height: 47px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  color: var(--ink);
  border-right: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  font-weight: 600;
}
.brand-grid a:hover { background: var(--link); color: var(--paper); }

/* The mark sits beside the name and never replaces it — 15 of the 38 makes
   have no usable logo, so a cell has to read correctly without one. Fixed box
   so the names still line up down the column whether or not a mark is there. */
.brand-mark {
  flex: 0 0 28px;
  width: 28px;
  height: 20px;
  object-fit: contain;
  object-position: left center;
}
/* A mark is never recoloured. Not inverted in dark mode, not tinted, not
   filtered — a manufacturer's colours are part of the mark, and a Toyota that
   is red on paper and cyan at night is not a Toyota. Whatever the file
   contains is what renders, on both themes. */

.category-links { display: flex; flex-wrap: wrap; gap: 18px 34px; }
.category-links a { font-weight: 700; }

/* ── product recalls ──────────────────────────────────────────────────── */
/* A list, not a table. The CPSC hazard field is a sentence — 107 characters
   at the median, 491 at the worst — and the units field is prose as well, so
   neither survives being a column. See productList() in render.js. */

ul.results { list-style: none; margin: 0; padding: 0; max-width: 760px; }
ul.results li { padding: 20px 0; border-bottom: 1px solid var(--rule); }
ul.results li:first-child { border-top: 1px solid var(--rule); }
ul.results h3 {
  margin: 0 0 6px;
  font-size: 1.12rem;
  line-height: 1.3;
  letter-spacing: -.02em;
}
ul.results p { margin: 0; font: .95rem/1.55 var(--font-serif); color: var(--ink); }
ul.results p.meta {
  margin-top: 7px;
  font-family: var(--font-sans);
  font-size: .78rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* The category cells carry a name and a count, so the count is pushed to the
   far end instead of sitting against the name. */
.brand-grid a .n {
  margin-left: auto;
  color: var(--muted);
  font-size: .8rem;
  font-variant-numeric: tabular-nums;
}

/* ── record pages ─────────────────────────────────────────────────────── */

.breadcrumbs {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 21px;
  color: var(--muted);
  font-size: .81rem;
}
.breadcrumbs a { color: var(--link); }

.record-section { margin-top: 48px; }
.record-section > h2 { padding-bottom: 12px; border-bottom: 3px solid var(--ink); }

.section-note {
  max-width: 650px;
  margin: 14px 0 24px;
  color: var(--muted);
  font: .96rem/1.6 var(--font-serif);
}

/* ── tables ───────────────────────────────────────────────────────────── */
/* Scroll, never drop a column. See the file header. */

.table-scroll { overflow-x: auto; }
table {
  width: 100%;
  border-collapse: collapse;
  font-size: .9rem;
  font-variant-numeric: tabular-nums;
}
th {
  padding: 12px 11px;
  border-bottom: 2px solid var(--ink);
  color: var(--muted);
  text-align: left;
  font-size: .69rem;
  letter-spacing: .055em;
  line-height: 1.25;
  text-transform: uppercase;
}
td { padding: 14px 11px; border-bottom: 1px solid var(--rule); vertical-align: top; }
td:first-child, th:first-child { padding-left: 0; }
td:last-child, th:last-child { padding-right: 0; }
.numeric { text-align: right; white-space: nowrap; }
.numeric a { color: var(--link); }

/* ── one recall campaign ──────────────────────────────────────────────── */

.campaign { padding: 25px 0 34px; border-bottom: 1px solid var(--rule); }
.campaign__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 8px;
  color: var(--muted);
  font-size: .71rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.facts {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin: 0 0 28px;
  padding: 16px 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}
.facts div { min-width: 0; }
.facts dt {
  margin-bottom: 4px;
  color: var(--muted);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.facts dd { margin: 0; font-size: .83rem; line-height: 1.45; }

/* The manufacturer's own words, set in the reading serif because they are the
   only prose on the page a person actually reads. */
.quoted-prose { max-width: 760px; display: grid; gap: 18px; }
.quoted-prose p { margin: 0; font: .99rem/1.65 var(--font-serif); }

.action-line {
  max-width: 740px;
  margin: 24px 0 0;
  padding: 13px 0;
  border-top: 2px solid var(--ink);
  border-bottom: 2px solid var(--ink);
  font-weight: 700;
}

.comparison {
  margin: 42px 0 0;
  padding: 16px 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
  color: var(--muted);
  font: .92rem/1.55 var(--font-serif);
}

.investigation {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: 5px 12px;
  padding: 18px 0;
  border-bottom: 1px solid var(--rule);
}
.investigation strong { font-variant-numeric: tabular-nums; }
.investigation p { grid-column: 2; margin: 0; color: var(--muted); font-size: .83rem; }

/* ── the advisory ─────────────────────────────────────────────────────── */
/* The loudest element on the site, and the only one allowed to be. Set from
   the federal do-not-drive / park-outside fields. */

.advisory {
  margin-bottom: 34px;
  padding: 22px 25px 26px;
  border-top: 6px solid var(--alert);
  border-bottom: 2px solid var(--alert);
  background: var(--alert-paper);
  color: var(--alert-ink);
}
.advisory p {
  margin: 0 0 8px;
  font-size: .71rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
}
.advisory h2 {
  max-width: 800px;
  margin-bottom: 12px;
  color: var(--alert-ink);
  font-size: clamp(1.6rem, 3vw, 2.55rem);
}
.advisory h2:last-child { margin-bottom: 0; }
.advisory a { color: var(--alert-ink); font-weight: 700; }

/* ── prose pages ──────────────────────────────────────────────────────── */

.reading { max-width: 760px; }
.reading .record-heading { margin-bottom: 38px; }
.reading h2 { margin: 37px 0 10px; font-size: 1.5rem; }
.reading p { margin: 0; font: 1.04rem/1.76 var(--font-serif); }

.not-found { padding-top: 90px; }

/* ── footer ───────────────────────────────────────────────────────────── */

.site-footer {
  max-width: 1180px;
  margin: 0 auto;
  padding: 23px 24px 32px;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 18px;
  font-size: .75rem;
}
.site-footer p { margin: 0; }
.site-footer div { display: flex; gap: 17px; }
.site-footer a { color: var(--muted); }

/* ── phone ────────────────────────────────────────────────────────────── */

@media (max-width: 680px) {
  .site-header__inner { min-height: 65px; padding: 0 16px; }
  .site-header nav { gap: 6px 12px; font-size: .72rem; }
  .main { padding: 27px 16px 58px; }
  .index-intro { padding: 28px 0 43px; }
  .search > div { display: block; }
  .search button { width: 100%; height: 45px; }
  .brand-grid { grid-template-columns: repeat(2, 1fr); }
  .record-heading { padding: 16px 0 28px; }
  .record-section { margin-top: 37px; }
  .facts { grid-template-columns: 1fr 1fr; gap: 16px 20px; }
  .campaign { padding-top: 20px; }
  .campaign__meta { display: grid; }
  /* Full-bleed, so the one element that must not be missed is not inset. */
  .advisory { margin-left: -16px; margin-right: -16px; padding: 20px 16px 24px; }
  .investigation { grid-template-columns: 1fr; }
  .investigation p { grid-column: 1; }
  .site-footer { padding: 20px 16px 26px; display: grid; }
  .site-footer div { flex-wrap: wrap; }
}
