/* Perceivio design system v1. Tokens sampled from Logo/logo-icon.png. */
/* Standards named on this site verified against: W3C WAI (WCAG 2.2),
   EUR-Lex (EAA), ETSI (EN 301 549), ada.gov (ADA), access-board.gov (508). */

:root {
  /* Grounds and the two colours of type. */
  --paper: #ffffff;
  --tint: #eaf1fb;
  --tint-deep: #d9e6f8;
  --ink: #0b1a31;
  --muted: #3e4c63;
  /* Brand, in two roles. --brand is the non-text accent: focus rings and marks.
     --link is the colour of link text. They part company in the dark theme,
     where a blue dark enough for white paper cannot be read on ink. */
  --brand: #2875f5;
  --brand-ink: #174ea6;
  --link: var(--brand-ink);
  --accent: #0f766e;
  --line: #c3d3e8;
  --ok: #166534;
  --warn: #92400e;
  --err: #b42318;
  /* The navy band: section grounds, the footer, the skip link. In the dark
     theme the band steps up from the ground rather than down, so it stays a
     band, and every light-on-navy pair above holds its value. */
  --navy: #0b1a31;
  /* Filled controls: solid buttons, the pressed header toggles, step numbers.
     --on-fill is the type or glyph that sits on them. */
  --fill: var(--navy);
  --on-fill: #ffffff;
  --fill-hover: var(--brand-ink);
  /* Rating colours. They are filled surfaces, so they hold in both themes. */
  --sev-critical: #b42318;
  --sev-serious: #92400e;
  --sev-moderate: #174ea6;
  --sev-minor: #3e4c63;
  --sel-bg: var(--brand);
  --sel-ink: #ffffff;
  --radius: 14px;
  --font: "Public Sans", -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
  --measure: 68ch;
}

/* Dark theme: opt in from the header control, remember the choice, never
   assume it. Light is the site's own theme and stays the default, so nothing
   below applies until the reader asks for it. Every pair here is measured; the
   table lives in ACCESSIBILITY-CHECK.md. */
html[data-theme="dark"] {
  --paper: #061020;
  --tint: #10203a;
  --tint-deep: #16294a;
  --ink: #eef2f9;      /* 16.96:1 on --paper */
  --muted: #bcc9dd;    /* 11.37:1 on --paper, 9.72:1 on --tint */
  --line: #2d4670;
  --navy: #152846;
  --brand: #6ea8ff;    /* focus rings: 7.90:1 on --paper */
  --link: #9cc2ff;     /* 10.50:1 on --paper, 8.97:1 on --tint */
  --accent: #62d6c2;
  --ok: #8ee0a4;
  /* --err and --warn are read as type (the checker's verdicts), not only as
     fills, so they need a dark value: #b42318 on this ground is 2.9:1. */
  --err: #ff9c8f;      /* 9.47:1 on --paper */
  --warn: #f0b866;     /* 10.70:1 on --paper */
  --fill: #eef2f9;
  --on-fill: #06101e;
  --fill-hover: #cfdcf2;
  --sel-bg: #2b5aa0;
  --sel-ink: #ffffff;
  color-scheme: dark;
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  font-family: var(--font);
  font-size: 1.0625rem;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper);
}

h1, h2, h3 {
  line-height: 1.15;
  letter-spacing: -0.02em;
  text-wrap: balance;
  /* A long word in the display face, carrying the letter spacing a reader may
     ask for (WCAG 1.4.12), can be wider than a phone. Left alone the heading
     would push the whole page sideways, so it may break mid-word when there is
     no other way to fit. */
  overflow-wrap: anywhere;
}

h1 { font-size: clamp(2.5rem, 1.6rem + 4vw, 3.9rem); font-weight: 800; margin: 0 0 1rem; }
h2 { font-size: clamp(1.7rem, 1.3rem + 2vw, 2.4rem); font-weight: 800; margin: 0 0 0.75rem; }
h3 { font-size: 1.25rem; font-weight: 700; margin: 0 0 0.5rem; }
/* A section heading opens its own section, so the air above it is set here
   rather than left to whatever the block before it happened to leave. h2
   carries a bottom margin only, and both a list and a grid end flush, which
   left the next heading touching them: measured at 0 px above the grid on the
   free audit terms page and 2 px above a list there. 3rem is more than the 2rem
   that separates a heading from its own content, so the break between two
   sections is never the smallest gap on the page. Keyed on a preceding sibling,
   so a heading that already opens its block keeps the margin it had, and the
   blog's own post rhythm, which sets its own, still wins. */
* + h2 { margin-top: 3rem; }

h2 + p.lead, h1 + p.lead { font-size: 1.2rem; }

p { margin: 0 0 1rem; max-width: var(--measure); }
ul, ol { margin: 0 0 1rem; padding-left: 1.4rem; max-width: var(--measure); }
li { margin-bottom: 0.4rem; }
li::marker { color: var(--link); }

a { color: var(--link); text-underline-offset: 0.18em; }
a:hover { color: var(--ink); }

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

::selection { background: var(--sel-bg); color: var(--sel-ink); }

/* One width for every page. The running head, the sections and the foot all
   take it, so the head is the same width wherever the reader is: it used to
   follow the home page's own wider measure, so the same head visibly narrowed
   and widened as you moved between pages. The insets keep clear of a notch
   where the platform reports one, and are zero where it does not. */
.wrap {
  max-width: 78rem;
  margin-inline: auto;
  padding-left: max(clamp(1rem, 4vw, 2rem), env(safe-area-inset-left, 0px));
  padding-right: max(clamp(1rem, 4vw, 2rem), env(safe-area-inset-right, 0px));
}

.skip {
  position: absolute; left: 1rem; top: -4rem; z-index: 100;
  background: var(--navy); color: #fff; padding: 0.75rem 1.25rem;
  border-radius: 0 0 10px 10px; font-weight: 700; transition: top 0.15s;
}
.skip:focus { top: 0; color: #fff; }

/* ---------- Header ----------
   One header for every page: one set of words, one set of destinations, one
   set of controls. Only the relative paths change with the page's depth. */
.site-header {
  border-bottom: 1px solid var(--line);
  background: var(--paper);
  position: sticky; top: 0; z-index: 50;
}
/* The running head wraps rather than pushing its controls off the side, which
   matters most at 200% text size, where a single row would not fit. Its own row
   is given room to breathe: 1.25rem between the brand, the links, the two
   toggles and the call to action, and 0.85rem above and below them, so the
   controls read as separate things rather than a line of things touching. */
.header-in { display: flex; flex-wrap: wrap; align-items: center; gap: 1.25rem; padding-block: 0.85rem; }
/* The line along the foot of the running head is how far the reader has come
   through the page. It is one transform on a decorative element, so it costs no
   layout and moves on the compositor, and it belongs to the head rather than to
   one page: it used to exist on the home page alone, which was one more way the
   same head looked different from page to page. */
.prog { position: absolute; left: 0; right: 0; bottom: 2px; height: 4px; background: var(--brand); transform: scaleX(var(--p, 0)); transform-origin: 0 50%; }
.brand { display: flex; align-items: center; gap: 0.6rem; text-decoration: none; color: var(--ink); font-weight: 800; font-size: 1.3rem; margin-right: auto; }
.brand img { width: 2rem; height: 2rem; }
.site-nav ul { display: flex; flex-wrap: wrap; gap: 1.5rem; list-style: none; margin: 0; padding: 0; max-width: none; align-items: center; }
.site-nav .nav-pause, .site-nav .nav-theme, .site-nav .nav-cta { display: none; }
/* The call to action is the one thing in the row that must read whole, so it
   neither shrinks nor breaks to a second line inside the head. */
.header-cta { flex: none; }
.header-cta .btn { white-space: nowrap; }

/* ---------- Anchors and focus under a sticky head ----------
   The running head is sticky, so anything scrolled to or focused below has to
   keep clear of it (WCAG 2.4.11). Its height moves with the text size and the
   viewport, so site.js measures it and writes --hdr-offset. The rem fallback
   is what a browser without scripting gets: the folded head, brand, wordmark
   and one menu button, which no viewport makes taller than 5.5rem. */
html { scroll-padding-top: var(--hdr-offset, 5.5rem); }
section[id], div[id] { scroll-margin-top: var(--hdr-offset, 5.5rem); }
a, button, summary, [tabindex] { scroll-margin-top: var(--hdr-offset, 5.5rem); }
/* A link that stands on its own in a list gets a target at least 24 pixels tall
   (WCAG 2.5.8 Target Size), rather than leaning on the inline exception. Links
   inside a sentence are left alone: their size is decided by the line they sit
   in, which is the case the exception is written for.
   Buttons are left out. A button is already a flex row with a 2.9rem minimum
   height, so it clears the target on its own, and this rule carried a pseudo
   class that outranked the button's own display and padding: a button standing
   alone in a paragraph was turned back into a block, its flex centring lost and
   its line pushed towards the top of the box. */
li > a, td > a, .footer-grid a, .tag-list a, p > a:only-child:not(.btn) { display: inline-block; padding-block: 0.28rem; }
/* ---------- Services in the running head ----------
   Services is a disclosure rather than a link, so every service page is one
   press from the head instead of reachable from the footer alone. It is a
   native details element: it opens and closes with no scripting at all, and the
   links inside are in the tab order only while it is open. */
.nav-sub-wrap { position: relative; }
.nav-menu > summary {
  cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 0.4rem;
  padding-block: 0.5rem; font-weight: 600; color: var(--ink);
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::marker { content: ""; }
/* The caret is two borders rather than a picture, so it takes the item's own
   colour and turns over when the panel is open. */
.nav-menu > summary::after {
  content: ""; width: 0.42rem; height: 0.42rem; margin-top: -0.22rem;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); transition: transform 0.2s, margin-top 0.2s;
}
.nav-menu[open] > summary::after { transform: rotate(225deg); margin-top: 0.2rem; }
.nav-menu > summary:hover { color: var(--link); text-decoration: underline; }
.nav-sub {
  position: absolute; left: -0.9rem; top: calc(100% + 0.4rem); z-index: 60;
  display: block; min-width: 15.5rem; margin: 0; padding: 0.35rem 0;
  list-style: none; max-width: none;
  background: var(--paper); border: 2px solid var(--ink); border-radius: 10px;
}
.nav-sub li { margin: 0; border-top: 0; }
.nav-sub li + li { border-top: 1px solid var(--line); }
.nav-sub a { display: flex; align-items: center; min-height: 2.6rem; padding: 0.55rem 1.05rem; }
.nav-sub a:hover, .nav-sub a:focus-visible { background: var(--tint); }
.site-nav a { text-decoration: none; color: var(--ink); font-weight: 500; }
.site-nav a:hover { color: var(--link); text-decoration: underline; }
/* Where the reader is: the section link they are on stands heavier, and a
   section that holds the page says so on its own row as well. The head used to
   carry no mark of the current page at all. */
.site-nav a[aria-current="page"] { font-weight: 700; }
.nav-menu:has([aria-current="page"]) > summary { font-weight: 700; }
.nav-toggle { display: none; }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  min-height: 2.9rem; padding: 0.7rem 1.4rem; border-radius: 999px;
  font-weight: 700; font-size: 1rem; text-decoration: none; cursor: pointer;
  border: 2px solid transparent; transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.btn-primary { background: var(--fill); color: var(--on-fill); }
.btn-primary:hover { background: var(--fill-hover); color: var(--on-fill); }
.btn-secondary { background: var(--paper); color: var(--ink); border-color: var(--ink); }
.btn-secondary:hover { background: var(--tint); color: var(--ink); }
/* An outline button that inverts on hover. It carries the running head's and the
   panel's call to action, so it is defined here rather than on one page. */
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn-ghost:hover { background: var(--ink); color: var(--paper); }
/* The light button keeps its own pale ground, so it reads the same on the navy
   bands in either theme. The band selector is written out in full: a button
   inside a band would otherwise take the band's own white link colour, and
   white type on a white button is no button at all. */
.btn-light, a.btn-light { background: #fff; color: var(--navy); }
.btn-light:hover, a.btn-light:hover { background: #e6eefc; color: var(--navy); }
.band-navy a.btn-light { background: #fff; color: var(--navy); }
.band-navy a.btn-light:hover { background: #e6eefc; color: var(--navy); }
.btn-ghost-light { background: transparent; color: #fff; border-color: #fff; }
.btn-ghost-light:hover { background: rgba(255, 255, 255, 0.14); color: #fff; }
.btn svg { flex: none; }

.pause-btn {
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: 2.9rem; padding: 0.5rem 0.9rem; border-radius: 999px;
  background: transparent; border: 1px solid var(--line); color: var(--muted);
  font: inherit; font-size: 0.9rem; font-weight: 600; cursor: pointer;
}
.pause-btn:not([aria-pressed="true"]):hover { border-color: var(--ink); color: var(--ink); }
.pause-btn[aria-pressed="true"] { background: var(--fill); border-color: var(--fill); color: var(--on-fill); }
.pause-btn[aria-pressed="true"]:hover { background: var(--fill-hover); border-color: var(--fill-hover); color: var(--on-fill); }
.pause-btn svg { flex: none; }

/* Pause and theme are one control in two dressings: same shape, same pressed
   state, same two-glyph swap. Each carries both of its glyphs and shows the
   one the state calls for, so pressing it changes what you see and not only
   what assistive technology reads. */
.header-in > .pause-btn, .header-in > .theme-btn {
  width: 2.9rem; min-width: 2.9rem; padding: 0; justify-content: center; flex: none;
}
/* In the running head the label is for assistive technology only; the panel has
   room to print it. */
.header-in > .pause-btn [data-label], .header-in > .theme-btn [data-label] {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.pause-btn:not(.theme-btn) [data-icon="play"],
.pause-btn.theme-btn [data-icon="sun"] { display: none; }
.pause-btn:not(.theme-btn)[aria-pressed="true"] [data-icon="pause"],
.pause-btn.theme-btn[aria-pressed="true"] [data-icon="moon"] { display: none; }
.pause-btn:not(.theme-btn)[aria-pressed="true"] [data-icon="play"],
.pause-btn.theme-btn[aria-pressed="true"] [data-icon="sun"] { display: inline; }
/* Both controls need scripting, so neither is offered without it. */
html:not(.js) .pause-btn, html:not(.js) .theme-btn { display: none; }

/* ---------- Sections ---------- */
.section { padding-block: clamp(3rem, 7vw, 5.5rem); }
.section + .section { border-top: 1px solid var(--line); }
.band-tint { background: var(--tint); }
.band-navy { background: var(--navy); color: #fff; }
.band-navy h2, .band-navy h3 { color: #fff; }
.band-navy p, .band-navy li { color: #e4ebf6; }
.band-navy a { color: #fff; }
.section-head { max-width: var(--measure); margin-bottom: 2rem; }
/* A button standing under the lead takes its own line and its own space, rather
   than sitting on the last line of the lead above it. */
.section-head p.lead + p { margin-top: 1.4rem; }
.section-head p.lead { color: var(--muted); margin-bottom: 0; }
.band-navy .section-head p.lead { color: #c9d5e8; }

/* Global + local pause: freeze motion, keep content fully visible. */
html[data-motion="off"] *, html[data-motion="off"] *::before, html[data-motion="off"] *::after {
  animation: none !important; transition: none !important;
}
html[data-motion="off"] .rv { opacity: 1 !important; transform: none !important; }

/* Scroll reveals: visible without JS; hidden only when JS runs and motion is on. */
html.js[data-motion="on"] .rv { opacity: 0; transform: translateY(14px); transition: opacity 0.5s ease-out, transform 0.5s ease-out; }
html.js[data-motion="on"] .rv.in { opacity: 1; transform: none; }

/* ---------- Breadcrumb ----------
   One quiet line above the heading, on every page that has one: the steps, a
   separator between them, and the current page in ink. The list sits on one
   baseline, so the home link (which carries target padding) and the plain
   current page do not fall half a line apart, which is how they used to sit.
   Each step stays a 24 pixel target in its own right (WCAG 2.2, 2.5.8), so the
   type steps down without the target stepping down with it. */
.tag-list {
  list-style: none; padding: 0; margin: 0; max-width: none;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.4rem 0.6rem;
  font-size: 0.95rem; line-height: 1.5; color: var(--muted);
}
.tag-list li { margin: 0; font-weight: 500; }
.tag-list li + li::before { content: "/"; margin-right: 0.6rem; color: var(--muted); font-weight: 400; }
.tag-list a { color: var(--link); font-weight: 600; }
.tag-list li[aria-current="page"] { color: var(--ink); font-weight: 700; }

/* ---------- Services ---------- */
.service-list { list-style: none; margin: 2rem 0 0; padding: 0; max-width: none; border-top: 2px solid var(--ink); }
.service-list li { margin: 0; }
.service-row {
  display: grid; gap: 0.25rem 1.5rem; padding: 1.25rem 0.25rem;
  border-bottom: 1px solid var(--line); text-decoration: none; color: inherit;
}
.service-row:hover { background: var(--tint); }
.service-row h3 { color: var(--link); margin: 0; }
.service-row:hover h3 { color: var(--ink); text-decoration: underline; }
.service-row p { margin: 0; color: var(--muted); font-size: 1rem; }
/* The trailing affordance is an authored SVG rather than an arrow glyph, so it
   takes the row's colour, scales with nothing but its own box, and is the same
   mark as the running head's button. */
.service-row .go { display: inline-flex; align-items: center; font-weight: 700; white-space: nowrap; }
.service-row .go svg { display: block; }

/* ---------- Why ---------- */
.why-grid { display: grid; gap: 0; margin-top: 2rem; border-top: 2px solid var(--ink); }
.why-item { display: grid; gap: 0.3rem; padding: 1.5rem 0; border-bottom: 1px solid var(--line); }
.why-item h3 { margin: 0; }
.stamp-word {
  display: inline-block; border: 2px solid var(--ink); border-radius: 6px;
  padding: 0.05rem 0.55rem; font-size: 0.8rem; font-weight: 800;
  letter-spacing: 0.08em; text-transform: uppercase;
  margin-right: 0.6rem; vertical-align: 0.15rem; white-space: nowrap;
}
.stamp-word.teal { border-color: var(--accent); color: var(--accent); }
.why-item p { margin: 0; color: var(--muted); }
.why-item p strong { color: var(--ink); }

/* ---------- Method ---------- */
.method-grid { display: grid; gap: 2rem; margin-top: 2rem; }
.method-panel { min-width: 0; border: 1px solid var(--line); border-radius: var(--radius); padding: 1.5rem; background: var(--paper); }
.method-panel h3 { margin-top: 0; }
table.checks { width: 100%; border-collapse: collapse; margin: 0; font-size: 1rem; font-variant-numeric: tabular-nums; }
/* A panel is a grid item, so without this it cannot shrink below the widest
   unbreakable run inside its table, and a narrow screen carrying the letter
   spacing a reader may ask for (WCAG 1.4.12) is pushed sideways. The cells may
   break mid-word when there is no other way to fit, which is the same rule the
   headings already follow. */
table.checks th, table.checks td { overflow-wrap: anywhere; }
table.checks caption { text-align: left; font-weight: 700; padding-bottom: 0.5rem; }
table.checks th, table.checks td { text-align: left; padding: 0.6rem 0.5rem; border-top: 1px solid var(--line); vertical-align: top; }
table.checks th { font-weight: 600; }
table.checks td.role { color: var(--muted); }
.manual-first {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--tint); padding: 1.25rem 1.5rem; margin-top: 2rem; max-width: none;
}
.manual-first p { margin: 0; }
.manual-first strong { color: var(--link); }

/* ---------- Deliverables ---------- */
.deliv-grid { display: grid; gap: 2rem; margin-top: 2rem; }
.deliv-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 1.25rem; }
.deliv-list li { display: grid; grid-template-columns: 2rem 1fr; gap: 0.75rem; margin: 0; }
.deliv-list svg { margin-top: 0.25rem; }
.deliv-list h3 { font-size: 1.1rem; margin-bottom: 0.2rem; }
.deliv-list p { margin: 0; color: var(--muted); font-size: 1rem; }
.sev-table { border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.sev-table table { width: 100%; border-collapse: collapse; margin: 0; font-size: 1rem; }
.sev-table caption { text-align: left; font-weight: 700; padding: 1rem 1.25rem 0.5rem; }
.sev-table th, .sev-table td { text-align: left; padding: 0.65rem 1.25rem; border-top: 1px solid var(--line); vertical-align: top; }
.sev-pill {
  display: inline-block; padding: 0.15rem 0.7rem; border-radius: 999px;
  font-weight: 700; font-size: 0.9rem; color: #fff; white-space: nowrap;
}
.sev-critical { background: var(--sev-critical); }
.sev-serious { background: var(--sev-serious); }
.sev-moderate { background: var(--sev-moderate); }
.sev-minor { background: var(--sev-minor); }

/* ---------- Process ---------- */
.steps { list-style: none; counter-reset: step; padding: 0; margin: 0; display: grid; gap: 0; max-width: none; }
.steps li {
  counter-increment: step; margin: 0; padding: 1.1rem 0 1.1rem 3.5rem; position: relative;
  border-top: 1px solid var(--line);
}
.steps li::before {
  content: counter(step); position: absolute; left: 0; top: 1rem;
  width: 2.3rem; height: 2.3rem; border-radius: 50%;
  background: var(--fill); color: var(--on-fill); font-weight: 800;
  display: inline-flex; align-items: center; justify-content: center;
}
.steps li:first-child { border-top: 0; }
.steps h3 { font-size: 1.1rem; margin-bottom: 0.2rem; }
.steps p { margin: 0; color: var(--muted); }

/* ---------- Forms ---------- */
.audit-form { margin-top: 2rem; max-width: var(--measure); }
.field { margin-bottom: 1.4rem; }
.field > label, fieldset.checks > legend {
  display: block; font-weight: 700; margin-bottom: 0.45rem; padding: 0;
}
.req { color: var(--link); font-weight: 700; }
.hint { font-size: 0.95rem; color: var(--muted); margin: 0.3rem 0 0; }
.field input[type="text"],
.field input[type="email"],
.field input[type="url"],
.field select,
.field textarea {
  display: block; width: 100%; font: inherit; color: var(--ink);
  background: var(--paper); border: 1px solid var(--muted); border-radius: 10px;
  padding: 0.7rem 0.9rem; margin: 0;
}
.field input[type="text"],
.field input[type="email"],
.field input[type="url"],
.field select { min-height: 2.9rem; }
.field textarea { min-height: 8rem; resize: vertical; }
fieldset.checks { border: 1px solid var(--muted); border-radius: 10px; padding: 1rem 1.25rem 0.75rem; margin: 0 0 1.4rem; max-width: none; }
.check-row { display: flex; gap: 0.7rem; align-items: flex-start; padding-block: 0.45rem; }
/* 1.5rem rather than 1.4, so the control itself is a 24 pixel target even before
   its label is counted (WCAG 2.5.8). The label is clickable as well. */
.check-row input { width: 1.5rem; height: 1.5rem; flex: none; margin-top: 0.15rem; accent-color: var(--brand); }
.check-row label { font-weight: 400; }
/* What the form says about itself after a send. It keeps its place in the flow
   while it is empty, because a live region that is taken out of the page is not
   announced reliably, and it is an empty paragraph with no margin until then.
   The border colour is decoration; the words carry the outcome. */
.form-status { margin: 0; }
.form-status:not(:empty) {
  margin-top: 1.4rem; padding: 0.9rem 1.1rem; color: var(--ink);
  background: var(--tint); border: 1px solid var(--line); border-radius: 10px;
  max-width: var(--measure);
}
.form-status.ok:not(:empty) { border-color: var(--ok); }
.form-status.err:not(:empty) { border-color: var(--err); }

/* ---------- FAQ ---------- */
.faq-list { margin-top: 2rem; border-top: 2px solid var(--ink); max-width: var(--measure); }
.faq-list details { border-bottom: 1px solid var(--line); padding: 0; margin: 0; }
.faq-list summary {
  cursor: pointer; font-weight: 700; font-size: 1.1rem; letter-spacing: -0.01em;
  padding: 1.1rem 2.5rem 1.1rem 0; position: relative; list-style: none; min-height: 24px;
}
.faq-list summary::-webkit-details-marker { display: none; }
/* The chevron is drawn from two borders rather than a picture, so it takes the
   link colour and stays visible in the dark theme. */
.faq-list summary::after {
  content: ""; position: absolute; right: 0.45rem; top: 1.3rem;
  width: 0.7rem; height: 0.7rem;
  border-right: 3px solid var(--link); border-bottom: 3px solid var(--link);
  transform: rotate(45deg);
  transition: transform 0.15s;
}
.faq-list details[open] summary::after { transform: rotate(180deg); }
.faq-list details p { color: var(--muted); padding-right: 2rem; }
.faq-list details p strong { color: var(--ink); }

/* ---------- Final CTA ---------- */
.final-cta { text-align: left; }
.final-cta .btn-row { display: flex; flex-wrap: wrap; gap: 0.9rem; margin-top: 1.5rem; }
.final-cta .mark { color: var(--brand); }

/* ---------- Footer ---------- */
.site-footer { background: var(--navy); color: #e4ebf6; padding-block: 3rem 2rem; }
/* Each column is a fraction of the grid, so at 200% text its longest word can
   be wider than its share and the five columns together push the page sideways
   (measured: 61 pixels past the viewport at 1280). Long words may break as a
   last resort, the way the headings already do, and at an ordinary text size
   nothing here needs it. */
.footer-grid { display: grid; gap: 2rem; overflow-wrap: anywhere; }
.site-footer h2, .site-footer h3 { color: #fff; font-size: 1rem; }
.site-footer ul { list-style: none; padding: 0; margin: 0; }
.site-footer li { margin-bottom: 0.5rem; }
.site-footer a { color: #e4ebf6; }
.site-footer a:hover { color: #fff; }
.footer-brand p { color: #c9d5e8; font-size: 1rem; }
/* The brand column ends in a link inside a paragraph, where every other link in
   the footer is an item in a list. The target padding those lists need is
   inline padding, and inside a paragraph it pushes the words off the line
   rhythm: the link's box grew by twice the padding, so baseline alignment
   dropped its text a further half of that. Measured on the home page it landed
   20.5px below the next line of the paragraph, floating between that paragraph
   and the link lists beside it. Cancelling the padding with an equal negative
   margin puts the text back on the grid and keeps the hit area its full height,
   and one line of space between the two paragraphs then reads as one step of
   the same rhythm rather than a stray gap. */
.footer-brand p + p { margin-top: 1.6rem; }
.footer-brand p:last-child { margin-bottom: 0; }
.footer-brand p:last-child a { margin-block: -0.28rem; }
.footer-base { border-top: 1px solid rgba(255, 255, 255, 0.24); margin-top: 2rem; padding-top: 1.25rem; font-size: 0.95rem; color: #c9d5e8; display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; }

/* ---------- Responsive ---------- */
@media (min-width: 56rem) {
  .strip-grid { grid-template-columns: 1fr 1fr; }
  .service-row { grid-template-columns: 1fr auto; align-items: baseline; }
  .service-row p { grid-column: 1; }
  .service-row .go { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
  .method-grid { grid-template-columns: 1fr 1fr; }
  .deliv-grid { grid-template-columns: 1.1fr 0.9fr; align-items: start; }
  /* Five columns: the brand, three link groups, and Learn, which carries the
     guides, the posts and the checker. */
  .footer-grid { grid-template-columns: 1.2fr 1fr 1fr 0.9fr 1.1fr; }
}

/* Below the full row the section links, the two toggles and the call to action
   cannot share one line, so the panel takes all of them. One breakpoint, the
   same on every page, so the running head behaves the same wherever you are,
   and it opens only where the row fits on one line: measured, the row needs 1085
   pixels of content inside 64 pixels of inset, 1149, and about 1166 with the
   scrollbar. The panel therefore holds the head below 1200, where before it let
   the row fold onto a second line between 1088 and 1166. */
@media (max-width: 74.99rem) {
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 2.9rem; min-height: 2.9rem; border-radius: 10px;
    background: transparent; border: 1px solid var(--line); color: var(--ink);
    font: inherit; font-weight: 700; cursor: pointer;
  }
  .site-nav { display: none; width: 100%; }
  .site-header.open .site-nav { display: block; }
  /* The head is stuck to the top of the viewport, so a panel taller than the
     screen puts its own foot off the screen and there is no way to scroll to it:
     at 360 by 760 the Services list alone makes the open head 1196 pixels tall,
     which left the call to action at the end of the panel impossible to reach.
     The panel scrolls inside the head instead, in the room under the brand row,
     so the head never grows past the screen. This covers the no-script state
     too, where the panel is simply on show from the start. */
  .site-nav { max-height: calc(100vh - 6.5rem); overflow-y: auto; overscroll-behavior: contain; }
  .site-nav ul { flex-direction: column; gap: 0; padding-block: 0.5rem; align-items: stretch; }
  .site-nav li { border-top: 1px solid var(--line); }
  .site-nav a { display: block; padding: 0.8rem 0.25rem; white-space: normal; }
  /* In the panel the Services disclosure sits in the flow and opens in place,
     so nothing hangs off the side of a phone. */
  .nav-sub-wrap { position: static; }
  /* Flex rather than block, so the caret keeps its box and sits at the far end
     of the row instead of collapsing to a two pixel corner next to the word. */
  .nav-menu > summary { display: flex; align-items: center; justify-content: space-between; padding: 0.8rem 0.25rem; }
  .nav-sub {
    position: static; min-width: 0; border: 0; border-radius: 0;
    padding: 0 0 0.5rem 1.1rem; background: transparent;
  }
  .site-nav .nav-sub li, .site-nav .nav-sub li + li { border-top: 0; }
  .nav-sub a { min-height: 2.6rem; padding: 0.6rem 0.25rem; }
  /* Without scripting the panel cannot open, so the links stand on the page
     instead. The Services disclosure still works: details needs no script. */
  html:not(.js) .nav-toggle { display: none; }
  html:not(.js) .site-nav { display: block; }
  .header-in { flex-wrap: wrap; }
  .header-in > .pause-btn, .header-in > .theme-btn { display: none; }
  .site-nav .nav-pause, .site-nav .nav-theme { display: list-item; }
  .site-nav .nav-pause .pause-btn, .site-nav .nav-theme .theme-btn {
    width: 100%; min-width: 0; justify-content: center; margin-block: 0.5rem;
  }
  /* The panel has room for the controls' own words, so it prints them. */
  .site-nav .nav-pause .pause-btn [data-label],
  .site-nav .nav-theme .theme-btn [data-label] {
    position: static; width: auto; height: auto; margin: 0; padding: 0;
    overflow: visible; clip: auto; white-space: normal;
  }
  /* The call to action leaves the row and travels in the panel, so a phone keeps
     a thin one-line head instead of a banner pinned over every screen. */
  .header-cta { display: none; }
  .site-nav .nav-cta { display: list-item; border-top: 0; }
  .site-nav .nav-cta a.btn {
    display: inline-flex; width: 100%; justify-content: center;
    margin-block: 0.6rem 0.85rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  html .rv { opacity: 1 !important; transform: none !important; }
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
}

/* The home page carries its own design layer in assets/css/home.css,
   loaded only by index.html. Everything above is shared by all pages. */

/* ---------- Sample report ----------
   Each finding is one full-width document, never a two-column table: the
   labels sit above their values, so a word like "Severity" is never broken
   across lines, and the long fields (steps, expected and actual, the fix) get
   the full measure. The rating colour is the finding's left rule; the pill
   still carries the rating in words. */
.rf-cover {
  display: grid; gap: 1rem 2rem; margin: 2rem 0 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
}
.rf-cover > div, .rf-meta > div { min-width: 0; }
.rf-cover dt, .rf-meta dt {
  font-size: 0.85rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted);
}
.rf-cover dd, .rf-meta dd { margin: 0.25rem 0 0; color: var(--ink); }
.rf {
  margin-top: 2.5rem; border: 1px solid var(--line); border-left: 6px solid var(--sev-minor);
  border-radius: var(--radius); background: var(--paper); overflow: hidden;
}
.rf-critical { border-left-color: var(--sev-critical); }
.rf-serious { border-left-color: var(--sev-serious); }
.rf-moderate { border-left-color: var(--sev-moderate); }
.rf-head { background: var(--tint); padding: clamp(1.25rem, 3vw, 2rem); border-bottom: 1px solid var(--line); }
.rf-id { font-family: var(--mono); font-size: 0.9rem; color: var(--muted); margin: 0 0 0.4rem; }
.rf-head h2 { margin-top: 0; font-size: clamp(1.35rem, 1.1rem + 1vw, 1.75rem); max-width: 40ch; margin-bottom: 1.25rem; }
.rf-meta {
  display: grid; gap: 1rem 2rem; margin: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
}
.rf-body { padding: clamp(1.25rem, 3vw, 2rem); }
.rf-body h3 { font-size: 1.1rem; margin-top: 1.75rem; }
.rf-body > h3:first-child { margin-top: 0; }
.rf-body p { color: var(--muted); }
.rf-body p strong { color: var(--ink); }
.rf-steps { padding-left: 0; list-style: none; counter-reset: rfstep; display: grid; gap: 0.6rem; }
.rf-steps li {
  counter-increment: rfstep; position: relative; margin: 0; padding-left: 2.6rem; min-height: 1.8rem;
}
.rf-steps li::before {
  content: counter(rfstep); position: absolute; left: 0; top: 0;
  width: 1.8rem; height: 1.8rem; border-radius: 999px; display: grid; place-items: center;
  background: var(--fill); color: var(--on-fill); font-weight: 700; font-size: 0.9rem;
}
.rf code, .rf kbd { font-family: var(--mono); font-size: 0.9em; overflow-wrap: anywhere; }
.rf p code, .rf li code { background: var(--tint); border-radius: 4px; padding: 0.05rem 0.3rem; }
.rf kbd { border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 4px; padding: 0 0.35rem; background: var(--paper); color: var(--ink); }
.rf-compare { display: grid; gap: 1rem; margin-top: 1.75rem; }
.rf-compare > div { min-width: 0; border: 1px solid var(--line); border-top: 4px solid var(--ok); border-radius: 10px; padding: 1rem 1.25rem; }
.rf-compare > .rf-actual { border-top-color: var(--err); }
.rf-compare h3 { margin-top: 0; }
.rf-compare p { margin: 0; }
.rf-code {
  margin: 0 0 1rem; padding: 1rem 1.25rem; border-radius: 10px; white-space: pre-wrap;
  background: var(--navy); color: #e6edf8; font-size: 0.9rem; line-height: 1.6;
}
.rf-code code { overflow-wrap: anywhere; }
.rf-retest { border-top: 1px solid var(--line); padding-top: 1rem; margin: 1.5rem 0 0; max-width: none; }
.rf-adds { margin-top: 3.5rem; }
@media (min-width: 48rem) {
  .rf-compare { grid-template-columns: 1fr 1fr; }
}

/* The broken component, drawn beside the write-up. The drawing is invented
   and decorative (aria-hidden); the caption under it says in words what it
   shows, so nothing is carried by the picture alone. */
.rf-body { display: grid; gap: 2rem; }
.rf-text, .rf-fig { min-width: 0; }
.rf-fig { margin: 0; }
.rf-fig figcaption { margin-top: 0.75rem; font-size: 0.95rem; color: var(--muted); }
.mock {
  border: 1px solid var(--line); border-radius: 12px; overflow: hidden;
  background: #fff; color: #1f2937; box-shadow: 0 10px 30px -18px rgba(11, 26, 49, 0.45);
}
.mock p { margin: 0; max-width: none; }
.rf-body .mock p { color: inherit; }
.rf-body .mock .mock-bar { background: #e5e9f0; color: #4b5563; font-family: var(--mono); font-size: 0.75rem; padding: 0.5rem 0.9rem; border-bottom: 1px solid #d1d7e0; }
.mock-pad { padding: 1.1rem; display: grid; gap: 0.6rem; font-size: 0.95rem; }
.mock-label { font-weight: 700; font-size: 0.85rem; }
.mock-select { border: 1px solid #9ca3af; border-radius: 8px; padding: 0.55rem 0.75rem; display: flex; justify-content: space-between; }
.mock-menu { list-style: none; margin: -0.3rem 0 0; padding: 0.3rem; border: 1px solid #d1d7e0; border-radius: 8px; box-shadow: 0 6px 16px -10px rgba(0,0,0,0.4); }
.mock-menu li { margin: 0; padding: 0.45rem 0.6rem; border-radius: 6px; }
.mock-menu li.is-focus { outline: 3px solid var(--sev-critical); outline-offset: -3px; background: #fdecea; }
.rf-body .mock .mock-loop { color: var(--sev-critical); font-weight: 700; font-size: 0.85rem; }
.mock-total { display: flex; justify-content: space-between; border-top: 1px solid #e5e7eb; padding-top: 0.6rem; }
.mock-btn { text-align: center; font-weight: 700; padding: 0.65rem; border-radius: 8px; background: #0b1a31; color: #fff; }
.mock-btn.is-blocked { opacity: 0.45; outline: 2px dashed var(--sev-critical); outline-offset: 3px; }
.rf-body .mock .mock-tag { justify-self: start; font-size: 0.8rem; font-weight: 700; padding: 0.15rem 0.6rem; border-radius: 999px; color: #fff; }
.mock-bad { background: #b42318; }
.mock-good { background: #166534; }
.mock-cards { grid-template-columns: 1fr 1fr; gap: 0.75rem; }
.mock-card { background: #f4f5f7; border-radius: 10px; padding: 0.7rem; display: grid; gap: 0.4rem; align-content: start; }
.mock-img { aspect-ratio: 4 / 3; border-radius: 6px; background: linear-gradient(135deg, #c7d2e3, #e4e9f1); }
.rf-body .mock .mock-name { font-weight: 700; font-size: 0.85rem; }
.mock-price { font-size: 1rem; }
.mock-price s { font-size: 0.8rem; margin-left: 0.3rem; }
.rf-body .mock .mock-price.is-fail { color: #8a94a0; }
.rf-body .mock .mock-price.is-pass { color: #5b6470; }
.rf-body .mock .mock-cap { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: #4b5563; font-weight: 700; }
.mock-page { gap: 0.45rem; }
.mock-region { border: 2px solid #166534; border-radius: 8px; padding: 0.5rem 0.7rem; display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.25rem 0.75rem; font-size: 0.85rem; }
.mock-region span { font-family: var(--mono); color: #374151; }
.mock-region.is-ok b { color: #166534; }
.mock-main { min-height: 5.5rem; align-content: start; }
.mock-region.is-missing { border: 2px dashed #b42318; background: #fdecea; }
.mock-region.is-missing b { color: #b42318; }
.mock-region i { flex-basis: 100%; font-style: normal; color: #4b5563; }
@media (min-width: 64rem) {
  .rf-body { grid-template-columns: minmax(0, 1fr) minmax(0, 22rem); gap: 3rem; align-items: start; }
  .rf-fig { position: sticky; top: 6rem; }
}
