/* ==========================================================================
   Tokens
   ========================================================================== */
:root {
  --accent: #5b4bd6;
  --accent-soft: #efedff;
  --accent-text: #fff;

  --bg: #ffffff;
  --bg-alt: #f6f7fb;
  --surface: #ffffff;
  --border: #e4e6ef;
  --border-strong: #cfd2e2;

  --text: #16182b;
  --text-soft: #5b6076;
  --text-faint: #8b90a6;

  --good: #0f9960;
  --good-bg: #e6f7ef;
  --warn: #b25000;
  --warn-bg: #fdf0e5;
  --bad: #c8304b;
  --bad-bg: #fdecef;

  --radius: 12px;
  --radius-lg: 18px;
  --shadow: 0 1px 2px rgba(16, 18, 40, .06), 0 8px 24px -12px rgba(16, 18, 40, .12);
  --shadow-lift: 0 2px 4px rgba(16, 18, 40, .07), 0 16px 32px -16px rgba(16, 18, 40, .22);

  --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --wrap: 1140px;
  --narrow: 760px;
}

/* Dark theme.
   Light is the default for everyone. Dark applies only when the visitor picks
   it from the header toggle; the choice is stored in localStorage and
   re-applied by an inline script before first paint, so there is no flash.
   Deliberately NOT keyed to prefers-color-scheme — a visitor arriving with a
   dark OS still gets light until they choose otherwise. */
:root[data-theme="dark"] {
  --accent: #8b7dff;
  --accent-soft: #232046;
  --accent-text: #0e0f1c;

  --bg: #0e0f1a;
  --bg-alt: #141625;
  --surface: #171930;
  --border: #262943;
  --border-strong: #363a5c;

  --text: #eceef8;
  --text-soft: #a4a9c4;
  --text-faint: #767c9a;

  --good: #4ade9a;
  --good-bg: #13301f;
  --warn: #f0a55a;
  --warn-bg: #33210f;
  --bad: #ff7d95;
  --bad-bg: #351320;

  --shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px -12px rgba(0, 0, 0, .6);
  --shadow-lift: 0 2px 4px rgba(0, 0, 0, .5), 0 16px 32px -16px rgba(0, 0, 0, .7);

  color-scheme: dark;
}

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

/* `scrollbar-gutter` keeps the space the scrollbar would take, whether or not
   the page is long enough to need one. Without it a short page is 15px wider
   than a long one, and every centred thing on it — the logo, the nav, the
   whole column — jumps sideways on the way in. It reads as the layout
   breaking, and the search page, being short until you type, hit it every
   time. */
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; scrollbar-gutter: stable; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

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

h1, h2, h3 { line-height: 1.2; letter-spacing: -.02em; margin: 0 0 .5em; font-weight: 700; }
h1 { font-size: clamp(2rem, 1.3rem + 2.6vw, 3rem); letter-spacing: -.03em; }
h2 { font-size: clamp(1.4rem, 1.1rem + 1vw, 1.8rem); }
h3 { font-size: 1.15rem; }
p { margin: 0 0 1.1em; }

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { text-decoration-thickness: 2px; }

code {
  font-family: var(--mono);
  font-size: .87em;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: .1em .35em;
  /* Inline code is usually one unbreakable token, and some of them are long:
     the Wi-Fi payload on the QR page, `WIFI:T:WPA;S:NetworkName;P:ThePassword;;`,
     is 326px wide and a 320px phone has 280px of column, so the paragraph was
     pushed past the edge of the screen. `anywhere` lets it break where it has
     to, and counts in the min-content width, so a flex or grid parent cannot
     be widened by it either. */
  overflow-wrap: anywhere;
}

:focus-visible { outline: 2.5px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

.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-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-text);
  padding: .7rem 1.1rem; border-radius: 0 0 var(--radius) 0; font-weight: 600;
}
.skip-link:focus { left: 0; }

.wrap { max-width: var(--wrap); margin-inline: auto; padding-inline: 20px; }
.wrap.narrow { max-width: var(--narrow); }
.section { margin-block: 3.5rem; }
.muted { color: var(--text-soft); }

/* ==========================================================================
   Header / footer
   ========================================================================== */
.site-header {
  position: sticky; top: 0; z-index: 30;
  background: color-mix(in srgb, var(--bg) 85%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid var(--border);
}
.header-inner { display: flex; align-items: center; justify-content: space-between; gap: 1rem; min-height: 62px; }

.brand {
  display: inline-flex; align-items: center; gap: .55rem;
  font-weight: 700; font-size: 1.12rem; letter-spacing: -.02em;
  color: var(--text); text-decoration: none;
}
.brand-mark { fill: var(--accent); flex: none; }
.brand--footer { font-size: 1rem; margin-bottom: .5rem; }

/* The nav holds the page links and nothing else. It is the one header item
   allowed to give way when the three do not fit side by side: `min-width: 0`
   lets it shrink past its content width and `flex-wrap` lets the links reflow
   onto a second row instead of pushing the page wider than the screen. */
.site-nav { display: flex; gap: .35rem; flex-wrap: wrap; min-width: 0; }

/* The language button and the theme toggle. `flex: none` is the point: they
   keep their size whatever the links do, so they never move or shrink. */
.header-actions { display: flex; align-items: center; flex: none; }
.site-nav a {
  color: var(--text-soft); text-decoration: none; font-size: .94rem; font-weight: 550;
  padding: .45rem .7rem; border-radius: 8px;
}
.site-nav a:hover { color: var(--text); background: var(--bg-alt); }
.nav-short { display: none; }

.site-footer {
  margin-top: 5rem; padding-block: 2.5rem 2rem;
  border-top: 1px solid var(--border); background: var(--bg-alt);
  font-size: .93rem;
}
.footer-top { display: flex; flex-wrap: wrap; gap: 1.5rem 3rem; justify-content: space-between; }
.footer-blurb { color: var(--text-soft); max-width: 44ch; margin: 0; }
.footer-nav { display: flex; flex-wrap: wrap; gap: .3rem 1.4rem; align-content: start; }
.footer-nav a { color: var(--text-soft); text-decoration: none; }
.footer-nav a:hover { color: var(--accent); text-decoration: underline; }
.copyright { margin: 2rem 0 0; color: var(--text-faint); font-size: .87rem; }

.breadcrumbs {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  padding-top: 1.1rem; font-size: .86rem; color: var(--text-faint);
}
.breadcrumbs a { color: var(--text-soft); text-decoration: none; }
.breadcrumbs a:hover { color: var(--accent); text-decoration: underline; }
.breadcrumbs .sep { color: var(--border-strong); }

/* ==========================================================================
   Buttons
   ========================================================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
  background: var(--accent); color: var(--accent-text);
  border: 1px solid transparent; border-radius: 10px;
  padding: .6rem 1.1rem;
  font: inherit; font-weight: 600; font-size: .95rem;
  text-decoration: none; cursor: pointer;
  transition: transform .12s ease, filter .12s ease, background .12s ease;
}
.btn:hover { filter: brightness(1.08); transform: translateY(-1px); }
.btn:active { transform: translateY(0); }
.btn-lg { padding: .8rem 1.5rem; font-size: 1rem; }
.btn-small { padding: .4rem .8rem; font-size: .85rem; border-radius: 8px; }
.btn-ghost {
  background: var(--surface); color: var(--text); border-color: var(--border-strong);
}
.btn-ghost:hover { background: var(--bg-alt); filter: none; }

/* ==========================================================================
   Hero + home
   ========================================================================== */
.hero { padding-block: 3.5rem 2.5rem; text-align: center; }
.hero h1 { max-width: 16ch; margin-inline: auto; }
.hero-sub {
  max-width: 62ch; margin: 0 auto 1.8rem; font-size: 1.1rem; color: var(--text-soft);
}
.hero-actions { display: flex; flex-wrap: wrap; gap: .7rem; justify-content: center; }

.promises {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 1px;
  background: var(--border); border: 1px solid var(--border);
  border-radius: var(--radius-lg); overflow: hidden;
}
.promise { background: var(--surface); padding: 1.2rem 1.3rem; display: grid; gap: .3rem; }
.promise strong { font-size: .97rem; }
.promise span { color: var(--text-soft); font-size: .9rem; line-height: 1.5; }

.section-title { margin-bottom: .3rem; }
.section-sub { color: var(--text-soft); max-width: 68ch; margin-bottom: 1.4rem; }
.section-title + .grid { margin-top: 1.4rem; }

/* ==========================================================================
   Cards
   ========================================================================== */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 1rem; }

.card {
  display: grid; align-content: start; gap: .35rem;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 1.15rem 1.2rem 1.1rem;
  text-decoration: none; color: inherit; box-shadow: var(--shadow);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
}
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lift); border-color: var(--border-strong); }
.card-cat {
  font-size: .71rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent);
}
.card-title { font-size: 1.06rem; margin: 0; }
.card-text { color: var(--text-soft); font-size: .92rem; margin: 0 0 .5rem; line-height: 1.55; }
.card-go { font-size: .87rem; font-weight: 600; color: var(--accent); margin-top: auto; }
.card:hover .card-go span { display: inline-block; transform: translateX(3px); transition: transform .15s ease; }

.related { margin-top: 3.5rem; }

/* ==========================================================================
   Page head + prose
   ========================================================================== */
.page-head { padding-block: 2rem 1.5rem; }
.eyebrow {
  display: inline-block; margin-bottom: .6rem;
  font-size: .73rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-soft);
  padding: .25rem .6rem; border-radius: 99px;
}
.lede { font-size: 1.1rem; color: var(--text-soft); margin-bottom: 0; }
.lede p:last-child { margin-bottom: 0; }
.byline { margin-top: 1rem; font-size: .88rem; color: var(--text-faint); }

.prose { margin-top: 3rem; }
.prose h2 { margin-top: 2.4rem; }
.prose h3 { margin-top: 1.8rem; }
.prose ul, .prose ol { padding-left: 1.3rem; margin: 0 0 1.2rem; }
.prose li { margin-bottom: .55rem; }
.prose li::marker { color: var(--text-faint); }
.prose strong { font-weight: 650; }

.updated {
  margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--border);
  font-size: .86rem; color: var(--text-faint);
}
.notice {
  background: var(--warn-bg); border: 1px solid color-mix(in srgb, var(--warn) 30%, transparent);
  color: var(--warn); padding: .9rem 1.1rem; border-radius: var(--radius); font-size: .93rem;
}
/* A notice that reports a verdict should say so in its colour as well as its
   words. Three tools were already setting these two classes and nothing was
   styling them, so a passing check and a failing one looked identical. */
.notice.ok {
  background: var(--good-bg); border-color: color-mix(in srgb, var(--good) 30%, transparent);
  color: var(--good);
}
.notice.err {
  background: var(--bad-bg); border-color: color-mix(in srgb, var(--bad) 32%, transparent);
  color: var(--bad);
}

/* ==========================================================================
   FAQ
   ========================================================================== */
.faq { margin-top: 3rem; }
.faq-item { border-bottom: 1px solid var(--border); }
.faq-item summary {
  cursor: pointer; padding: 1rem 2rem 1rem 0; font-weight: 600; position: relative;
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+"; position: absolute; right: .3rem; top: 50%; transform: translateY(-50%);
  font-size: 1.4rem; font-weight: 400; color: var(--text-faint); line-height: 1;
}
.faq-item[open] summary::after { content: "\2212"; }
.faq-item summary:hover { color: var(--accent); }
.faq-answer { color: var(--text-soft); padding-bottom: .6rem; }
.faq-answer p { margin: 0; }

/* ==========================================================================
   Ad slots
   ========================================================================== */
.ad-slot { margin-block: 2.5rem; min-height: 90px; text-align: center; }
.ad-slot--placeholder {
  display: grid; place-items: center;
  border: 1.5px dashed var(--border-strong); border-radius: var(--radius);
  background: var(--bg-alt); color: var(--text-faint);
  font-size: .8rem; letter-spacing: .05em; text-transform: uppercase;
  min-height: 110px;
}

/* ==========================================================================
   Tool shell
   ========================================================================== */
.tool {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); padding: 1.5rem; box-shadow: var(--shadow);
}
.sub { font-size: .8rem; text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); margin: 1.8rem 0 .8rem; }
.field-label { display: block; font-size: .85rem; font-weight: 600; color: var(--text-soft); margin: 1.2rem 0 .4rem; }

.dropzone {
  display: grid; place-items: center; gap: .3rem; text-align: center;
  border: 2px dashed var(--border-strong); border-radius: var(--radius);
  padding: 2.6rem 1.5rem; cursor: pointer; background: var(--bg-alt);
  transition: border-color .15s ease, background .15s ease;
}
.dropzone:hover, .dropzone.is-over { border-color: var(--accent); background: var(--accent-soft); }
.dz-icon { color: var(--accent); margin-bottom: .3rem; }
.dz-title { margin: 0; font-weight: 600; font-size: 1.02rem; }
.dz-hint { margin: 0; font-size: .85rem; color: var(--text-soft); }
.link-look { color: var(--accent); text-decoration: underline; }

.controls {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 1.3rem;
  margin-top: 1.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border);
}
.control { display: grid; gap: .45rem; align-content: start; border: 0; padding: 0; margin: 0; min-width: 0; }
.control--wide { grid-column: 1 / -1; }
.control label, .control legend { font-size: .88rem; font-weight: 600; padding: 0; }
.control-hint { margin: 0; font-size: .8rem; color: var(--text-faint); line-height: 1.45; }

select, input[type="text"] {
  font: inherit; font-size: .93rem; color: var(--text);
  background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: 9px; padding: .55rem .7rem; width: 100%;
}
input[type="range"] { width: 100%; accent-color: var(--accent); }
input[type="color"] { border: 1px solid var(--border-strong); border-radius: 9px; padding: 2px; height: 42px; width: 100%; background: var(--bg); cursor: pointer; }
output { font-variant-numeric: tabular-nums; }

.checks { display: flex; flex-wrap: wrap; gap: .5rem 1.4rem; }
.check { display: inline-flex; align-items: center; gap: .45rem; font-weight: 450; font-size: .92rem; cursor: pointer; }
.check input { accent-color: var(--accent); width: 17px; height: 17px; }

.text-area {
  width: 100%; font: inherit; font-size: .97rem; line-height: 1.6; color: var(--text);
  background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: .9rem 1rem; resize: vertical;
}
.text-area--out { background: var(--bg-alt); }

.tool-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-top: .9rem; }
.tool-bar-note { font-size: .85rem; color: var(--text-faint); }

.panel { margin-top: 1.2rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-alt); }
.panel > summary { cursor: pointer; padding: .8rem 1rem; font-weight: 600; font-size: .92rem; }
.panel > *:not(summary) { padding: 0 1rem 1rem; }

/* --- results list --------------------------------------------------------- */
.results { margin-top: 1.5rem; padding-top: 1.3rem; border-top: 1px solid var(--border); }
.results-head { display: flex; flex-wrap: wrap; gap: .8rem; align-items: center; justify-content: space-between; }
.results-summary { margin: 0; font-size: .93rem; color: var(--text-soft); }

.file-list { list-style: none; margin: 1rem 0 0; padding: 0; display: grid; gap: .5rem; }
.file-row {
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1.6fr) auto;
  gap: .5rem 1rem; align-items: center;
  padding: .75rem .9rem; border: 1px solid var(--border);
  border-radius: 10px; background: var(--bg-alt);
}
.file-row.is-error { border-color: color-mix(in srgb, var(--bad) 40%, transparent); }
.file-name {
  font-size: .9rem; font-weight: 550;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.file-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; font-size: .87rem; }
.file-actions { justify-self: end; }
.arrow { color: var(--text-faint); }
.dims { font-size: .8rem; }
.err { color: var(--bad); }

.badge {
  font-size: .75rem; font-weight: 700; padding: .12rem .45rem; border-radius: 99px;
  font-variant-numeric: tabular-nums;
}
.badge-good { background: var(--good-bg); color: var(--good); }
.badge-warn { background: var(--warn-bg); color: var(--warn); }

/* ==========================================================================
   Password generator
   ========================================================================== */
.password-output {
  display: flex; flex-wrap: wrap; align-items: center; gap: .8rem;
  background: var(--bg-alt); border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: 1rem 1.1rem;
}
.pw-field {
  flex: 1 1 260px; min-width: 0;
  font-family: var(--mono); font-size: 1.15rem; font-weight: 600;
  word-break: break-all; line-height: 1.45;
}
.pw-field.is-empty { color: var(--text-faint); font-family: var(--font); font-size: .95rem; font-weight: 400; }
.pw-buttons { display: flex; gap: .5rem; margin-left: auto; }

.strength { margin-top: 1rem; }
.strength-bar { height: 7px; border-radius: 99px; background: var(--bg-alt); border: 1px solid var(--border); overflow: hidden; }
.strength-bar span { display: block; height: 100%; width: 0; border-radius: 99px; transition: width .25s ease, background .25s ease; }
.strength-bar span.weak { background: var(--bad); }
.strength-bar span.ok { background: var(--warn); }
.strength-bar span.good { background: var(--good); }
.strength-bar span.great { background: var(--accent); }
.strength-text { margin: .55rem 0 0; font-size: .87rem; color: var(--text-soft); }

/* ==========================================================================
   Word counter
   ========================================================================== */
.stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 1px;
  background: var(--border); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; margin-bottom: 1.3rem;
}
.stat { background: var(--surface); padding: .85rem .5rem; text-align: center; display: grid; gap: .1rem; }
.stat-num { font-size: 1.45rem; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.stat-label { font-size: .73rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint); }

.limits { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; }
.limit { display: grid; grid-template-columns: minmax(0, 11rem) 1fr auto; gap: .8rem; align-items: center; font-size: .86rem; }
.limit-name { color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.limit-bar { height: 6px; border-radius: 99px; background: var(--border); overflow: hidden; }
.limit-bar i { display: block; height: 100%; background: var(--accent); }
.limit-left { font-variant-numeric: tabular-nums; color: var(--text-faint); white-space: nowrap; }
.limit.is-over .limit-bar i { background: var(--bad); }
.limit.is-over .limit-left { color: var(--bad); font-weight: 600; }

.keywords { margin: 0; padding-left: 1.4rem; display: grid; gap: .3rem; font-size: .9rem; }
.keywords li { display: flex; justify-content: space-between; gap: 1rem; }

/* ==========================================================================
   Case converter
   ========================================================================== */
.case-buttons { display: flex; flex-wrap: wrap; gap: .45rem; margin: 1rem 0 .3rem; }
.case-btn {
  font: inherit; font-size: .85rem; font-weight: 550; cursor: pointer;
  background: var(--bg-alt); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 8px; padding: .4rem .75rem;
  transition: background .12s ease, border-color .12s ease, color .12s ease;
}
.case-btn:hover { border-color: var(--accent); color: var(--accent); }
.case-btn.is-active { background: var(--accent); border-color: var(--accent); color: var(--accent-text); }

/* ==========================================================================
   Color converter
   ========================================================================== */
.color-head { display: grid; grid-template-columns: minmax(0, 150px) minmax(0, 1fr); gap: 1.5rem; align-items: start; }
.swatch-big {
  aspect-ratio: 1; border-radius: var(--radius); border: 1px solid var(--border);
  display: grid; place-items: center; overflow: hidden;
}
.swatch-big input[type="color"] { opacity: 0; width: 100%; height: 100%; border: 0; padding: 0; cursor: pointer; }
.color-fields { display: grid; gap: .75rem; }
.field { display: grid; gap: .3rem; }
.field label { font-size: .78rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint); }
.field input { font-family: var(--mono); }
.field-note { margin: 0; font-size: .84rem; color: var(--bad); min-height: 1.2em; }

.palette { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: .4rem; }
.swatch {
  border: 1px solid var(--border); border-radius: 9px; cursor: pointer;
  aspect-ratio: 1.6; display: grid; place-items: center; padding: 0;
  font: inherit; font-family: var(--mono); font-size: .72rem; font-weight: 600;
  transition: transform .12s ease;
}
.swatch:hover { transform: scale(1.05); }

.contrast { display: grid; gap: .8rem; }
.contrast-row { display: grid; grid-template-columns: 62px 1fr; gap: 1rem; align-items: center; }
.contrast-demo {
  display: grid; place-items: center; aspect-ratio: 1; border-radius: 10px;
  border: 1px solid var(--border); font-size: 1.3rem; font-weight: 700;
}
.contrast-info { font-size: .89rem; }
.pills { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .4rem; }
.pill { font-size: .74rem; font-weight: 650; padding: .15rem .5rem; border-radius: 99px; }
.pill.pass { background: var(--good-bg); color: var(--good); }
.pill.fail { background: var(--bad-bg); color: var(--bad); }

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 640px) {
  body { font-size: 16px; }
  .tool { padding: 1.1rem; border-radius: var(--radius); }
  .file-row { grid-template-columns: 1fr auto; }
  .file-meta { grid-column: 1 / -1; }
  .file-actions { grid-row: 1; grid-column: 2; }
  .color-head { grid-template-columns: 1fr; }
  .swatch-big { aspect-ratio: 2.6; }
  .limit { grid-template-columns: 1fr auto; }
  .limit-bar { grid-column: 1 / -1; grid-row: 2; }
  .hero { padding-block: 2.5rem 1.5rem; }

  /* The compact header lives at the end of this file, in one block. */
}

/* ==========================================================================
   Comparison tables (articles)
   ========================================================================== */
/* ==========================================================================
   Code blocks
   ==========================================================================
   `pre` had no rule at all, and both of its problems followed from that. The
   text could not wrap — `white-space: pre` is the point of the element — and
   with nothing to scroll in, a long line took the whole page sideways: the
   JSON sample on the CSV post ran 211px past a 375px screen. And the `code`
   inside drew its inline pill, so a block came out as a stack of bordered
   line fragments rather than one box.

   So: the same scrolling, bordered box the comparison tables sit in, and the
   inline pill switched off inside it. A code block and a table are the two
   things in a post too wide for a phone, and they should look like siblings
   while they handle it the same way. */
pre {
  overflow-x: auto; margin: 0 0 1.6rem; padding: .95rem 1.1rem;
  background: var(--bg-alt);
  border: 1px solid var(--border); border-radius: var(--radius);
  font-size: .86rem; line-height: 1.55;
}
pre code {
  padding: 0; font-size: inherit;
  background: none; border: 0; border-radius: 0;
}

.table-wrap { overflow-x: auto; margin: 0 0 1.6rem; border: 1px solid var(--border); border-radius: var(--radius); }
table { border-collapse: collapse; width: 100%; font-size: .9rem; min-width: 480px; }
thead th {
  text-align: left; background: var(--bg-alt); font-size: .76rem;
  text-transform: uppercase; letter-spacing: .05em; color: var(--text-faint);
  padding: .7rem .9rem; border-bottom: 1px solid var(--border); white-space: nowrap;
}
tbody td { padding: .75rem .9rem; border-bottom: 1px solid var(--border); vertical-align: top; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--bg-alt); }
td strong { white-space: nowrap; }

/* Callout used for the "what to actually pick" summary */
.verdict {
  background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  border-radius: var(--radius); padding: 1.2rem 1.35rem; margin: 2rem 0;
}
.verdict h2, .verdict h3 { margin-top: 0; }
.verdict p:last-child { margin-bottom: 0; }

/* Per-option heading with a one-line "best for" strap */
.best-for {
  display: block; font-size: .82rem; font-weight: 650; color: var(--accent);
  text-transform: uppercase; letter-spacing: .05em; margin: -.3em 0 .8em;
}

/* ==========================================================================
   Theme toggle
   ========================================================================== */
.theme-toggle {
  display: inline-grid; place-items: center;
  width: 36px; height: 36px; flex: none;
  margin-left: .35rem; padding: 0;
  background: transparent; color: var(--text-soft);
  border: 1px solid var(--border); border-radius: 9px;
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.theme-toggle:hover { background: var(--bg-alt); color: var(--text); border-color: var(--border-strong); }
.theme-toggle svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* Show the icon for the theme you would switch TO, which is the convention
   people expect: a moon while in light mode, a sun while in dark. */
.theme-toggle .icon-moon { display: block; }
.theme-toggle .icon-sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-sun { display: block; }

/* ==========================================================================
   Password generator — crack-time display
   ========================================================================== */
.mode-tabs {
  display: inline-flex; gap: 2px; padding: 3px; margin-bottom: 1.1rem;
  background: var(--bg-alt); border: 1px solid var(--border); border-radius: 10px;
}
.mode-tab {
  font: inherit; font-size: .88rem; font-weight: 600; cursor: pointer;
  padding: .4rem .9rem; border: 0; border-radius: 7px;
  background: transparent; color: var(--text-soft);
  transition: background .15s ease, color .15s ease;
}
.mode-tab:hover { color: var(--text); }
.mode-tab.is-active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }

.pw-input-wrap { display: flex; gap: .5rem; align-items: center; }
.pw-input {
  flex: 1; min-width: 0;
  font-family: var(--mono); font-size: 1.05rem; color: var(--text);
  background: var(--bg-alt); border: 1px solid var(--border-strong);
  border-radius: var(--radius); padding: .8rem 1rem;
}
.pw-input::placeholder { font-family: var(--font); font-size: .95rem; color: var(--text-faint); }
.pw-privacy {
  display: flex; align-items: center; gap: .4rem;
  margin: .6rem 0 0; font-size: .82rem; color: var(--text-faint);
}
.pw-privacy svg { flex: none; color: var(--good); }

/* --- the hero ------------------------------------------------------------- */
.crack {
  --crack-color: var(--text-faint);
  --crack-tint: var(--bg-alt);
  position: relative; overflow: hidden;
  margin-top: 1.4rem; padding: 1.5rem 1.4rem 1.2rem;
  text-align: center;
  background: var(--crack-tint);
  border: 1px solid color-mix(in srgb, var(--crack-color) 28%, transparent);
  border-radius: var(--radius-lg);
  transition: background .35s ease, border-color .35s ease;
}
/* A soft wash of the tier colour bleeding up from the bottom. */
.crack::after {
  content: ""; position: absolute; inset: auto 0 0 0; height: 70%;
  background: radial-gradient(ellipse at 50% 120%,
    color-mix(in srgb, var(--crack-color) 22%, transparent), transparent 70%);
  pointer-events: none; transition: background .35s ease;
}
.crack > * { position: relative; z-index: 1; }

.crack-label {
  font-size: .72rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-faint);
}
.crack-value {
  margin: .35rem 0 .2rem;
  font-size: clamp(1.75rem, 1.1rem + 3.4vw, 3.1rem);
  font-weight: 800; line-height: 1.1; letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
  color: var(--crack-color);
  transition: color .35s ease;
}
.crack-sub { margin: 0; font-size: .9rem; color: var(--text-soft); min-height: 1.4em; }

.crack[data-tier="none"]     { --crack-color: var(--text-faint); }
.crack[data-tier="instant"]  { --crack-color: var(--bad);  --crack-tint: var(--bad-bg); }
.crack[data-tier="trivial"]  { --crack-color: var(--bad);  --crack-tint: var(--bad-bg); }
.crack[data-tier="weak"]     { --crack-color: var(--warn); --crack-tint: var(--warn-bg); }
.crack[data-tier="fair"]     { --crack-color: var(--warn); --crack-tint: var(--warn-bg); }
.crack[data-tier="good"]     { --crack-color: var(--good); --crack-tint: var(--good-bg); }
.crack[data-tier="strong"]   { --crack-color: var(--good); --crack-tint: var(--good-bg); }
.crack[data-tier="extreme"]  { --crack-color: var(--accent); --crack-tint: var(--accent-soft); }

/* --- log-scale timeline --------------------------------------------------- */
.timeline { margin-top: 1.2rem; }
.timeline-track {
  position: relative; height: 8px; border-radius: 99px;
  background: color-mix(in srgb, var(--text-faint) 18%, transparent);
}
.timeline-fill {
  height: 100%; width: 0; border-radius: 99px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--crack-color) 45%, transparent), var(--crack-color));
  transition: width .45s cubic-bezier(.22, 1, .36, 1), background .35s ease;
}
.timeline-pin {
  position: absolute; top: 50%; left: 0; width: 0; height: 0;
  transition: left .45s cubic-bezier(.22, 1, .36, 1);
}
.timeline-pin-dot {
  position: absolute; top: -7px; left: -7px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--crack-color);
  border: 2.5px solid var(--surface);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--crack-color) 50%, transparent);
  transition: background .35s ease;
}
.timeline-ticks {
  display: flex; justify-content: space-between; margin-top: .45rem;
  font-size: .68rem; letter-spacing: .03em; color: var(--text-faint);
}

/* --- attacker chips ------------------------------------------------------- */
.attackers { border: 0; padding: 0; margin: 1.4rem 0 0; }
.attackers legend {
  padding: 0; margin-bottom: .55rem;
  font-size: .78rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--text-faint);
}
.attacker-chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.attacker-chip {
  font: inherit; font-size: .84rem; font-weight: 600; cursor: pointer;
  padding: .4rem .8rem; border-radius: 99px;
  background: var(--bg-alt); color: var(--text-soft);
  border: 1px solid var(--border-strong);
  transition: background .14s ease, color .14s ease, border-color .14s ease, transform .14s ease;
}
.attacker-chip:hover { color: var(--text); border-color: var(--accent); transform: translateY(-1px); }
.attacker-chip.is-active {
  background: var(--accent); color: var(--accent-text); border-color: var(--accent);
}
.attacker-desc {
  margin: .7rem 0 0; font-size: .87rem; color: var(--text-soft);
  min-height: 2.6em; max-width: 62ch;
}

/* --- pattern warnings ----------------------------------------------------- */
.pw-warnings { list-style: none; margin: 1.1rem 0 0; padding: 0; display: grid; gap: .4rem; }
.pw-warnings li {
  display: flex; gap: .5rem; align-items: baseline;
  font-size: .87rem; line-height: 1.5;
  background: var(--warn-bg); color: var(--warn);
  border: 1px solid color-mix(in srgb, var(--warn) 25%, transparent);
  border-radius: 9px; padding: .55rem .8rem;
}
.pw-warnings li::before { content: "!"; font-weight: 800; flex: none; }

.pwgen .strength { margin-top: 1.4rem; }
.pwgen .controls { margin-top: 1.5rem; }

@media (max-width: 640px) {
  .crack { padding: 1.2rem 1rem 1rem; }
  .timeline-ticks { font-size: .62rem; }
  .attacker-desc { min-height: 3.4em; }
}

/* ==========================================================================
   Blog post closing
   The block that hands the reader the tool and the next post. Styled as a
   quiet aside rather than a CTA banner — Rule 2 wants prose, not a billboard.
   ========================================================================== */
.post-closing {
  margin-top: 2.6rem;
  padding: 1.3rem 1.4rem;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
}
.post-closing p { margin-bottom: .8rem; }
.post-closing p:last-child { margin-bottom: 0; }
.post-closing a { font-weight: 600; }

/* ==========================================================================
   Support button + Chrome extensions
   ========================================================================== */
.bmc {
  display: inline-flex; align-items: center; gap: .5rem;
  margin-top: 1rem; padding: .5rem .9rem;
  /* The site's own button colour rather than Buy Me a Coffee's yellow, so it
     reads as part of the footer instead of a badge pasted onto it. The label
     and the cup carry the recognition; the tokens follow the theme, so it is
     purple on light and lilac on dark like every other button here. */
  background: var(--accent); color: var(--accent-text);
  border-radius: 9px; font-size: .88rem; font-weight: 650;
  text-decoration: none;
  transition: transform .12s ease, filter .12s ease;
}
.bmc:hover { transform: translateY(-1px); filter: brightness(1.08); }

.ext {
  margin: 2rem 0;
  padding: 1.4rem 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}
.ext-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: .8rem; }
.ext-head h2 { margin: 0 0 .2rem; font-size: 1.3rem; }
.ext-cat {
  font-size: .7rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--accent); background: var(--accent-soft);
  padding: .2rem .55rem; border-radius: 99px;
}
.ext-tagline { font-size: 1.02rem; color: var(--text); font-weight: 550; margin-top: .5rem; }
.ext-why {
  color: var(--text-soft); font-size: .93rem;
  border-left: 2px solid var(--border-strong); padding-left: .9rem;
}
.ext .btn { margin-top: .3rem; }

/* ==========================================================================
   Language switcher
   ========================================================================== */
.lang-switch { position: relative; display: inline-block; }
.lang-btn {
  display: inline-flex; align-items: center; gap: .3rem;
  height: 36px; padding: 0 .55rem; margin-left: .2rem;
  font: inherit; font-size: .82rem; font-weight: 650;
  background: transparent; color: var(--text-soft);
  border: 1px solid var(--border); border-radius: 9px; cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.lang-btn:hover { background: var(--bg-alt); color: var(--text); border-color: var(--border-strong); }
.lang-btn svg { flex: none; }
.lang-caret { opacity: .6; transition: transform .18s ease; }
.lang-btn[aria-expanded="true"] { background: var(--bg-alt); color: var(--text); border-color: var(--border-strong); }
.lang-btn[aria-expanded="true"] .lang-caret { transform: rotate(180deg); }

/* The menu floats above the page instead of taking part in the header's
   layout: absolute inside the .lang-switch, so opening it cannot move a
   single pixel of anything else. */
.lang-menu {
  position: absolute; top: calc(100% + .45rem); right: 0; z-index: 60;
  min-width: 190px; max-width: calc(100vw - 2rem);
  margin: 0; padding: .35rem;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .3), 0 4px 12px -4px rgba(0, 0, 0, .12);
  animation: lang-pop .16s ease-out;
}
@keyframes lang-pop {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .lang-menu { animation: none; }
  .lang-caret { transition: none; }
}

.lang-menu li { margin: 0; }
.lang-item {
  display: flex; align-items: center; gap: .6rem;
  padding: .5rem .55rem; border-radius: 9px;
  font-size: .92rem; color: var(--text); text-decoration: none;
  transition: background .12s ease;
}
.lang-item:hover, .lang-item:focus-visible { background: var(--bg-alt); }
.lang-code {
  flex: none; min-width: 30px; padding: .15rem 0;
  font-size: .68rem; font-weight: 750; letter-spacing: .06em; text-align: center;
  color: var(--text-soft); background: var(--bg-alt);
  border: 1px solid var(--border); border-radius: 6px;
}
.lang-name { flex: 1; }
.lang-check { flex: none; color: var(--accent); }
.lang-item[aria-current="true"] { font-weight: 650; }
.lang-item[aria-current="true"] .lang-code {
  color: var(--accent); background: var(--accent-soft); border-color: transparent;
}

/* ==========================================================================
   Navigation dropdowns
   ========================================================================== */
.nav-item { position: relative; display: inline-block; }
.site-nav .nav-link {
  display: inline-flex; align-items: center; gap: .25rem;
  color: var(--text-soft); text-decoration: none; font-size: .94rem; font-weight: 550;
  padding: .45rem .7rem; border-radius: 8px;
}
.site-nav .nav-link:hover { color: var(--text); background: var(--bg-alt); }
.nav-caret { opacity: .55; flex: none; transition: transform .18s ease; }
.nav-link[aria-expanded="true"] { color: var(--text); background: var(--bg-alt); }
.nav-link[aria-expanded="true"] .nav-caret { transform: rotate(180deg); }

/* Absolute, like the language menu: opening one must not move a pixel of the
   page. The bridge on ::before covers the gap between the trigger and the
   panel so the pointer can cross it without the menu closing underneath. */
.nav-menu {
  position: absolute; top: calc(100% + .45rem); left: 0; z-index: 60;
  min-width: 230px; max-width: calc(100vw - 2rem);
  padding: .35rem;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  box-shadow: 0 18px 40px -12px rgba(0, 0, 0, .3), 0 4px 12px -4px rgba(0, 0, 0, .12);
  animation: lang-pop .16s ease-out;
}
.nav-menu::before {
  content: ""; position: absolute; left: 0; right: 0; top: -.5rem; height: .5rem;
}
@media (prefers-reduced-motion: reduce) { .nav-menu { animation: none; } }

.nav-menu-list { list-style: none; margin: 0; padding: 0; }
.nav-menu-list li { margin: 0; }
.nav-menu-list a {
  display: flex; align-items: center; justify-content: space-between; gap: .8rem;
  padding: .45rem .55rem; border-radius: 9px;
  font-size: .92rem; color: var(--text); text-decoration: none;
}
.nav-menu-list a:hover, .nav-menu-list a:focus-visible { background: var(--bg-alt); }
.nav-menu-label { min-width: 0; }
.nav-menu-count {
  flex: none; font-size: .72rem; font-weight: 700; color: var(--text-faint);
  background: var(--bg-alt); border-radius: 99px; padding: .1rem .45rem;
}
.nav-menu-list a:hover .nav-menu-count { color: var(--text-soft); }
.nav-menu-all {
  display: block; margin-top: .3rem; padding: .5rem .55rem;
  border-top: 1px solid var(--border);
  font-size: .86rem; font-weight: 650; color: var(--accent); text-decoration: none;
}
.nav-menu-all:hover { background: var(--bg-alt); border-radius: 9px; }

/* A category card leads with its count, so the badge reads as a quantity
   rather than as a label. */
.card--cat .card-cat { color: var(--accent); background: var(--accent-soft); }

.chip-row { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0; }
.chip {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .4rem .75rem; border-radius: 99px;
  background: var(--surface); border: 1px solid var(--border);
  font-size: .88rem; font-weight: 550; color: var(--text); text-decoration: none;
}
.chip:hover { border-color: var(--border-strong); background: var(--bg-alt); }
.chip-count { font-size: .74rem; font-weight: 700; color: var(--text-faint); }
.section-title a { color: inherit; text-decoration: none; }
.section-title a:hover { color: var(--accent); }

@media (max-width: 860px) {
  /* Below this the header has no room for a panel wide enough to read, and a
     hover menu is meaningless on a touch screen anyway. The links still work;
     they just go straight to the index. */
  .nav-menu { display: none !important; }
  .nav-caret { display: none; }
}


/* The credit and the verse sit with the footer links rather than across the
   bottom of the page: they belong to the same column, directly under About,
   Contact, Privacy Policy and Terms of Use. Below the breakpoint the footer
   is one column anyway, so they simply follow. */
/* Three rows in the right-hand column, beside the brand block — where the
   footer links have always been. Top to bottom: the links on one line, the
   copyright, then the credit and the verse sharing a line. */
.footer-bottom { display: flex; flex-direction: column; align-items: flex-end; text-align: right; }
.footer-bottom .footer-nav { flex-wrap: nowrap; gap: 1.4rem; }
.footer-bottom .copyright { margin-top: 1.1rem; }
.footer-meta {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .1rem .6rem;
  margin: .4rem 0 0; color: var(--text-faint); font-size: .84rem;
}
.footer-meta a { color: var(--text-soft); text-decoration: none; border-bottom: 1px solid var(--border-strong); }
.footer-meta a:hover { color: var(--accent); border-bottom-color: currentColor; }
.footer-sep { color: var(--border-strong); }

/* The separator is a join between two lines, not a bullet in a list: when the
   line wraps on a narrow screen it has nothing left to separate. */
@media (max-width: 520px) {
  .footer-sep { display: none; }
  .footer-meta { gap: .15rem 0; flex-direction: column; }
}

/* Once the footer stacks there is no right-hand column to sit in, and the
   links need to be allowed to wrap again. */
@media (max-width: 720px) {
  .footer-bottom { align-items: flex-start; text-align: left; }
  .footer-bottom .footer-nav { flex-wrap: wrap; gap: .3rem 1.2rem; }
  .footer-meta { justify-content: flex-start; }
}

/* The consent settings entry point. Quiet, and hidden until Google's message
   is actually live — a "privacy settings" link that opens nothing reads as a
   dark pattern. */
.consent-settings {
  margin-top: .9rem; padding: 0;
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: .84rem; color: var(--text-soft);
  border-bottom: 1px solid var(--border-strong);
}
.consent-settings:hover { color: var(--accent); border-bottom-color: currentColor; }

/* ==========================================================================
   Blog covers
   ========================================================================== */
.post-hero { margin: 1.5rem 0 0; }
.post-hero img {
  display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630;
  border-radius: var(--radius-lg); border: 1px solid var(--border);
}
/* The thumbnail sits flush to the card's top edge, above the padding. */
.card--post { padding-top: 0; overflow: hidden; }
.card-img {
  display: block; width: calc(100% + 2.4rem); height: auto; aspect-ratio: 1200 / 630;
  margin: 0 -1.2rem .55rem; object-fit: cover;
  border-bottom: 1px solid var(--border);
}

/* ==========================================================================
   Site search
   ========================================================================== */
.search-form { display: flex; gap: .6rem; margin: 0 0 1.2rem; }
.search-input {
  flex: 1 1 auto; min-width: 0;
  padding: .7rem .9rem;
  font: inherit; font-size: 1.02rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.search-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}
.search-input::placeholder { color: var(--text-faint); }
/* Safari draws its own clear button on a search field and it sits badly in a
   field this size. */
.search-input::-webkit-search-decoration,
.search-input::-webkit-search-cancel-button { -webkit-appearance: none; }

.search-status { color: var(--text-soft); font-size: .92rem; margin: 0 0 1.1rem; min-height: 1.4em; }
.search-noscript { color: var(--text-soft); }
/* A result card carries an h2, so the prose rules would style it as a heading
   in the flow. It is a card. */
#search-results .card-title { margin: 0 0 .35rem; font-size: 1.04rem; }
#search-results { margin-bottom: 2rem; }

@media (max-width: 460px) {
  .search-form { flex-wrap: wrap; }
  .search-form .btn { width: 100%; }
}

/* --------------------------------------------------------------------------
   Search results, grouped
   --------------------------------------------------------------------------
   Three kinds of thing answer the same query and they are not interchangeable:
   a tool does the job, a guide compares the options, a post explains it. A flat
   list made the reader work that out from a label on every card. A heading says
   it once for the whole run, and a colour makes the run findable when you are
   scrolling rather than reading.

   The colours are the palette's own — the accent, then the two it already uses
   for "good" and "warning" — so nothing new enters the scheme. */
.search-group { margin: 0 0 2.2rem; --kind: var(--text-soft); }
.search-group--tool  { --kind: var(--accent); }
.search-group--guide { --kind: var(--good); }
.search-group--post  { --kind: var(--warn); }

.search-group-title {
  display: flex; align-items: center; gap: .55rem;
  margin: 0 0 .9rem; padding-bottom: .5rem;
  font-size: .82rem; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase;
  color: var(--kind);
  border-bottom: 2px solid var(--kind);
}
.search-group-count {
  margin-left: auto;
  padding: .1rem .5rem;
  font-size: .78rem; font-weight: 700; letter-spacing: 0;
  color: var(--kind);
  background: color-mix(in srgb, var(--kind) 12%, transparent);
  border-radius: 999px;
}
/* A left edge in the group's colour, so a card belongs to its section even
   once the heading has scrolled away. */
/* Cards in a row stretch to the tallest, and these carry no footer link to push
   to the bottom, so a short one left its text sitting against the top edge with
   a hole under it. Centred, the text sits in its card instead of on it. */
#search-results .card { border-left: 3px solid var(--kind); align-content: center; }
#search-results .card-title { margin: 0 0 .35rem; font-size: 1.04rem; }
/* The paragraph's trailing margin is 8px of nothing at the foot of the card,
   which is what made the text look pinned to the top: the padding above was
   even, the space below was not. `align-content` handles the taller cards in a
   row; this handles the rest. */
#search-results .card-text { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   The blog index, by month
   -------------------------------------------------------------------------- */
.month-group { margin: 0 0 2.4rem; }
.month-title {
  display: flex; align-items: baseline; gap: .6rem;
  margin: 0 0 1rem; padding-bottom: .45rem;
  font-size: 1.02rem; font-weight: 700;
  color: var(--text);
  border-bottom: 1px solid var(--border);
}

.month-count {
  margin-left: auto;
  font-size: .82rem; font-weight: 600;
  color: var(--text-faint);
}
/* The date sits with the category, which is where the eye already is. */
.card-date { color: var(--text-faint); font-weight: 600; }
.card-date::before { content: "·"; margin: 0 .4rem; color: var(--border-strong); }

/* --------------------------------------------------------------------------
   The search icon in the nav
   -------------------------------------------------------------------------- */
/* A square, so the hover and focus background is a square rather than a tall
   pill, and the glass sits in the middle of it both ways. The width is tied to
   the height the other nav links already have. */
.nav-icon { display: block; }
.site-nav a:has(.nav-icon) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.45rem;
  padding-inline: 0;
  aspect-ratio: 1;
}

/* --- QR generator: pickers that show the shape instead of naming it ------- */
.qr-group {
  display: grid; gap: .6rem; border: 0; padding: 1.5rem 0 0; margin: 1.5rem 0 0;
  border-top: 1px solid var(--border);
}
.qr-picker { display: flex; flex-wrap: wrap; gap: .45rem; }
.qr-opt {
  width: 2.9rem; height: 2.9rem; padding: 0; cursor: pointer;
  display: grid; place-items: center; overflow: hidden;
  border: 1px solid var(--border-strong); border-radius: 11px;
  background: var(--bg); color: var(--text-soft);
  transition: border-color .14s ease, color .14s ease, transform .14s ease;
}
.qr-opt:hover { color: var(--text); border-color: var(--accent); transform: translateY(-1px); }
.qr-opt svg { width: 1.45rem; height: 1.45rem; display: block; }
.qr-opt img { width: 1.9rem; height: 1.9rem; object-fit: contain; display: block; }
.qr-opt[aria-checked="true"] {
  color: var(--accent); border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);
}

/* --- a tool whose output deserves its own column -------------------------
   The card breaks out of the narrow wrap it sits in, so the controls keep
   roughly the width they had and the preview is added beside them, with the
   pair still centred. The negative margin does the widening rather than a
   transform, because a transformed ancestor would stop the preview sticking.
   Below the breakpoint none of this applies and the card stacks as before. */
@media (min-width: 1000px) {
  .tool--split {
    --split-w: min(1100px, calc(100vw - 3rem));
    margin-inline: calc((100% - var(--split-w)) / 2);
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(290px, 340px);
    gap: 2rem;
    align-items: start;
  }
  .qr-main { min-width: 0; }
  .qr-aside { position: sticky; top: 1.25rem; min-width: 0; }
  .qr-aside .results { margin-top: 0; padding-top: 0; border-top: 0; }
  .qr-aside .notice { margin-top: 0; }
  .qr-aside .tool-bar { margin-top: 0; }
  .qr-main > .controls:first-of-type { margin-top: 1.2rem; padding-top: 1.2rem; }
}

/* ==========================================================================
   The compact header — every narrow-width header rule, in one place
   ==========================================================================
   These two blocks are last in the file on purpose, and they are the only
   place these rules are written.

   The header used to be tuned in three separate media queries scattered
   through the file, and twice a fix had to be re-stated further down because
   a base rule of equal specificity sat after it and won: `.site-nav .nav-link`
   beat the compact padding, and `.theme-toggle` beat the compact 32px — the
   toggle was still 36px on a phone for that reason, which is where the last
   few pixels of the overflow came from. Blocks that come after every base
   rule cannot lose that way. Add narrow-header rules here, not above.

   What the header does when it runs out of room: the links wrap, and only the
   links — `.site-nav { min-width: 0; flex-wrap: wrap }` is what allows it, and
   `.header-actions { flex: none }` is what keeps the language button and the
   theme toggle out of it. With `nowrap` the only possible outcome was a nav
   wider than the screen, which is what happened: about 12px of sideways scroll
   on every page, worst in Spanish, where the labels are longest.

   At 320-340px the links genuinely cannot sit beside the brand and the
   controls, so there they take a second row; from 360px up the header is one
   row in all three languages, measured. Nothing is clipped or hidden at any
   width, and a longer label in some future language costs a second row rather
   than a sideways scrollbar.
   ========================================================================== */

/* The full-size header runs out of room well above the phone breakpoint: at
   700px the Spanish labels alone pushed it onto a second row, 81px tall. The
   short labels and a tighter link box are what `nav-short` is for, and they
   buy enough to keep one row down to 640px in all three languages. */
@media (max-width: 780px) {
  .site-nav { gap: .15rem .1rem; justify-content: flex-end; }
  .site-nav a,
  .site-nav .nav-link { padding: .4rem .45rem; font-size: .89rem; white-space: nowrap; }
  .site-nav a:has(.nav-icon) { width: 2.2rem; }
  .nav-long { display: none; }
  .nav-short { display: inline; }
}

/* A phone. Here the controls shrink too, and the wordmark goes: it costs about
   70px that the links need more, and the mark alone still identifies the site
   and still goes home. */
@media (max-width: 640px) {
  .header-inner { gap: .25rem; }
  .brand { font-size: 1.02rem; }
  .site-header .brand span { display: none; }

  .site-nav { gap: .1rem 0; }
  .site-nav a,
  .site-nav .nav-link { padding: .4rem .25rem; font-size: .83rem; }
  .site-nav a:has(.nav-icon) { width: 1.8rem; }

  /* The globe says "language" to someone browsing; the code beside it says it
     again, and more precisely. On a phone the code alone carries it. */
  .lang-btn { height: 32px; margin-left: .15rem; padding: 0 .3rem; gap: .2rem; font-size: .78rem; }
  .lang-btn svg:not(.lang-caret) { display: none; }
  .lang-caret { width: 11px; height: 11px; }
  .lang-menu { min-width: 170px; }

  .theme-toggle { width: 32px; height: 32px; margin-left: .15rem; }
}
