/* LALA SEO — palette taken from the logo, not chosen alongside it.
 *
 * Sampled from High-Resolution-Color-Logo.png: the ground is #11111F and the
 * brand purple is #8062D6, which together make up 99% of the mark.
 *
 * That brand purple measures 4.13:1 on the ground — enough for large or bold
 * display type, NOT enough for body text or a small link. So it fills the
 * button, the mark and big numbers, while text-sized accents use a lighter
 * tint of the same hue. Using #8062D6 for links would be faithful to the logo
 * and unreadable, and this tool reports contrast failures on other people's
 * sites.
 */
:root {
  color-scheme: dark;

  --bg: #11111F;           /* the logo's own ground */
  --bg-deep: #0B0B16;      /* header/footer */
  --surface: #191930;      /* raised panels */
  --surface-2: #2D254D;    /* hover — also from the logo */
  --border: #2D254D;
  --border-brand: rgba(128, 98, 214, .35);

  --brand: #8062D6;        /* the logo purple — display only, 4.13:1 */
  --brand-deep: #644EA8;
  --brand-text: #B49EEE;   /* same hue, text-safe — 8.07:1 */

  --ink: #EDEAF6;          /* body — 15.8:1 */
  --muted: #A69DC4;
  --accent: var(--brand-text);

  --critical: #F26D5B;   /* validated: >=15 dE vs warning & ok, normal + CVD */
  --warning: #FFD166;
  --info: #8FB4F5;
  --ok: #6FD39B;

  /* How far in the hero starts. Below ~1280px this is the centred position
     (50vw - 31rem); above it the value stops growing, so a wide window gives
     its extra space to the content instead of to an empty left margin. */
  /* The edge of a screenshot.
     --border is #2D254D against a #11111F ground: about the same value, so a
     dark screenshot simply bled into the page and the owner could not tell
     where the picture stopped. This is drawn from --ink instead, so it reads
     as a hairline on every surface we use (--bg, --surface, --bg-deep)
     without turning into a frame that shouts. */
  --frame: rgba(237, 234, 246, .42);

  --stage-inset: clamp(1.4rem, calc(50vw - 31rem), 10rem);

  --radius: 10px;
  --font: ui-sans-serif, -apple-system, "Segoe UI", Inter, system-ui, sans-serif;
  --display: "Airwaves", ui-sans-serif, system-ui, sans-serif;
  --wordmark: "Monoton", ui-serif, Georgia, serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* Served from public/ rather than the asset pipeline. Propshaft resolves these
   to a digest but does not serve the result, and a wordmark that silently falls
   back to Georgia is not a brand.

   Monoton is the wordmark face and nothing else. Every glyph is a set of
   concentric stripes, which is striking at 28px in a logo and illegible in a
   heading, let alone a sentence. Airwaves is oblique display caps — headings
   and labels only, never body copy. */
@font-face {
  font-family: "Monoton";
  src: url("/fonts/Monoton-Regular.ttf") format("truetype");
  font-display: swap;
}
@font-face {
  font-family: "Airwaves";
  src: url("/fonts/Airwaves-Oblique.ttf") format("truetype");
  font-style: oblique;
  font-display: swap;
}
}

* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font); line-height: 1.55; }
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
code { font-family: var(--mono); font-size: .85em; }

/* A word that has a definition, inside a sentence that is about something
   else. It must be findable without competing with the sentence: a dotted
   underline is the long-standing convention for exactly this, and it keeps
   the body text one colour so a paragraph with three of them still reads as
   a paragraph rather than as a list of links. The full link colour arrives
   on hover, once somebody has shown interest in the word. */
a.gloss {
  color: inherit;
  text-decoration: underline dotted;
  text-decoration-color: var(--muted);
  text-underline-offset: 3px;
  cursor: help;
}
a.gloss:hover, a.gloss:focus-visible {
  color: var(--accent);
  text-decoration-color: currentColor;
}

.site-header {
  display: flex; align-items: baseline; gap: 1rem; flex-wrap: wrap;
  padding: 1rem 1.5rem; border-bottom: 1px solid var(--border); background: var(--surface);
}
.wordmark { font-weight: 650; letter-spacing: -.02em; text-decoration: none; color: var(--ink); }
.tagline { color: var(--muted); font-size: .82rem; }

.wrap { max-width: 62rem; margin: 0 auto; padding: 2rem 1.5rem 5rem; }

/* Flush-left by default: every page that uses a lede is left-aligned
   (.legal, .features-head, sites, plans, .prose, .auth), and auto margins
   inside a 46em container indented it ~80px off the h1 above it. The one
   centred context, .pricing-head, re-centres it in its own rule below. */
.lede { color: var(--muted); max-width: 36rem; margin: 0 0 1.8rem; font-size: 1.05rem; line-height: 1.6; }

.audit-form { margin-bottom: 2.5rem; }

/* The bar inside an account, where pasting a domain is the whole purpose of
   the page. Bigger than the landing page's field rather than smaller: there is no
   headline competing with it here, so the field IS the page. */
.audit-form--bar .url-input { padding: 1rem 1.1rem; font-size: 1.05rem; }
.audit-form--bar .btn { padding: 1rem 1.6rem; font-size: 1rem; }
.audit-form--bar .field-row { max-width: 46rem; }
.field-row { display: flex; gap: .6rem; flex-wrap: wrap; }
.url-input {
  flex: 1 1 22rem; padding: .8rem 1rem; font-size: 1rem; font-family: var(--mono);
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--ink);
}
.url-input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.btn {
  padding: .8rem 1.4rem; font-size: .95rem; font-weight: 550; cursor: pointer;
  background: var(--accent); color: #fff; border: 0; border-radius: var(--radius);
}
.btn:hover { filter: brightness(1.08); }

.options { margin-top: .9rem; font-size: .88rem; }
.options summary { cursor: pointer; color: var(--muted); }
/* Now that Options is always open, auto-fit was stretching two number inputs
   across the full width and wrapping "Include PageSpeed Insights" mid-phrase.
   The numbers are small and fixed; the checkboxes want a whole row each. */
.options-grid input[type=number] {
  /* Fixed and narrow. A four-digit page count does not need half the viewport,
     and the full-width rule left over from the collapsed layout was what made
     these two fields swallow the row. */
  width: 6.5rem; display: block; padding: .45rem .6rem;
  border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--ink);
}
.hint { font-size: .8em; opacity: .75; }

.section-title { font-size: 1.05rem; margin: 2rem 0 .8rem; }

.table { width: 100%; border-collapse: collapse; font-size: .9rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.table th { text-align: left; font-weight: 550; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); padding: .6rem .8rem; border-bottom: 1px solid var(--border); }
.table td { padding: .55rem .8rem; border-bottom: 1px solid var(--border); vertical-align: top; }
.table tr:last-child td { border-bottom: 0; }
.table--dense td, .table--dense th { padding: .4rem .6rem; font-size: .84rem; }
.table .url { max-width: 26rem; word-break: break-all; }
.scroll-x { overflow-x: auto; }

.muted { color: var(--muted); }
.flash { padding: .7rem 1rem; border-radius: var(--radius); background: color-mix(in srgb, var(--ok) 12%, transparent); border: 1px solid color-mix(in srgb, var(--ok) 35%, transparent); margin-bottom: 1.2rem; }
.flash--error { background: color-mix(in srgb, var(--critical) 12%, transparent); border-color: color-mix(in srgb, var(--critical) 35%, transparent); }

.crumbs { margin-bottom: 1rem; font-size: .88rem; }
.report-head h1 { margin: 0 0 .2rem; font-size: 1.6rem; letter-spacing: -.02em; word-break: break-all; }

.running { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: 1.2rem; padding: .9rem 1.1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.spinner { width: .85rem; height: .85rem; border: 2px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin .8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }

/* Explanation of a finding: what is wrong, why it costs you, how to fix it.
   Visually subordinate to the finding itself - the finding is the verified
   fact, the explanation is help understanding it. */
.explain { margin: .9rem 0 0; padding: .9rem 1.1rem; background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); }
.explain__p { margin: 0 0 .7rem; line-height: 1.6; font-size: .95rem; }
.explain__p:last-of-type { margin-bottom: 0; }
.explain__label { display: block; font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: .15rem; }
.explain__p--fix .explain__label { color: var(--accent, #b45309); }
.explain__meta { margin: .8rem 0 0; padding-top: .6rem; border-top: 1px solid var(--border); font-size: .78rem; color: var(--muted); }
.explain__by { margin: 0 0 .6rem; font-size: .78rem; color: var(--muted); }
.explain__who { font-family: var(--display); font-style: oblique; font-size: .95rem; color: var(--brand-text); letter-spacing: .01em; }

/* The locked explanation. Deliberately quiet - it sits under a finding the
   reader can already see in full, so it is an offer, not a wall. */
.explain--locked { border-style: dashed; }
.explain__locked-lede { margin: 0 0 .4rem; font-weight: 600; }
.explain__teaser { margin: 0 0 .7rem; padding-left: 1.1rem; color: var(--muted); font-size: .93rem; }
.explain__teaser li { margin: .15rem 0; }
.explain__cta { margin: 0; display: flex; gap: .7rem; align-items: center; flex-wrap: wrap; font-size: .87rem; }
.btn--small { padding: .3rem .8rem; font-size: .87rem; }

/* Checks that errored. Styled as a caution, never as a finding - it reports an
   absence of knowledge, not a defect on the site. */
.incomplete { margin: 1rem 0; padding: .9rem 1.1rem; border: 1px solid var(--border); border-left: 3px solid #b45309; border-radius: var(--radius); background: var(--bg); font-size: .92rem; }
.incomplete ul { margin: .5rem 0 0; padding-left: 1.2rem; }
.incomplete li { margin: .15rem 0; }

/* The what-we-checked table. Quiet by design: it orients the reader, it does
   not compete with the sections it points at. */
.summary-table { margin: 1.5rem 0 2rem; }
.summary-table table { width: 100%; border-collapse: collapse; }
.summary-row { border-top: 1px solid var(--border); }
.summary-row th { text-align: left; padding: .7rem .8rem .7rem 0; font-weight: 600; white-space: nowrap; }
.summary-value { padding: .7rem .9rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.summary-detail { padding: .7rem 0; color: var(--muted); font-size: .9rem; }
.summary-row--bad .summary-value { color: #dc2626; font-weight: 600; }
.summary-row--warn .summary-value { color: #b45309; font-weight: 600; }
.summary-row--good .summary-value { color: #15803d; }
@media (max-width: 640px) {
  .summary-row th, .summary-value, .summary-detail { display: block; padding: .2rem 0; white-space: normal; }
  .summary-row { padding: .6rem 0; }
}

/* Scope picker. Above the fold because it changes what the report means. */
/* margin, not padding: a <legend> renders on the fieldset's top border, so
   padding-top pushes the fields down and leaves the legend flush against the
   control above it. Measured at 0px of separation before this. */
.scope-picker { border: 0; padding: 0; margin: 1.5rem 0 0; display: grid; gap: .5rem; }
.scope-picker legend { padding: 0; font-weight: 600; font-size: .95rem; }
/* Same trap as .scope-folder: a class selector outranks the user-agent
   [hidden] rule, so display:flex keeps a hidden element on screen. Any new
   display: rule in this file needs its own [hidden] guard. */
.scope-opt[hidden] { display: none !important; }
.scope-opt { display: flex; align-items: flex-start; gap: .55rem; cursor: pointer; line-height: 1.4; }
.scope-opt > span { display: grid; }
.scope-opt strong { font-weight: 600; font-size: .95rem; }
.scope-opt .muted { font-size: .84rem; }
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
/* A scoped audit's skipped checks read as a limit, not an alarm. */
.incomplete--scoped { border-left-color: var(--muted); }

/* A class selector beats the user-agent [hidden] rule, so display:flex here
   made the section picker visible even when it was marked hidden. */
/* A class selector beats the user-agent [hidden] rule, so display:flex here
   made the section picker visible even when it was marked hidden. */
.scope-folder[hidden] { display: none !important; }
.scope-folder { display: flex; gap: .7rem; align-items: baseline; flex-wrap: wrap; margin: .5rem 0 0; font-size: .9rem; }
.section-list { display: grid; gap: .25rem; }
.section-opt { display: flex; align-items: center; gap: .5rem; cursor: pointer; padding: .18rem .4rem; border-radius: 4px; }
.section-opt:hover { background: color-mix(in srgb, var(--accent, #b45309) 9%, transparent); }
.section-url { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .85rem; }
/* The domain is context; the folders are the decision being made. */
.seg-host { color: var(--muted); }
.seg-sep { color: var(--muted); opacity: .6; }
.seg-folder { color: var(--accent, #b45309); font-weight: 600; }
.section-opt input:checked ~ .section-url .seg-folder { text-decoration: underline; text-underline-offset: 3px; }

.queue-note { margin: .5rem 0 0; font-size: .86rem; max-width: 46em; }

/* ---- Pricing ---------------------------------------------------------- */
.pricing { max-width: 1100px; margin: 0 auto; padding: 1rem 0 3rem; }
.pricing-head { text-align: center; margin-bottom: 2.2rem; }
.pricing-head .lede { max-width: 40em; margin: .6rem auto 1.2rem; color: var(--muted); }

.currency-toggle { display: inline-flex; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; }
.cur { padding: .3rem 1rem; font-size: .85rem; text-decoration: none; color: var(--muted); }
.cur--on { background: var(--brand); color: #fff; }

/* --- Plan comparison -------------------------------------------------------
   One table: the offer down the left, a column per plan. Replaced four plan
   cards on 13 October 2026 - each card listed its own features in its own
   words, so comparing two plans meant reading two lists and finding the
   difference yourself. A table puts the difference on one line. */
/* position: relative is load-bearing. The waitlist forms carry a visually
   hidden <label>, which is absolutely positioned; without a positioned
   ancestor inside the scroll box it is laid out against the PAGE, escapes the
   box's clipping, and made a phone scroll sideways by two hundred pixels to
   reach a label nobody can see. */
.plans-wrap {
  position: relative;
  border: 1px solid var(--border); border-top-color: var(--border-brand); border-radius: var(--radius);
  background: var(--surface); overflow-x: auto; -webkit-overflow-scrolling: touch;
}
.plans { width: 100%; min-width: 41rem; border-collapse: collapse; font-size: .95rem; table-layout: fixed; }
.plans th, .plans td { padding: .95rem 1.15rem; text-align: left; vertical-align: middle; }
.plans tbody th, .plans tbody td, .plans tfoot td { border-top: 1px solid var(--border); }

.plans thead th { background: var(--bg-deep); vertical-align: top; padding-top: 2.1rem; padding-bottom: 1.1rem; position: relative; }
.plans__corner { width: 31%; font-size: .72rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.plans__name { display: block; font-size: .72rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
/* The euro list carries half-units, so "EUR 133.50/month" is five characters
   longer than "$149/month". The two parts are written with no whitespace
   between them, which made the whole thing one unbreakable word - it pushed
   the table 5px past its wrapper on 15 October 2026 and clipped the last
   column. The <wbr> in the markup gives it somewhere to break; this keeps
   each half unbreakable so it breaks between them and nowhere else.

   A container query was tried first and did nothing: a table cell does not
   reliably establish one, and the font stayed at its maximum. */
.plans__price { display: block; margin-top: .3rem; font-size: 1.55rem; font-weight: 700; line-height: 1.2; color: var(--ink); font-variant-numeric: tabular-nums; }
.plans__price > .plans__amount { white-space: nowrap; }
/* Set when any price in the chosen currency is long enough to overflow the
   column - today that is every euro price, because of the half-units. The
   column width is untouched: every currency lays the table out identically,
   which is the point. */
/* Measured, not guessed. At 1.25rem "EUR 133.50" is 84px and "/month" 42px
   in a 125px box - 3px too many, so Agency alone dropped "/month" to a second
   line and sat out of step with the other three. Both parts give a little:
   80.6 + 37.7 + 1.6 = 120, with 5px to spare. */
.plans--compact-price .plans__price { font-size: 1.2rem; }
.plans--compact-price .plans__per { font-size: .7rem; margin-left: .1rem; }
.plans__per { font-size: .78rem; font-weight: 500; color: var(--muted); margin-left: .15rem; white-space: nowrap; }
.plans__signup { display: inline-block; margin-top: .4rem; font-size: .8rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--brand-text); }
.plans__soon { display: block; margin-top: .35rem; font-size: .72rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.plans__flag {
  position: absolute; top: .65rem; left: 1.15rem; padding: .08rem .5rem; border-radius: 999px;
  background: var(--brand); color: #fff; font-size: .64rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}

.plans tbody th { font-weight: 550; color: var(--ink); }

/* ---- /guides: questions that open -------------------------------------
   Closed, the page is a list of eleven questions and fits on one screen.
   That was the whole point of the rewrite on 16 October 2026: a reader who
   already knows how to run an audit should never scroll past the answer.

   The marker is drawn rather than left to the browser, because the default
   triangle sits on the first line of a wrapping question instead of beside
   the middle of it, and because it has to turn.
   ---------------------------------------------------------------------- */
.guides__controls { display: flex; gap: .5rem; margin: 0 0 1.6rem; }

.qa { border-bottom: 1px solid var(--border); }
.qa:first-of-type { border-top: 1px solid var(--border); }

.qa__q {
  display: flex; align-items: baseline; gap: .9rem;
  padding: 1rem .2rem; cursor: pointer; list-style: none;
  font-size: 1.04rem; font-weight: 600; color: var(--ink);
}
.qa__q::-webkit-details-marker { display: none; }
.qa__q:hover, .qa__q:focus-visible { color: var(--brand-text); }

/* The arrow, before the text, so every question starts on one vertical line
   whether it wraps or not. */
.qa__q::before {
  content: ""; flex: 0 0 auto; width: .5rem; height: .5rem;
  margin-top: .35rem; border-right: 2px solid var(--brand-text);
  border-bottom: 2px solid var(--brand-text); transform: rotate(-45deg);
  transition: transform .18s ease;
}
.qa[open] > .qa__q::before { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .qa__q::before { transition: none; } }

.qa__text { flex: 1 1 auto; min-width: 0; }
.qa__tags { flex: 0 0 auto; display: flex; align-items: center; gap: .5rem; }
.qa__min { color: var(--muted); font-size: .8rem; font-weight: 400; }
@media (max-width: 34rem) {
  /* The badges drop under the question rather than squeezing it to two words. */
  .qa__q { flex-wrap: wrap; }
  .qa__tags { width: 100%; padding-left: 1.4rem; }
}

.qa__a { padding: .2rem .2rem 1.8rem 1.4rem; }
.qa__a h3 { font-size: .76rem; letter-spacing: .09em; text-transform: uppercase;
            color: var(--brand-text); margin: 1.6rem 0 .5rem; }
/* The one-sentence answer. Set above the explanation and a little larger,
   because a reader who opened the question for one fact should find it
   without reading a paragraph first. */
.qa__answer, .guide__answer {
  font-size: 1.08rem; font-weight: 600; line-height: 1.55;
  color: var(--ink); margin: 0 0 1rem;
}
.guide__answer { margin-bottom: 1.6rem; }

.qa__lede { font-size: 1.02rem; color: var(--muted); margin: 0 0 1.1rem; }
.qa__shot { margin: 0 0 1.4rem; }
.qa__shot img { width: 100%; max-width: 100%; height: auto; display: block;
                border: 1px solid var(--frame); border-radius: 10px; background: var(--bg-deep);
                box-shadow: 0 6px 18px rgba(0, 0, 0, .35); }
.qa__shot figcaption { margin-top: .5rem; font-size: .84rem; line-height: 1.5; color: var(--muted); }
.qa__steps { padding-left: 1.2rem; line-height: 1.6; margin: 0; }
.qa__steps li { margin-bottom: .9rem; min-width: 0; }
.qa__steps strong { display: block; font-weight: 600; }
.qa__see { display: block; margin-top: .2rem; font-size: .9rem; color: var(--muted); }
.qa__money { border-left: 3px solid var(--brand); padding-left: 1rem; margin: 1.6rem 0 0; }
.qa__money h3 { margin-top: 0; color: var(--ok); }
.qa__caution { font-size: .94rem; color: var(--muted); margin-top: 1.4rem; }
.qa__more { margin: 1.6rem 0 0; font-size: .9rem; }

/* On paper every answer is open, for the same reason a filtered report prints
   in full: there is nothing to click. Printed dark, this page would also be
   twelve sheets of near-black, so the colours are handed back to the ink. */
@media print {
  .guides__controls, .qa__q::before, .qa__more, .qa__tags { display: none !important; }

  .qa, .qa__a, .qa__money, .qa__caution { background: #fff !important; color: #111 !important; }
  .qa { border-bottom: 1px solid #ccc !important; }
  .qa:first-of-type { border-top: 1px solid #ccc !important; }

  /* The browser hides a closed <details>' contents; this puts them back. */
  .qa:not([open]) > .qa__a { display: block !important; }

  .qa__q { color: #111 !important; font-size: 1.15rem; padding-left: 0; }
  /* A question alone at the foot of a sheet, with its answer overleaf, is the
     one break worth forbidding. */
  .qa__q { break-after: avoid; }
  .guides__stage h2, .grp { break-after: avoid; }

  .qa__a h3 { color: #333 !important; }
  .qa__money { border-left: 3px solid #999 !important; }
  .qa__money h3 { color: #111 !important; }
  .qa__see, .qa__caution, .qa__shot figcaption { color: #444 !important; }

  /* These two name their own colour, so the blanket rule on .qa__a does not
     reach them. On screen they are light text on a dark ground; left alone
     they print as pale grey on white and the opening paragraph of every
     answer is the hardest line on the page to read. */
  .qa__lede, .guides .lede, .qa__steps strong { color: #111 !important; }

  /* A screenshot that fills a sheet on its own breaks the reading. */
  .qa__shot img, .guide__shot img {
    max-height: 9cm; width: auto; max-width: 100%;
    border: 1px solid #ccc !important; background: #fff !important;
  }
  .qa__shot, .guide__shot { break-inside: avoid; }
}

/* ---- /guides ----------------------------------------------------------
   Reference pages, so they inherit .legal's measure and only add what the
   shape needs: a plan badge, a step list where the "you should see" half is
   visibly subordinate to the "do this" half, and one panel that is allowed
   to stand out because it answers the question the owner built the page for.
   ---------------------------------------------------------------------- */
.guides__kicker {
  margin: 0 0 .3rem; font-size: .7rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--brand-text);
}
.guides__journey h2 { margin: 0 0 .5rem; font-size: 1.3rem; }
.guides__stage { margin: 2.6rem 0 0; }
.guides__stage-blurb { margin: -.3rem 0 1rem; }
.guides__list { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.1rem; }
@media (min-width: 46rem) { .guides__list { grid-template-columns: 1fr 1fr; } }
.guides__item {
  padding: 1rem 1.1rem; border: 1px solid var(--border); border-radius: 10px;
}
.guides__item h3 { margin: 0 0 .35rem; font-size: 1rem; }
.guides__item p { margin: 0; font-size: .92rem; line-height: 1.55; }
.guides__meta { display: flex; align-items: center; gap: .55rem; margin: 0 0 .5rem !important; }
.guides__plan {
  font-size: .66rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  padding: .12rem .45rem; border-radius: 999px;
  border: 1px solid var(--border); color: var(--muted);
}
/* Free is the one worth catching an eye, because it is the one somebody can
   act on without a decision. */
.guides__plan--free { border-color: var(--brand); color: var(--brand-text); }

/* The screen each guide describes. On a card it is a thumbnail that crops to
   a consistent band, so eleven screenshots of different shapes still line up
   in a grid. On the guide itself it runs full measure and keeps its own
   proportions, because there it is evidence rather than decoration. */
.guides__shot {
  display: block; margin: -1rem -1.1rem .85rem; border-radius: 10px 10px 0 0;
  overflow: hidden; background: var(--bg-deep); line-height: 0;
}
.guides__shot img {
  width: 100%; height: 118px; object-fit: cover; object-position: top left;
  display: block; opacity: .92;
}
.guides__item:hover .guides__shot img, .guides__item:focus-within .guides__shot img { opacity: 1; }

.guide__shot { margin: 1.4rem 0 2rem; }
.guide__shot img {
  width: 100%; max-width: 100%; height: auto; display: block;
  border: 1px solid var(--frame); border-radius: 10px; background: var(--bg-deep);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .35);
}
.guide__shot figcaption {
  margin-top: .55rem; font-size: .85rem; line-height: 1.5; color: var(--muted);
}

.guide__back { margin: 0 0 1.2rem; font-size: .9rem; }
.guide__badges { display: flex; align-items: center; gap: .6rem; margin: 0 0 .4rem; }
.guide__who { margin: 0 0 1.6rem; }
.guide__problem { max-width: none; }
.guide__steps { padding-left: 1.3rem; line-height: 1.6; }
.guide__steps li { margin-bottom: 1rem; }
.guide__do { display: block; }
.guide__see { display: block; margin-top: .2rem; font-size: .9rem; }
.guide__money { border-left: 3px solid var(--brand); padding-left: 1.1rem; }
.guide__caution { margin: 1.6rem 0; }
.guide__next { margin-top: 3rem; }

/* ---- /plugins ---------------------------------------------------------
   The download row. The button and the facts about the file share a line
   and a baseline, because a version and a size read as a caption to the
   button rather than as a sentence of their own. It wraps on a phone,
   where there is no room for both. */
.plugins__get {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .55rem .9rem;
  margin: 1.4rem 0 2rem;
}
.plugins__meta { font-size: .85rem; line-height: 1.45; }

/* Beside the heading, not under it. The pricing page's own badge is
   display:block for its table and drops to its own line here. */
.plugins__item h2 { display: flex; flex-wrap: wrap; align-items: baseline; gap: .55rem; }
.plugins__soon {
  font-size: .68rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: .12rem .45rem; border-radius: 999px;
  border: 1px solid var(--border); color: var(--muted);
}

/* The section for the thing that does not exist is dimmed, so the eye goes
   to the one that does without having to read first. */
.plugins__item--later { opacity: .82; }

.plugins h3 { margin: 1.8rem 0 .6rem; font-size: .98rem; }
.plugins__steps { line-height: 1.7; padding-left: 1.3rem; }
.plugins__steps li { margin-bottom: .55rem; }

/* The named benefits. Each one opens with its point in bold and explains it
   underneath, so the list can be read either at a glance or in full. */
.plugins__why { line-height: 1.65; padding-left: 1.1rem; margin: 1.2rem 0 0; }
.plugins__why li { margin-bottom: .9rem; }
.plugins__why li span.muted { display: block; margin-top: .3rem; font-size: .9rem; }
.plugins__why code,
.plugins__steps code { font-size: .88em; padding: .05rem .3rem; border-radius: 3px; background: var(--border); }

/* The download caption sits under the button rather than beside it now that
   the button carries the version in its own label. */
.plugins__get { margin: 1.3rem 0 .5rem; }
.plugins__meta { margin: 0 0 .4rem; }
/* A heading across the table. It says which rows every plan shares and which
   only paying buys, so the reader does not have to work that out from ticks. */
.plans .plans__section th {
  padding-top: 1.35rem; padding-bottom: .55rem; background: var(--bg-deep);
  font-size: .74rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--brand-text);
}
.plans__section-note { margin-left: .7rem; font-size: .8rem; font-weight: 400; letter-spacing: 0; text-transform: none; color: var(--muted); }
.plans tbody td { color: var(--ink); font-variant-numeric: tabular-nums; }
.plans__note { display: block; margin-top: .15rem; font-size: .8rem; font-weight: 400; color: var(--muted); }

/* The recommended plan is a tinted column, top to bottom - it lifts rather
   than shouts, so the other three do not look like the wrong answer. */
.plans .plans__pick { background: color-mix(in srgb, var(--brand) 11%, transparent); }
.plans thead .plans__pick { background: color-mix(in srgb, var(--brand) 20%, var(--bg-deep)); }

/* A tick and a dash, and never colour alone: each carries its meaning in an
   aria-label, and the two are different shapes as well as different colours. */
.plans__yes { display: inline-flex; width: 1.3rem; height: 1.3rem; border-radius: 50%; background: color-mix(in srgb, var(--ok) 18%, transparent); align-items: center; justify-content: center; }
.plans__yes svg { width: .85rem; height: .85rem; fill: none; stroke: var(--ok); stroke-width: 2.6; stroke-linecap: round; stroke-linejoin: round; }
.plans__no { color: var(--muted); font-weight: 600; padding-left: .35rem; }

.plans tfoot td { padding-top: 1.1rem; padding-bottom: 1.2rem; vertical-align: top; }
.plans__cta { display: block; text-align: center; white-space: nowrap; padding: .5rem; font-size: .85rem; }
.plans__waitlist { display: flex; flex-direction: column; gap: .4rem; }
.plans__waitlist-input {
  box-sizing: border-box; width: 100%; padding: .5rem .65rem; font-size: .85rem; font-family: inherit;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: rgba(0, 0, 0, .3); color: var(--ink);
}
.plans__waitlist-input:focus { outline: 2px solid var(--brand-text); outline-offset: 1px; border-color: var(--brand-text); }
.plans__waitlist-btn { width: 100%; padding: .5rem; font-size: .85rem; }
.plans-foot { margin: .9rem 0 0; font-size: .85rem; color: var(--muted); text-align: center; }

/* On a phone the table scrolls sideways and the left column stays put, so a
   figure is never separated from the thing it is a figure for. */
@media (max-width: 760px) {
  .plans th:first-child, .plans td:first-child { position: sticky; left: 0; z-index: 1; background: var(--surface); box-shadow: 1px 0 0 var(--border); }
  .plans thead th:first-child { background: var(--bg-deep); }
  .plans th, .plans td { padding: .8rem .8rem; }
  .plans__corner { width: 11.5rem; }
  .plans { min-width: 40rem; }
}

.pricing-notes { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border); }
.pricing-notes ul { margin: .8rem 0 0; padding-left: 1.1rem; display: grid; gap: .6rem; max-width: 46em; }
.pricing-notes li { font-size: .92rem; line-height: 1.55; color: var(--muted); }
.pricing-notes strong { color: var(--ink); }

/* ---- Footer ----------------------------------------------------------- */
.footer-nav { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1.4rem; margin-bottom: 1.4rem; }
.footer-nav h3 { font-size: .78rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin: 0 0 .5rem; font-family: var(--font); }
.footer-nav ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .3rem; }
.footer-nav a { font-size: .88rem; }
.footer-soon { color: var(--muted); font-size: .88rem; }

/* ---- Score ring -------------------------------------------------------- */
.score { display: flex; flex-direction: column; align-items: center; gap: .3rem; }
.score__ring { width: 112px; height: 112px; transform: rotate(-90deg); }
.score__track { fill: none; stroke: var(--border); stroke-width: 9; }
.score__arc {
  fill: none; stroke-width: 9; stroke-linecap: round;
  transition: stroke-dashoffset .6s ease;
}
/* Bands, not a gradient - see Audit#score_band. */
.score__arc--good { stroke: var(--ok); }
.score__arc--fair { stroke: var(--warning); }
.score__arc--poor { stroke: var(--critical); }
/* The SVG is rotated so the arc starts at twelve o'clock; the text has to be
   rotated back or it renders on its side. */
.score__svgnum, .score__svgden {
  transform: rotate(90deg); transform-origin: 60px 60px;
  text-anchor: middle; fill: var(--ink); font-family: var(--display); font-style: oblique;
}
.score__svgnum { font-size: 34px; }
.score__svgden { font-size: 13px; fill: var(--muted); font-family: var(--font); font-style: normal; }
.score__label { font-size: .7rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }

/* ---- Severity at a glance ---------------------------------------------- */
/* A coloured rail down the left edge, so critical, warning and info are
   separable without reading a single word. */
.finding { border-left: 3px solid var(--border); }
.finding--critical { border-left-color: var(--critical); }
.finding--warning  { border-left-color: var(--warning); }
.finding--info     { border-left-color: var(--info); }

.finding--critical .badge { background: color-mix(in srgb, var(--critical) 18%, transparent); color: var(--critical); }
.finding--warning  .badge { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }
.finding--info     .badge { background: color-mix(in srgb, var(--info) 18%, transparent); color: var(--info); }
.badge { padding: .1rem .5rem; border-radius: 999px; font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }

.stat--critical { color: var(--critical); }
.stat--warning  { color: var(--warning); }
.stat--info     { color: var(--info); }

/* ---- Narrow screens ---------------------------------------------------- */
/* Verified at 375px on the deployed site: the report page does not overflow and
   the wide pages table scrolls inside its own container. The form was the one
   real break - the input wrapped and left the button floating at its natural
   width on the line below. */
@media (max-width: 40rem) {
  .field-row { flex-direction: column; }
  /* flex: none as well as width. The input carries flex: 1 for the desktop row,
     and in a column that makes it grow to fill the height instead of the width
     - it rendered as a 700px-tall box. */
  .field-row .url-input, .field-row .btn { width: 100%; flex: none; box-sizing: border-box; }

  /* The header is a row of three links plus a lockup; let it wrap rather than
     squeeze the wordmark. */
  .site-header { flex-wrap: wrap; gap: .4rem .8rem; }
  .brand-tag { display: none; }

  /* Tap targets: 44px is the accessibility floor and these were ~28px. */
  .scope-opt, .section-opt { min-height: 44px; align-items: center; }

  .scorecard { gap: 1rem; }
}

/* AI Overviews readiness. Visually distinct from findings on purpose: it is a
   checklist of Google's stated requirements, not a set of new problems. */
.ai-ready { margin: 2rem 0; padding: 1.3rem 1.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.ai-ready h2 { margin: 0 0 .5rem; }
.ai-ready__lede { color: var(--muted); margin: 0 0 1rem; line-height: 1.6; }
.ai-ready__lede em { color: var(--text); font-style: normal; }
.ai-ready__verdict { margin: 0 0 1.2rem; padding: .6rem .9rem; border-radius: var(--radius); font-weight: 600; }
.ai-ready__verdict--ok { background: rgba(34,197,94,.12); }
.ai-ready__verdict--blocked { background: rgba(234,179,8,.12); }
.ai-req { list-style: none; margin: 0; padding: 0; }
.ai-req__item { padding: .85rem 0; border-top: 1px solid var(--border); }
.ai-req__item:first-child { border-top: none; }
.ai-req__head { display: flex; gap: .55rem; align-items: baseline; }
.ai-req__mark { font-weight: 700; }
.ai-req__item--met .ai-req__mark { color: #22c55e; }
.ai-req__item--blocked .ai-req__mark { color: #eab308; }
.ai-req__google { margin: .3rem 0 .2rem 1.4rem; color: var(--muted); font-size: .93rem; }
.ai-req__plain { margin: 0 0 .3rem 1.4rem; font-size: .93rem; }
.ai-req__blockers { margin: .4rem 0 0 1.4rem; font-size: .88rem; }
.ai-ready__source { margin: 1.1rem 0 0; padding-top: .9rem; border-top: 1px solid var(--border); color: var(--muted); font-size: .87rem; }

/* Coverage sits above the score deliberately: the denominator has to be read
   before any finding count can be interpreted. */
.coverage { margin: 1.2rem 0 0; padding: .85rem 1rem; background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--accent, #2b6cb0); border-radius: var(--radius); line-height: 1.5; font-size: .95rem; }
.coverage strong { display: block; margin-bottom: .15rem; }
.coverage .muted { display: block; margin-top: .3rem; font-size: .87rem; }

.scorecard { display: flex; gap: 2rem; flex-wrap: wrap; align-items: flex-end; margin: 1.4rem 0 .6rem; padding: 1.2rem 1.4rem; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.score__num { font-size: 2.6rem; font-weight: 600; letter-spacing: -.03em; }
.score__den { color: var(--muted); }
.score__label, .stat span:last-child { display: block; font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.stat__num { font-size: 1.5rem; font-weight: 600; }
.stat--critical { color: var(--critical); }
.stat--warning { color: var(--warning); }
.stat--info { color: var(--info); }
.disclaimer { font-size: .82rem; color: var(--muted); max-width: 44rem; margin: 0 0 1.5rem; }

.panel { margin: 2rem 0; }
.panel h2 { font-size: 1.1rem; margin: 0 0 .9rem; }

.summary { display: grid; gap: .5rem 1.5rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.2rem; margin: 0; }
.summary div { display: flex; justify-content: space-between; gap: 1rem; font-size: .88rem; border-bottom: 1px dotted var(--border); padding-bottom: .35rem; }
.summary dt { color: var(--muted); }
.summary dd { margin: 0; font-variant-numeric: tabular-nums; text-align: right; }

.finding { background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--border); border-radius: var(--radius); padding: 1rem 1.2rem; margin-bottom: .9rem; }
.finding--critical { border-left-color: var(--critical); }
.finding--warning { border-left-color: var(--warning); }
.finding--info { border-left-color: var(--info); }
.finding header { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-bottom: .4rem; }
.finding h3 { font-size: 1rem; margin: 0; font-weight: 600; }
.finding p { margin: 0; color: var(--muted); font-size: .92rem; }
/* Dark ink, not white. These three backgrounds are light pastels chosen to
   read against a dark page, so white on them measured 1.44:1 (warning),
   2.10:1 (info) and 2.95:1 (critical) - all failing AA for text this small.
   The page ground passes on all three, 6.3:1 to 13:1. */
.badge { font-size: .68rem; font-weight: 650; text-transform: uppercase; letter-spacing: .06em; padding: .15rem .45rem; border-radius: 4px; color: var(--bg); }
.badge--critical { background: var(--critical); }
.badge--warning { background: var(--warning); }
.badge--info { background: var(--info); }
.count { font-size: .78rem; color: var(--muted); }
.code { margin-left: auto; font-size: .72rem; color: var(--muted); }
.finding details { margin-top: .7rem; font-size: .86rem; }
.finding summary { cursor: pointer; color: var(--accent); }
.urls { list-style: none; padding: .5rem 0 0; margin: 0; display: grid; gap: .25rem; }
.urls code { word-break: break-all; }

.pill { display: inline-block; min-width: 1.5rem; text-align: center; font-size: .75rem; padding: .1rem .35rem; border-radius: 4px; background: var(--border); }
.pill--critical { background: color-mix(in srgb, var(--critical) 20%, transparent); color: var(--critical); }
.pill--warning { background: color-mix(in srgb, var(--warning) 20%, transparent); color: var(--warning); }
.pill--info { background: color-mix(in srgb, var(--info) 20%, transparent); color: var(--info); }

.state { font-size: .78rem; padding: .1rem .45rem; border-radius: 4px; background: var(--border); }
.state--completed { background: color-mix(in srgb, var(--ok) 20%, transparent); color: var(--ok); }
.state--running, .state--pending { background: color-mix(in srgb, var(--info) 20%, transparent); color: var(--info); }
.state--failed { background: color-mix(in srgb, var(--critical) 20%, transparent); color: var(--critical); }

.status { font-variant-numeric: tabular-nums; }
.status--2 { color: var(--ok); }
.status--3 { color: var(--warning); }
.status--4, .status--5, .status--0 { color: var(--critical); }

.psi { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.psi__card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.2rem; }
.psi__card h3 { margin: 0 0 .4rem; font-size: .95rem; }
.psi__score { font-size: 2rem; font-weight: 600; margin: 0; }
.psi__score span { font-size: .8rem; font-weight: 400; color: var(--muted); }
.psi__metrics { list-style: none; padding: 0; margin: .6rem 0 0; font-size: .85rem; }
.psi__metrics li { display: flex; justify-content: space-between; border-bottom: 1px dotted var(--border); padding: .25rem 0; }
.psi__field { font-size: .84rem; margin: .7rem 0 0; }

.link-btn { background: none; border: 0; color: var(--muted); cursor: pointer; font-size: .82rem; padding: 0; text-decoration: underline; }
.api-hint { font-size: .85rem; word-break: break-all; }

/* Lead capture */
.capture { background: var(--surface); border: 1px solid var(--accent); border-radius: var(--radius); padding: 1.4rem 1.6rem; margin: 1.5rem 0 2rem; }
.capture h2 { margin: 0 0 .4rem; font-size: 1.05rem; }
.capture p { margin: 0 0 .9rem; color: var(--muted); font-size: .92rem; max-width: 46rem; }
.capture__form { display: flex; gap: .6rem; flex-wrap: wrap; }
.capture__form .url-input { flex: 1 1 18rem; font-family: var(--font); }
.capture__note { font-size: .8rem; margin: .7rem 0 0; }

/* Footer disclaimer. Google's own third-party-tools guidance asks that tools
   not imply endorsement or claim access to internal ranking data. */
.site-footer { border-top: 1px solid var(--border); margin-top: 3rem; padding: 1.2rem 1.5rem 2.5rem; }
.site-footer p { max-width: 62rem; margin: 0 auto; color: var(--muted); font-size: .8rem; line-height: 1.6; }

/* --- accounts, history and change timeline --- */
.site-nav { margin-left: auto; display: flex; gap: 1rem; align-items: baseline; font-size: .85rem; }
.site-nav a { color: var(--muted); text-decoration: none; }
.site-nav a:hover { color: var(--accent); text-decoration: underline; }
.site-nav form { display: inline; }

.auth { max-width: 30rem; }
.auth h1 { font-size: 1.6rem; letter-spacing: -.02em; margin: 0 0 .4rem; }
.auth-form { display: flex; flex-direction: column; gap: 1rem; margin: 1.5rem 0; }
.auth-form label { display: flex; flex-direction: column; gap: .35rem; font-size: .88rem; color: var(--muted); }
/* .url-input carries flex: 1 1 22rem for the horizontal search row. Inside a
   column-direction label that basis becomes the HEIGHT, which rendered the
   fields several hundred pixels tall. Reset it here. */
.auth-form .url-input { font-family: var(--font); flex: 0 0 auto; width: 100%; }
.auth-form input[type=submit] { align-self: flex-start; }
.auth-alt { font-size: .85rem; color: var(--muted); }

/* --- Choosing a plan while signing up ---------------------------------------
   Three buttons and nothing else. A real radio group underneath, so it works
   with scripting off and keeps what has been typed; the radio itself is
   hidden and its card is the thing you press. */
.auth--plan { max-width: 32rem; }
.plan-pick { border: 0; margin: 0; padding: 0; min-width: 0; }
.plan-pick__legend { padding: 0; margin-bottom: .45rem; font-size: .88rem; color: var(--muted); }
.plan-pick__options { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .6rem; }
.auth-form .plan-pick__option { display: block; position: relative; cursor: pointer; gap: 0; }
.plan-pick__radio { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.plan-pick__card {
  display: flex; flex-direction: column; gap: .2rem; padding: .8rem .85rem;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  transition: border-color .12s ease, background-color .12s ease;
}
.plan-pick__name { font-size: .72rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.plan-pick__price { font-size: 1.35rem; font-weight: 700; line-height: 1.1; color: var(--ink); font-variant-numeric: tabular-nums; }
.plan-pick__per { margin-left: .12rem; font-size: .75rem; font-weight: 500; color: var(--muted); }
.plan-pick__option:hover .plan-pick__card { border-color: var(--border-brand); }
/* The chosen one is told apart by more than colour: a heavier border, a tinted
   ground and a tick. */
.plan-pick__radio:checked + .plan-pick__card {
  border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, var(--surface));
}
.plan-pick__radio:checked + .plan-pick__card .plan-pick__name { color: var(--brand-text); }
.plan-pick__radio:checked + .plan-pick__card .plan-pick__name::after { content: " \2713"; }
.plan-pick__radio:focus-visible + .plan-pick__card { outline: 2px solid var(--brand-text); outline-offset: 2px; }
@media (max-width: 420px) {
  .plan-pick__options { grid-template-columns: 1fr; }
  .plan-pick__card { flex-direction: row; align-items: baseline; justify-content: space-between; }
}

.changes { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: .4rem; }
.change {
  display: grid; grid-template-columns: 7.5rem 1fr auto; gap: .8rem; align-items: baseline;
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--border); border-radius: var(--radius); padding: .65rem .9rem;
}
.change--fixed { border-left-color: var(--ok); }
.change--new { border-left-color: var(--critical); }
.change--advisory { border-left-color: var(--info); }
.change__tag { font-size: .7rem; font-weight: 650; text-transform: uppercase; letter-spacing: .05em; }
.change--fixed .change__tag { color: var(--ok); }
.change--new .change__tag { color: var(--critical); }
.change--advisory .change__tag { color: var(--info); }
/* A tone we never actually scored, rather than a verdict of neutral. */
.change__tag--quiet { color: var(--muted); font-weight: 500; text-transform: none; letter-spacing: 0; }

/* A year of search demand in one cell. The sparkline takes its colour from
   the trend, so the direction is readable before the number is. Colour is
   never the only signal - the signed percentage sits beside it. */
.demand { display: inline-flex; align-items: center; gap: .4rem; }
.demand__spark { display: block; overflow: visible; }
.demand__spark--rising { color: var(--ok); }
.demand__spark--falling { color: var(--critical); }
.demand__spark--flat, .demand__spark--unknown { color: var(--muted); }
.demand__pct { font-size: .72rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.demand__pct--rising { color: var(--ok); }
.demand__pct--falling { color: var(--critical); }
.demand__pct--flat { color: var(--muted); }
.demand__peak { display: block; font-size: .68rem; }

/* Button and its price on one line. A div rather than a p, because button_to
   renders a form and a form cannot live inside a paragraph. */
.ai-volume-cta { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }

/* Findings listed inside a table cell, beside what the page earns. */
.page-findings { margin: 0; padding: 0; list-style: none; display: grid; gap: .25rem; }
.page-findings li { display: flex; align-items: baseline; gap: .35rem; font-size: .78rem; }

/* The brief: three multiple-choice questions, asked once. */
.brief-form { display: grid; gap: 1.6rem; max-width: 34rem; }
.brief-q { border: 0; padding: 0; margin: 0; display: grid; gap: .45rem; }
.brief-q legend { font-weight: 650; margin-bottom: .5rem; padding: 0; }
.brief-opt { display: flex; align-items: center; gap: .55rem; cursor: pointer; }
.brief-opt input { margin: 0; }

/* One rung of the ladder. */
.rung { margin-top: 2rem; }
.rung:first-of-type { margin-top: 1rem; }
.change__delta { font-family: var(--mono); font-size: .8rem; color: var(--muted); font-variant-numeric: tabular-nums; }

.chart { width: 100%; max-width: 45rem; height: auto; }
.chart__grid { stroke: var(--border); stroke-width: 1; }
.chart__line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.chart__dot { fill: var(--surface); stroke: var(--accent); stroke-width: 2; }
.chart__label { fill: var(--muted); font-size: 11px; font-family: var(--font); }
.score-note { font-size: .84rem; max-width: 48rem; margin-bottom: 1rem; }

@media (max-width: 34rem) { .change { grid-template-columns: 1fr; gap: .25rem; } }

/* --- progress view --- */
.progress-head { margin-bottom: 1rem; }
.progress-headline { font-size: 1.25rem; font-weight: 600; letter-spacing: -.01em; margin: 0 0 .2rem; }
.sub { font-size: .95rem; font-weight: 600; margin: 1.6rem 0 .6rem; }
.sub-note { font-weight: 400; font-size: .8rem; }
.change--open { border-left-color: var(--warning); }
.change--open .change__tag { color: var(--warning); }
.progress-down { color: var(--ok); font-weight: 600; }
.bar--critical { fill: var(--critical); }
.bar--warning { fill: var(--warning); }
.bar--info { fill: var(--info); }
.legend { display: flex; gap: .5rem; align-items: center; font-size: .78rem; color: var(--muted); margin: .4rem 0 0; }
.legend__key { display: inline-block; width: .7rem; height: .7rem; border-radius: 2px; margin-left: .6rem; }
.legend__key--critical { background: var(--critical); margin-left: 0; }
.legend__key--warning { background: var(--warning); }
.legend__key--info { background: var(--info); }

/* Verification status shown on every finding */
.verify { font-size: .78rem; margin: .55rem 0 0; font-family: var(--mono); }
.verify--ok { color: var(--ok); }
.verify--none { color: var(--muted); }

/* ---- Brand treatment ------------------------------------------------ */

body {
  background-color: var(--bg);
  background-image:
    radial-gradient(900px 520px at 10% -12%, rgba(128, 98, 214, .18), transparent 62%),
    radial-gradient(760px 480px at 90% 2%, rgba(100, 78, 168, .14), transparent 60%);
  background-attachment: fixed;
  color: var(--ink);
}

/* Headings are set in the body face, not the display one.
   
   Airwaves is oblique display CAPS. It suits a six-character tagline in a
   logo lockup and fails at everything longer: "FINDINGS YOU CAN TRUST" in an
   oblique caps face has no ascenders or descenders left to give a word its
   shape, so the reader spells it out instead of recognising it. Reported as
   having to "put pressure to see the words", which is exactly what losing
   word-shape feels like.
   
   All-caps also throws away the first thing a reader uses to find a heading -
   a capital letter where the sentence starts. The markup is already written
   in sentence case, so removing text-transform is enough; nothing has to be
   rewritten.
   
   The hero headline reached this conclusion first and overrode the rule
   locally (.stage__title: body font, normal, no transform, weight 800). This
   is that treatment applied where it always belonged. Presence now comes from
   weight, size and the brand colour, which survive being read. */
h1, h2 {
  font-family: var(--font);
  font-style: normal;
  text-transform: none;
  font-weight: 650;
  /* Display text wants tighter tracking than body text, the opposite of what
     caps wanted. */
  letter-spacing: -.01em;
  text-wrap: balance;
  color: var(--brand-text);
}
h1 { font-size: 1.9rem; font-weight: 700; letter-spacing: -.02em; }
h3 { color: var(--ink); }

/* The wordmark. Monoton appears here and nowhere else. */
.site-header .brand,
.brand-word {
  font-family: var(--wordmark);
  font-size: 1.25rem;
  letter-spacing: .04em;
  color: var(--brand-text);
  /* none, not uppercase: the brand is "lala SEO", and the markup carries the
     case. text-transform here would silently overrule it. */
  text-transform: none;
}
.brand-word__seo { text-transform: uppercase; }

/* Deleted by accident on 25 Sep: a regex tidying up the typing animation used
   .*? across a blank line and took three unrelated rules with it. The header
   logo then rendered at its natural 1000x1000. */
.brand-lockup { display: flex; align-items: center; gap: .55rem; text-decoration: none; }
.brand-lockup:hover { text-decoration: none; opacity: .85; }
.brand-mark { width: 34px; height: 34px; }
.brand-tag {
  font-family: var(--display);
  font-style: oblique;
  text-transform: uppercase;
  font-size: .68rem;
  letter-spacing: .06em;
  color: var(--muted);
}

/* Brand purple at full strength is safe here: large, bold, on a light fill. */
.btn {
  background: linear-gradient(180deg, var(--brand), var(--brand-deep));
  color: #FFFFFF;
  border: 1px solid var(--brand-deep);
  font-weight: 650;
  box-shadow: 0 1px 0 rgba(255, 255, 255, .18) inset, 0 6px 18px rgba(128, 98, 214, .25);
}
.btn:hover { filter: brightness(1.08); }

/* Must follow .btn above: same specificity, so source order decides. A ghost
   button that silently renders as a solid one is how the waitlist CTA ended up
   competing with "Run an audit" for attention. */
.btn--ghost {
  background: none;
  color: var(--brand-text);
  border: 1px solid var(--brand-text);
  box-shadow: none;
}
.btn--ghost:hover { background: color-mix(in srgb, var(--brand-text) 14%, transparent); filter: none; }

.panel, .card, .scorecard {
  background: linear-gradient(180deg, var(--surface), var(--bg));
  border: 1px solid var(--border);
  border-top-color: var(--border-brand);
  border-radius: var(--radius);
}

.masthead, header.site, .site-header { background: var(--bg-deep); border-bottom: 1px solid var(--border); }

a { color: var(--brand-text); text-decoration-color: rgba(180, 158, 238, .45); }
a:hover { color: #CDBFF6; }

.url-input { background: rgba(0, 0, 0, .3); border: 1px solid var(--border); color: var(--ink); }
.url-input:focus { outline: 2px solid var(--brand-text); outline-offset: 1px; border-color: var(--brand-text); }

/* Large and bold, so full-strength brand purple clears its bar. */
.score__num { color: var(--brand); font-family: var(--display); font-style: oblique; }
.stat__num { font-family: var(--display); font-style: oblique; }

.summary-table th a { color: var(--ink); }
.seg-folder { color: var(--brand-text); }
.explain__label { color: var(--brand-text); }
.explain__p--fix .explain__label { color: var(--brand-text); }

::selection { background: rgba(128, 98, 214, .4); color: #fff; }

/* Legal pages. Long prose, so a narrower measure than the report: past about
   72 characters the eye loses the start of the next line. */
.legal { max-width: 46em; }
.legal h2 { margin: 2rem 0 .6rem; font-size: 1.05rem; }
.legal p, .legal li { line-height: 1.65; }
.legal ul { display: grid; gap: .5rem; padding-left: 1.1rem; margin: .8rem 0; }
.legal__meta { margin: .2rem 0 1.4rem; font-size: .82rem; color: var(--muted); }
.legal .table { margin: 1rem 0; }
.legal .table td, .legal .table th { vertical-align: top; }


/* ---- Hero form, revised 24 Sep ---------------------------------------- */

/* Scope reads across the page rather than down it: three short choices in a
   column made the form look longer than the work it asks for. */
.scope-picker--row { display: flex; flex-wrap: wrap; gap: .9rem; align-items: stretch; }
.scope-picker--row legend { width: 100%; margin-bottom: .2rem; }
.scope-picker--row .scope-opt {
  flex: 1 1 13rem; align-items: flex-start; padding: .6rem .7rem;
  border: 1px solid var(--border); border-radius: var(--radius);
  background: rgba(0, 0, 0, .18);
}
.scope-picker--row .scope-opt:hover { border-color: var(--brand-text); }
.scope-picker--row .scope-opt:has(input:checked) {
  border-color: var(--brand-text);
  background: color-mix(in srgb, var(--brand-text) 10%, transparent);
}

/* Options are open, so they need the shape of a section rather than the
   indent of a disclosure. */
.options--open { border: 0; padding: 0; margin: 1.8rem 0 0; }
.options--open legend { padding: 0; font-weight: 600; font-size: .95rem; }

/* A nav item that is the primary action should not look like the others. */
.nav-cta {
  padding: .35rem .75rem; border-radius: var(--radius);
  border: 1px solid var(--brand-text); color: var(--brand-text) !important;
}
.nav-cta:hover { background: color-mix(in srgb, var(--brand-text) 16%, transparent); text-decoration: none !important; }

@media (max-width: 40rem) {
  .scope-picker--row { flex-direction: column; }
  .scope-picker--row .scope-opt { flex: none; }
}

/* ---- Features page ---------------------------------------------------- */
.features-head { max-width: 46em; }
.features-head .lede { margin-top: .6rem; }
.feature-grid {
  margin-top: 2rem; display: grid; gap: 1.1rem;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}
.feature {
  padding: 1.1rem 1.2rem; border: 1px solid var(--border);
  border-radius: var(--radius); background: rgba(0, 0, 0, .18);
}
.feature h2, .feature h3, .feature h4 { margin: 0 0 .7rem; font-size: 1rem; color: var(--brand-text); }
.feature ul { margin: 0; padding-left: 1.05rem; display: grid; gap: .42rem; font-size: .88rem; }
.feature li { line-height: 1.45; }
.feature li strong { color: var(--ink); }
.features-note {
  margin: 2rem 0 0; padding: .9rem 1.1rem; border-radius: var(--radius);
  border: 1px dashed var(--border); max-width: 52em;
  font-size: .88rem; line-height: 1.6; color: var(--muted);
}
.features-note strong { color: var(--ink); }
.features-cta { margin-top: 2rem; display: flex; gap: .7rem; flex-wrap: wrap; }
.features-cta .btn { text-decoration: none; }

/* ======================================================================
   Hero stage — rebuilt 24 Sep from a reference layout the owner chose.
   Oversized clipped headline, form left, logo mark in concentric rings.
   ====================================================================== */

/* Break out of .wrap's 62rem measure to run edge to edge. 100vw includes the
   scrollbar's width, so overflow-x is clipped here rather than letting the
   document scroll sideways. */
.stage {
  position: relative;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  width: 100vw;
  overflow: hidden;
  padding: 3.5rem 0 3rem;
  background:
    radial-gradient(120% 90% at 78% 40%, rgba(128, 98, 214, .16), transparent 62%),
    var(--bg);
}

/* The crop is the point. nowrap plus a vw-driven size keeps the cut in roughly
   the same place at every width, landing mid-"a" of Impact. */
.stage__title {
  /* Big, but a headline rather than a stunt: it wraps, it is never clipped,
     and at 34 characters it has to stay readable. The previous treatment was
     built for a two-word brand line cropped at the viewport edge - neither
     applies to a sentence that has to be read. */
  /* The containing block the caret is positioned against. */
  position: relative;
  margin: 0 0 .7rem;
  padding-left: var(--stage-inset);
  padding-right: 1.5rem;
  max-width: 22ch;
  font-family: var(--font);
  font-style: normal;
  text-transform: none;
  font-weight: 800;
  font-size: clamp(2.4rem, 5.4vw, 4.6rem);
  line-height: 1.02;
  letter-spacing: -.03em;
  /* Optical, not metric: the "R" carries about 0.055em of empty space on its
     left, so aligning the text box leaves the ink inset from the form below.
     Measured at 1440 - ink lands on 160.0 against the field's 160.0. */
  text-indent: -.055em;
  color: var(--brand-text);
}

.stage__grid {
  max-width: none;
  margin: 0;
  padding: 0 1.5rem 0 var(--stage-inset);
  display: grid;
  /* The art left the grid: inside a centred 62rem column it sat mid-page with
     320px of empty margin beyond it at 1680px, which read as stranded rather
     than placed. It is now anchored to the stage edge instead. */
  /* Grows into the space the smaller inset freed, but never past the zone the
     art occupies: 26rem is reserved on the right so the two can't meet.
     A fixed 54rem looked right at 1680 and overlapped the rings at 1280. */
  grid-template-columns: minmax(0, min(54rem, calc(100vw - var(--stage-inset) - 26rem)));
  align-items: center;
}

.stage__kicker {
  margin: 0 0 1.3rem; color: var(--muted); font-size: 1rem; line-height: 1.55; max-width: 40rem;
}
.stage__kicker strong { color: var(--ink); }
.stage__form .audit-form { max-width: none; margin: 0; text-align: left; }
.stage__form .url-input { padding: .8rem 1rem; font-size: 1rem; }
.stage__form .btn { padding: .8rem 1.7rem; font-size: 1rem; }

/* Rings: a crawl spreading out from one page. Sized off the container so they
   scale with the column rather than fighting it. */
/* Anchored to the stage, not the grid, and allowed to run off the right edge -
   the stage clips it, so the bleed costs nothing. */
.stage__art {
  position: absolute;
  /* Pinned to the same point the contours converge on - 77.5% across, half
     way down - so the two cannot drift apart as the window resizes. Anchoring
     the mark to the right edge while the focus was a percentage put them
     190px apart at 1440. */
  top: 50%;
  left: 77.5%;
  transform: translate(-50%, -50%);
  width: clamp(14rem, 22vw, 24rem);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  pointer-events: none;
}

.stage__mark {
  position: relative; width: 26%; height: auto;
  filter: drop-shadow(0 0 26px rgba(128, 98, 214, .45));
}

@media (max-width: 56rem) {
  .stage__grid { grid-template-columns: 1fr; gap: 2rem; }
  /* The art is decoration; on a narrow screen the form is the whole point. */
  .stage__art { display: none; }
  /* No crop on a phone. At this width the cut left only "Disciplined" on
     screen, which reads as a different word - and the reference wraps to two
     lines here for the same reason. */
  .stage__title {
    font-size: 15.5vw;
    white-space: normal;
    padding-right: 1.5rem;
    margin-bottom: 1.6rem;
  }
}

/* ---- Header on small screens ------------------------------------------
   Signed in, the bar held five items including an email address, and the
   longest single unbreakable string on the site. "Your sites" and "Sign out"
   each wrapped mid-phrase, which is what made the page look thrown together.
   ---------------------------------------------------------------------- */

/* A navigation label that breaks across two lines reads as two labels. */
.site-nav a,
.site-nav .link-btn { white-space: nowrap; }
/* A long display name must not push the controls off the row. */
/* It is a link now, so it has to look like the others beside it rather than
   like muted text that happens to be clickable. */
.nav-user {
  max-width: 14ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--muted); text-decoration: none;
}
.nav-user:hover, .nav-user:focus-visible { color: var(--ink); text-decoration: underline; }

@media (max-width: 48rem) {
  .site-header {
    /* The lockup owns the first row; the nav gets its own beneath it, so the
       two never compete for the same line. */
    flex-direction: column;
    align-items: flex-start;
    gap: .75rem;
    padding: .9rem 1.1rem;
  }

  .site-nav {
    margin-left: 0;
    width: 100%;
    flex-wrap: wrap;
    align-items: center;
    gap: .55rem 1rem;
    font-size: .9rem;
  }

  /* The address is not an action and not information the reader needs on a
     phone - it is the widest item in the row and it was squeezing every
     control that is actually useful. The account page still shows it. */
  .nav-user { display: none; }

  .nav-cta { padding: .4rem .8rem; }

  /* 44px is the accessibility floor for a tap target; these were ~20px. */
  .site-nav a,
  .site-nav .link-btn { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ---- Hero rhythm on small screens -------------------------------------
   Every group sat flush against the control above it, so "What should we
   check?" read as a caption for the Run audit button rather than a heading
   for what follows. Space is what separates one question from the next.
   ---------------------------------------------------------------------- */
@media (max-width: 56rem) {
  .stage { padding: 2rem 0 2.5rem; }
  .stage__grid { padding: 0 1.4rem; }
  .stage__kicker { margin-bottom: 1.4rem; font-size: .96rem; }

  .scope-picker--row { margin-top: 1.7rem; gap: .7rem; }
  .scope-picker--row legend { margin-bottom: .5rem; }
  .options--open { margin-top: 2rem; }
  .options--open legend { margin-bottom: .3rem; }

  /* The submit sits directly under the field in a column, so it needs its own
     breathing room rather than inheriting the row gap. */
  .stage__form .field-row { gap: .7rem; }
}

/* ---- Options as a panel ------------------------------------------------
   The two number fields stopped 412px short of the cards above them at
   1680px, so the block's right edge was ragged and the group read as loose
   text rather than one control set. A panel gives it the same edges as
   everything else in the column.
   ------------------------------------------------------------------------ */
.options--open .options-grid {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: rgba(0, 0, 0, .18);
  margin-top: .6rem;
}

@media (max-width: 56rem) {
  .stage__grid { grid-template-columns: minmax(0, 1fr); }
  .options--open .options-grid { padding: .9rem 1rem; }
}

/* The art hid below 56rem but began colliding with the form well above that -
   measured overlapping it by 34px at 960px. Decoration must never sit on top
   of the control it decorates, so it leaves earlier. */
@media (max-width: 68rem) {
  .stage__art { display: none; }
}

/* A code is read back character by character, so it is set in the mono face
   and tracked out - the same reason the audit URL field is mono. */
.coupon-input {
  font-family: var(--mono); text-transform: uppercase; letter-spacing: .12em;
  width: 100%; box-sizing: border-box;
}
.small { font-size: .85rem; }



/* ---- Options panel, rebuilt 24 Sep ------------------------------------
   The scope radio group above this is gone: "the whole site" is what almost
   everyone wants, and the exception is now one checkbox in here. Five loose
   controls in a flex row read as leftovers, so each is a row of its own with
   a hit area you can actually hit.
   ---------------------------------------------------------------------- */
.options--open .options-grid {
  display: grid;
  /* Two content-width tracks for the number fields, then one that absorbs the
     rest. Without that third track the tracks were sized purely by the number
     fields, so a full-width row spanning 1 / -1 got 268px of an 864px panel
     and its own columns collapsed to one. The number fields keep their size;
     only the leftover space is claimed. */
  grid-template-columns: minmax(0, 1fr);
  gap: .55rem 1.1rem;
  padding: 1rem 1.1rem;
}

.opt { color: var(--muted); }

.opt--num { display: grid; gap: .3rem; }
.opt--num label { font-size: .85rem; }

/* Max depth shares the switch row, so it reads as one of them: label beside
   the field on a single line, not stacked above it. Stacked, its input sat
   below the checkbox labels and the row looked broken. The padding matches
   .opt--check so the four items share a baseline. */
.opt-checks .opt--num {
  display: flex; align-items: center; gap: .5rem;
  align-self: start;
  /* Less padding on top than .opt--check: centring the label against a 32px
     input pushes it about 5px below the checkbox labels, and the row is meant
     to read as one line. Measured, not guessed. */
  padding: .19rem .45rem .5rem; margin: 0 -.45rem;
}
.opt-checks .opt--num label {
  font-size: .85rem; font-weight: 550; color: var(--ink); white-space: nowrap;
}
.opt-checks .opt--num input { width: 3.4rem; max-width: none; text-align: center; }

/* The three switches share one row. Each used to span the full width of the
   panel, which stacked them into a tall column. They drop back to one per row
   on a narrow screen, where three abreast would leave each hint about 100px
   to wrap into. */
/* The plain-English page scope, in place of the number box that used to ask
   the visitor to guess. */
.opt-scope {
  /* Spans the panel. Without this it was a single grid item in the two-column
     mobile grid - 138px wide - so a 41-character sentence wrapped onto three
     lines and pushed the whole options panel down the page. The text was never
     the problem; the column was. */
  grid-column: 1 / -1;
  margin: 0 0 .2rem; color: var(--muted); font-size: .88rem;
  white-space: nowrap;
}

/* The sentence is ~41 characters and the panel is ~293px on a 375px phone, so
   it lands a few pixels over one line at the full size. A step down keeps it
   on one line rather than pushing the whole panel down a second row. nowrap
   above guarantees it; this makes sure the guarantee fits. */
@media (max-width: 30rem) {
  .opt-scope { font-size: .76rem; }
}
.opt-scope strong { color: var(--ink); font-weight: 600; }

.opt-checks {
  grid-column: 1 / -1;
  display: grid;
  /* Four controls, one line: a narrow column for the depth field and three
     equal ones for the switches. auto-fit cannot express "one narrow plus
     three equal" - it made all four the same width, which fitted only three
     and wrapped the last onto a second row. The switches keep equal widths so
     their hints line up. It steps down to two columns, then one, at the
     widths measured below. */
  grid-template-columns: 9rem repeat(3, minmax(0, 1fr));
  gap: .2rem .4rem;
  align-items: start;
}

.opt--check {
  display: flex; align-items: flex-start; gap: .6rem;
  padding: .5rem .6rem; margin: 0 -.6rem;
  border-radius: 8px; cursor: pointer;
}
.opt--check:hover { background: rgba(255, 255, 255, .035); }
.opt--check input { margin-top: .15rem; width: 1rem; height: 1rem; accent-color: var(--brand); flex: none; }
.opt--check > span { display: grid; gap: .1rem; line-height: 1.35; }
.opt--check strong { color: var(--ink); font-weight: 550; font-size: .9rem; }
.opt--check .hint { font-size: .8rem; opacity: .8; }

/* Four controls share a ~680px panel, so each switch gets about 200px. At the
   default sizes "Include PageSpeed Insights" wrapped and the row went ragged -
   one item on one line, three on two. Trimmed a step so every label and hint
   sits on a single line, and only inside this row. */
.opt-checks .opt--check strong { font-size: .85rem; }
.opt-checks .opt--check .hint { font-size: .76rem; }
.opt-checks .opt--check { gap: .45rem; padding: .5rem .45rem; margin: 0 -.45rem; }

/* Dimmed, not hidden: the limits still exist, they just do not apply to a
   one-page audit. Removing them would make the panel jump. */
.opt--off { opacity: .4; }
.opt--off input { cursor: not-allowed; }

@media (max-width: 40rem) {
  .options--open .options-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Stepping down, measured rather than guessed. Four abreast needs about
   1240px before the hints stop fitting on one line (1180 wraps them, 1240 does
   not), so below 78rem it becomes 2x2 rather than dropping straight to a
   single tall column - four stacked rows on a 1280px laptop was worse than the
   three-across layout it replaced. One per row only on a phone. */
@media (max-width: 78rem) {
  /* Two EQUAL columns. Keeping the narrow depth track here put the fourth
     control into a 104px cell on the second row and wrapped its hint onto
     three lines - a narrow first column only works while everything fits on
     one row. */
  .opt-checks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .opt-checks .opt--num input { width: 3.4rem; }
}

@media (max-width: 52rem) {
  .opt-checks { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .opt-checks .opt--num { padding-bottom: .35rem; }
}

/* ---- Dashboard ---------------------------------------------------------
   Allowance first, because it is the only thing here that changes what
   someone can do today and the only thing they cannot find out elsewhere.
   ------------------------------------------------------------------------ */
.dash-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 1rem; flex-wrap: wrap; margin-bottom: 1.6rem;
}
.dash-head h1 { margin: 0; }
.dash-head .btn { text-decoration: none; }

.dash-cards {
  display: grid; gap: 1rem; margin-bottom: 2.2rem;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
}
.card {
  padding: 1.1rem 1.2rem; border: 1px solid var(--border);
  border-radius: var(--radius); background: rgba(0, 0, 0, .18);
}
.card h2 {
  margin: 0 0 .5rem; font-size: .72rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
  font-family: var(--font); font-style: normal;
}
.card__figure {
  margin: 0; font-size: 2.1rem; line-height: 1; color: var(--brand-text);
  font-family: var(--display); font-style: oblique;
}
.card__figure--text { font-size: 1.6rem; }

/* Usage page. The unit beside a figure - "/5", "min", "days" - is part of the
   number, not a label under it: "3/5" read as "3" is a different fact. */
.card__figure-sub {
  font-size: .9rem; color: var(--muted); font-style: normal;
  letter-spacing: .02em; margin-left: .15rem;
}

/* A heading between card groups, so the page reads as sections rather than
   one undifferentiated wall of cards. */
.dash-section {
  margin: 2.2rem 0 1rem; font-size: .72rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
  font-family: var(--font); font-style: normal;
}

/* Spans the grid. Used for the coupon-expiry notice, which is one fact about
   the whole account rather than one card among several. */
.card--wide { grid-column: 1 / -1; margin-bottom: 1rem; }

/* Only when a coupon is nearly out. Warning, not critical: nothing is broken
   and nothing is lost - a plan is about to end, which is a date, not a fault.
   Uses the validated status token rather than a hand-picked colour. */
.card--warn { border-color: var(--warning); }
.card--warn h2 { color: var(--warning); }

.dash-head__actions { display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }

/* Row actions sit under the thing they act on, not at the far end of the row.
   Wraps rather than overflowing, so a narrow screen stacks them instead of
   pushing the table sideways. */
.row-actions { display: flex; gap: .4rem; flex-wrap: wrap; margin-top: .4rem; }

/* The exact clock time under a relative one. Quieter than the phrase above it,
   because "3 minutes ago" is what you read and the stamp is what you check. */
.stamp { display: block; font-size: .78rem; opacity: .72; margin-top: .15rem; white-space: nowrap; }

/* The audit bar on the all-audits page. The full form on the landing page is
   a hero with an options panel; here it is one row under a list, so it drops
   the stage framing and keeps the field and the button. */
.audit-form--bar { margin-top: .4rem; max-width: 44rem; }
.audit-form--bar .field-row { margin-bottom: .5rem; }

/* A validation message belongs to the field below it, so it is as wide as the
   field row and sits directly on top of it. Left full width it stretched over
   the hero artwork and read as a notice about the whole page. */
.audit-form__error {
  /* The same width as the field row it belongs to, so the message and the box
     it is about line up rather than the message spanning the page. */
  max-width: 46rem; margin: 0 0 .6rem;
}

/* A full-bleed band inside the shell is not full-bleed - it is 124px of
   sideways scroll.

   .stage escapes .wrap with `margin-left: calc(50% - 50vw); width: 100vw`,
   arithmetic that only lands on zero when its containing block is centred.
   Inside .app-shell__main the containing block is the off-centre 1fr track of
   `13.5rem 1fr`, so at a 972px viewport the band started at x=124 and ran to
   x=1096. Measured in the browser, not reasoned: document.scrollWidth 1096
   against clientWidth 972.

   The menu stays on the signed-in home page; the band simply stops escaping
   when it is inside the shell. */
.app-shell__main .stage,
.app-shell__main .stage--tight {
  margin-left: 0; margin-right: 0; width: auto;
}

/* Stopping the band escaping was only half of it. Two values inside the hero
   are still measured against the VIEWPORT while the container is 13.5rem of
   menu and 2rem of gap narrower:

     --stage-inset  clamp(1.4rem, calc(50vw - 31rem), 10rem) - up to 10rem of
                    left padding sized for a full-width page, which pushed the
                    headline most of the way across a column that no longer
                    has the room.
     font-size      clamp(2.4rem, 5.4vw, 4.6rem) - 5.4% of the WINDOW, set in
                    a box much narrower than the window.

   Together they clipped the headline: the owner reported reaching this screen
   from the menu and seeing "Run a Free Te". Both are re-based on the column
   the hero is actually in. */
.app-shell__main .stage {
  --stage-inset: 1.4rem;
}
.app-shell__main .stage__title {
  font-size: clamp(1.9rem, 3.4vw, 3.1rem);
  max-width: 100%;
}

/* ---- Signed-in app shell: left menu beside the page ------------------- */
.app-shell { display: grid; grid-template-columns: 13.5rem 1fr; gap: 2rem; align-items: start; }
.app-shell__main { min-width: 0; }   /* lets wide tables scroll instead of stretching the grid */

.app-nav { position: sticky; top: 1.5rem; }

.app-nav__group {
  margin: 0 0 .45rem; font-size: .68rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); font-family: var(--font);
}
.app-nav__list { list-style: none; margin: 0 0 1.5rem; padding: 0; }
.app-nav__list li { margin: 0; }
.app-nav__list a {
  display: block; padding: .42rem .6rem; border-radius: 7px;
  color: var(--ink); text-decoration: none; font-size: .95rem;
  border-left: 2px solid transparent;
}
.app-nav__list a:hover { background: color-mix(in srgb, var(--brand) 12%, transparent); }

/* The current page is marked by colour AND a left bar, so it is not carried
   by hue alone. */
.app-nav__list a.is-current {
  color: var(--brand-text); border-left-color: var(--brand);
  background: color-mix(in srgb, var(--brand) 16%, transparent);
}

/* The site the per-site group is about. Both forms truncate: a host like
   www.a-very-long-business-name.co.uk is wider than the 13.5rem column, and
   wrapping it pushed the menu items down the page. */
.app-nav__site,
.app-nav__switch > summary {
  margin: 0 0 .45rem; padding: .3rem .6rem; border-radius: 7px;
  background: color-mix(in srgb, var(--brand) 8%, transparent);
  font-size: .9rem; font-weight: 600; color: var(--brand-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* "Viewing" above the hostname. Small, muted and on its own line, so the
   host itself stays the loud thing while the label removes the ambiguity
   between the site in view and the one you could switch to. */
.app-nav__switch-label {
  display: block; font-size: .62rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); font-weight: 400;
}
/* Standing alone above the hostname rather than inside its box. */
.app-nav__switch-label--solo { margin: 0 0 .1rem; padding: 0 .6rem; }
/* Not a link: it names what the list below it is for. */
.app-nav__switch-heading {
  padding: .2rem .4rem .25rem; font-size: .62rem; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted);
}
.app-nav__switch > summary { cursor: pointer; list-style: none; }
.app-nav__switch > summary::-webkit-details-marker { display: none; }
.app-nav__switch > summary::after { content: " ▾"; color: var(--muted); font-weight: 400; }
.app-nav__switch[open] > summary::after { content: " ▴"; }
.app-nav__switch ul {
  list-style: none; margin: 0 0 .5rem; padding: .25rem 0 .25rem .6rem;
  border-left: 2px solid var(--border);
}
.app-nav__switch a {
  display: block; padding: .25rem .4rem; border-radius: 6px; font-size: .88rem;
  color: var(--ink); text-decoration: none;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-nav__switch a:hover { background: color-mix(in srgb, var(--brand) 12%, transparent); }

/* A locked item is still a link to a page that explains the plan, so it is
   not dimmed into looking disabled - only marked. */
.app-nav__list a.app-nav__item { display: flex; align-items: baseline; gap: .4rem; }
.app-nav__label { flex: 1 1 auto; min-width: 0; }
/* The hint sits under the label, quiet enough to read past. */
.app-nav__hint {
  display: block; font-size: .72rem; color: var(--muted);
  font-family: var(--font); line-height: 1.35;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.app-nav__list a.is-current .app-nav__hint { color: inherit; opacity: .75; }
.app-nav__lock {
  flex: 0 0 auto; font-size: .62rem; letter-spacing: .06em; text-transform: uppercase;
  padding: .08rem .3rem; border-radius: 4px; font-family: var(--font);
  color: var(--muted); border: 1px solid var(--border);
}

.app-nav__plan { margin: 0; padding-top: 1rem; border-top: 1px solid var(--border); font-size: .85rem; }
.app-nav__plan a { text-decoration: none; display: block; }
.app-nav__plan-name { display: block; color: var(--brand-text); font-weight: 600; }

/* Below this the menu is a row above the content rather than a column beside
   it - a 13.5rem column on a phone leaves nothing for the page. */
@media (max-width: 54rem) {
  .app-shell { grid-template-columns: 1fr; gap: 1.2rem; }
  .app-nav { position: static; }
  .app-nav__group { display: none; }
  .app-nav__list { display: flex; flex-wrap: wrap; gap: .3rem; margin-bottom: .6rem; }
  .app-nav__list a { border-left: 0; border: 1px solid var(--border); }
  .app-nav__list a.is-current { border-color: var(--brand); }
  /* The site group keeps its heading on a phone - without it the per-site
     items read as a second, unexplained row of buttons. Targeted by its own
     class: :not(:first-of-type) counts <p> siblings, so it matched "Account"
     as well and left "Your work" as the only group without a label. */
  .app-nav__group--site { display: block; margin-top: .4rem; }
  .app-nav__site, .app-nav__switch {
    display: inline-block; margin-right: .4rem;
    /* An inline-block shrink-to-fits to max-content, and the host carries
       white-space: nowrap - so without a cap the ellipsis never engages and
       www.a-very-long-business-name.co.uk scrolls the page sideways. */
    max-width: 100%;
  }
  .app-nav__switch ul { border-left: 0; padding-left: 0; }
  .app-nav__plan { padding-top: .8rem; }
}
.card__of { font-size: .9rem; color: var(--muted); margin-left: .35rem; font-family: var(--font); font-style: normal; }
.card__label { margin: .5rem 0 0; font-size: .82rem; color: var(--muted); }
.card__note { margin: .6rem 0 0; font-size: .82rem; }

/* "2 of 3" is a number; the bar is the feeling. */
.meter { margin-top: .8rem; height: 5px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.meter span { display: block; height: 100%; background: var(--brand); border-radius: 999px; }

.dash-running { list-style: none; margin: 0 0 2rem; padding: 0; display: grid; gap: .6rem; }
.dash-running li {
  display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
  padding: .7rem .9rem; border: 1px solid var(--border-brand);
  border-radius: var(--radius); background: rgba(128, 98, 214, .07); font-size: .9rem;
}

.dash-empty {
  padding: 2rem 1.5rem; text-align: center;
  border: 1px dashed var(--border); border-radius: var(--radius);
}
.dash-empty p { margin: 0 0 .5rem; }
.dash-empty .btn { margin-top: .8rem; display: inline-block; text-decoration: none; }

/* Work done, not score moved: a score drifts with crawl noise, a closed
   issue is something a person actually did. */
.delta { font-size: .82rem; font-weight: 600; margin-right: .5rem; white-space: nowrap; }
.delta--good { color: var(--ok); }
.delta--bad { color: var(--critical); }

@media (max-width: 40rem) {
  .dash-head { flex-direction: column; align-items: stretch; }
  .dash-head .btn { text-align: center; }
}

/* Registration facts: context, not a score. Set as quietly as the rest of the
   report's metadata so nobody reads the registration date as a metric. */
.domain-facts {
  margin: .8rem 0 0; display: flex; flex-wrap: wrap; gap: .4rem 1.4rem;
  font-size: .84rem; color: var(--muted);
}
.domain-facts strong { color: var(--ink); font-weight: 550; }
.domain-facts--urgent, .domain-facts--urgent strong { color: var(--warning); }

/* ---- Below the fold on the landing page --------------------------------
   The hero asks for a URL. This is for the reader who wants to know what
   they are about to get before they hand one over.
   ------------------------------------------------------------------------ */
/* ---- What the product does, between the hero and the explanation -----------
   Added 22 October 2026. Six cards and a list of what is free, set as a grid
   that falls to one column on a phone. The cards are quiet on purpose: the
   hero above is the loud thing, and this is a place to be read, not to be
   pressed. */
.suite { margin-top: 3.5rem; }
.suite__title {
  margin: 0 0 .6rem; font-size: 1.5rem; letter-spacing: -.01em; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none;
}
.suite__lede { margin: 0 0 1.8rem; max-width: 46rem; color: var(--muted); font-size: 1rem; line-height: 1.65; }
.suite__grid {
  display: grid; gap: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}
.suite__card {
  padding: 1.1rem 1.2rem 1.2rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); display: flex; flex-direction: column;
}
.suite__card h3 {
  margin: 0 0 .5rem; font-size: 1.02rem; color: var(--brand-text);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}
.suite__card p { margin: 0 0 .9rem; color: var(--muted); font-size: .9rem; line-height: 1.6; flex: 1; }
.suite__more { font-size: .88rem; }
.suite__free {
  margin-top: 1.4rem; padding: 1.1rem 1.3rem; border: 1px solid var(--border);
  border-left: 3px solid var(--brand); border-radius: var(--radius); background: var(--surface);
}
.suite__free h3 {
  margin: 0 0 .6rem; font-size: 1rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}
.suite__free ul { margin: 0 0 .8rem; padding-left: 1.2rem; display: grid; gap: .3rem; color: var(--muted); font-size: .92rem; line-height: 1.55; }
.suite__free li strong { color: var(--ink); font-weight: 600; }
.suite__free p { margin: 0; font-size: .9rem; color: var(--muted); }

.pitch { margin-top: 3.5rem; }
/* The separator belongs to whichever block comes second, not to a particular
   one. "What happens when you paste a URL" moved above the cards on 26 Sep;
   the rule moved with the position, or it would have floated above the
   section's first heading with nothing over it. */
.pitch-row {
  display: grid; gap: 1.6rem;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  margin-top: 2.6rem; padding-top: 1.8rem; border-top: 1px solid var(--border);
}
.pitch h2 {
  margin: 0 0 .5rem; font-size: 1rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}
.pitch p { margin: 0; color: var(--muted); font-size: .92rem; line-height: 1.6; }

.pitch-how { margin-top: 0; }
.pitch-how ol {
  margin: 1rem 0 0; padding-left: 1.2rem; display: grid; gap: .55rem;
  max-width: 46em; font-size: .92rem; line-height: 1.55; color: var(--muted);
}
.pitch-how li strong { color: var(--ink); font-weight: 550; }
.pitch-how .muted { margin-top: 1.1rem; font-size: .88rem; }

/* ---- Dotted contour field ----------------------------------------------
   Purple contours radiating from the mark and running off every edge, so the
   hero reads as one field rather than a graphic parked in the corner.

   Behind everything and non-interactive: it is decoration, and a decoration
   that eats a click on the URL field would be a bug.
   ------------------------------------------------------------------------ */
.stage__field {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  /* Fades out towards the left so the headline and the form keep a clean
     ground to sit on - the reference does the same, densest at the focus. */
  -webkit-mask-image: radial-gradient(125% 115% at 77.5% 50%, #000 52%, rgba(0, 0, 0, .78) 74%, transparent 100%);
          mask-image: radial-gradient(125% 115% at 77.5% 50%, #000 52%, rgba(0, 0, 0, .78) 74%, transparent 100%);
}

/* The content sits above the field. Without this the stacking context puts
   the SVG over the form, and the field is exactly the wrong thing to have on
   top of an input.
   z-index only - .stage__art is absolutely positioned against the stage, and
   setting position here dropped it back into flow, landing the trophy on top
   of the URL field. */
.stage__title,
.stage__grid { position: relative; z-index: 1; }
.stage__art { z-index: 1; }

/* The mark keeps its glow but loses the drawn rings - the field supplies
   them now, and two sets of circles read as a mistake. */
.stage__art { filter: drop-shadow(0 0 40px rgba(128, 98, 214, .28)); }
.stage__mark { width: 38%; }

@media (max-width: 68rem) {
  /* The art hides at this width; the field would then be circles around
     nothing. */
  .stage__field { display: none; }
}

/* The offer, between the headline and the promise. A step down from the
   headline's 800, and the only line between it and the form. Not bold: an H2
   defaults to 700 and at this size that competes with the H1. */
.stage__offer {
  margin: 0 0 1.6rem;
  padding-left: var(--stage-inset);
  padding-right: 1.5rem;
  max-width: 42ch;
  font-size: clamp(1.05rem, 1.6vw, 1.3rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}

@media (max-width: 56rem) {
  .stage__title { max-width: none; }
  /* On a phone every line above the form costs a thumb-scroll, so the gap
     closes up. */
  .stage__offer { margin-bottom: 1.1rem; }
}

/* ---- Typewriter headline ----------------------------------------------
   The characters exist from the first paint and never move; only their
   opacity changes. That is what keeps the line from reflowing as it types,
   and what lets the text stay complete in the DOM throughout.

   .is-typing is added by the script, so a headline whose JavaScript never
   runs is a headline that is simply visible.
   ---------------------------------------------------------------------- */
/* A word is one unbreakable box, so lines break between words exactly as they
   would in untouched text. One span per character let the browser break
   anywhere, and the break points then moved as the caret advanced - which is
   what made the headline flip between four lines and three and shunt the whole
   form up and down on a phone. */
.tw-w { white-space: nowrap; }
.is-typing .tw-c { opacity: 0; }
.is-typing .tw-c.tw-on { opacity: 1; }

/* Positioned, not inline. In the flow its width was a second thing that could
   tip the last word of a line over, so the caret itself helped shuffle the
   headline between four lines and three. Out of flow it costs the layout
   nothing, and the script gives it the current character's own left/top so it
   still follows the text onto later lines. */
.tw-caret {
  position: absolute;
  width: .055em;
  background: var(--brand-text);
  animation: tw-blink .9s steps(1, end) infinite;
  pointer-events: none;
}
@keyframes tw-blink { 0%, 49% { opacity: 1 } 50%, 100% { opacity: 0 } }

/* Motion is decoration. The script returns early for anyone who has asked for
   less of it; this is the belt to that braces - if the spans ever exist
   without the script running, they stay visible. */
@media (prefers-reduced-motion: reduce) {
  .is-typing .tw-c { opacity: 1; }
  .tw-caret { display: none; animation: none; }
}

/* "Free" carries the offer, so it is set larger and in white against the
   headline's purple. Two steps up on a 1.12 scale is 1.25em.
   line-height is pinned so the taller word does not grow its line box and
   push the second line down - the form sits under this and must not move. */
.stage__title .hl {
  font-size: 1.25em;
  line-height: .85;
  color: #FFFFFF;
  /* <em> for the emphasis a screen reader should convey, not for italics. */
  font-style: normal;
}

/* ---- Finding filters ---------------------------------------------------
   Narrowing the list, never shortening it: every count stays on screen
   whichever filter is active, so filtering to Critical cannot be mistaken
   for "there are only three problems".
   ------------------------------------------------------------------------ */
.filters {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin: 0 0 1.2rem;
}
.filters__label { font-size: .82rem; color: var(--muted); margin-right: .2rem; }
.filters__btn {
  display: inline-flex; align-items: center; gap: .4rem;
  padding: .38rem .7rem; font: inherit; font-size: .85rem; cursor: pointer;
  color: var(--muted); background: transparent;
  border: 1px solid var(--border); border-radius: 999px;
}
.filters__btn:hover { border-color: var(--brand-text); color: var(--ink); }
.filters__btn.is-on {
  color: var(--ink); border-color: var(--brand-text);
  background: color-mix(in srgb, var(--brand-text) 14%, transparent);
}
.filters__n {
  font-variant-numeric: tabular-nums; font-size: .78rem;
  padding: .05rem .38rem; border-radius: 999px;
  background: rgba(255, 255, 255, .07); color: var(--ink);
}
.filters__btn--critical .filters__n { background: color-mix(in srgb, var(--critical) 26%, transparent); }
.filters__btn--warning  .filters__n { background: color-mix(in srgb, var(--warning) 26%, transparent); }
.filters__btn--info     .filters__n { background: color-mix(in srgb, var(--info) 26%, transparent); }

.filters__none { color: var(--muted); font-size: .9rem; }

/* [hidden] has to win. A display rule on .finding would silently override the
   attribute - that exact bug shipped twice before, on .scope-folder and on
   .scope-opt, and both times the element stayed on screen with the script
   convinced it had hidden it. */
.finding[hidden],
.filters[hidden],
.filters__none[hidden] { display: none !important; }

@media (max-width: 40rem) {
  .filters__btn { min-height: 40px; }
}

/* ---- Report tools ------------------------------------------------------ */
.report-tools {
  display: flex; flex-wrap: wrap; align-items: center; gap: .5rem;
  margin: 1rem 0 .4rem;
}
.report-tools__btn {
  display: inline-flex; align-items: center;
  padding: .42rem .85rem; font: inherit; font-size: .86rem; cursor: pointer;
  color: var(--ink); background: transparent; text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--radius);
}
.report-tools__btn:hover { border-color: var(--brand-text); color: var(--brand-text); }
.report-tools__btn.is-done { border-color: var(--ok); color: var(--ok); }
.report-tools__note { font-size: .8rem; }

/* ---- Print -------------------------------------------------------------
   A dark theme printed straight would be a black page: unreadable, and a
   cartridge's worth of ink for one report. This is also the closest thing to
   the PDF an agency hands a client, so it has to stand on its own - link
   destinations spelled out, and nothing on it that only works on screen.
   ------------------------------------------------------------------------ */
@media print {
  :root { color-scheme: light; }

  body, .wrap, .panel, .finding, .table, .scorecard {
    background: #fff !important;
    color: #111 !important;
    box-shadow: none !important;
  }

  /* Interface, not report. None of it means anything on paper. */
  .site-header, .site-footer, .report-tools, .filters, .filters__none,
  .explain--locked, .stage__field, .stage__art, .crumbs, .flash,
  .app-nav, .row-actions {
    display: none !important;
  }

  /* With the menu gone the page is one column, and the grid's empty first
     track would otherwise leave an inch of blank paper down every sheet. */
  .app-shell { display: block !important; }

  /* A filtered report must print in full: paper has no buttons to undo it. */
  .finding[hidden] { display: block !important; }

  /* The rule-set caveat rides on .flash, which is hidden above as interface.
     It is not interface here: without it a printed comparison claims credit
     for differences that are ours, and paper cannot be corrected later. */
  .cmp-caveat {
    display: block !important;
    border: 1px solid #111 !important;
    background: #fff !important;
    color: #111 !important;
    padding: .6rem .8rem;
  }

  /* Columns are the point of this table; let it keep them rather than
     scrolling off the edge of a sheet nobody can scroll. */
  .scroll-x { overflow-x: visible !important; }
  .cmp-table { font-size: .8rem; }
  .cmp-table th, .cmp-table td { border-bottom: 1px solid #ddd !important; }
  .cmp-cell--now { font-weight: 600; }
  .cmp-row, .cmp-divide { break-inside: avoid; }
  .cmp-table thead { display: table-header-group; }   /* repeat heads per page */
  .badge { border: 1px solid #111 !important; background: #fff !important; color: #111 !important; }
  .cmp-verdict { border: 1px solid #555 !important; color: #111 !important; background: #fff !important; }

  a { color: #111 !important; text-decoration: underline; }
  /* On paper a link is just underlined text unless its destination is
     printed, and these reports exist to be handed to whoever will do the
     fixing. */
  .findings a[href^="http"]::after,
  .urls a[href^="http"]::after { content: " (" attr(href) ")"; font-size: .85em; word-break: break-all; }

  .finding, .panel { break-inside: avoid; border: 1px solid #ccc !important; }
  h1, h2, h3 { color: #111 !important; break-after: avoid; }
  details { display: block; }
  details > summary { display: none; }   /* print the URLs, not the toggle */

  .muted, .hint { color: #555 !important; }

  /* Every colour token on this page was chosen to sit on a dark ground, so on
     white paper the near-white ones disappear and the pastels go to nothing.
     The blanket rule at the top of this block only reaches a handful of
     containers; anything that sets its own colour has to be named. */
  .cmp-table tbody th, .cmp-cell, .cmp-cell--now, .cmp-divide th,
  .change, .change span, .change__tag,
  .cmp-page, .cmp-status, .cmp-verdict, .cmp-row--pagehead > th,
  .ring__cap, .stat, .stat__figure, .stat__move {
    color: #111 !important;
  }

  /* The ring's arc is the one thing greyscale cannot carry, so on paper the
     band is read from the word under it. Printers drop backgrounds by default;
     forcing the stroke keeps the gauge from printing as an empty circle. */
  .ring__track { stroke: #ddd !important; }
  .ring__arc { stroke: #111 !important; }
  .ring__value { fill: #111 !important; }
  .stat { border: 1px solid #ccc !important; background: #fff !important; break-inside: avoid; }

  /* The mark columns are the point of this table on paper - a tick and a cross
     both survive black ink, which is why the glyphs were chosen over colour. */
  .mark--hit, .mark--clear { color: #111 !important; }
  .cmp-row--page > th, .cmp-row--page > td { border-bottom: 1px solid #ddd !important; }
  .cmp-row--page, .cmp-row--pagehead { break-inside: avoid; }
  .check__code, .cmp-key, .cmp-note, .stamp, .change__delta,
  .cmp-page__why, .cmp-sample { color: #444 !important; }

  /* `details { display: block }` and the hidden summary above print these open
     - which is what paper needs. A printed comparison with the addresses
     folded behind a triangle nobody can click is a page of numbers with the
     "where" thrown away. The left edge distinguishes the lists by line style
     as well as colour, because in grey they are otherwise the same bar. */
  .cmp-page__url { word-break: break-all; }
  /* The mark glyphs were chosen over colour precisely so they survive black
     ink; this only stops them inheriting a pastel that prints as nothing. */
  .mark--hit, .mark--clear { color: #111 !important; }

  /* The change rows are dark slabs on screen. Printed as they are, they come
     out as four black bars with nothing readable on them. */
  .change {
    background: #fff !important;
    border: 1px solid #ccc !important;
    border-left: 3px solid #111 !important;
    break-inside: avoid;
  }

  @page { margin: 16mm; }
}

/* The account form is a column of full-width fields; the auth pages it shares
   styling with are narrower because they ask for two things, not five. */
.auth--wide { max-width: 34rem; }
.account-rights { margin-top: 2.2rem; padding-top: 1.4rem; border-top: 1px solid var(--border); }
.account-rights h2 { font-size: 1rem; margin: 0 0 .5rem; }
.account-rights p { line-height: 1.6; }
.inline-form { display: inline; }
.inline-form .link-btn { font-size: inherit; }

.crawl-delay { margin: .8rem 0 0; font-size: .85rem; line-height: 1.55; }
.crawl-delay strong { color: var(--ink); font-weight: 550; }

.retry-row { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; margin: 1rem 0 0; }
.retry-row form { display: inline; }

.account-rights__body { line-height: 1.6; }

/* Crawl figures — response mix and TTFB distribution
   ------------------------------------------------------------------------ */
.figures { display: grid; gap: 2rem; }
@media (min-width: 46rem) { .figures { grid-template-columns: 1fr 1fr; } .figures .panel__title { grid-column: 1 / -1; } }

.figure { margin: 0; }
.figure__cap { font-size: .9rem; color: var(--muted); margin-bottom: .7rem; }
.figure__note { font-size: .8rem; color: var(--muted); margin-top: .6rem; }

/* One stacked bar. Segments are sized in user units (viewBox is 100 wide) and
   stretched, so preserveAspectRatio must be none or the gaps would scale. */
.stackbar { width: 100%; height: 14px; display: block; }
.stackbar__seg--ok       { fill: var(--ok); }
.stackbar__seg--info     { fill: var(--info); }
.stackbar__seg--warning  { fill: var(--warning); }
.stackbar__seg--critical { fill: var(--critical); }
.stackbar__seg--failed   { fill: var(--muted); }

.figure__legend { list-style: none; padding: 0; margin: .8rem 0 0; display: flex; flex-wrap: wrap; gap: .3rem 1.1rem; }
.figure__legend li { display: flex; align-items: center; gap: .4rem; font-size: .85rem; }
.figure__legend-label { color: var(--muted); }
.figure__legend-val { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 600; }

.swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.swatch--ok       { background: var(--ok); }
.swatch--info     { background: var(--info); }
.swatch--warning  { background: var(--warning); }
.swatch--critical { background: var(--critical); }
.swatch--failed   { background: var(--muted); }

/* Histogram. One series, so one hue — the caption names it, no legend. */
.histo { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; align-items: end; height: 130px; }
.histo__col { display: grid; grid-template-rows: 1.1rem 1fr 1.1rem; height: 100%; gap: 3px; justify-items: center; }
.histo__bar { width: 100%; background: var(--brand); border-radius: 4px 4px 0 0; align-self: end; min-height: 0; }
.histo__val { font-size: .75rem; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.1rem; }
.histo__lab { font-size: .68rem; color: var(--muted); line-height: 1.1rem; white-space: nowrap; }

/* Glossary
   ------------------------------------------------------------------------ */
.glossary__jump {
  display: flex; flex-wrap: wrap; gap: .4rem .5rem;
  margin: 1.5rem 0 2rem; padding-bottom: 1.5rem; border-bottom: 1px solid var(--border);
}
.glossary__jump a {
  font-size: .8rem; padding: .25rem .6rem; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--border);
  color: var(--muted); text-decoration: none;
}
.glossary__jump a:hover { border-color: var(--border-brand); color: var(--brand-text); }

.glossary__list { margin: 0; display: grid; gap: 1.4rem; }
.glossary__item { scroll-margin-top: 5rem; }
.glossary__item dt { font-weight: 700; color: var(--ink); margin-bottom: .3rem; font-size: 1.02rem; }
.glossary__item dd { margin: 0; color: var(--muted); line-height: 1.7; }
.glossary__item:target dt { color: var(--brand-text); }
.glossary__cta { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border); color: var(--muted); }

/* Lala's site-specific advice — the metered, paid-for one
   ------------------------------------------------------------------------ */
.explain--site { border-left: 2px solid var(--brand); padding-left: .9rem; }
.explain--site .explain__by strong { color: var(--brand-text); }
.explain--ask { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; margin-top: .6rem; }
.btn--quiet {
  background: transparent; border: 1px solid var(--border-brand); color: var(--brand-text);
}
.btn--quiet:hover { background: color-mix(in srgb, var(--brand) 14%, transparent); }


/* Side-by-side comparison -------------------------------------------------
   Audits of one site in columns, newest on the left. Severity colour appears
   only on the badge that already carries a word, and the movement tag spells
   out "better" or "worse" - so nothing in this table means anything by colour
   alone, which matters doubly because it is built to be printed in grey.
   ------------------------------------------------------------------------ */
.cmp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.cmp-head h2 { margin: 0; }
.cmp-note { margin: .3rem 0 1rem; font-size: .88rem; }
.cmp-caveat { margin: 0 0 1rem; }

.cmp-table { width: 100%; }
.cmp-table thead th { vertical-align: bottom; text-align: left; white-space: nowrap; }
/* The row label is the widest thing here and the numbers are the narrow part,
   so the label takes the slack and the columns stay the same width as each
   other - unequal number columns read as unequal numbers. */
/* Row labels are sentences - a finding title is "Scripts in the head blocking
   first paint", not a column heading - so the global uppercase .table th rule
   is undone here. Shouting a sentence makes it slower to read, and these are
   the longest strings on the page. */
.cmp-table tbody th {
  text-align: left;
  font-weight: 500;
  text-transform: none;
  letter-spacing: 0;
  font-size: .88rem;
  color: var(--ink);
  line-height: 1.45;
}
.cmp-table tbody th .badge { margin-right: .4rem; vertical-align: 1px; }
.cmp-table thead th:first-child, .cmp-table tbody th { width: 42%; }

/* Centred on purpose. Three numbers of different widths, left-aligned, cannot
   be compared by running the eye down them - which is the entire job of this
   table. The row label stays left, because it is prose. */
.cmp-cell { white-space: nowrap; font-variant-numeric: tabular-nums; text-align: center; }
/* The current run is the one being explained; the older columns are context,
   and holding them back stops three equal columns reading as a tie. */
.cmp-cell--now { font-weight: 600; color: var(--ink); }
.cmp-row--summary th, .cmp-row--summary .cmp-cell { font-weight: 600; }

.cmp-divide th {
  padding-top: 1.1rem;
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  font-weight: 600;
}

/* Status: the last column, and the answer to "so what?" for every kind of row
   in the table - a summary figure, a finding count, or one affected page. It
   had been sitting beside the address, which put it off-screen as soon as the
   table scrolled sideways. */
.cmp-status { text-align: left; white-space: nowrap; }
.cmp-status-head { text-align: left !important; }
.cmp-verdict {
  display: inline-block;
  padding: 0 .35rem;
  border-radius: 3px;
  border: 1px solid var(--border);
  font-size: .72rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* The word is the signal; the colour repeats it. Nothing here is colour-only. */
.cmp-verdict--better, .cmp-verdict--fixed { border-color: var(--ok); color: var(--ok); }
.cmp-verdict--worse, .cmp-verdict--new { border-color: var(--critical); color: var(--critical); }
.cmp-verdict--same, .cmp-verdict--open { border-color: var(--border); color: var(--muted); }

.cmp-key { margin: .9rem 0 0; font-size: .82rem; line-height: 1.5; }

/* The rule's identifier, beside the title it belongs to. Used by the changes
   list and the comparison table, and styled in neither until now - so every
   one of them read as "first paintRENDER_BLOCKING". */
.check__code {
  margin-left: .45rem;
  font-size: .76em;
  color: var(--muted);
  white-space: nowrap;
}


/* Score ring ---------------------------------------------------------------
   The triage score as a gauge. The arc is a status, not a series, so it wears
   the reserved status colours - and the band is written underneath, because an
   arc's colour is the one thing that does not survive a greyscale print.
   -------------------------------------------------------------------------- */
.ring { margin: .55rem 0 0; display: flex; flex-direction: column; align-items: center; gap: .15rem; }
.ring__svg { display: block; }
.ring__track { fill: none; stroke: var(--surface-2); stroke-width: 7; }
.ring__arc {
  fill: none;
  stroke-width: 7;
  stroke-linecap: round;
  /* Lengthening the dash rather than animating a transform keeps the arc
     anchored at twelve o'clock while it fills. */
  transition: stroke-dasharray .4s ease;
}
.ring--good .ring__arc { stroke: var(--ok); }
.ring--fair .ring__arc { stroke: var(--warning); }
.ring--poor .ring__arc { stroke: var(--critical); }
/* The digits wear ink, never the band colour: text that changes hue with its
   value is the first thing to fail a contrast check. */
.ring__value { fill: var(--ink); font-size: 1.45rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.ring__cap { font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.ring__empty {
  display: grid;
  place-items: center;
  border: 2px dashed var(--border);
  border-radius: 50%;
}

/* The rule between the two reports ----------------------------------------
   Asked for directly: a line dividing one report from the next. It runs the
   full height of the table so the eye never has to work out which column a
   number belongs to.
   -------------------------------------------------------------------------- */
.cmp-table th.cmp-col,
.cmp-table .cmp-cell { border-left: 1px solid var(--border); }
.cmp-table th.cmp-col { text-align: center; vertical-align: top; padding-bottom: .8rem; }
.cmp-col__name { display: block; }
.cmp-table th.cmp-col .stamp { white-space: nowrap; font-weight: 400; }
/* The current report is the subject; the older ones are its context. A tinted
   column says that without a label. */
.cmp-table th.cmp-col:first-of-type,
.cmp-table .cmp-cell--now { background: color-mix(in srgb, var(--surface-2) 30%, transparent); }

/* Affected pages ----------------------------------------------------------
   Rows of the comparison table, not a table inside it, so every mark sits in
   the column of the run it belongs to without being told to.
   -------------------------------------------------------------------------- */
.cmp-row--pagehead > th {
  padding-top: .55rem;
  font-size: .74rem;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--muted);
}
.cmp-sample { display: block; font-weight: 400; line-height: 1.5; margin-top: .2rem; }

.cmp-row--page > th, .cmp-row--page > td { border-bottom: 1px solid var(--border); }
.cmp-page { padding-left: 1.4rem !important; font-weight: 400 !important; }
.cmp-page__url { font-size: .78rem; overflow-wrap: anywhere; }
.cmp-page__why { display: block; font-size: .74rem; }


.cmp-cell--mark { text-align: center; }
.mark { font-size: .92rem; line-height: 1; }
.mark--hit { color: var(--critical); }
.mark--clear { color: var(--ok); }



/* Headline figures --------------------------------------------------------
   Four tiles for the reader who will look at exactly one thing. Replaced a
   two-bar chart: two bars is not a trend, and on an unchanged site it drew two
   identical columns that said nothing.
   -------------------------------------------------------------------------- */
.statstrip {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: .9rem;
  margin: 1rem 0 1.4rem;
}
.stat {
  padding: .9rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface-2) 22%, transparent);
}
.stat--ring { display: flex; flex-direction: column; align-items: center; text-align: center; }
.stat__label { margin: 0; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.stat__figure { margin: .35rem 0 0; font-size: 2rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.stat__move { margin: .4rem 0 0; font-size: .74rem; line-height: 1.4; }
/* The word already says better or worse; the colour only repeats it. */
.stat__move--better { color: var(--ok); }
.stat__move--worse { color: var(--critical); }
.stat__move--flat { color: var(--muted); }

/* Dashboard: plan bar and allowances ---------------------------------------
   The owner's instruction was that every kind of account - free, paid, comped
   by coupon - sees the real-time state of every allowance in one place. So
   nothing here is hidden from the tier that has it: a limit you cannot see is
   one you can only discover by hitting it.
   -------------------------------------------------------------------------- */
.plan-bar {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  flex-wrap: wrap;
  margin: 1.4rem 0;
  padding: .9rem 1.1rem;
  border: 1px solid var(--border);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface-2) 22%, transparent);
}
/* Access that is about to end is the one thing on this page that can be taken
   away, so it gets the only colour change on the bar. */
.plan-bar--ending { border-left-color: var(--warning); }
.plan-bar__label { margin: 0; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; }
.plan-bar__name { margin: .1rem 0 0; font-size: 1.35rem; font-weight: 600; line-height: 1.1; }
.plan-bar__facts { flex: 1 1 14rem; }
.plan-bar__facts p { margin: 0 0 .15rem; font-size: .85rem; line-height: 1.5; }
.plan-bar__note { color: var(--ok); font-size: .85rem; }
.plan-bar__warn { color: var(--warning); font-weight: 550; }
.plan-bar__actions { display: flex; gap: .4rem; flex-wrap: wrap; }

.allowances {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: .9rem;
  margin: .8rem 0 1.6rem;
}
.allowance {
  padding: .9rem 1rem 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--surface-2) 18%, transparent);
}
/* 70% is early enough to act on and late enough not to nag. Both states carry
   a word in the body text as well, so neither is signalled by colour alone. */
.allowance--warn { border-color: var(--warning); }
.allowance--spent { border-color: var(--critical); }

.allowance__label { margin: 0; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.allowance__figure { margin: .3rem 0 0; font-size: 1.9rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.allowance__figure--text { font-size: 1.35rem; }
.allowance__of { font-size: .95rem; font-weight: 400; color: var(--muted); }
.allowance__unit { margin: .2rem 0 0; font-size: .76rem; }
/* The remaining count in words as well as in the bar: a rectangle is not a
   quantity, and nobody can read "4 left" off one. */
.allowance__left { margin: .5rem 0 0; font-size: .78rem; line-height: 1.5; }
.allowance--warn .allowance__left strong { color: var(--warning); }
.allowance--spent .allowance__left strong { color: var(--critical); }
.allowance__detail { margin: .5rem 0 0; font-size: .74rem; line-height: 1.5; }

@media print {
  .plan-bar, .allowance { background: #fff !important; border: 1px solid #ccc !important; break-inside: avoid; }
  .plan-bar__name, .allowance__figure, .allowance__left { color: #111 !important; }
}

/* Affected URLs on a report finding. The address links; the reason it matched
   sits under it as text. They were one unclickable <code> blob, on the list
   whose whole purpose is "go here and fix this". */
.urls .url-link { word-break: break-all; }
.urls .url-why { display: block; font-size: .92em; }
@media print { .urls .url-why { color: #444 !important; } }

/* The crumbs row carries a forward link as well as the way back. */
.crumbs__right { float: right; }
@media print { .crumbs__right { display: none !important; } }

/* What an affected page already ranks for. The line that turns a technical
   finding into a commercial one. */
.finding__weight { margin: .5rem 0 0; font-size: .85rem; color: var(--ink); }
.url-ranks { display: block; font-size: .74rem; color: var(--muted); margin-top: .15rem; }
.url-ranks strong { color: var(--accent); font-weight: 550; }
@media print {
  .finding__weight, .url-ranks { color: #111 !important; }
  .url-ranks strong { color: #111 !important; }
}

/* A fetch control sits in each insight panel's header, beside its title. */
.insight-form { display: flex; gap: .4rem; align-items: center; flex-wrap: wrap; }
.url-input--small { max-width: 16rem; font-size: .85rem; padding: .35rem .6rem; }
@media print { .insight-form { display: none !important; } }

/* A sub-report inside an insight panel: a second fetch button under the one
   that produced the summary. */
.panel h4.sub { margin: 1.4rem 0 .2rem; font-size: .9rem; }
.panel h4.sub + .cmp-head { margin-bottom: .2rem; }

/* ─────────────────────────────────────────────────────────────────────────
   Dashboard: the task panel, the four tiles, and the split below them.

   Shape borrowed from a reference the owner liked; palette emphatically not.
   The reference is a light-theme product with four grey cards - the useful
   idea in it is the ORDER: say what to do, then show how far through you are,
   then the counts, then the detail. Everything here reads from real figures;
   there is no placeholder tile waiting for an integration we do not have.
   ───────────────────────────────────────────────────────────────────────── */

.dash-head__sub { margin: .25rem 0 0; font-size: .95rem; }

.assist {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  margin: 1.5rem 0 1.1rem; padding: 1rem 1.2rem;
  border: 1px solid var(--border-brand); border-radius: var(--radius);
  background: linear-gradient(180deg, var(--surface), var(--bg));
}
.assist__mark { display: grid; place-items: center; width: 40px; height: 40px; flex: none;
  border-radius: 50%; color: var(--brand-text);
  background: color-mix(in srgb, var(--brand) 18%, transparent); }
.assist__body { flex: 1 1 18rem; }
.assist__body h2 { margin: 0; font-size: 1.02rem; }
.assist__body p { margin: .2rem 0 0; font-size: .93rem; color: var(--muted); }
.assist__body strong { color: var(--ink); }
.assist__link { flex: none; font-size: .9rem; white-space: nowrap; }

/* The KPI row. Icon, figure, delta, note - the second reference's top band,
   in our own palette. The delta is omitted entirely where there is no
   previous crawl: an invented 0% reads as "no progress" when the truth is
   "nothing to compare yet". */
/* auto-fit, not a media query. These cards live in the 1fr track beside a
   13.5rem menu, so a `min-width: 64rem` viewport query put four cards into a
   column barely wide enough for two - measured in the browser at an 800px
   window, where "Needs a second crawl to compare" wrapped over four lines. */
.kpis { display: grid; gap: .9rem; margin: 1.4rem 0 1rem;
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr)); }

.kpi {
  display: flex; align-items: center; gap: .7rem; padding: .95rem 1rem; min-width: 0;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.kpi__icon {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 9px; font-size: 1rem; font-weight: 700; color: var(--bg);
}
.kpi__icon--critical { background: var(--critical); }
.kpi__icon--warning  { background: var(--warning); }
.kpi__icon--ok       { background: var(--ok); }
.kpi__icon--info     { background: var(--info); }
.kpi__label { margin: 0; font-size: .7rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); }
.kpi__value { margin: .1rem 0 0; font-size: 1.5rem; line-height: 1.1; font-weight: 600;
  font-variant-numeric: tabular-nums; font-family: var(--display); font-style: oblique; }
.kpi__delta { font-size: .78rem; font-family: var(--font); font-style: normal; margin-left: .3rem; }
.kpi__delta--better { color: var(--ok); }
.kpi__delta--worse  { color: var(--critical); }
.kpi__delta--flat   { color: var(--muted); }
.kpi__note { margin: .1rem 0 0; font-size: .74rem; }

/* The dashboard greeting band. Deliberately its own class: the landing
   page's banner (.stage) is centred, and reusing that silently centred this
   whole card. (Who is reading, what to do, and the gauge beside it.) */
.dashhero { display: grid; gap: .9rem; margin: 0 0 1.6rem;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
/* The gauge should stay the narrower of the two when they do sit side by side. */
@media (min-width: 70rem) { .dashhero { grid-template-columns: 1.9fr 1fr; } }

.dashhero__card, .dashhero__gauge {
  text-align: left;
  padding: 1.2rem 1.3rem; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface);
}
/* A single soft glow from the brand hue, as in the reference - carried by the
   background only, never by text, so nothing here depends on it to be read. */
.dashhero__card {
  border-color: var(--border-brand);
  background:
    radial-gradient(60% 120% at 88% 15%, color-mix(in srgb, var(--brand) 26%, transparent), transparent 70%),
    linear-gradient(180deg, var(--surface), var(--bg));
}
.dashhero__hi { margin: 0; font-size: .8rem; }
.dashhero__name { margin: .1rem 0 .7rem; font-size: 1.75rem; line-height: 1.1;
  font-family: var(--display); font-style: oblique; }
.dashhero__lede { margin: 0; font-size: .95rem; max-width: 42ch; }
.dashhero__lede strong { color: var(--ink); }
.dashhero__since { margin: 1rem 0 .35rem; font-size: .76rem; }
.dashhero__insights { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: .4rem; }
.dashhero__insights li {
  font-size: .8rem; padding: .18rem .55rem; border-radius: 999px;
  border: 1px solid var(--border); background: rgba(0, 0, 0, .25);
}
.dashhero__insights li.is-better { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, transparent); }
.dashhero__insights li.is-worse  { color: var(--critical); border-color: color-mix(in srgb, var(--critical) 40%, transparent); }
.dashhero__actions { display: inline-flex; gap: .5rem; margin-top: 1.1rem; }
.dashhero__actions .btn { text-decoration: none; }

.dashhero__gauge { display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .45rem; text-align: center; }
.dashhero__gauge .btn { text-decoration: none; }

.tiles { display: grid; gap: .9rem; margin: 0 0 1.6rem;
  grid-template-columns: repeat(auto-fit, minmax(13.5rem, 1fr)); }

.tile {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: .3rem; padding: 1.1rem 1rem;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.tile--setup { gap: .45rem; }
.tile__name { margin: .1rem 0 0; font-size: .78rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.tile__figure { margin: .2rem 0 0; font-size: 2.4rem; line-height: 1; font-weight: 600;
  font-variant-numeric: tabular-nums; font-family: var(--display); font-style: oblique; }
.tile__figure--critical { color: var(--critical); }
.tile__figure--warning  { color: var(--warning); }
.tile__figure--ok       { color: var(--ok); }
.tile__figure--none     { color: var(--muted); }
.tile__note, .tile__next { margin: 0; font-size: .76rem; line-height: 1.4; }
.tile__link { margin-top: .35rem; font-size: .82rem; }
.tile--figure { justify-content: space-between; }
.tile .btn { margin-top: .2rem; text-decoration: none; }

.dash-split { display: grid; gap: 1rem; margin-bottom: 2rem; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); }
@media (min-width: 72rem) { .dash-split { grid-template-columns: 1.6fr 1fr; } }

.panel--wide, .panel--rail {
  margin: 0; padding: 1.1rem 1.2rem;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
}
.panel__head { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
.panel__head h2 { margin: 0 0 .9rem; font-size: 1rem; }
.panel__link, .panel__more { font-size: .85rem; }
.panel__more { margin: .8rem 0 0; }
.panel__pct { font-size: .85rem; color: var(--brand-text); font-variant-numeric: tabular-nums; }
.panel__sub { margin: 1.4rem 0 .6rem; font-size: 1rem; }
.panel__empty { text-align: center; padding: 1.4rem .5rem; }
.panel__empty p { margin: 0 0 .5rem; }
.panel__empty .btn { margin-top: .6rem; display: inline-block; text-decoration: none; }

.scoreboard { list-style: none; margin: 0; padding: 0; display: grid; gap: .2rem; }
.scoreboard__row {
  display: flex; align-items: center; gap: .9rem;
  padding: .6rem 0; border-top: 1px solid var(--border);
}
.scoreboard__row:first-child { border-top: 0; }
.scoreboard__who { flex: 1 1 auto; min-width: 0; }
.scoreboard__host { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
.scoreboard__who p.muted { margin: .1rem 0 0; font-size: .8rem; }
.scoreboard__delta { margin: 0; flex: none; font-size: .9rem; font-variant-numeric: tabular-nums; text-align: right; }
.scoreboard__delta span { display: block; font-size: .7rem; }
.scoreboard__delta.is-better { color: var(--ok); }
.scoreboard__delta.is-worse { color: var(--critical); }
.scoreboard__delta.is-flat { color: var(--muted); }
/* The ring inside a row is decoration beside the figures, not a caption. */
.scoreboard__row .ring__cap { display: none; }

.checklist { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; counter-reset: step; }
.checklist__item { display: flex; align-items: flex-start; gap: .6rem; font-size: .9rem; }
.checklist__tick {
  flex: none; display: grid; place-items: center; width: 18px; height: 18px; margin-top: .15rem;
  border: 1px solid var(--border); border-radius: 50%; font-size: .7rem; color: var(--bg);
}
.checklist__item.is-done .checklist__tick { background: var(--ok); border-color: var(--ok); }
.checklist__item.is-done .checklist__label { color: var(--muted); text-decoration: line-through; }
.checklist__label { display: block; }
.checklist__note { display: block; font-size: .76rem; }

.learn { list-style: none; margin: 0; padding: 0; display: grid; gap: .6rem; font-size: .9rem; }
.learn li { display: grid; gap: .1rem; }
.learn span { font-size: .76rem; }

@media (max-width: 40rem) {
  .assist { flex-direction: column; align-items: flex-start; }
  .tile__figure { font-size: 2rem; }
}

/* ─── Analytics consent banner ──────────────────────────────────────────────
   Fixed to the bottom, not an overlay. It must not hold the page hostage
   until somebody answers: a modal that blocks the content is one of the
   patterns regulators have specifically said is not consent.

   The two buttons are deliberately IDENTICAL. A bright "Accept" beside a
   grey "Decline" is a dark pattern with case law attached - refusing has to
   be as easy as agreeing, or the agreement is not freely given. */
.consent {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem 1.5rem; flex-wrap: wrap;
  padding: .9rem 1.2rem;
  padding-bottom: calc(.9rem + env(safe-area-inset-bottom, 0px));
  background: var(--bg-deep); border-top: 1px solid var(--border-brand);
  box-shadow: 0 -8px 24px rgba(0, 0, 0, .45);
}
.consent__text { flex: 1 1 24rem; min-width: 0; }
.consent__lede { margin: 0; font-weight: 600; font-size: .95rem; }
.consent__detail { margin: .2rem 0 0; font-size: .82rem; color: var(--muted); max-width: 62ch; }
.consent__choices { display: flex; gap: .6rem; flex: none; }
/* button_to wraps each button in its own form, which would otherwise stack. */
.consent__choices form { margin: 0; }
.consent__btn { white-space: nowrap; }

/* An inline form inside a sentence, for "you can change your answer". */
.inline-form { display: inline; margin: 0; }

@media (max-width: 34rem) {
  .consent { flex-direction: column; align-items: stretch; }
  .consent__choices { justify-content: stretch; }
  .consent__choices form, .consent__btn { flex: 1 1 auto; width: 100%; }
}

/* The plugin key, shown once. Wraps rather than overflows: it is ~50
   characters and the panel is narrower than that on a phone. */
.key-reveal {
  display: inline-block; margin-top: .35rem; padding: .35rem .6rem;
  font-family: var(--mono); font-size: .85rem; overflow-wrap: anywhere;
  background: var(--bg); border: 1px solid var(--border-brand); border-radius: 6px;
}

/* ─── Audit scope: whole site / one section / this page ─────────────────── */
/* Spans the row. .opt-checks is three equal columns sized for the switches;
   dropping a three-option group into one of them squeezed it to a third of
   the width and wrapped "This page only" onto its own line. */
.opt--scope { grid-column: 1 / -1; display: flex; align-items: center; gap: .9rem; flex-wrap: wrap; }
.opt__legend { font-size: .8rem; font-weight: 600; color: var(--muted); }
.opt-radio { display: inline-flex; align-items: center; gap: .35rem; cursor: pointer; }
.opt-radio input { margin: 0; }
.opt-radio span { font-size: .9rem; }
.opt-radio strong { font-weight: 500; }
.opt-radio input:checked + span strong { font-weight: 650; color: var(--brand-text); }

.opt--section { grid-column: 1 / -1; display: grid; gap: .25rem; }
.opt--section select { max-width: 22rem; }
/* hidden must win over the grid display, or the field shows regardless. */
.opt--section[hidden] { display: none; }

/* The paid half of the features page. Marked, not hidden: somebody deciding
   whether to sign up should be able to see where free ends before they do,
   not discover it afterwards. */
.features-split { margin: 3rem 0 .4rem; }
/* Free is the thing worth marking. Inverted 10 Oct on the owner's
   instruction: tagging half the product "Paid" taught visitors a word for
   what they could not have, and at launch the paid data cannot be bought at
   all. So the free work wears a badge and everything else wears nothing -
   the absence is the signal, and it stops reading as a wall. */


/* Features page, 4th build. The first three failed for reasons worth keeping:
   full-width report panels read as the report itself; window chrome fixed that
   but still asked a reader to study a whole report; rows fixed that but every
   screenshot was of a site scoring 100/100, so the page showed a tool finding
   nothing. The fix was the evidence, not the layout.

   What the research changed here: a tinted tray behind each shot and a width
   under 100% is what makes an image read as an object (Fathom, Sitebulb) -
   browser chrome is decoration that asserts what the reader already knows.
   Captions are back on every shot, because a cropped screenshot without one
   is the Baymard abandonment case, and because Mayer's spatial-contiguity
   effect is the strongest-measured result in this literature. */

/* Scale, measured against the reference the owner kept pointing at rather
   than guessed. Searchable's feature page at 1440px: h1 72px, weight 400,
   line-height 0.95, letter-spacing -3.6px (-0.05em), a hero section 1314px
   tall, and a 1336px content measure. Ours was a 40px headline in a 992px
   column with a 400px fold - the structure was already right and the page
   still read as a cramped document rather than a product page, because
   everything on it was roughly half size. */

/* This page breaks out of .wrap's 62rem measure. Only above 70rem, so the
   narrow layout keeps the shared gutter rather than inventing its own. */
@media (min-width: 70rem) {
  .features {
    width: 76rem;
    margin-left: calc(50% - 38rem);
    margin-right: calc(50% - 38rem);
  }
}

/* ---- The fold: claim, working input, one real finding cut by the edge ---- */
/* Explanation left, report right. The stacked version put the claim on top
   and gave the full width to the product, which looked right but introduced
   nothing: a reader met a screenshot before being told what it was. The left
   column now answers "what is this and how did you get it" before the eye
   lands on the picture. */
.fold {
  display: grid;
  grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
  gap: 1.4rem 2.6rem;
  align-items: center;
  margin: 1.6rem 0 2.4rem;
}
.fold h1 {
  font-size: clamp(2rem, 3.8vw, 3.1rem);
  line-height: 1.02;
  letter-spacing: -.03em;
  margin: 0 0 .9rem;
  max-width: 17ch;
}
.fold__sub { color: var(--muted); line-height: 1.5; margin: 0 0 1rem; max-width: 30em; font-size: .95rem; }
.fold__sub strong { color: var(--ink); }
.fold__form { margin: 0 0 .8rem; max-width: 30rem; }
.fold__form .url-input { padding: .85rem 1rem; font-size: 1rem; }
.fold__form .btn { padding: .85rem 1.5rem; font-size: 1rem; }
/* How the report was produced, stated before the picture of it. */
.fold__how {
  margin: 0 0 .9rem; padding-left: 1.05rem;
  display: grid; gap: .4rem;
  font-size: .88rem; line-height: 1.45; color: var(--muted);
}
.fold__how strong { color: var(--ink); }
.fold__trust { margin: 0; font-size: .82rem; color: var(--muted); }

/* The hero shot runs the full measure and is clipped, so the report visibly
   continues past the fold. The fade is the signal that it is cut rather than
   finished - without it the crop just looks like a short image. */
.fold__shot { margin: 0; min-width: 0; }
.tray--hero { position: relative; max-height: 17rem; overflow: hidden; padding: .9rem .9rem 0; }
.tray--hero .tray__img { border-radius: 8px 8px 0 0; border-bottom: 0; }
.tray--hero::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 7rem;
  pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--bg) 92%);
}


/* ---- Feature rows: one claim, the picture of that claim beside it ---- */
.band-head { margin: 2.8rem 0 .5rem; font-size: clamp(1.4rem, 2.4vw, 2rem); letter-spacing: -.02em; }
.band-sub { color: var(--muted); max-width: 44em; margin: 0 0 .5rem; line-height: 1.6; }
.band-sub--lead { margin: 0 0 1rem; font-size: 1rem; }

/* Every feature is a named, collapsible section. The owner's note, 11 Oct:
   "Most features are just explaining the features like a dialogue but nobody
   knows - one or 2 words definition of these features." Correct: the headings
   were arguments, not labels, so a reader could not learn what anything was
   CALLED or scan the list.

   The summary carries the name, the Free badge and a one-line definition, so
   the page reads as an index of twelve named features even fully collapsed -
   the definition is deliberately in the summary rather than inside, because
   hiding "what is this" behind a click is the fault being fixed. */
.frow {
  margin: .9rem 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: rgba(0, 0, 0, .18);
}
.frow[open] { background: rgba(0, 0, 0, .28); border-color: var(--border-brand); }

.frow__summary {
  display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap;
  padding: .95rem 1.1rem;
  cursor: pointer;
  list-style: none;
}
.frow__summary::-webkit-details-marker { display: none; }
/* The twisty is drawn rather than defaulted, so it sits where the eye is. */
.frow__summary::after {
  content: "+";
  margin-left: auto;
  font-size: 1.1rem; line-height: 1; color: var(--muted);
  transition: transform .15s ease;
}
.frow[open] .frow__summary::after { content: "−"; }
.frow__summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.frow__summary:hover .frow__name { color: var(--brand-text); }

.frow__name {
  margin: 0;
  font-weight: 650; font-size: 1.05rem; letter-spacing: -.01em; color: var(--ink);
}
.frow__def { color: var(--muted); font-size: .9rem; line-height: 1.4; }

.frow__body {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  gap: 1.4rem 2.6rem;
  align-items: center;
  padding: .2rem 1.1rem 1.3rem;
  border-top: 1px solid var(--border);
}
.frow__claim { font-size: 1rem; margin: 1rem 0 .5rem; }

/* The check categories have no screenshot - they are lists of what we test,
   and there is no honest single picture of "we check hreflang". One column. */
.frow--plain .frow__body { grid-template-columns: 1fr; }
.frow__text h2, .frow__text h3 {
  margin: 0 0 .6rem;
  font-size: clamp(1.15rem, 1.9vw, 1.55rem);
  line-height: 1.15; letter-spacing: -.02em;
}
.frow__text > p { margin: 0 0 .9rem; color: var(--muted); line-height: 1.55; font-size: 1rem; }
.frow__text ul { margin: 0; padding-left: 1.05rem; display: grid; gap: .5rem; font-size: .92rem; }
.frow__text li { line-height: 1.45; color: var(--muted); }
.frow__text li strong { color: var(--ink); }

.frow__shot, .fold__shot { margin: 0; min-width: 0; }

/* The tray is what separates picture from page: a different surface value,
   padding so the shot never touches the page, and a shadow to lift it. */
.tray {
  padding: 1rem;
  border-radius: 12px;
  background:
    radial-gradient(120% 120% at 50% 0%, rgba(128, 98, 214, .14), transparent 70%),
    rgba(255, 255, 255, .035);
  border: 1px solid var(--border);
}
.tray__img {
  display: block; width: 100%; height: auto;
  border: 1px solid var(--frame);
  border-radius: 7px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, .45);
}
.frow__shot figcaption, .fold__shot figcaption {
  margin-top: .75rem; font-size: .82rem; line-height: 1.5; color: var(--muted);
}

/* ---- Where free stops ---------------------------------------------------- */
.splitline { width: 100%; margin-top: .9rem; }
.splitline th[scope="row"] { font-weight: 450; }
.splitline td { text-align: center; width: 9rem; }
.splitline .yes { color: var(--ok); font-weight: 650; }
.splitline .no { color: var(--muted); }
.splitline .maybe { color: var(--brand-text); font-size: .85rem; }
.splitline__note {
  margin: .7rem 0 0; font-size: .85rem; color: var(--muted);
  max-width: 46em; line-height: 1.6;
}

@media (max-width: 52rem) {
  .fold, .frow__body { grid-template-columns: 1fr; gap: 1rem; }
  .fold { margin-bottom: 2rem; }
  .splitline td { width: auto; }
}
/* A dense table scaled to a phone is a grey smear, so the tray scrolls
   sideways and the image keeps a width its own text survives. Focusable,
   because a scroll container no keyboard can reach fails WCAG 2.1.1 - the
   previous build shipped a :focus-visible rule with nothing to focus. */
@media (max-width: 40rem) {
  .tray { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tray__img { min-width: 30rem; }
  .tray:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
}

/* Three groups inside the free half: the cards answer three different
   questions and used to sit in one flat grid that made them look alike. */
.feature-group { margin: 1.8rem 0 0; }
.feature-group__title {
  margin: 0; font-size: .78rem; font-weight: 650;
  letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
}
.feature-group .feature-grid { margin-top: .8rem; }

/* Free and paid both get a word, not just paid. Before this the free half was
   unlabelled, so a reader only learned it was free on reaching the paid one. */
/* The badge sits on every free sample, not only on the band that introduces
   one. The owner's note: "why free is mentioned at 1 or 2 or 3 places - it
   should be mentioned in each and every report sample which is completely
   free." A reader who lands mid-page, which is how most of them arrive from
   search, never sees the band heading above. */
.frow__name--free::after,
.band-head--free::after {
  content: "Free";
  margin-left: .65rem;
  font-size: .6rem; font-weight: 650; letter-spacing: .07em; text-transform: uppercase;
  vertical-align: .35em;
  padding: .18rem .5rem; border-radius: 4px;
  color: var(--ok); border: 1px solid var(--ok);
}
.frow__name--free::after {
  font-size: .55rem; padding: .14rem .4rem; margin-left: .55rem; vertical-align: .3em;
}
/* Cards inside a Free band do not repeat the badge - the band already said
   it, and six of them in a grid is noise rather than information. */

/* ─── Glossary ──────────────────────────────────────────────────────────── */
.glossary__group { margin: 2.2rem 0; }
.glossary__group h2 { margin: 0 0 .6rem; font-size: 1.05rem; }
.glossary__table { width: 100%; }
/* The term column is the thing being looked up, so it reads as a label
   rather than as another sentence. */
/* .glossary__table .glossary__term, not .glossary__term alone: `.table th`
   is (0,1,1) and a lone class is (0,1,0), so the bare selector lost the
   cascade and the terms kept rendering in caps even after being told not
   to. Two classes beat it outright rather than relying on source order. */
.glossary__table .glossary__term {
  width: 12rem; white-space: nowrap; vertical-align: top;
  font-weight: 600; color: var(--brand-text); text-align: left;
  /* .table th uppercases and shrinks column headers, which is right for a
     column header and wrong for the word being defined - "hreflang" and
     "noindex" are written lower case on purpose, and HREFLANG is the same
     unreadable all-caps the headings were just taken out of. th is still
     correct markup here: the term labels its row, so a screen reader
     announces it before reading the definition. */
  text-transform: none;
  font-size: 1rem;
  letter-spacing: normal;
}
.glossary__table td { vertical-align: top; }
/* A linked term should be findable after the jump, not flush against the
   header that covers it. */
.glossary__table tr:target th { color: var(--ink); }
.glossary__table tr:target { background: color-mix(in srgb, var(--brand) 12%, transparent); }
.glossary__jump { display: flex; flex-wrap: wrap; gap: .4rem .9rem; margin: 1rem 0 0; font-size: .9rem; }

@media (max-width: 34rem) {
  /* Two columns of prose in 375px is four words a line. Stacked, the term
     becomes a heading for the definition under it, which is what it is. */
  .glossary__table, .glossary__table tbody, .glossary__table tr { display: block; }
  .glossary__table thead { display: none; }
  .glossary__table tr { padding: .7rem 0; border-bottom: 1px solid var(--border); }
  .glossary__term { display: block; width: auto; white-space: normal; padding: 0 0 .2rem; }
  .glossary__table td { display: block; padding: 0; }
}

/* Said on the report, not only in the policy: a visitor who bookmarks an
   unclaimed report should know it has an end date before they rely on it. */
.report-expiry {
  margin: .8rem 0 0; padding: .6rem .9rem;
  border: 1px solid var(--border-brand); border-radius: var(--radius);
  background: rgba(0, 0, 0, .22);
  font-size: .88rem; color: var(--muted);
}
.report-expiry strong { color: var(--ink); }

/* The seed form: a word, a mode, a button, on one line where there is room. */
.seed-form { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; margin: .6rem 0; }
.seed-form .url-input { flex: 1 1 18rem; min-width: 0; }
.seed-form select { flex: 0 0 auto; }
.section-sub { font-size: .95rem; margin: 1.4rem 0 .5rem; }
/* Two URL fields need the room; one line each below a tablet. */
.seed-form--pages .url-input { flex: 1 1 15rem; }

/* The glossary's opening definition. Set apart from the category tables
   because it answers a different question: not "what does this word mean"
   but "what is this page about". */
.glossary__lead {
  margin: 1.6rem 0 2rem;
  padding: 1.2rem 1.4rem;
  border: 1px solid var(--border-brand);
  border-radius: var(--radius);
  background:
    radial-gradient(70% 140% at 92% 10%, color-mix(in srgb, var(--brand) 20%, transparent), transparent 70%),
    linear-gradient(180deg, var(--surface), var(--bg));
}
.glossary__lead h2 { margin: 0; font-size: 1.05rem; }
.glossary__lead-expansion {
  margin: .2rem 0 .7rem;
  font-size: 1.5rem; line-height: 1.2; font-weight: 650; letter-spacing: -.02em;
  color: var(--brand-text);
}
.glossary__lead p:last-child { margin: 0; max-width: 68ch; }

/* --- Areas covered --------------------------------------------------------
   What an audit looked at, area by area. Four outcomes, four marks, and the
   mark is never colour alone: each row also says its outcome in words. */
.areas__lede { margin: 0 0 1rem; color: var(--muted); max-width: 46rem; }
.areas__lede strong { color: var(--ink); }
.area { border-top: 1px solid var(--border); }
.area:last-of-type { border-bottom: 1px solid var(--border); }
.area__summary {
  /* Fixed tracks, not "auto": every area is its own grid, and a track sized
     to its content put "Nothing found" at a different place on every row. */
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr) 10.5rem;
  gap: .15rem 1rem; align-items: baseline; padding: .7rem .2rem .7rem 1.4rem;
  cursor: pointer; list-style: none; position: relative;
}
.area__summary::-webkit-details-marker { display: none; }
.area__summary::before {
  content: ""; position: absolute; left: .2rem; top: 1.05rem; width: .4rem; height: .4rem;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: rotate(-45deg); transition: transform .12s ease;
}
.area[open] > .area__summary::before { transform: rotate(45deg); }
.area__summary:hover { background: color-mix(in srgb, var(--surface-2) 35%, transparent); }
.area__name { font-weight: 600; }
.area__state { font-size: .9rem; }
.area__count { font-size: .85rem; color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; }
.area__about { grid-column: 1 / -1; font-size: .85rem; color: var(--muted); }
.area--flagged .area__state { color: var(--warning); font-weight: 600; }
.area--clear .area__state { color: var(--ok); }
.area--not_applicable .area__state, .area--not_run .area__state { color: var(--muted); }
.area__checks { list-style: none; margin: 0; padding: .2rem 0 1rem 1.4rem; }
.area__check {
  display: grid; grid-template-columns: 1rem minmax(0, 1fr) auto; gap: .1rem .6rem;
  align-items: baseline; padding: .45rem 0; border-top: 1px dotted var(--border); font-size: .92rem;
}
.area__mark { width: .6rem; height: .6rem; border-radius: 50%; align-self: center; background: var(--muted); }
.area__check--flagged .area__mark { background: var(--warning); border-radius: 2px; }
.area__check--clear .area__mark { background: var(--ok); }
.area__check--not_applicable .area__mark, .area__check--not_run .area__mark { background: transparent; border: 1.5px solid var(--muted); }
.area__outcome { font-size: .82rem; color: var(--muted); text-align: right; }
.area__check--flagged .area__outcome { color: var(--warning); font-weight: 600; }
.area__tests { grid-column: 2 / -1; margin: .15rem 0 0; padding-left: 1.1rem; color: var(--muted); font-size: .85rem; }
.area__tests li { padding: .05rem 0; }
@media (max-width: 640px) {
  .area__summary { grid-template-columns: 1fr; }
  .area__count { text-align: left; }
  .area__check { grid-template-columns: 1rem 1fr; }
  .area__outcome { grid-column: 2; text-align: left; }
}
@media print {
  .area > .area__checks { display: block; }
  .area__summary::before { display: none; }
}

/* Where a check's authority comes from, under the finding it belongs to. */
.basis { margin: .45rem 0 0; font-size: .82rem; color: var(--muted); }
.basis a { color: var(--accent); }

/* The blog ---------------------------------------------------------------
   Rebuilt 23 October 2026 as a hub of cards, from the two pages the owner
   pointed to. Topic cards first, then each topic's articles as cards with the
   guide's own screenshot as the picture. Built from the same tokens as the
   home page's service cards so the two read as one site. */
.blog { max-width: 70rem; margin: 0 auto; }
.blog [hidden] { display: none !important; }
.blog__crumbs { margin: 0 0 1.2rem; font-size: .85rem; color: var(--muted); }
.blog__crumbs a { color: var(--muted); }
.blog__head { max-width: 48rem; margin-bottom: 2.4rem; }
.blog__eyebrow {
  margin: 0 0 .4rem; font-size: .78rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--brand-text); font-weight: 600;
}
.blog h1 { margin: 0 0 .9rem; }
.blog__lede { margin: 0 0 1.4rem; color: var(--muted); font-size: 1.04rem; line-height: 1.7; }
.blog__search { display: grid; gap: .35rem; max-width: 30rem; }
.blog__search label { font-size: .82rem; color: var(--muted); }
.blog__search input {
  width: 100%; padding: .7rem .9rem; border-radius: var(--radius); font: inherit; font-size: .96rem;
  color: var(--ink); background: var(--surface); border: 1px solid var(--border);
}
.blog__search input:focus-visible { outline: 2px solid var(--brand-text); outline-offset: 2px; }

.blog__label, .blog__stage h2, .blog__band h2 {
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: -.01em;
}
.blog__label { margin: 0 0 .9rem; font-size: 1.15rem; color: var(--ink); }

.blog__cats { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.blog__cat {
  display: flex; flex-direction: column; gap: .45rem; padding: 1.3rem 1.3rem 1.2rem;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  color: var(--ink); text-decoration: none;
  transition: border-color .15s, transform .15s, background .15s;
}
.blog__cat:hover { border-color: var(--border-brand); background: var(--surface-2); transform: translateY(-2px); }
.blog__cat-icon {
  display: grid; place-items: center; width: 2.8rem; height: 2.8rem; margin-bottom: .3rem;
  border-radius: 50%; color: var(--brand-text); background: rgba(128, 98, 214, .16);
}
.blog__cat-name { font-size: 1.15rem; font-weight: 650; }
.blog__cat-blurb { flex: 1; color: var(--muted); font-size: .92rem; line-height: 1.55; }
.blog__cat-count { margin-top: .4rem; font-size: .88rem; color: var(--brand-text); font-weight: 600; }

.blog__stage { margin-top: 3rem; scroll-margin-top: 1.5rem; }
.blog__stage h2 { margin: 0 0 .35rem; font-size: 1.45rem; color: var(--ink); }
.blog__stage-blurb { margin: 0 0 1.2rem; color: var(--muted); max-width: 46rem; }

.blog__grid { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fill, minmax(16.5rem, 1fr)); }
.blog__card {
  display: flex; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); overflow: hidden; scroll-margin-top: 1.5rem;
  transition: border-color .15s, transform .15s;
}
.blog__card:hover, .blog__card:focus-within { border-color: var(--border-brand); transform: translateY(-2px); }
.blog__card-link { display: flex; flex-direction: column; width: 100%; color: inherit; text-decoration: none; }
.blog__card-link:focus-visible { outline: 2px solid var(--brand-text); outline-offset: -2px; }
.blog__thumb {
  /* height: auto, because the tag carries height="360" so the browser can
     reserve the space before the picture arrives - and an attribute beats an
     aspect-ratio unless the stylesheet says otherwise. */
  display: block; box-sizing: border-box; width: 100%; height: auto; aspect-ratio: 16 / 9;
  object-fit: cover; object-position: top left;
  background: var(--bg-deep); border-bottom: 1px solid var(--border);
}
.blog__thumb--whole { object-fit: contain; object-position: center; padding: .6rem; background: var(--bg-deep); }
.blog__thumb--blank { display: grid; place-items: center; color: var(--brand-text); }
.blog__thumb--blank .blog__icon { width: 2.6rem; height: 2.6rem; opacity: .7; }
.blog__card-body { display: flex; flex-direction: column; flex: 1; padding: 1rem 1.1rem 1.1rem; }
.blog__card-title {
  margin: 0 0 .5rem; font-size: 1.05rem; line-height: 1.35; color: var(--brand-text);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}
.blog__card-link:hover .blog__card-title { text-decoration: underline; text-underline-offset: 3px; }
.blog__card-excerpt { margin: 0 0 .9rem; flex: 1; color: var(--muted); font-size: .9rem; line-height: 1.55; }
.blog__card-meta { margin: 0; display: flex; align-items: center; gap: .6rem; font-size: .8rem; color: var(--muted); }
.blog__free {
  padding: .1rem .55rem; border: 1px solid var(--ok); border-radius: 99px; color: var(--ok);
  font-weight: 600; letter-spacing: .02em;
}

.blog__none { margin: 2rem 0 0; color: var(--muted); }

.blog__band {
  display: flex; align-items: center; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap;
  margin-top: 3rem; padding: 1.4rem 1.6rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface);
}
.blog__band + .blog__band { margin-top: 1rem; }
.blog__band h2 { margin: 0 0 .3rem; font-size: 1.15rem; color: var(--ink); }
.blog__band p { margin: 0; max-width: 40rem; color: var(--muted); font-size: .95rem; line-height: 1.6; }
.blog__band--cta { border-left: 3px solid var(--brand); }
.blog .panel { margin-top: 3rem; }

@media (prefers-reduced-motion: reduce) {
  .blog__cat, .blog__card { transition: none; }
  .blog__cat:hover, .blog__card:hover, .blog__card:focus-within { transform: none; }
}
@media (max-width: 40rem) {
  .blog__band { padding: 1.2rem; }
}

/* The free tools -----------------------------------------------------------
   Built 23 October 2026. Shares the blog's cards and bands so /tools and /blog
   read as one site rather than two. Only what is particular to a tool lives
   here: the form, the facts strip, the notes list and the raw-file block. */
.tool { max-width: 56rem; margin: 0 auto; }
.tool__head { margin-bottom: 1.8rem; }
.tool h1 { margin: 0 0 .8rem; }
.tool__lede { margin: 0; color: var(--muted); font-size: 1.04rem; line-height: 1.7; max-width: 44rem; }

.tool__form { margin: 0 0 2rem; padding: 1.2rem 1.3rem; border: 1px solid var(--border);
  border-left: 3px solid var(--brand); border-radius: var(--radius); background: var(--surface); }
.tool__form label { display: block; margin-bottom: .45rem; font-size: .85rem; color: var(--muted); }
.tool__row { display: flex; gap: .6rem; flex-wrap: wrap; }
.tool__row .url-input { flex: 1 1 18rem; min-width: 0; }
.tool__hint { margin: .6rem 0 0; font-size: .82rem; }

.tool__error {
  margin: 0 0 2rem; padding: .9rem 1.1rem; border-radius: var(--radius);
  border: 1px solid var(--critical); color: var(--critical); background: rgba(242, 109, 91, .08);
}

.tool__result { margin-bottom: 2.6rem; }
.tool__result-title {
  margin: 0 0 1rem; font-size: 1.2rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}
.tool__host { color: var(--brand-text); overflow-wrap: anywhere; }
.tool__sub {
  margin: 1.8rem 0 .7rem; font-size: 1rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}

.notes { list-style: none; margin: 0 0 1.6rem; padding: 0; display: grid; gap: .55rem; }
.notes__item {
  display: grid; grid-template-columns: 1.5rem 1fr; gap: .6rem; align-items: start;
  padding: .7rem .9rem; border-radius: var(--radius); background: var(--surface);
  border: 1px solid var(--border); font-size: .93rem; line-height: 1.55;
}
.notes__mark {
  display: grid; place-items: center; width: 1.5rem; height: 1.5rem; border-radius: 50%;
  font-size: .78rem; font-weight: 700; background: rgba(166, 157, 196, .18); color: var(--muted);
}
.notes__item--bad { border-color: rgba(242, 109, 91, .45); }
.notes__item--bad .notes__mark { background: rgba(242, 109, 91, .18); color: var(--critical); }
.notes__item--warn { border-color: rgba(255, 209, 102, .4); }
.notes__item--warn .notes__mark { background: rgba(255, 209, 102, .16); color: var(--warning); }
.notes__item--ok .notes__mark { background: rgba(111, 211, 155, .16); color: var(--ok); }
.notes__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.tool__facts { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); margin-bottom: 1.2rem; }
.tool__fact { padding: .8rem 1rem; border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); }
.tool__fact-label { margin: 0 0 .2rem; font-size: .76rem; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.tool__fact-value { margin: 0; font-size: 1.25rem; color: var(--ink); font-weight: 600; }

.tool__yes { color: var(--ok); }
.tool__no { color: var(--critical); font-weight: 600; }
.tool__list { margin: 0 0 1rem; padding-left: 1.2rem; display: grid; gap: .3rem; font-size: .88rem; }
.tool__list code, .tool__facts code { overflow-wrap: anywhere; }
.tool__pre {
  margin: 0; padding: .9rem 1rem; max-height: 22rem; overflow: auto; font-size: .82rem;
  background: var(--bg-deep); border: 1px solid var(--border); border-radius: var(--radius);
  white-space: pre-wrap; overflow-wrap: anywhere; color: var(--muted);
}
.tool__note { margin: .8rem 0 0; font-size: .85rem; }
.tool__cta { margin-top: 1.8rem; }

.tool__about { margin-top: 2.6rem; }
.tool__about h2 {
  margin: 2rem 0 .6rem; font-size: 1.2rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: -.01em;
}
.tool__about h2:first-child { margin-top: 0; }
.tool__about p { color: var(--muted); line-height: 1.7; }
.tool__points { margin: 0 0 1rem; padding-left: 1.2rem; display: grid; gap: .5rem; color: var(--muted); line-height: 1.6; }
.tool__points strong { color: var(--ink); }
.tool__next { margin-top: 1.4rem; font-size: .93rem; }

/* The report example page --------------------------------------------------
   One real report, section by section. The screenshots are wide and detailed,
   so they get the full column and a hairline edge against the page. */
.sample__step { margin-top: 2.8rem; }
.sample__step h2 {
  margin: 0 0 .9rem; font-size: 1.25rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: -.01em;
}
.sample__shot { margin: 0 0 1rem; }
.sample__shot img {
  display: block; width: 100%; height: auto; border-radius: var(--radius);
  border: 1px solid var(--frame); background: var(--bg-deep);
}
.sample__step p { color: var(--muted); line-height: 1.7; }
.sample__step strong { color: var(--ink); }
.about__points { margin: 0 0 1.2rem; padding-left: 1.2rem; display: grid; gap: .55rem; color: var(--muted); line-height: 1.65; }
.about__points strong { color: var(--ink); }
.updates__codes { display: block; font-size: .76rem; overflow-wrap: anywhere; }
.updates__feed { list-style: none; margin: 1rem 0; padding: 0; display: grid; gap: .5rem; }
.updates__feed li {
  display: grid; grid-template-columns: 7rem 4rem 1fr; gap: .7rem; align-items: baseline;
  padding: .6rem .8rem; border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); font-size: .9rem;
}
.updates__date { color: var(--muted); font-size: .82rem; }
.updates__source {
  font-size: .7rem; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  color: var(--brand-text);
}
.updates__source--blog { color: var(--ok); }
@media (max-width: 40rem) {
  .updates__feed li { grid-template-columns: 1fr; gap: .2rem; }
}

/* "This list is not current" on /blog/google-updates. Loud on purpose: the
   page's whole argument is that stale advice is the enemy, so it may not be
   quietly stale itself. */
.updates__warn {
  margin: 1.2rem 0; padding: .9rem 1.1rem; border-radius: var(--radius);
  border: 1px solid var(--warning); border-left: 3px solid var(--warning);
  background: rgba(255, 209, 102, .08); color: var(--ink); line-height: 1.6;
}

/* The three cards on /tools -------------------------------------------------
   Rebuilt 23 October 2026. They were blog cards, which meant a 16:9 picture
   block holding a single magnifying glass - a lot of empty space saying
   nothing, and no obvious thing to press. The owner asked for a button
   instead. The whole card is still the link; the button is a styled span. */
.toolcards { display: grid; gap: 1.1rem; grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr)); }
.toolcard {
  display: flex; flex-direction: column; padding: 1.4rem 1.4rem 1.5rem;
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface);
  color: var(--ink); text-decoration: none;
  transition: border-color .15s, transform .15s, background .15s;
}
.toolcard:hover, .toolcard:focus-visible { border-color: var(--border-brand); background: var(--surface-2); transform: translateY(-2px); }
.toolcard:focus-visible { outline: 2px solid var(--brand-text); outline-offset: 2px; }
.toolcard__name {
  margin: 0 0 .55rem; font-size: 1.12rem; color: var(--brand-text);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: 0;
}
.toolcard:hover .toolcard__name { text-decoration: underline; text-underline-offset: 3px; }
.toolcard__what { margin: 0 0 1rem; flex: 1; color: var(--muted); font-size: .92rem; line-height: 1.6; }
.toolcard__meta { margin: 0 0 1.1rem; display: flex; align-items: center; gap: .6rem; font-size: .8rem; color: var(--muted); }
/* A span, not a control: it must not look clickable on its own when the whole
   card already is, so no separate hover state of its own. */
.toolcard__cta { align-self: flex-start; }
@media (prefers-reduced-motion: reduce) {
  .toolcard { transition: none; }
  .toolcard:hover, .toolcard:focus-visible { transform: none; }
}

/* /site-map ----------------------------------------------------------------
   Every public page on one screen. Two columns on a wide window, one on a
   phone; each entry is a link with a line under it saying what is behind it,
   because a column of bare link text is a column a reader has to click
   through to understand. */
.sitemap { max-width: 64rem; margin: 0 auto; }
.sitemap__jump { display: flex; flex-wrap: wrap; gap: .5rem; margin: 0 0 2.4rem; }
.sitemap__chip {
  font-size: .82rem; padding: .3rem .8rem; border-radius: 99px; text-decoration: none;
  border: 1px solid var(--border); background: var(--surface); color: var(--muted);
}
.sitemap__chip:hover { color: var(--ink); border-color: var(--border-brand); background: var(--surface-2); }

.sitemap__group { margin-top: 2.8rem; scroll-margin-top: 1.5rem; }
.sitemap__group h2 {
  margin: 0 0 .3rem; font-size: 1.3rem; color: var(--ink);
  font-family: var(--font); font-style: normal; text-transform: none; letter-spacing: -.01em;
}
.sitemap__blurb { margin: 0 0 1.2rem; color: var(--muted); font-size: .95rem; }

.sitemap__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 1rem 2.4rem; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.sitemap__item { padding-left: .9rem; border-left: 2px solid var(--border); }
.sitemap__item:hover { border-left-color: var(--brand); }
.sitemap__link { display: block; font-size: 1rem; font-weight: 600; margin-bottom: .2rem; }
.sitemap__note { display: block; color: var(--muted); font-size: .88rem; line-height: 1.55; }
.sitemap__note a { color: var(--muted); text-decoration: underline; text-underline-offset: 2px; }
