/* ==========================================================================
   Four Drills — the design system.
   ONE stylesheet, shared by marketing pages, wizard, previews and the PDFs.
   The brand is literally this file (site-plan.md §2).

   No webfonts — speed, and the audience's older devices.
   Accessibility floor: WCAG 2.1 AA. Navy-on-white and white-on-navy both pass.
   Amber is decorative and never the only carrier of meaning.
   ========================================================================== */

/* ---------------------------------------------------------------- tokens */

:root {
  --navy:  #1e2a44;  /* primary — headers, buttons, table heads */
  --navy2: #2c3d63;  /* hover / secondary headings */
  --amber: #e8a13d;  /* accent — the brand dot, tags, section ribbons, focus rings */
  --ink:   #232630;  /* body text */
  --grey:  #5a6072;  /* secondary text */
  --line:  #d8dce6;  /* borders */
  --bg:    #f4f6fa;  /* page background / tinted panels */
  --ok:    #2e7d52;  /* green — positive states, ticks */
  --warn:  #b3541e;  /* amber-red — warnings, outstanding */

  --font: 'Segoe UI', Helvetica, Arial, sans-serif;

  --wrap: 1080px;
  --gap: 24px;
  --radius: 6px;

  /* Amber at full strength fails AA on white, so text that must be read uses
     this darkened form. The token above stays for rules, chips and fills. */
  --amber-text: #8a5a08;
}

/* ------------------------------------------------------------------ reset */

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

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

body {
  margin: 0;
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  color: var(--ink);
  background: #fff;
}

img { max-width: 100%; height: auto; }

h1, h2, h3, h4 {
  color: var(--navy);
  line-height: 1.2;
  margin: 0 0 .5em;
  font-weight: 750;
}

h1 { font-size: 2.4rem; letter-spacing: -0.02em; }
h2 { font-size: 1.75rem; letter-spacing: -0.01em; }
h3 { font-size: 1.2rem; font-weight: 700; }

p { margin: 0 0 1em; }

a { color: var(--navy2); text-decoration: underline; text-underline-offset: 2px; }
a:hover { color: var(--navy); }

/* LINK COLOUR RULE — the mirror of the BUTTON COLOUR RULE further down:
 *
 *   Light surface (white, --bg tint)           →  navy2, darkening to navy on hover
 *   Dark surface (navy header, navy band,      →  WHITE in every state:
 *                 navy footer)                    :link :visited :hover :active :focus
 *
 * On a dark surface, hover may change the AMBER accent only — the wordmark's
 * dot, or an amber underline on a nav item. It must never change the text
 * colour, because the only darker colour in this palette is the navy the text
 * is already sitting on.
 *
 * That is exactly what broke the wordmark: `a:hover` is (0,1,1) and `.wordmark`
 * is (0,1,0), so hovering the logo repainted it navy-on-navy and it vanished.
 *
 * Every state is written out deliberately, and `:is()` lifts the specificity
 * above `a:hover`. Do not trim this back to relying on source order — that is
 * all that was keeping `.site-footer a` (also (0,1,1)) alive, and it would
 * break the moment someone reorders this file. */
.site-header a:is(:link, :visited, :hover, :active, :focus),
section.navy-band a:is(:link, :visited, :hover, :active, :focus),
.site-footer a:is(:link, :visited, :hover, :active, :focus) {
  color: #fff;
}

/* THE ONE EXCEPTION, and it is an accessibility fix rather than a preference.
 *
 * `.btn--onnavy` is the amber-filled button, and the whole reason it exists is
 * that navy-on-navy is invisible: see the BUTTON COLOUR RULE below. But it only
 * ever appears ON a navy band, so the rule above was repainting its label white,
 * and white on #e8a13d is 2.18:1. Lighthouse caught it; it had been shipping.
 *
 * Specificity has to beat (0,2,2) above, hence the same shape with the class
 * added. Both surfaces are listed even though only .navy-band carries one of
 * these today, because the next one will go in the footer and nobody will
 * remember this. */
section.navy-band a.btn--onnavy:is(:link, :visited, :hover, :active, :focus),
.site-footer a.btn--onnavy:is(:link, :visited, :hover, :active, :focus) {
  color: #2a1d05;
}

/* Visible focus rings, amber, everywhere. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible {
  outline: 3px solid var(--amber);
  outline-offset: 2px;
  border-radius: 2px;
}

/* ---------------------------------------------------------------- layout */

.wrap {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 20px;
}

.wrap--narrow { max-width: 760px; }

section { padding: 56px 0; }
section.tinted { background: var(--bg); }
section.navy-band { background: var(--navy); color: #fff; }
section.navy-band h2,
section.navy-band h3 { color: #fff; }
section.navy-band a { color: #fff; }

/* Clipped rather than parked at left:-9999px. Off-canvas positioning is a real
   element 9999px wide of the layout, which at 375px is the one thing that made
   the document scrollable sideways on a phone. */
.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 100;
  background: var(--navy);
  color: #fff;
  padding: 12px 18px;
  clip-path: inset(50%);
}
.skip-link:focus { clip-path: none; }

/* In the heading outline, off the screen. For headings that a screen reader
   needs in order to walk the page and that a sighted reader does not, because
   the layout already says it (see the price cards on /pricing).
   Not `display: none`: that would take it out of the outline as well. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
  border: 0;
}

/* --------------------------------------------------- wordmark and header */

/* FOUR DRILLS bold + amber full stop. Navy header band on every page. */
/* The lockup: brand icon then wordmark, one link.

   `align-items: center` and not baseline: the icon is a square with no baseline
   of its own, so baseline alignment hangs it off the text's and drops it a
   couple of pixels low, which at 30px is visible and looks like a mistake.

   The gap is the comfortable spacing the icon needs to stop reading as part of
   the F. It shrinks at mobile widths along with the type, below. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  font-size: 1.1rem;
  white-space: nowrap;
}
.wordmark__icon {
  display: block;
  flex: 0 0 auto;      /* never squashed: the icon is the one part that stays */
  width: 30px;
  height: 30px;
}
.wordmark__icon svg { display: block; width: 100%; height: 100%; }
/* The only thing hover is allowed to change on the navy band: the amber dot. */
.wordmark .dot { color: var(--amber); transition: color .12s ease-in-out; }
.wordmark:hover .dot,
.wordmark:focus-visible .dot { color: #f6bd6a; }

.site-header {
  background: var(--navy);
  border-bottom: 3px solid var(--amber);
}
.site-header .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  min-height: 64px;
  flex-wrap: wrap;
  padding-top: 8px;
  padding-bottom: 8px;
}
.site-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 18px;
  align-items: center;
}
/* The sign-out control is a <button> inside a POST form (see
   partials/header.ejs), and it has to be indistinguishable from the links
   beside it. Every rule that dresses a nav item names both, here and in the
   collapsed menu below, rather than a second set of rules for .navbtn that
   would drift the first time one of them changed. */
.site-nav__signout { display: inline-flex; margin: 0; }
.site-nav .navbtn {
  font: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}
.site-nav a,
.site-nav .navbtn {
  position: relative;
  color: #fff;
  text-decoration: none;
  font-size: .93rem;
  padding: 11px 2px;      /* keeps the hit area ≥44px, measured not guessed */
  display: inline-block;
}
/* Hover and current-page are carried by an amber rule, not by a colour change.
   See the LINK COLOUR RULE above: on this navy band the only thing hover may
   move is the amber accent.

   A pseudo-element rather than the inset box-shadow this used to be, for one
   reason: the shadow can only sit at the very bottom of the padded box, which
   is 11px under the text and reads as a border on the header rather than as an
   underline on the word. `bottom: 5px` puts the rule about 6px below the text,
   close enough to belong to it and clear enough not to touch the descenders. */
.site-nav a::after,
.site-nav .navbtn::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 5px;
  height: 3px;
  border-radius: 2px;
  background: var(--amber);
  opacity: 0;
  transition: opacity .12s ease-in-out;
}
.site-nav a:hover::after,
.site-nav a:focus-visible::after,
.site-nav a[aria-current="page"]::after,
.site-nav .navbtn:hover::after,
.site-nav .navbtn:focus-visible::after { opacity: 1; }

/* ---------------------------------------------------------- notice band */

/* The pack's top ribbon, reused as a site notice bar where needed
   (e.g. "PREVIEW — pay to unlock your clean copy"). */
.notice-band {
  background: var(--amber);
  color: #2a1d05;
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  text-align: center;
  padding: 8px 20px;
}

/* ------------------------------------------------------- preview banner */

/* Pre-launch only, rendered by layout.ejs behind PREVIEW_MODE. Slim, amber,
   and the first thing in the body on every page.

   It is NOT .notice-band. That one is the pack's ribbon: uppercase, wide
   letter-spacing, a label. This is a sentence with a semicolon in it, and
   uppercasing a sentence costs the word shapes that make it readable at a
   glance, which is the whole job of a warning nobody asked to read.

   Amber on #2a1d05 rather than white: the same pairing the notice band and the
   tag chips use, and the reason --amber-text exists a few rules down. Amber
   with white text is 1.9:1 and fails at any size. */
.preview-band {
  background: var(--amber);
  color: #2a1d05;
  border-bottom: 1px solid rgba(42, 29, 5, .25);
}
.preview-band p {
  max-width: 1180px;
  margin: 0 auto;
  padding: 7px 20px;
  font-size: .85rem;
  font-weight: 600;
  line-height: 1.35;
  text-align: center;
}

/* ------------------------------------------------------------- tag chips */

/* Uppercase navy/amber chips (SECTION 1, PROCEDURE 1 OF 4) — section eyebrows. */
.tag {
  display: inline-block;
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  padding: 5px 10px;
  border-radius: 3px;
  background: var(--navy);
  color: #fff;
  margin-bottom: 14px;
}
.tag--amber { background: var(--amber); color: #2a1d05; }
.tag--ghost {
  background: transparent;
  color: var(--navy);
  border: 1.5px solid var(--line);
}

/* --------------------------------------------------------------- buttons */

/* BUTTON COLOUR RULE — one decision, applied everywhere:
 *
 *   Light background (white, --bg tint)  →  .btn          navy fill, white text
 *   Navy band (.navy-band, footer, PDF)  →  .btn--onnavy  amber fill, dark text
 *   Secondary action beside a primary    →  .btn--secondary  white fill, navy text
 *
 * Why: navy-on-light and dark-on-amber are the two pairings that clear WCAG AA
 * in this palette. Navy on a navy band is invisible, and amber on white fails
 * contrast for text — which is also why --amber-text exists for prose.
 *
 * The practical test: a primary button must never be the same hue as the
 * surface behind it. If you add a new band colour, add the matching button
 * modifier here rather than overriding .btn at the call site.
 */

.btn {
  display: inline-block;
  font-family: inherit;
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.2;
  padding: 15px 26px;       /* ≥44px tall */
  min-height: 48px;
  border: 2px solid var(--navy);
  border-radius: var(--radius);
  background: var(--navy);
  color: #fff;
  text-decoration: none;
  cursor: pointer;
  text-align: center;
  transition: background-color .12s ease-in-out;
}
.btn:hover { background: var(--navy2); border-color: var(--navy2); color: #fff; }

.btn--secondary { background: #fff; color: var(--navy); }
.btn--secondary:hover { background: var(--bg); color: var(--navy); }

.btn--onnavy { background: var(--amber); border-color: var(--amber); color: #2a1d05; }
.btn--onnavy:hover { background: #f0b055; border-color: #f0b055; color: #2a1d05; }

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
  margin: 24px 0;
}

/* --------------------------------------------------------- amber-left box */

/* The .box callout with bold uppercase mini-title — "why this matters" panels. */
.box {
  background: var(--bg);
  border-left: 5px solid var(--amber);
  padding: 20px 24px;
  margin: 28px 0;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.box > :last-child { margin-bottom: 0; }
.box-title {
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--navy);
  margin: 0 0 8px;
}
.box--ok   { border-left-color: var(--ok); }
.box--warn { border-left-color: var(--warn); }

/* ------------------------------------------------------------- KV tables */

/* The grey-label / white-value two-column grid — spec lists, inclusions. */
.kv {
  width: 100%;
  border-collapse: collapse;
  border: 1px solid var(--line);
  margin: 20px 0;
  font-size: .97rem;
}
.kv th, .kv td {
  text-align: left;
  padding: 12px 16px;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.kv th {
  background: var(--bg);
  color: var(--navy);
  font-weight: 700;
  width: 34%;
}
.kv tr:last-child th, .kv tr:last-child td { border-bottom: 0; }

.table-scroll { overflow-x: auto; }

/* ---------------------------------------------------------- step circles */

.steps { counter-reset: step; list-style: none; padding: 0; margin: 32px 0 0; }
.step {
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 20px;
  padding-bottom: 32px;
  align-items: start;
}
.step::before {
  counter-increment: step;
  content: counter(step);
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--navy);
  color: #fff;
  font-weight: 800;
  font-size: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.step h3 { margin-bottom: .35em; }
.step > div > :last-child { margin-bottom: 0; }

/* ---------------------------------------------------------- action cards */

/* The four coloured procedure cards from the summary card — the site's
   most recognisable asset and the homepage hero visual. */
.action-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin: 32px 0 0;
}
.action-card {
  border-radius: var(--radius);
  padding: 22px;
  color: #fff;
  border-top: 5px solid rgba(255, 255, 255, .35);
}
.action-card h3 { color: #fff; margin-bottom: .4em; }
.action-card p { margin-bottom: 0; font-size: .95rem; color: #eef1f7; }
.action-card .tag { background: rgba(255, 255, 255, .18); color: #fff; }

.action-card--1 { background: #9d2b2b; }  /* evacuation */
.action-card--2 { background: #2d5b8e; }  /* invacuation */
.action-card--3 { background: #33384a; }  /* lockdown */
.action-card--4 { background: var(--navy); } /* communication */

/* ---------------------------------------------------------- scope cards */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
  margin: 32px 0 0;
}

/* Four price cards across the 1080px wrap. At the default 260px minimum they
   wrap 3 + 1, which reads as a mistake rather than a hierarchy. */
.cards--four { grid-template-columns: repeat(auto-fit, minmax(228px, 1fr)); }

/* A lone card (the voluntary price on /small-venues) should not stretch to
   full width — it looks like a banner rather than a price. */
.cards--one { grid-template-columns: minmax(260px, 340px); }
.card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 24px;
  background: #fff;
  display: flex;
  flex-direction: column;
}
/* The card is a flex column, whose default `align-items: stretch` blows an
   inline-block chip out to the full card width. Chips hug their text. */
.card > .tag { align-self: flex-start; }

.card h3 { margin-bottom: .3em; }
.card .verdict {
  font-weight: 700;
  color: var(--navy2);
  margin-bottom: .6em;
}
.card p { font-size: .96rem; }

/* THE CARD CTA IS PINNED TO THE BOTTOM, on every card on the site: the scope
 * cards on the homepage, the four price cards, the lone voluntary card.
 *
 * Three things do it together and all three are load-bearing:
 *   .cards { align-items: stretch }  every card is as tall as the tallest
 *   .card  { display: flex; column }  the CTA can be pushed within the card
 *   .card-cta { margin-top: auto }    it goes to the bottom of that column
 * Remove any one and the row goes ragged, with each link floating wherever its
 * own paragraph happened to end.
 *
 * The rule above the link is what makes the pinning legible: without it the
 * gap on a short card reads as a mistake rather than as an aligned row. */
.card .card-cta {
  margin-top: auto;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-weight: 700;
}
/* A pinned CTA needs breathing room above it, but the paragraph immediately
   before it should not double up its own bottom margin against that. */
.card .card-cta { margin-bottom: 0; }

/* CLICKABLE-CARD HOVER — one treatment, everywhere a card leads somewhere.
 *
 * :focus-within, not just :hover, so a keyboard user tabbing to the CTA gets
 * the same signal a mouse user gets. The lift is 2px and the shadow is soft on
 * purpose: these are 260px-wide reading surfaces, and a card that jumps reads
 * as a button, which none of them are (the link inside is). */
.card:has(a):hover,
.card:has(a):focus-within,
.gallery-page:hover,
.gallery-page:focus-visible {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(30, 42, 68, .13), 0 2px 6px rgba(30, 42, 68, .07);
  border-color: var(--navy2);
}
.card, .gallery-page {
  transition: transform .14s ease-in-out, box-shadow .14s ease-in-out, border-color .14s ease-in-out;
}
/* The primary card already carries an inset ring plus a shadow, and the shared
   hover shadow above would drop the ring. Restate both. */
.card--primary:has(a):hover,
.card--primary:has(a):focus-within {
  box-shadow: inset 0 0 0 1px var(--navy), 0 10px 24px rgba(30, 42, 68, .17), 0 2px 6px rgba(30, 42, 68, .08);
  border-color: var(--navy);
}

/* The primary card — Standard on /pricing, the 200–799 card on the homepage.
 *
 * The emphasis is an inset ring rather than a thicker border, deliberately: a
 * 2px border makes this card's content box 1px shorter than its 1px-bordered
 * neighbours, which knocked the pinned CTA a pixel out of line along the row.
 * Same box metrics as every other card, heavier edge. Don't "simplify" this
 * back to `border: 2px`. */
.card--primary {
  border-color: var(--navy);
  box-shadow: inset 0 0 0 1px var(--navy), 0 2px 14px rgba(30, 42, 68, .1);
}
.card--primary .price { color: var(--navy); }

/* Cards sit in a grid, so they already stretch to the tallest in the row;
   `margin-top: auto` on the CTA is what pins it to the bottom of each. Both
   halves are needed — remove either and the row goes ragged. */
.cards { align-items: stretch; }

/* `.card p` is (0,1,1) and would otherwise win the font-size, so the price
   needs the extra class to land. Do not simplify back to a bare `.price`. */
.price,
.card .price {
  font-size: 1.9rem;
  font-weight: 800;
  color: var(--navy);
  line-height: 1.1;
  margin: 6px 0 2px;
}
.price small,
.card .price small {
  font-size: .95rem;
  font-weight: 600;
  color: var(--grey);
  white-space: nowrap;
}

/* -------------------------------------------------------- checklist ticks */

/* Green square-tick rows — pricing inclusions, trust lists. */
.ticks { list-style: none; padding: 0; margin: 18px 0; }
.ticks li {
  position: relative;
  padding: 7px 0 7px 32px;
  font-size: .97rem;
}
/* Green square with a white tick. Drawn as an inline SVG rather than crossed
   gradients — the gradient version read as a cross at small sizes, which is
   the opposite of what a checklist means. Inline data URI, no network fetch. */
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 11px;
  width: 18px;
  height: 18px;
  border-radius: 2px;
  background-color: var(--ok);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 6.5L9.5 17 4 11.5' fill='none' stroke='%23fff' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 13px 13px;
}

/* ------------------------------------------------------------ phrase block */

/* Dashed-border announcement phrases — used sparingly, on /your-pack. */
.phrase {
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  padding: 18px 22px;
  margin: 24px 0;
  text-align: center;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--navy);
  background: #fff;
}

/* ------------------------------------------------------------------- hero */

.hero { background: var(--bg); border-bottom: 1px solid var(--line); }
.hero .wrap {
  display: grid;
  /* The right column is sized by what has to be legible in it, not by taste:
     the inside-page render needs about 370px to keep this venue's exits
     readable. See .hero-art below. */
  grid-template-columns: minmax(0, 1.05fr) minmax(300px, .95fr);
  gap: 26px 48px;
  align-items: center;
  /* Deliberately tighter than the 56px section rhythm. The hero has to end
     high enough that the next section's "DOES THIS APPLY TO YOU?" chip is
     visible without scrolling on a 1300×900 laptop — that chip is what tells a
     visitor the page continues, and the scope split is the whole funnel. */
  padding-top: 34px;
  padding-bottom: 34px;
}
.hero h1 { font-size: 2.7rem; margin-bottom: .35em; }
.hero .sub { font-size: 1.16rem; color: var(--grey); max-width: 34em; margin-bottom: .6em; }
.hero .btn-row { margin: 18px 0 0; }

/* ------------------------------------------------- hero: the three steps */

/* The whole job under the CTA, in three moves. Small circles, not the big
   `.steps` numerals used on /how-it-works: this is a reassurance strip, and at
   48px it competes with the button above it. */
/* The strip spans both hero columns rather than sitting inside the copy one.
   Three steps with arrows between them need roughly 640px; the copy column is
   about 520px at the 1080px wrap, and the strip broke onto two lines with an
   arrow orphaned at the start of the second. Full width, it is one clean line,
   and it still sits directly under the CTA. */
.hero-steps {
  grid-column: 1 / -1;
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 0;
}
.hero-steps li {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 1rem;
  font-weight: 600;
  color: var(--navy);
  line-height: 1.3;
}
.hero-steps__n {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--navy);
  color: #fff;
  font-size: .82rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}
.hero-steps__aside { color: var(--grey); font-weight: 400; }
/* The arrow between steps. Decorative: the list is ordered and numbered, so
   nothing here is the only carrier of the sequence. */
.hero-steps li + li::before {
  content: "";
  flex: 0 0 auto;
  width: 26px;
  height: 10px;
  margin: 0 12px;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 26 10'%3E%3Cpath d='M1 5h22M19 1l4 4-4 4' fill='none' stroke='%23e8a13d' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat center / contain;
}

/* --------------------------------------------------------- hero: the art */

/* Two real renders of the same pack, built by scripts/build-pack-cover.js from
   the template that prints the PDF. Never stock photography — the brand shows
   its own paperwork, done well (site-plan §2).
 *
 * THE OVERLAP IS MEASURED, not composed by eye. The page render sits behind and
 * to the left; the cover sits in front, bottom right. The cover's left edge
 * lands at about 64% of the container, and the page render's right ~20% is the
 * white gradient the build script fades its cut edge into. So the cover covers
 * nothing but that fade. Move either one and the cover starts sitting on the
 * exits table, which is the only part of this composition doing any persuading.
 *
 * The page render also cannot go below about 370px wide: at that size its 10pt
 * body type renders around 10px on screen, and "your building's own exits, in
 * the pack" stops being a claim anyone can check. That number is why the hero
 * grid gives this column a 300px floor.
 *
 * aspect-ratio on the anchor, and width/height on both images, mean the whole
 * block reserves its space before a byte of image arrives. */
.hero-art {
  position: relative;
  display: block;
  width: 100%;
  max-width: 480px;
  aspect-ratio: 480 / 345;
  margin: 0 auto 30px;
  text-decoration: none;
  transition: transform .15s ease-in-out;
}
.hero-art img {
  position: absolute;
  display: block;
  height: auto;
  border-radius: 3px;
}
.hero-art__inside {
  top: 0;
  left: 0;
  width: 79%;
  border: 1px solid var(--line);
  transform: rotate(-1.2deg);
  box-shadow: 0 10px 26px rgba(30, 42, 68, .14), 0 2px 6px rgba(30, 42, 68, .08);
}
.hero-art__cover {
  bottom: 0;
  right: 0;
  /* 33%, and not a point wider. The page render's right 21% is the white
     gradient the build script fades its cut edge into, so the cover's left
     edge has to stay right of that: 100% - 33% = 67%, clear of the 79% x 79%
     the render occupies, with enough left over for what the 3deg rotation
     adds to its corners. At 35.5% the cover lands on "Brewery Lane pavement". */
  width: 33%;
  border: 1px solid rgba(255, 255, 255, .3);
  transform: rotate(3deg);
  box-shadow: 0 18px 40px rgba(30, 42, 68, .3), 0 2px 6px rgba(30, 42, 68, .16);
}
/* A picture that happens to be a link looks like a picture. The caption is the
   affordance, and it earns its line by naming what you are looking at. */
.hero-art__cta {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -28px;
  text-align: center;
  font-size: 1rem;
  font-weight: 700;
  color: var(--navy2);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* Far enough below that the caption's own line box clears the container, and
   the container carries the matching margin so the next row does not close up
   on it. At -28px the top of this text sat inside the frame and the cover
   render ran through it on a phone. */
.hero-art__cta { bottom: -34px; }
.hero-art { margin-bottom: 46px; }
.hero-art:hover { transform: translateY(-3px); }
.hero-art:hover .hero-art__cta,
.hero-art:focus-visible .hero-art__cta { color: var(--navy); }

/* ---------------------------------------------------------- authority strip */

/* Three checkable claims between the promise and the first ask. Each badge
   links to the thing that settles it, so none of them is just a sticker. */
.authority { padding: 0; border-bottom: 1px solid var(--line); }
.authority .wrap { padding-top: 16px; padding-bottom: 16px; }

.authority-strip {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 10px 16px;
}
.authority-strip a {
  display: flex;
  align-items: center;
  gap: 11px;
  height: 100%;
  min-height: 48px;         /* ≥44px tap target, measured not guessed */
  padding: 9px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--navy);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
  transition: border-color .12s ease-in-out, background-color .12s ease-in-out;
}
.authority-strip a:hover,
.authority-strip a:focus-visible {
  border-color: var(--navy2);
  background: #fff;
  color: var(--navy);
}
.authority-strip__mark {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--amber);
  color: #2a1d05;
  font-size: .9rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
}
.authority-strip__sub { color: var(--grey); font-weight: 400; }

/* ----------------------------------------------------------- timeline strip */

.timeline {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 24px;
  margin-top: 28px;
}
.timeline div { border-top: 3px solid var(--amber); padding-top: 14px; }
.timeline dt {
  font-weight: 800;
  font-size: .8rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 6px;
}
.timeline dd { margin: 0; font-size: .97rem; color: #e7eaf1; }

/* ------------------------------------------------------------ trust strip */

.trust {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 24px;
  margin-top: 8px;
}
.trust p {
  font-style: italic;
  color: var(--grey);
  font-size: .96rem;
  border-left: 3px solid var(--line);
  padding-left: 16px;
  margin: 0;
}

/* ------------------------------------------------------------------ prose */

.prose h2 { margin-top: 1.6em; }
.prose h3 { margin-top: 1.5em; }
.prose > :first-child { margin-top: 0; }
.lead { font-size: 1.14rem; color: var(--grey); }

.faq h3 { margin-top: 1.8em; }

/* ----------------------------------------------------------------- footer */

.site-footer {
  background: var(--navy);
  color: #c9d1e0;
  padding: 44px 0 32px;
  font-size: .92rem;
  margin-top: 0;
}
.site-footer a { color: #fff; }
.site-footer h2 {
  color: #fff;
  font-size: .78rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  margin-bottom: 10px;
}
.footer-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  padding: 0 0 24px;
  margin: 0 0 24px;
  list-style: none;
  border-bottom: 1px solid rgba(255, 255, 255, .16);
}
.footer-nav a { text-decoration: none; }
.footer-nav a:hover { text-decoration: underline; }
.footer-legal p { margin-bottom: 1em; max-width: 68ch; }
.footer-meta {
  border-top: 1px solid rgba(255, 255, 255, .16);
  padding-top: 20px;
  margin-top: 8px;
  color: #9fabc2;
  font-size: .86rem;
}
.footer-meta p { margin: 0; }

/* The legal row: Privacy · Terms · Company information · Accessibility · Contact.

   THE SEPARATOR TRAILS ITS ITEM, it does not lead the next one. This row wraps
   to two lines at 375px, and with a leading `li + li::before` the wrap puts a
   middot at the START of the second row, reading as a bullet against nothing.
   As a trailing ::after the break instead leaves "Company information ·" at the
   end of the first row, which reads as "continued" and is what a wrapped list
   of links should look like. Measured in a browser at 375px, not assumed.

   The middot is decorative, so aria-hidden is unnecessary but `content` in a
   pseudo-element is already ignored by most screen readers; the list semantics
   carry the separation for anyone who is not looking at it. */
.footer-legal-links {
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
}
.footer-legal-links li:not(:last-child)::after {
  content: '·';
  opacity: .55;
  margin-left: 12px;
}

/* The trading disclosure line (partials/company-line.ejs).

   COMPACT, AND IT WRAPS RATHER THAN TRUNCATES. Every part of it is there
   because a regulation asks for it, so there is no ellipsis and no "..." on a
   narrow screen: the registered office is the longest field and the one most
   likely to be cut, and it is also the one the Trading Disclosures Regs are
   most specific about. `text-wrap: balance` keeps the wrapped lines even
   instead of leaving one word alone on the last row.

   No longer in the site footer: it appears on /terms and /privacy, on white,
   and inherits its colour from the parent rather than setting one here. */
.company-line {
  margin: 0;
  line-height: 1.55;
  text-wrap: balance;
}
.company-line a { color: inherit; }

/* ------------------------------------------------------------------- misc */

.muted { color: var(--grey); }
.centred { text-align: center; }
.note { font-size: .93rem; color: var(--grey); }

/* Used where a promised asset is not yet on the server. Honest, not hidden. */
.pending {
  border: 2px dashed var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  padding: 22px 24px;
  margin: 24px 0;
  color: var(--grey);
}
.pending > :last-child { margin-bottom: 0; }
.pending .box-title { color: var(--warn); }

/* ---------------------------------------------------- the nav toggle */

/* Created by static/nav.js, which also puts .has-nav-toggle on <html>. Hidden
   at every width until it exists, and hidden again above the breakpoint where
   all nine links fit across the band. Keep 860px in step with nav.js. */
.nav-toggle { display: none; }

@media (max-width: 860px) {
  .has-nav-toggle .nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding: 8px 14px;
    font: inherit;
    font-size: 1rem;
    font-weight: 700;
    color: #fff;
    background: transparent;
    border: 1.5px solid rgba(255, 255, 255, .45);
    border-radius: var(--radius);
    cursor: pointer;
  }
  .has-nav-toggle .nav-toggle:hover { border-color: var(--amber); }

  /* Three bars that fold into a cross, drawn rather than shipped as a glyph:
     the ✕ and ☰ characters are missing, or sized differently, on exactly the
     older phones this audience has. The middle bar is the span itself; the
     other two are its pseudo-elements, which is what lets them rotate. */
  .nav-toggle__bars {
    position: relative;
    display: block;
    width: 18px;
    height: 2px;
    background: #fff;
  }
  .nav-toggle__bars::before,
  .nav-toggle__bars::after {
    content: "";
    position: absolute;
    left: 0;
    width: 18px;
    height: 2px;
    background: #fff;
    transition: transform .15s ease-in-out;
  }
  .nav-toggle__bars::before { top: -6px; }
  .nav-toggle__bars::after  { top: 6px; }

  .site-header--open .nav-toggle__bars { background: transparent; }
  .site-header--open .nav-toggle__bars::before {
    transform: translateY(6px) rotate(45deg);
    background: var(--amber);
  }
  .site-header--open .nav-toggle__bars::after {
    transform: translateY(-6px) rotate(-45deg);
    background: var(--amber);
  }

  .has-nav-toggle .site-nav { display: none; }
  .has-nav-toggle .site-header--open .site-nav {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex-basis: 100%;
    gap: 0;
    margin: 6px 0 4px;
    border-top: 1px solid rgba(255, 255, 255, .18);
  }
  .has-nav-toggle .site-header--open .site-nav__signout { display: block; }
  .has-nav-toggle .site-header--open .site-nav__signout .navbtn { width: 100%; text-align: left; }
  .has-nav-toggle .site-header--open .site-nav a,
  .has-nav-toggle .site-header--open .site-nav .navbtn {
    padding: 13px 4px;      /* ≥44px tall, full width of the band */
    font-size: 1rem;
    border-bottom: 1px solid rgba(255, 255, 255, .12);
  }
  /* A full-width underline on a stacked row reads as a divider, not as "you are
     here", and there is already a divider under every row. In the open menu the
     amber rule moves to the leading edge instead. */
  .has-nav-toggle .site-header--open .site-nav a::after,
  .has-nav-toggle .site-header--open .site-nav .navbtn::after { display: none; }
  .has-nav-toggle .site-header--open .site-nav a:hover,
  .has-nav-toggle .site-header--open .site-nav a:focus-visible,
  .has-nav-toggle .site-header--open .site-nav a[aria-current="page"],
  .has-nav-toggle .site-header--open .site-nav .navbtn:hover,
  .has-nav-toggle .site-header--open .site-nav .navbtn:focus-visible {
    box-shadow: inset 3px 0 0 var(--amber);
    padding-left: 12px;
  }
}

/* ------------------------------------------------------------ responsive */

@media (max-width: 860px) {
  .hero .wrap { grid-template-columns: 1fr; gap: 32px; }
  .hero h1 { font-size: 2.1rem; }
  h1 { font-size: 2rem; }
  h2 { font-size: 1.5rem; }
  .hero-art { max-width: 380px; }
}

/* The steps run out of room for a horizontal rule of three well before the
   phone breakpoint. Stacked, the numerals carry the sequence on their own. */
@media (max-width: 720px) {
  .hero-steps { flex-direction: column; align-items: flex-start; gap: 12px; }
  .hero-steps li + li::before { display: none; }
}

@media (max-width: 560px) {
  body { font-size: 16px; }
  section { padding: 40px 0; }
  .btn { display: block; width: 100%; }
  .btn-row { flex-direction: column; align-items: stretch; }
  .step { grid-template-columns: 40px 1fr; gap: 14px; }
  .step::before { width: 38px; height: 38px; font-size: 1.05rem; }

  /* Tap targets. A secondary link sitting beside a full-width button is a 26px
     line of text otherwise, which is half the floor. */
  .btn-row > a:not(.btn) {
    display: block;
    text-align: center;
    min-height: 44px;
    padding: 11px 8px;
  }
  /* At 375px the header carries the lockup and the Menu button on one row.
     THE TYPE GIVES WAY, THE ICON DOES NOT: the mark is what identifies the site
     at a glance, and it is already the smallest thing in the bar. Shaving the
     wordmark's size and letter-spacing buys back more width than shrinking a
     30px square ever would, and costs nothing anyone notices. */
  .wordmark {
    padding: 8px 0;
    gap: 9px;
    font-size: 1rem;
    letter-spacing: .09em;
  }

  /* The hero art is the one thing on the page that must not shrink below
   * legibility: the whole reason the inside page is there is that a stranger
   * can read this venue's own exits in it. At 375px there are only 335px to
   * work with, so the page render takes all of them and the cover drops to a
   * corner. That re-stacks the composition, so the overlap has to be re-checked
   * against the same rule as the desktop one: the cover may only sit on the
   * white the build script fades the cut edges into, which is the render's
   * right 20% and bottom 17%. At 26% wide, tucked into the bottom right corner
   * of a container 500/480 as tall as it is wide, it does. */
  .hero-art {
    max-width: 100%;
    aspect-ratio: 480 / 500;
  }
  .hero-art__inside { width: 100%; }
  .hero-art__cover { width: 26%; }
}

/* Reduced motion respected — there is nothing beyond subtle hover anyway. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

/* -------------------------------------------------------------- printing */

@media print {
  /* The preview banner is a screen warning about typing into the site, which a
     sheet of paper cannot be. It goes with the header and the footer. */
  .site-header, .site-footer, .btn-row, .skip-link, .preview-band { display: none; }
  body { font-size: 11pt; }
  a { text-decoration: none; color: var(--ink); }
}

/* ==========================================================================
   The guided intake (/check and /assess).

   Ported from the prototype wizard's stylesheet (proto/static/index.html
   <style> block), which is the approved look. Every rule is scoped under
   `.wizard` because the prototype used bare `.card`, `.row`, `.nav`, `.add`
   and `.pill` class names that the marketing pages already use for different
   components — unscoped, the price cards and the wizard cards fight.

   Layout matches the marketing pages: a <section> for the vertical rhythm and
   `.wrap--narrow` for the 760px column, which is the prototype's `.shell`
   width. The wizard itself no longer sets its own width or padding.
   ========================================================================== */

/* The prototype puts the whole assessment on the tinted background with white
   question cards floating on it. That contrast is what separates one question
   from the page; on plain white the cards disappear. */
.wizard-page { background: var(--bg); }

.wizard-title {
  font-size: 1.9rem;
  margin-bottom: .25em;
}
.wizard-intro {
  color: var(--grey);
  font-size: 1.05rem;
  margin-bottom: 26px;
  max-width: 60ch;
}

/* ---- progress ---------------------------------------------------------- */

.wizard .progress { display: flex; gap: 6px; margin: 0 0 6px; }
.wizard .progress i { flex: 1; height: 5px; border-radius: 3px; background: var(--line); }
.wizard .progress i.on { background: var(--amber); }

.wizard .stepname {
  font-size: 12px;
  color: var(--grey);
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 14px;
}

/* ---- the question card ------------------------------------------------- */

.wizard .card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 26px 28px;
  margin-bottom: 16px;
  box-shadow: 0 1px 4px rgba(30, 42, 68, .06);
  display: block;          /* the marketing .card is a flex column */
}
.wizard .card h2 { font-size: 19px; color: var(--navy); margin-bottom: 6px; }
.wizard .card p { margin-bottom: 0; font-size: inherit; }

/* The "why we ask" explainer — plain-English reason plus the guidance para. */
.wizard .why {
  background: var(--bg);
  border-left: 3px solid var(--amber);
  padding: 10px 14px;
  font-size: 13px;
  color: var(--grey);
  margin: 12px 0 18px;
  border-radius: 0 4px 4px 0;
}

/* ---- fields ------------------------------------------------------------ */

.wizard label {
  display: block;
  font-weight: 600;
  font-size: 13.5px;
  margin: 14px 0 4px;
  color: var(--navy2);
}
.wizard label .opt { font-weight: 400; color: var(--grey); }

.wizard input[type=text],
.wizard input[type=email],
.wizard input[type=number],
.wizard input[type=date],
.wizard select,
.wizard textarea {
  width: 100%;
  padding: 11px;
  border: 1.5px solid var(--line);
  border-radius: 5px;
  font: inherit;
  /* 16px minimum: anything smaller makes iOS Safari zoom on focus, and this
     audience fills the form on a phone while walking the building. */
  font-size: 16px;
  min-height: 46px;        /* ≥44px tap target */
  background: #fff;
  color: var(--ink);
}
.wizard textarea { min-height: 70px; resize: vertical; }
.wizard input:focus, .wizard select:focus, .wizard textarea:focus { border-color: var(--navy2); }

.wizard .row { display: flex; gap: 12px; flex-wrap: wrap; }
.wizard .row > div { flex: 1; min-width: 180px; }

.wizard .check { display: flex; align-items: flex-start; gap: 9px; margin: 12px 0; font-size: 14px; }
.wizard .check input { margin-top: 3px; width: 18px; height: 18px; flex: 0 0 auto; min-height: 0; }

/* ---- repeatable rows (exits, safer areas, entry points, roster) --------- */

.wizard .rep {
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 12px 14px 14px;
  margin: 10px 0;
  background: #fbfcfe;
  position: relative;
}
.wizard .rep .del {
  position: absolute;
  top: 8px;
  right: 10px;
  background: none;
  border: none;
  color: var(--warn);
  cursor: pointer;
  font-size: 13px;
  font-weight: 700;
  padding: 4px 6px;
}
/* The first label in a row sits under the absolutely-positioned remove link,
   so the row needs a little headroom to keep them from colliding. */
.wizard .rep > .row:first-child label { margin-top: 4px; }

/* THE ADD BUTTON ENDS ITS FIELD GROUP.

   `display: block` and a real margin under it, and both halves are load
   bearing. A button is inline-block by default, and every label on this form
   that carries a ⓘ is inline-block too, so "+ Add exit" and "Primary assembly
   point" sat on the same line hard against each other: two inline boxes with
   nothing telling them they belong to different questions.

   Fixed here rather than on the exits step, because there are five of these
   lists across the nine steps and they would collide one at a time, each
   looking like its own small bug. The rule is the pattern: a list of repeated
   rows finishes with its add button, and whatever comes next is a new group and
   starts on its own line with 24px between them.

   `width: fit-content` keeps the block from stretching across the column, which
   is what a block-level button would otherwise do and would read as a primary
   action rather than the quiet one it is. */
.wizard .add {
  display: block;
  width: fit-content;
  background: none;
  border: 1.5px dashed var(--navy2);
  color: var(--navy2);
  border-radius: 5px;
  padding: 11px 16px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  margin: 6px 0 24px;
  min-height: 44px;
}
/* Nothing follows the last add button on a step, so the space under it would
   be a gap at the bottom of the card. Only trim it when it really is last. */
.wizard .card > .add:last-child { margin-bottom: 6px; }
.wizard .add:hover { background: #fff; }

/* ---- navigation: ghost Back left, navy Continue right ------------------ */

.wizard .nav {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin: 18px 0 0;
  gap: 12px;
}
.wizard button.primary {
  background: var(--navy);
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 14px 26px;
  font: inherit;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  min-height: 48px;
  /* Back sits left, Continue right — even when Back is hidden on step 1. */
  margin-left: auto;
}
.wizard button.primary:hover { background: var(--navy2); }
.wizard button.primary:disabled { background: var(--grey); cursor: default; }
.wizard button.ghost {
  background: none;
  border: none;
  color: var(--grey);
  font: inherit;
  font-size: 14px;
  cursor: pointer;
  padding: 12px 6px;
  min-height: 44px;
}
.wizard button.ghost:hover { color: var(--navy); }
.wizard .ghost-link { color: var(--navy2); font-size: 14px; }

/* ---- errors and screen-outs -------------------------------------------- */

.wizard .err {
  background: #fdf0ec;
  border: 1px solid #e8c4b5;
  color: var(--warn);
  border-radius: 6px;
  padding: 12px 16px;
  font-size: 14px;
  margin-bottom: 14px;
  display: none;
}

.wizard .screenout {
  background: #fff;
  border: 2px solid var(--amber);
  border-radius: 8px;
  padding: 24px 26px;
  margin-top: 10px;
}
.wizard .screenout h2 { font-size: 19px; color: var(--navy); margin-bottom: 6px; }
.wizard .screenout p { margin-bottom: 10px; }
.wizard .pill-ok {
  display: inline-block;
  background: #eaf5ef;
  border: 1px solid #bcdccb;
  color: var(--ok);
  border-radius: 20px;
  font-size: 12.5px;
  font-weight: 700;
  padding: 4px 14px;
  margin-bottom: 14px;
}
.wizard .benefits { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; margin: 16px 0 6px; }
.wizard .benefits > div { display: flex; gap: 8px; font-size: 13.5px; align-items: flex-start; }
.wizard .benefits b { color: var(--navy); }
.wizard .benefits .tick { color: var(--ok); font-weight: 800; flex: 0 0 auto; }
.wizard .legal-note { font-size: 11.5px; color: var(--grey); margin-top: 14px; line-height: 1.45; }

/* The free scope record on the under-200 screen. Sits between the voluntary
   offer and the legal note, deliberately as a link rather than a second button:
   the pack is the choice being offered here, and the record is the thing they
   are owed either way. Two buttons side by side would make them look like
   alternatives. */
.record-link { margin-top: 16px; font-size: 14px; line-height: 1.5; }
.record-link a { font-weight: 700; }
.record-link span { display: block; font-size: 12px; color: var(--grey); margin-top: 2px; }

@media (max-width: 560px) {
  .wizard .benefits { grid-template-columns: 1fr; }
  .wizard .card { padding: 20px 18px; }
  .wizard .nav { flex-wrap: wrap; }
  .wizard button.primary { width: 100%; margin-left: 0; }
}
/* The account link, rightmost in the header nav. Not part of the shared `nav`
   array because website-content.md §11 fixes the footer nav order and this
   must not appear there. */
.site-nav__account { font-weight: 700; }

/* ------------------------------------------------------- account area */

/* Status pills on the dashboard. Colour is never the only signal: the word
   inside carries the meaning for anyone who cannot separate the hues. */
.pill {
  display: inline-block;
  font-size: .74rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 20px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--navy);
  white-space: nowrap;
}
.pill--ok   { background: #eaf5ef; border-color: #bcdccb; color: var(--ok); }
.pill--warn { background: #fdf0ec; border-color: #e8c4b5; color: var(--warn); }
/* "Ready to create": the one state where the venue is waiting on nothing but
   their own click, so it takes the accent the rest of the site spends on
   "your bit". --amber-text rather than --amber, which is a fill colour and
   fails contrast as small uppercase type. */
.pill--go   { background: #fdf4e6; border-color: #f0d5a8; color: var(--amber-text); }

/* The dashboard table is data, not the label/value grid .kv is built for, so
   the first column loses its fixed width and tinted header treatment. */
.kv.dash th { width: auto; }
/* The "Next step" cell: one button, then the quieter links under it. The margin
   is on the button rather than the links because the button is the thing that
   must not collide with them, and it is the only element in the cell that is
   always present. */
.kv.dash .btn--sm { margin: 0 0 4px; white-space: nowrap; }

/* "Annual review due" is a compliance date and the renewal beneath it is a
   billing one. The review line carries the amber the rest of the site spends on
   "your bit"; the renewal stays grey, because it is our arrangement rather than
   their duty. Colour is never the only difference: both lines say in words
   which they are. */
.note--review { color: var(--amber-text); font-weight: 700; }

/* "Something changed? Update this pack" starts an amendment, so it is a form
   and its control is a button. It reads as the quiet link it sits among rather
   than as a third button in the cell: the loud one in this cell is whatever the
   venue's state says to do next. */
.amend-form { margin: 6px 0 0; }
button.linkish {
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-size: .93rem;
  color: var(--navy2);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  text-align: left;
}
button.linkish:hover { color: var(--navy); }
button.linkish:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }

/* ------------------------------------------- one block per venue, below the table

   The documents panel and the checklist for one building, kept together and
   named, once the account has more than one. Before this they were an
   unlabelled run of panels under a table, and on a two-hall account it was
   genuinely unclear which pack belonged to which building. These documents name
   real exits and real key locations; the wrong pack on the wrong wall is worse
   than no pack. */
.venue-block { margin: 34px 0 0; }
.venue-block--named {
  border-top: 3px solid var(--navy);
  padding-top: 6px;
}
.venue-block__name {
  margin: 18px 0 4px;
  font-size: 1.32rem;
  line-height: 1.3;
}
.venue-block__where {
  display: block;
  font-size: .93rem;
  font-weight: 400;
  color: var(--grey);
  margin-top: 3px;
}
.kv.dash td { font-size: .93rem; }
.kv.dash tr:first-child th { background: var(--navy); color: #fff; font-size: .78rem;
  letter-spacing: .06em; text-transform: uppercase; }

/* ON A PHONE THE ROWS BECOME CARDS.

   Six columns in a horizontally scrolling box puts the last one off the right
   edge, and the last one is now the button: Continue, See what to fix, Create
   my documents. A dashboard whose only action is reached by scrolling sideways
   is a dashboard with no action on the device this product is actually used on.

   So each row stacks, each cell keeps its column heading through data-label,
   and the header row goes because every cell is carrying its own label. The
   opt-in class is on the wrapper rather than on .table-scroll itself, because
   the other tables in the account area are two-column label/value grids that
   already read fine narrow and would be made worse by this. */
@media (max-width: 720px) {
  .table-scroll--cards { overflow-x: visible; }
  .table-scroll--cards .kv.dash,
  .table-scroll--cards .kv.dash tbody,
  .table-scroll--cards .kv.dash tr,
  .table-scroll--cards .kv.dash td { display: block; width: auto; }
  .table-scroll--cards .kv.dash { border: 0; margin: 16px 0 0; }
  .table-scroll--cards .kv.dash tr:first-child { display: none; }
  .table-scroll--cards .kv.dash tr {
    border: 1.5px solid var(--line);
    border-radius: var(--radius);
    background: #fff;
    margin: 0 0 16px;
    padding: 6px 0;
  }
  /* The label goes ABOVE the value, not beside it. A two-column grid here would
     put every <br> and <span> in these cells into a grid slot of its own, and
     the second line of a cell would land under the label. The cells are written
     as prose with line breaks in them, so the layout has to be one that does not
     care how many children they have. */
  .table-scroll--cards .kv.dash td {
    border-bottom: 0;
    padding: 8px 16px;
  }
  .table-scroll--cards .kv.dash td::before {
    content: attr(data-label);
    display: block;
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--grey);
    margin-bottom: 3px;
  }
  /* The venue's own name is the card's heading, not a labelled field. */
  .table-scroll--cards .kv.dash td:first-child {
    display: block;
    border-bottom: 1px solid var(--line);
    padding-bottom: 10px;
    margin-bottom: 4px;
    font-size: 1.05rem;
  }
  .table-scroll--cards .kv.dash td:first-child::before { content: none; }
}

/* Forms outside the wizard: the pay email field and the account resend picker. */
.pay-form, .resend-form { margin: 18px 0 28px; }
.pay-form label, .resend-form label {
  display: block;
  font-weight: 700;
  font-size: .9rem;
  color: var(--navy2);
  margin-bottom: 6px;
}
.pay-form input[type=email],
.resend-form select {
  width: 100%;
  max-width: 420px;
  padding: 12px;
  border: 1.5px solid var(--line);
  border-radius: 5px;
  font: inherit;
  font-size: 16px;      /* stops iOS Safari zooming on focus */
  min-height: 46px;
  background: #fff;
  color: var(--ink);
  margin-bottom: 14px;
  display: block;
}
.pay-form input:focus, .resend-form select:focus { border-color: var(--navy2); }

/* The save point, mid-walkthrough. Styled as a wizard card so it reads as part
   of the flow rather than an interruption bolted on top. */
.wizard .savepoint input[type=email] { max-width: 420px; }
.wizard .savepoint-actions {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 18px;
}
.wizard .savepoint-actions button.primary { margin-left: 0; }

/* ---------------------------------------------------------------------------
   The /example gallery: both sample packs, every page, as images.

   These are not downloads and there is no PDF behind them. The SAMPLE mark is
   baked into the pixels at render time (scripts/build-sample-gallery.js), so
   nothing here is holding it on: hiding a rule in dev tools reveals nothing.
   --------------------------------------------------------------------------- */

.gallery-head { margin-bottom: 26px; }

.gallery {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 20px;
}

/* The whole page is the control, so the tap target is the thumbnail itself. */
.gallery-page {
  display: block;
  width: 100%;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 5px;
  background: #fff;
  overflow: hidden;
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  box-shadow: 0 1px 4px rgba(30, 42, 68, .08);
}
.gallery-page:hover,
.gallery-page:focus-visible {
  border-color: var(--navy2);
  box-shadow: 0 3px 12px rgba(30, 42, 68, .18);
}
/* aspect-ratio rather than width/height attributes: the render scale is a
   build-script constant, but A4 is A4 and will not drift. No layout shift as
   the lazy-loaded thumbnails arrive. */
.gallery-page img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 210 / 297;
  background: var(--bg);
}
.gallery-n {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 6px 8px;
  padding: 9px 11px;
  border-top: 1px solid var(--line);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--navy2);
}

/* The blurred pages, labelled. The blur itself is in the pixels of the file and
   nothing here is holding it on, so nothing here can take it off: this only
   says out loud what the image already is. Removing these rules reveals
   nothing, because there is no sharp copy of those pages on the server. */
/* "Page 10" must never break across two lines. In a 160px column at 375px the
   number and the state chip together are a couple of pixels over the width, and
   without this the row splits mid-label into "PAGE" / "10". Let the chip drop
   to its own line instead. */
.gallery-n__no { white-space: nowrap; }

.gallery-n__state {
  flex: 0 0 auto;
  font-size: .72rem;
  font-weight: 800;
  letter-spacing: .06em;
  padding: 2px 7px;
  border-radius: 20px;
  background: var(--bg);
  border: 1px solid var(--line);
  color: var(--grey);
}
/* A readable page is worth opening and a blurred one is worth scanning, and the
   grid should say which is which before you click. The readable ones keep the
   full-strength card; the blurred ones sit back a little. */
.gallery-page--obscured { border-style: dashed; }
.gallery-page--obscured img { opacity: .92; }

/* The viewer. A native <dialog>, so Escape, the backdrop and focus trapping
   are the browser's job rather than ours. */
.viewer {
  width: 100%;
  max-width: min(94vw, 880px);
  max-height: 94vh;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: transparent;
  overflow: auto;              /* a full A4 page is taller than the viewport */
  overscroll-behavior: contain;
  /* View-only. There is no text layer to select in an image, so this is not
     what stops anyone copying the pack; it stops the drag-select smear you get
     when you swipe across a picture trying to scroll it, which on a phone is
     every second attempt. The document itself is protected by being pixels, by
     the baked watermark, and by ten of its thirteen pages being blurred. */
  user-select: none;
  -webkit-user-select: none;
}
/* Same on the grid: dragging a thumbnail should scroll the page, not lasso it. */
.gallery,
.gallery-page {
  user-select: none;
  -webkit-user-select: none;
}
.viewer::backdrop { background: rgba(20, 26, 40, .82); }

.viewer-bar {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  padding: 10px 12px;
  background: var(--navy);
  color: #fff;
  border-radius: 6px 6px 0 0;
}
.viewer-label { font-weight: 700; font-size: .9rem; }
.viewer-controls { display: flex; gap: 8px; }
.viewer-btn {
  font: inherit;
  font-size: .85rem;
  font-weight: 700;
  min-height: 44px;            /* the tap floor, same as everywhere else */
  padding: 0 14px;
  border-radius: 5px;
  border: 1px solid rgba(255, 255, 255, .4);
  background: transparent;
  color: #fff;
  cursor: pointer;
}
.viewer-btn:hover { background: rgba(255, 255, 255, .14); }
/* Amber fill on a navy surface, per the button colour rule above. */
.viewer-close { background: var(--amber); color: #2a1d05; border-color: var(--amber); }

.viewer-img {
  display: block;
  width: 100%;
  height: auto;
  background: #fff;
  border-radius: 0 0 6px 6px;
}

@media (max-width: 560px) {
  .gallery { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 14px; }
  .viewer-bar { justify-content: center; }
}

/* ==========================================================================
   THE PHONE READING FLOOR — deliberately the last block in this file.

   Every rule here is one line: on a small screen, text is at least 16px. It
   sits at the bottom because several of the components it corrects are defined
   *after* the responsive section above (the whole wizard block, the account
   area, the gallery), and at equal specificity the later rule wins. Moving this
   up the file silently un-fixes the wizard, which is the single most important
   thing on this site to get right on a phone: it is filled in one-handed, while
   walking a building.

   THE EXCEPTIONS, and why they are exceptions: the uppercase, letter-spaced
   chips and eyebrows (.tag, .box-title, .timeline dt, .pill, .gallery-n, the
   footer's section heading, .notice-band, .wizard .stepname). Those are not
   text to read, they are markers on the text below them. Set at 16px they
   out-shout the headings they introduce and the page loses its hierarchy, so
   they go to 12.5px, which is the legibility floor for uppercase at this
   weight rather than the reading floor for prose. Logged in DECISIONS.md.
   ========================================================================== */

@media (max-width: 560px) {
  .card p,
  .card .verdict,
  .note,
  .lead,
  .ticks li,
  .action-card p,
  .timeline dd,
  .trust p,
  .kv,
  .kv.dash td,
  .site-nav a,
  .site-nav .navbtn,
  .footer-nav a,
  .site-footer,
  .footer-legal p,
  .footer-meta,
  .price small,
  .card .price small,
  .viewer-label,
  .viewer-btn,
  .wizard .card p,
  .wizard label,
  .wizard .why,
  .wizard .check,
  .wizard .benefits > div,
  .wizard .legal-note,
  .wizard .err,
  .wizard .add,
  .wizard .ghost-link,
  .wizard button.primary,
  .wizard button.ghost,
  .wizard .screenout p,
  .wizard .pill-ok,
  .pay-form label,
  .resend-form label { font-size: 1rem; }

  .tag,
  .box-title,
  .timeline dt,
  .pill,
  .gallery-n,
  .gallery-n__state,
  .site-footer h2,
  .notice-band,
  .wizard .stepname { font-size: .78rem; }

  /* A bare text link on its own line is a 22px target. These are the two that
     stand alone rather than sitting inside a row that already has a floor. */
  .wizard .ghost-link,
  .card .card-cta a {
    display: inline-block;
    min-height: 44px;
    padding: 11px 0;
  }
}

/* The autosave marker, beside the step name in the wizard.

   Quiet by design: it is confirmation, not an announcement, and it appears
   after a pause in typing rather than on every keystroke. It matters most to
   somebody standing in a boiler room with one bar of signal who needs to know
   their answers are safe before they walk away from the field.

   role="status" and aria-live="polite" are on the element, so a screen reader
   hears "Saved" without being interrupted mid-sentence. The failed state is the
   only one that is coloured, and it says what happens next rather than just
   that something went wrong. */
.savedmark {
  margin-left: 10px;
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: none;
  color: var(--grey);
  opacity: 0;
  transition: opacity .18s ease-in-out;
}
.savedmark.is-saving, .savedmark.is-retrying { opacity: .75; }
.savedmark.is-saved { opacity: 1; color: var(--ok); }
.savedmark.is-failed { opacity: 1; color: var(--warn); text-transform: none; }

/* ------------------------------------------------------------------ the blog

   No new visual language, deliberately. The listing is the site's own prose
   column with dated links in it, and a post is the prose column with the same
   headings, lists and rules every other page uses. A compliance product's blog
   is not the place to introduce a card grid and a second type scale. */

.post-list { list-style: none; padding: 0; margin: 28px 0 0; }

.post-item {
  padding: 0 0 22px;
  margin: 0 0 22px;
  border-bottom: 1px solid var(--line);
}
.post-item:last-child { border-bottom: 0; }
.post-item__title { margin: 0 0 4px; font-size: 1.32rem; line-height: 1.3; }
.post-item__date, .post-date {
  margin: 0 0 8px;
  font-size: .88rem;
  color: var(--grey);
}
.post-item__desc { margin: 0; }

/* The date under a post's h1, before the prose starts. */
.post-date { margin: 0 0 26px; }

/* Sources. Long URLs, so they wrap rather than pushing the column wide. */
.post-sources { padding-left: 20px; }
.post-sources li { margin-bottom: 6px; word-break: break-word; font-size: .93rem; }

/* The standing line at the foot of every post. Quiet, and always there. */
.post-standing {
  margin-top: 28px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

/* --------------------------------------------------- "coming before 2027"

   partials/attestation-soon.ejs, on the four pages that describe staff
   attestation in the present tense. It has to be visible enough that nobody can
   say they missed it and quiet enough that it does not read as a warning: the
   feature is included in the price and it is coming, which is good news with a
   date on it, not a caveat. Amber, like everything else on this site that means
   "your bit"; never the warning red. */
.soon-note {
  display: block;
  margin: 14px 0 0;
  padding: 11px 14px;
  border: 1px solid #f0d5a8;
  border-left: 4px solid var(--amber);
  border-radius: 6px;
  background: #fdf9f2;
  color: var(--navy2);
  font-size: .93rem;
  line-height: 1.5;
}
.soon-note__tag {
  display: inline-block;
  margin-right: 7px;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--amber-text);
}

/* The contact address, big enough to read off the screen and copy onto paper. */
.contact-address {
  font-size: 1.24rem;
  font-weight: 700;
  margin: 4px 0 12px;
  word-break: break-word;
}

/* The one and two venue rule, under the 3+ card it qualifies. */
.price-rule {
  margin: 10px 0 0;
  padding-top: 10px;
  border-top: 1px solid var(--line);
  font-size: .93rem;
  color: var(--navy2);
}

@media (max-width: 560px) {
  .soon-note, .price-rule { font-size: 1rem; }
}

/* ---------------------------------------------------------------- field help

   The ⓘ beside a label, and the panel it opens. One component, used by the
   wizard, the account forms, the review gate, sign-in and the pay screen; the
   words all live in server/lib/help-text.js.

   NO HOVER. Not as an oversight and not as a progressive extra: this form is
   filled in on a phone, one-handed, walking a building, and hover does not
   exist there. It opens on a tap and closes on a tap, and it is a <details>
   element so that is true before any JavaScript loads. static/field-help.js
   only adds closing when you tap somewhere else.

   The panel is absolutely positioned rather than pushing the form down, so
   opening one does not move the field you were about to type in out from under
   your thumb. Its width is capped against the viewport, not the column, because
   the ⓘ in the third box of a three-across row would otherwise open off the
   right-hand edge of a phone. */

/* THE LABEL HAS TO LET THE ⓘ SIT BESIDE IT.

   Nearly every label on this site is display:block, which puts an inline-block
   ⓘ after it on a line of its own, floating above the input with nothing to
   attach it to. This pulls only the labels that actually have one back to
   inline-block; the input after it is width:100% and wraps to the next line by
   itself, so the field looks exactly as it did.

   `:has()` rather than a class on the label, so that adding help to a field is
   one call in the markup and nothing else. Where it is not supported the ⓘ
   drops to its own line and still opens, which is worse-looking and not
   broken. */
/* ================= THE FORM GROUP RULE, and it is one rule =================

   A label sits on its own line, with its ⓘ beside the label text, and the input
   is full width underneath. Everywhere. No field on this site may render with
   its label and its input side by side.

   WHY IT NEEDED SAYING. Text inputs are inline-block by default, and the ⓘ made
   labels inline-block too, so "Email address", the ⓘ and the input all sat on
   one line at the save point with the input squeezed into whatever was left.
   The wizard's own inputs already carried width:100%, which is not the same
   thing: an inline box asked for 100% still starts where the line has got to,
   so it either overflows or wraps depending on the label's length. The fix is to
   stop them being inline at all.

   Checkboxes and radios are excluded, and that exclusion is the whole reason
   this is a list rather than one selector. A block-level checkbox breaks every
   tick row on the site, and the tick rows are how somebody says their venue has
   lone working. Hidden inputs are excluded because a display rule on them is
   meaningless and it keeps the intent of the selector readable.

   Global rather than per-form, because "the sign-in page was fine and the
   account page was not" is the failure this replaces. */
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
select,
textarea {
  display: block;
}

label:has(+ details.fh) {
  display: inline-block;
  /* Room kept for the ⓘ on the same line. Without it a label long enough to
     wrap fills the row and pushes its own help button onto a line of its own,
     which is most labels on a phone. */
  max-width: calc(100% - 30px);
}

.fh {
  display: inline-block;
  position: relative;
  /* Baseline, not middle. An inline-block takes the baseline of its LAST line,
     so on a label that has wrapped to two lines the ⓘ lands at the end of the
     second line where the writing stops, rather than floating halfway up the
     side of the block. */
  vertical-align: baseline;
  margin-left: 5px;
}

.fh-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 1.5px solid var(--line);
  border-radius: 50%;
  background: #fff;
  color: var(--grey);
  font-size: 12px;
  font-weight: 800;
  font-style: italic;
  line-height: 1;
  cursor: pointer;
  user-select: none;
  /* A finger, not a mouse pointer. The circle stays 18px so it does not shout
     next to a 13.5px label, and the padding around it does the work: the tap
     target is 44px square and invisible. */
  position: relative;
}
.fh-btn::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
/* The disclosure triangle, gone. It is a circled i or it is nothing. */
.fh-btn::-webkit-details-marker { display: none; }
.fh-btn { list-style: none; }
.fh-btn::marker { content: ''; }

.fh-btn:hover, .fh[open] .fh-btn {
  border-color: var(--navy2);
  color: var(--navy);
  background: var(--bg);
}
.fh-btn:focus-visible { outline: 3px solid var(--amber); outline-offset: 2px; }

.fh-body {
  position: absolute;
  z-index: 30;
  top: calc(100% + 8px);
  left: -6px;
  width: min(22rem, calc(100vw - 3rem));
  padding: 12px 14px;
  border: 1.5px solid var(--line);
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 6px 22px rgba(30, 42, 68, .16);
  color: var(--navy2);
  font-size: 15px;
  font-weight: 400;
  font-style: normal;
  line-height: 1.5;
  /* Labels above these are 13.5px and semi-bold; the panel is prose and reads
     as prose. Never smaller than 15px: it is the explanation for somebody who
     has already told us they did not understand the label. */
  text-transform: none;
  letter-spacing: normal;
}

/* Near the right-hand edge the panel opens leftwards instead. Set by nothing
   but the markup order: the last field in a row is where this bites. */
.fh--left .fh-body { left: auto; right: -6px; }

/* ------------------------------------------- the "Getting protected" checklist

   One per venue on /dashboard. Amber is the progress bar and nothing else here:
   it is the accent that says "your bit", and spending it on ticks as well would
   leave the bar competing with six rows of the same colour. Done ticks are
   --ok green, which is the same green the paid pill already uses.

   Deliberately no percentage anywhere in this component. See lib/checklist.js. */

.chk {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: 22px 24px;
  margin: 22px 0;
}

.chk-head {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 28px;
  align-items: flex-start;
  justify-content: space-between;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--line);
}

.chk-title {
  font-weight: 700;
  color: var(--navy);
  margin: 0 0 6px;
}
.chk-venue {
  font-weight: 400;
  color: var(--grey);
  margin-left: 10px;
}
.chk-note {
  font-size: .93rem;
  color: var(--grey);
  margin: 0;
  max-width: 52ch;
}

.chk-progress { min-width: 160px; }
.chk-count {
  font-size: .93rem;
  font-weight: 700;
  color: var(--navy);
  margin: 0 0 7px;
}
/* What the count does NOT include, said out loud. The two scheduled rows (the
   SIA notification and the annual review) are not tasks and are not in the
   denominator, and a full bar sitting above two rows that look untouched reads
   as a broken count until something says otherwise. */
.chk-scheduled {
  font-size: .82rem;
  color: var(--grey);
  margin: 7px 0 0;
  line-height: 1.4;
}
/* Slim on purpose. A thick bar reads as a score. */
.chk-bar {
  height: 6px;
  border-radius: 3px;
  background: var(--bg);
  border: 1px solid var(--line);
  overflow: hidden;
}
.chk-bar span {
  display: block;
  height: 100%;
  background: var(--amber);
}

.chk-list { list-style: none; margin: 0; padding: 0; }

.chk-item {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 15px 0;
  border-bottom: 1px solid var(--line);
}
.chk-item:last-child { border-bottom: 0; padding-bottom: 0; }

.chk-mark { flex: 0 0 auto; padding-top: 1px; }
.chk-form { margin: 0; }

/* The tick: a 26px square that is also a 44px touch target via the button's
   own padding on small screens (the rule at the foot of this block). */
.chk-tick {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: 2px solid var(--line);
  border-radius: 5px;
  background: #fff;
  color: transparent;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1;
}
.chk-tick.is-on {
  background: var(--ok);
  border-color: var(--ok);
  color: #fff;
}
.chk-tick--btn {
  cursor: pointer;
  font-family: inherit;
  transition: border-color .12s ease-in-out, background-color .12s ease-in-out;
}
.chk-tick--btn:hover { border-color: var(--navy); }
.chk-tick--btn.is-on:hover { background: #26643f; border-color: #26643f; }

/* Auto-ticked rows are ours, not theirs. Flat, no border, no hover: nothing
   about them should look pressable, because pressing them does nothing. */
.chk-item--auto .chk-tick:not(.is-on) {
  border-style: dashed;
}

/* Informational rows get a dot rather than an empty box. An empty box next to
   "SIA notification" reads as an outstanding job, and it is not one. */
.chk-dot {
  display: block;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--bg);
  border: 1.5px solid var(--line);
  box-shadow: inset 0 0 0 6px #fff, inset 0 0 0 9px var(--line);
}

.chk-body { flex: 1 1 auto; min-width: 0; }
.chk-item-title {
  font-weight: 700;
  color: var(--navy);
  margin: 0 0 3px;
}
.chk-item.is-done .chk-item-title { color: var(--grey); }
.chk-why {
  font-size: .93rem;
  color: var(--grey);
  margin: 0;
  max-width: 62ch;
}
.chk-detail {
  font-size: .93rem;
  color: var(--navy);
  margin: 6px 0 0;
}
.chk-soon {
  font-size: .88rem;
  color: var(--amber-text);
  margin: 6px 0 0;
}
.chk-item--info .chk-detail { color: var(--grey); }
.chk-action { margin: 10px 0 0; }

.btn--sm {
  font-size: .93rem;
  padding: 10px 18px;
  min-height: 44px;
}

@media (max-width: 640px) {
  .chk { padding: 18px 16px; }
  .chk-progress { min-width: 100%; }
  .chk-note, .chk-why, .chk-detail, .chk-soon, .chk-count, .chk-scheduled { font-size: 1rem; }
  /* 44px target on a phone without making the square itself bigger. */
  .chk-tick--btn { width: 44px; height: 44px; }
  .chk-mark { padding-top: 0; }
}

/* ------------------------------------------------------------- admin area

   The operator's screens. They use the site's own design system rather than a
   second one, because a separate admin theme is a second stylesheet to keep in
   step, and this one already knows what a table and a pill look like.

   What IS different is the band: slim, navy, above the header, on every admin
   page and nothing else. The operator is looking at customers' buildings on
   these pages, and the one mistake that must be impossible is thinking an admin
   screen is a customer screen. */

.admin-band {
  background: var(--navy);
  color: #fff;
  border-bottom: 3px solid var(--amber);
}
.admin-band__in {
  max-width: 1180px;
  margin: 0 auto;
  padding: 7px 20px;
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}
.admin-band__mark {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  background: var(--amber);
  color: #2a1d05;
  padding: 3px 9px;
  border-radius: 3px;
}
.admin-band__nav { display: flex; gap: 16px; flex-wrap: wrap; }
.admin-band__nav a,
.admin-band__out button {
  color: #fff;
  font-size: .93rem;
  text-decoration: none;
  border-bottom: 2px solid transparent;
  padding: 3px 0;
}
.admin-band__nav a:hover { color: #fff; border-bottom-color: rgba(255, 255, 255, .5); }
.admin-band__nav a[aria-current] { border-bottom-color: var(--amber); }
.admin-band__out { margin: 0 0 0 auto; }
.admin-band__out button {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  font-family: inherit;
  cursor: pointer;
}
.admin-band__out button:hover { border-bottom-color: rgba(255, 255, 255, .5); }

/* ------------------------------------------------------------ stat tiles */

.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 14px;
  margin: 24px 0 34px;
}
.tile {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: 14px 16px;
}
/* The four scope-check splits are parts of the tile above them, not peers. */
.tile--split { background: var(--bg); }
.tile-label { font-weight: 700; color: var(--navy); margin: 0; font-size: .95rem; }
.tile-sub { font-size: .82rem; color: var(--grey); margin: 2px 0 0; }
.tile-n {
  font-size: 2.1rem;
  font-weight: 700;
  color: var(--navy);
  line-height: 1.1;
  margin: 8px 0 4px;
  font-variant-numeric: tabular-nums;
}
.tile-win { font-size: .82rem; color: var(--grey); margin: 0; }

/* Movement is never coloured green-good / red-bad. "Under 200, stopped" going
   up is not a failure and "checks started" going down is not a success; the
   arrow says which way, and the operator knows which way is which. */
.mv { font-weight: 700; color: var(--navy); }
.mv--up::before   { content: '▲ '; }
.mv--down::before { content: '▼ '; }
.mv--flat::before { content: '– '; }

/* ----------------------------------------------------------- admin tables */

.admin-table { font-size: .93rem; }
.admin-table td, .admin-table th { vertical-align: top; }
.admin-table .nowrap { white-space: nowrap; }
.admin-table .trunc {
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.admin-table tr.is-worst > td,
.admin-table tr.is-flagged > td { background: #fdf6ea; }
.admin-table a.sorted { font-weight: 700; }

.admin-search { margin: 18px 0 22px; }
.admin-search label { display: block; font-size: .93rem; color: var(--grey); margin-bottom: 6px; }
.admin-search__row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.admin-search input {
  font-family: inherit;
  font-size: 1rem;
  padding: 11px 12px;
  min-height: 44px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  min-width: 260px;
}

/* Contact status. Three states and they must not blur together: the middle one
   is a permission and the last one is a prohibition. */
.cs {
  display: inline-block;
  /* One line, always. Wrapped over four lines in a table cell it stops looking
     like a status and starts looking like a sentence somebody typed. The table
     scrolls sideways instead. */
  white-space: nowrap;
  font-size: .8rem;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 3px;
  border: 1.5px solid;
}
.cs--customer { color: #1d5b3c; border-color: #1d5b3c; background: #eaf5ef; }
.cs--opted_in { color: var(--amber-text); border-color: var(--amber); background: #fdf6ea; }
.cs--none     { color: #7a1f1f; border-color: #7a1f1f; background: #fbeeee; }

.admin-flag {
  border-left: 5px solid var(--amber);
  background: var(--bg);
  padding: 12px 16px;
  font-size: .93rem;
  margin: 18px 0;
}
.admin-unmeasured {
  color: var(--grey);
  font-size: .93rem;
  border-left: 3px solid var(--line);
  padding-left: 12px;
}
.admin-foot {
  margin-top: 34px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: .93rem;
  color: var(--grey);
}

@media (max-width: 640px) {
  .admin-band__out { margin-left: 0; }
  .tiles { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
  .admin-search input { min-width: 0; width: 100%; }
}

/* --------------------------------------------------- the optional consent tick

   It never gates anything, and it must not look like it does. Quiet, below the
   thing the visitor actually came for, unticked, and with the address field
   hidden until the box is ticked: asking for an email first and explaining why
   afterwards is how a form gets abandoned. */

.wizard .consent {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.wizard .consent-tick {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  font-size: 14.5px;
  color: var(--grey);
  cursor: pointer;
  /* Overrides the wizard's block form labels, which are for real fields. */
  font-weight: 400;
  margin: 0;
}
.wizard .consent-tick input {
  width: 18px;
  height: 18px;
  margin: 1px 0 0;
  flex: 0 0 auto;
  accent-color: var(--navy);
}
.wizard .consent-more {
  margin-top: 12px;
  padding-left: 28px;
  max-width: 420px;
}
.wizard .consent-more label { font-size: 13.5px; }
.wizard .consent-note,
.wizard .consent-ok,
.wizard .consent-err {
  font-size: 13px;
  line-height: 1.45;
  margin-top: 8px;
}
.wizard .consent-note { color: var(--grey); }
.wizard .consent-ok { color: var(--ok); font-weight: 700; }
.wizard .consent-err { color: #a12020; font-weight: 700; }

/* The same tick on /account, where it is a live switch rather than a capture. */
.consent-row {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.consent-row:last-of-type { border-bottom: 0; }
.consent-row form { margin: 0; }
.consent-row .consent-state { font-size: .93rem; color: var(--grey); margin: 4px 0 0; }

/* ------------------------------------------------- "Your documents"

   The seven downloads for one venue: the pack, its four procedures, the staff
   summary card and (where the venue has hirers) the hirer briefing. One per
   venue on /dashboard, once on /unlocked/:id, from
   partials/documents-list.ejs.

   THE ICON IS THE BRAND MARK WITH ONE SQUARE LIT. Its colours come from
   lib/brand-icon.js via lib/documents.js and are drawn into the inline SVG, so
   there are no drill colours in this file to fall out of step with the mark.
   What is here is the row: a thin left rule in the same colour, because at
   22px the icon alone is a hint and the rule is what makes four rows scan as
   four different things down the page.

   LOCKED IS GREYED, NOT HIDDEN. An unpaid venue sees every row, greyed, behind
   the unlock button. This is the clearest statement on the site of what
   unlocking gets you, and no link is rendered in that state: the gate is in
   routes/packs.js, not in this stylesheet. */

.docs {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: 20px 24px;
  margin: 22px 0;
}

.docs-head {
  display: flex;
  gap: 12px;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: wrap;
  margin-bottom: 4px;
}
.docs-title {
  font-weight: 800;
  color: var(--navy);
  font-size: 1.02rem;
  margin: 0;
}
.docs-venue {
  display: block;
  font-weight: 400;
  font-size: .84rem;
  color: var(--grey);
  margin-top: 2px;
}

.docs-list { list-style: none; margin: 10px 0 0; padding: 0; }

/* THE ROW, and its stripe.

   The stripe is a pseudo-element, not a border-left, for one reason: the Staff
   Summary Card's is four equal bands and a border cannot hold a gradient. One
   mechanism for all seven keeps every stripe the same 4px and the same full
   height, which is the whole point of the polish: previously the four
   procedures had a 3px rule and the other three had transparent, so half the
   list looked like it had lost something.

   The colour arrives as the --docs-stripe custom property, set inline per row
   from lib/documents.stripeCss(). No drill colour appears anywhere in this
   stylesheet: they live in lib/brand-icon.js, which is also what draws the mark,
   so the row and its icon cannot drift apart. */
.docs-item {
  position: relative;
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 12px 0 12px 16px;
  border-bottom: 1px solid var(--line);
}
.docs-item::before {
  content: "";
  position: absolute;
  left: 0;
  /* Full height of the row, edge to edge, including the padding. */
  top: 0;
  bottom: 0;
  width: 4px;
  border-radius: 2px;
  background: var(--docs-stripe, transparent);
}
.docs-item:last-child { border-bottom: 0; }

/* The master. Heavier, and separated from the six pages that come out of it,
   because "the pack is the master document" is the line printed at the foot of
   every standalone and the list should say the same thing. */
.docs-item.is-master { padding-bottom: 15px; margin-bottom: 4px; border-bottom-width: 1.5px; }
.docs-item.is-master .docs-name { font-weight: 800; }

.docs-icon {
  flex: 0 0 22px;
  line-height: 0;
  margin-top: 2px;
}
.docs-icon svg { display: block; }

.docs-body { flex: 1; min-width: 0; }
.docs-name { display: block; font-weight: 700; font-size: .96rem; }
a.docs-name { color: var(--navy2); }
.docs-sub {
  display: block;
  font-size: .82rem;
  color: var(--grey);
  line-height: 1.45;
  margin-top: 2px;
}

.docs-state { flex: 0 0 auto; padding-top: 2px; }
.docs-format {
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .08em;
  color: var(--grey);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 2px 6px;
}

/* The padlock, drawn in two elements rather than shipped as a glyph or an
   emoji: a padlock emoji is a different shape on every platform, several
   screen readers announce it, and this one is decorative with an .sr-only
   "Locked" beside it. Body 10x8 with a 6x5 shackle above it. */
.docs-lock {
  display: inline-block;
  position: relative;
  width: 10px;
  height: 8px;
  border-radius: 1.5px;
  background: var(--grey);
  margin: 6px 4px 0 0;
}
.docs-lock::before {
  content: '';
  position: absolute;
  left: 2px;
  top: -5px;
  width: 6px;
  height: 5px;
  border: 1.6px solid var(--grey);
  border-bottom: 0;
  border-radius: 4px 4px 0 0;
}

/* "Not for this venue": the hirer briefing on a venue with no hirers.

   The row is listed rather than dropped, so the list is seven long everywhere
   and the seventh explains itself instead of appearing to have gone missing.
   Quieter than the padlock, because a locked document is one they could have
   and this one is not. */
.docs-na {
  display: inline-block;
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--grey);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 3px 9px;
  white-space: nowrap;
}

/* Locked: the whole card steps back. Not display:none anywhere, and not
   opacity on the container either, which would take the unlock button with
   it. */
.docs--locked { background: var(--bg); }
.docs--locked .docs-name { color: var(--grey); font-weight: 700; }
.docs--locked .docs-sub { color: #8a90a0; }
/* The locked stripe comes from lib/documents.forVenue() as the same pale grey
   the icon uses; nothing to override here. */

.docs-unlock {
  margin: 16px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 14px;
  line-height: 1.5;
}
.docs-unlock span {
  display: block;
  font-size: 12.5px;
  color: var(--grey);
  margin-top: 8px;
}

@media (max-width: 560px) {
  .docs { padding: 18px 16px; }
  .docs-name { font-size: 1rem; }
  .docs-sub, .docs-unlock span { font-size: .88rem; }
  /* The PDF chip is a marker on the row, not text to read, so it stays with
     the chips and eyebrows in the phone reading floor above rather than going
     to 16px and out-shouting the document name. */
  .docs-format { font-size: .72rem; }
}

/* ------------------------------------------------- the account profile

   /account, three sections. Each is its own form with its own save button:
   one page-wide Save would mean a typo in the billing email discarding a logo
   upload, and would write eight columns the user never touched.

   Deliberately plain. This is a settings page a treasurer visits twice a year,
   and the wizard's card styling would make it look like another thing to
   complete. Nothing here is required and nothing here gates a download. */

.profile-block {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: 22px 24px 6px;
  margin: 14px 0 30px;
}
.profile-lede { color: var(--grey); font-size: .95rem; margin: 0 0 18px; }

.profile-form { margin: 0 0 26px; padding-bottom: 22px; border-bottom: 1px solid var(--line); }
.profile-form:last-of-type { border-bottom: 0; }

.profile-block .field { margin-bottom: 18px; max-width: 460px; }
.profile-block label {
  display: block;
  font-weight: 700;
  font-size: .93rem;
  color: var(--navy2);
  margin-bottom: 4px;
}
/* The note sits between the label and the field, not under it: it is usually
   the thing that tells you whether to fill the field in at all, and under the
   input it is read after the decision has been made. */
.field-note {
  font-size: 13px;
  line-height: 1.5;
  color: var(--grey);
  margin: 0 0 7px;
}
.profile-block input[type=text],
.profile-block input[type=email],
.profile-block input[type=tel],
.profile-block select,
.profile-block textarea {
  width: 100%;
  padding: 12px;
  border: 1.5px solid var(--line);
  border-radius: 5px;
  font: inherit;
  font-size: 16px;      /* stops iOS Safari zooming on focus */
  min-height: 46px;
  background: #fff;
  color: var(--ink);
  display: block;
}
.profile-block textarea { min-height: 92px; resize: vertical; line-height: 1.5; }
.profile-block input:focus,
.profile-block select:focus,
.profile-block textarea:focus { border-color: var(--navy2); outline: none; }

/* The file input is left as the browser's own. A styled one is a label with a
   hidden input behind it, which is a control that stops working the moment the
   CSS does, on the one form where the user has to be certain what they picked. */
.profile-block input[type=file] {
  font: inherit;
  font-size: 15px;
  padding: 10px 0;
  max-width: 100%;
}

/* The stored logo, shown at roughly the size it prints on a standalone's
   header so the preview answers "is that too busy" before the download does.
   The chequerboard is the same trick every image editor uses, and it is here
   for one reason: it shows whether the file has a transparent background,
   which is the difference between a crest that sits on the navy cover and one
   that arrives in its own white rectangle. */
.logo-preview {
  display: flex;
  gap: 20px;
  align-items: flex-start;
  flex-wrap: wrap;
  padding: 16px;
  margin-bottom: 18px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background:
    linear-gradient(45deg, #eef1f6 25%, transparent 25%, transparent 75%, #eef1f6 75%),
    linear-gradient(45deg, #eef1f6 25%, #fff 25%, #fff 75%, #eef1f6 75%);
  background-size: 16px 16px;
  background-position: 0 0, 8px 8px;
}
.logo-preview img {
  display: block;
  max-height: 90px;
  max-width: 240px;
  width: auto;
  height: auto;
}
.logo-preview > div { flex: 1; min-width: 200px; }
.logo-preview form { margin-top: 10px; }

/* The 6-digit code, when there is no email provider to send it. Monospace and
   spaced, because it is read off the screen and typed into the box below it. */
.code-inline {
  font-family: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
  font-size: 1.15rem;
  letter-spacing: .18em;
}

/* The billing nudge on /dashboard. A line, not a banner: it costs the customer
   nothing today and it must never compete with the download they came for. */
.nudge {
  border-left: 3px solid var(--amber);
  background: var(--bg);
  padding: 12px 16px;
  margin: 18px 0;
  font-size: 14px;
  line-height: 1.55;
  border-radius: 0 var(--radius) var(--radius) 0;
}

@media (max-width: 560px) {
  .profile-block { padding: 18px 16px 4px; }
  .field-note, .profile-lede, .nudge { font-size: .93rem; }
}

/* ------------------------------------------------- the safety register

   The homepage passage between "What you get" and the timeline band. It is the
   one section on the site that is not selling anything, and its styling is the
   argument: white, narrow, no tint, no band, no ticks, no button. It should
   read as somebody talking rather than as a panel.

   Slightly larger and looser than surrounding prose, which is the only signal
   it gets. Anything stronger (a rule, a tint, a pull-quote) would make it a
   device, and a device is what this passage must not look like. */

.register p {
  font-size: 1.06rem;
  line-height: 1.7;
  margin-bottom: 18px;
}
.register h2 { max-width: 22ch; }

/* The closing line: real people, named. Smaller, because it is the quietest
   thing on the page and should be. */
.register .register-note {
  font-size: .93rem;
  line-height: 1.65;
  color: var(--grey);
  margin-top: 26px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}

@media (max-width: 560px) {
  .register p { font-size: 1rem; line-height: 1.65; }
  .register h2 { max-width: none; }
  .register .register-note { font-size: .93rem; }
}

/* ------------------------------------------- the pre-generation check

   /review/:id. Three states: checking, results, unavailable.

   The severity colours are the only place on this site where red means stop.
   They are used sparingly and never alone: every issue carries the words
   "Needs fixing" or "Would be stronger" beside the colour, because a colour is
   not a message to somebody who cannot separate the hues. */

/* The checking step. Three dots, drawn, because a spinner GIF is a request that
   can fail and this is the screen a visitor stares at. Respects
   prefers-reduced-motion: the dots hold still and the text still says what is
   happening. */
.checking { margin: 28px 0; }
.checking-dots { display: inline-flex; gap: 8px; margin-bottom: 12px; }
.checking-dots i {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--amber);
  display: block;
  animation: checkpulse 1.2s ease-in-out infinite;
}
.checking-dots i:nth-child(2) { animation-delay: .18s; }
.checking-dots i:nth-child(3) { animation-delay: .36s; }
@keyframes checkpulse {
  0%, 100% { opacity: .3; transform: translateY(0); }
  50%      { opacity: 1;  transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
  .checking-dots i { animation: none; opacity: .6; }
}

/* Spelling suggestions: one row, one click. */
.spell-list { list-style: none; margin: 14px 0 30px; padding: 0; }
.spell {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  padding: 14px 0;
  border-bottom: 1px solid var(--line);
}
.spell:last-child { border-bottom: 0; }
.spell form { margin: 0; }
.spell-body { flex: 1; min-width: 240px; }
.spell-where {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--grey);
  margin: 0 0 4px;
}
.spell-where span { text-transform: none; letter-spacing: 0; font-weight: 400; }
.spell-where span::before { content: " · "; }
.spell-change { margin: 0; font-size: 1rem; line-height: 1.5; }
.spell-was { color: var(--warn); text-decoration: line-through; }
.spell-arrow { color: var(--grey); margin: 0 6px; }
.spell-now { font-weight: 700; color: var(--navy); }

/* Issues, grouped by the wizard step they belong to. */
.issue-group {
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: 18px 22px;
  margin: 16px 0;
}
.issue-head {
  display: flex;
  gap: 14px;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
}
.issue-head h3 { margin: 0; font-size: 1.02rem; }

.issue { padding: 16px 0 4px; border-bottom: 1px solid var(--line); }
.issue:last-child { border-bottom: 0; }
.issue p { margin: 0 0 7px; }
.issue-tag {
  font-size: .74rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.issue-field { color: var(--grey); font-weight: 400; letter-spacing: 0; text-transform: none; }
.issue-field::before { content: " · "; }
.issue--fix .issue-tag { color: var(--warn); }
.issue--improve .issue-tag { color: var(--amber-text); }
.issue-problem { font-weight: 700; color: var(--navy); font-size: 1rem; }
.issue-why, .issue-add { font-size: .93rem; line-height: 1.6; color: var(--ink); }
.issue-why strong, .issue-add strong { color: var(--navy2); }

/* The acknowledgement tick, for passable "improve" items. A real checkbox with
   `required` on it: the browser refuses the submit and says why, and the route
   refuses it again. */
.review-go { margin: 26px 0 8px; }
.ack {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 14px 16px;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  margin-bottom: 18px;
  font-size: .96rem;
  line-height: 1.55;
  cursor: pointer;
}
.ack input { width: 18px; height: 18px; margin: 2px 0 0; flex: 0 0 auto; accent-color: var(--navy); }

/* The provenance line. Quiet, and it says plainly what read the answers and
   what it did not do. */
.review-meta {
  margin-top: 30px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  font-size: .85rem;
  line-height: 1.6;
}

@media (max-width: 560px) {
  .issue-group { padding: 16px; }
  .spell-change, .issue-why, .issue-add, .ack { font-size: 1rem; }
  .issue-problem { font-size: 1rem; }
}

/* ------------------------------------- documents list: the three actions

   View, Download, Email on every row. Three compact controls rather than one
   button, because the three are genuinely different intentions: look at it
   now, keep a copy, send it to whoever prints things.

   ALL THREE ARE 44px TALL. That is not a rounding of "big enough": it is the
   measured floor for a tap target, and this list is used one-handed on a phone
   in a building. The row stacks below 560px rather than letting three controls
   compete for 90px of width each.

   The Email control is a <button> inside a POST form and the other two are
   links, because emailing sends something and viewing does not. They are
   styled identically, so the difference in HTTP method is not a difference the
   customer has to see. */

.docs-actions {
  display: flex;
  gap: 4px;
  align-items: center;
  flex: 0 0 auto;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.docs-act-form { margin: 0; display: inline-flex; }

.docs-act {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 0 12px;
  font: inherit;
  font-size: .86rem;
  font-weight: 700;
  color: var(--navy2);
  background: none;
  border: 1.5px solid transparent;
  border-radius: 5px;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.docs-act:hover,
.docs-act:focus-visible {
  border-color: var(--line);
  background: var(--bg);
  text-decoration: none;
}

/* Not available yet: shown, greyed, not interactive, and it says why on hover
   and to a screen reader. A control that disappears is a feature nobody knows
   about; one that explains itself is a promise. */
.docs-act.is-off {
  color: var(--grey);
  cursor: default;
  opacity: .65;
}
.docs-act.is-off:hover { border-color: transparent; background: none; }

.docs-note {
  margin: 14px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 13px;
  line-height: 1.55;
  color: var(--grey);
}

/* The confirmation, after an Email. Not a floating toast that fades: this
   confirms an email went to a specific address, and the one thing the reader
   wants to do is check the address. A line that stays until the page changes
   is more useful than an animation that takes the address away after four
   seconds. */
.toast {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  border-left: 3px solid var(--ok);
  background: #eaf5ef;
  padding: 12px 16px;
  margin: 0 0 18px;
  border-radius: 0 var(--radius) var(--radius) 0;
  font-size: 14px;
  line-height: 1.55;
}
.toast--warn { border-left-color: var(--warn); background: #fdf0ec; }
.toast strong { color: var(--navy); }
.toast-tick {
  flex: 0 0 auto;
  font-weight: 800;
  color: var(--ok);
}
.toast--warn .toast-tick { color: var(--warn); }

@media (max-width: 560px) {
  /* Stack: name and description across the full width, then the three actions
     on their own row, each still 44px and now wide enough to hit. */
  .docs-item { flex-wrap: wrap; }
  /* Sit beside the icon, but take enough of the row that the actions have to
     wrap below. `100%` here would push the title under its own icon and leave
     the icon stranded on a line of its own. */
  .docs-body { flex: 1 1 calc(100% - 34px); min-width: 0; }
  .docs-actions {
    flex: 1 1 100%;
    justify-content: flex-start;
    gap: 6px;
    margin-top: 8px;
    padding-left: 34px;   /* aligns under the text, clear of the icon */
  }
  .docs-act {
    border-color: var(--line);
    padding: 0 14px;
    font-size: .92rem;
  }
  .docs-note, .toast { font-size: .93rem; }
}

/* At the narrowest widths the three still fit in a row at 375px, but a long
   locale or a large text setting can push them; let them wrap rather than
   overflow the card. */
@media (max-width: 360px) {
  .docs-act { flex: 1 1 auto; }
}

/* ================================================================== the assistant

   A quiet button, bottom right, on every page including the wizard. It never
   opens itself: no timed pop-up, no unread badge, no bounce. Somebody filling in
   a walkthrough about their building's locks is concentrating, and a panel that
   appears over that is an interruption dressed as help.

   Built by static/chat.js, which creates the launcher rather than shipping it in
   the markup, so a visitor whose JavaScript did not arrive never sees a control
   that does nothing. Nothing here is styled for an element that does not exist
   until that script runs.
   ================================================================== */

.chat-launcher {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: 0;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 0;
  border-radius: 28px;
  background: var(--navy);
  color: #fff;
  cursor: pointer;
  box-shadow: 0 3px 14px rgba(30, 42, 68, .28);
  overflow: hidden;
  /* Only the width animates, and only on hover. Nothing about this element
     moves on its own. */
  transition: width .16s ease-in-out;
}
.chat-launcher:hover,
.chat-launcher:focus-visible { box-shadow: 0 4px 18px rgba(30, 42, 68, .34); }

/* The brand's amber dot, which is the full stop in the wordmark. The launcher
   is the mark's punctuation rather than a speech bubble: every chat widget on
   the internet is a speech bubble, and this one should look like this site. */
.chat-launcher__dot {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  margin: 0 21px;
  border-radius: 50%;
  background: var(--amber);
}

/* The label is always in the accessibility tree; it becomes visible on hover
   and on keyboard focus at desktop widths. */
.chat-launcher__label {
  white-space: nowrap;
  font-weight: 700;
  font-size: .95rem;
  opacity: 0;
  width: 0;
  transition: opacity .16s ease-in-out;
}
@media (min-width: 561px) and (hover: hover) {
  .chat-launcher:hover,
  .chat-launcher:focus-visible { width: 186px; }
  .chat-launcher:hover .chat-launcher__dot,
  .chat-launcher:focus-visible .chat-launcher__dot { margin: 0 12px 0 18px; }
  .chat-launcher:hover .chat-launcher__label,
  .chat-launcher:focus-visible .chat-launcher__label { opacity: 1; width: auto; padding-right: 20px; }
}
@media (prefers-reduced-motion: reduce) {
  .chat-launcher, .chat-launcher__label { transition: none; }
}

/* ---------------------------------------------------------------- the panel */

.chat-panel {
  position: fixed;
  right: 20px;
  bottom: 88px;
  z-index: 61;
  display: flex;
  flex-direction: column;
  width: 380px;
  max-height: min(620px, calc(100vh - 120px));
  background: #fff;
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 40px rgba(30, 42, 68, .22);
  overflow: hidden;
}
.chat-panel[hidden] { display: none; }

.chat-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  background: var(--navy);
  color: #fff;
}
.chat-title { margin: 0; font-weight: 800; font-size: 1rem; }
.chat-close {
  min-width: 44px;
  min-height: 44px;
  margin: -10px -10px -10px 0;
  padding: 0;
  font-size: 1.5rem;
  line-height: 1;
  color: #fff;
  background: none;
  border: 0;
  cursor: pointer;
  border-radius: var(--radius);
}
.chat-close:hover, .chat-close:focus-visible { background: rgba(255, 255, 255, .12); }

.chat-log {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.chat-msg { display: flex; }
.chat-msg--you { justify-content: flex-end; }
.chat-bubble {
  max-width: 86%;
  padding: 10px 13px;
  border-radius: 12px;
  font-size: .95rem;
  line-height: 1.55;
  /* Model replies contain newlines and are inserted with textContent, so the
     line breaks have to survive in CSS rather than in markup. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.chat-msg--bot .chat-bubble { background: var(--bg); border: 1px solid var(--line); color: var(--ink); }
.chat-msg--you .chat-bubble { background: var(--navy); color: #fff; }

/* The AI notice and the thinking line. Quiet, centred, not a message from
   anybody. */
.chat-status {
  margin: 0;
  font-size: .8rem;
  line-height: 1.5;
  color: var(--grey);
  text-align: center;
}

/* Starter questions, chosen by the page. Tappable, 44px, and they disappear
   once one is used. */
.chat-starters { display: flex; flex-direction: column; gap: 8px; margin-top: 2px; }
.chat-starter {
  min-height: 44px;
  padding: 10px 14px;
  font: inherit;
  font-size: .92rem;
  font-weight: 700;
  text-align: left;
  color: var(--navy2);
  background: #fff;
  border: 1.5px solid var(--line);
  border-radius: 20px;
  cursor: pointer;
}
.chat-starter:hover, .chat-starter:focus-visible { border-color: var(--navy2); background: var(--bg); }

.chat-form {
  display: flex;
  gap: 8px;
  padding: 12px 16px;
  border-top: 1px solid var(--line);
  margin: 0;
}
.chat-input {
  flex: 1;
  min-width: 0;
  min-height: 44px;
  padding: 10px 12px;
  font: inherit;
  font-size: 16px;   /* stops iOS Safari zooming on focus */
  border: 1.5px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
}
.chat-input:focus { border-color: var(--navy2); outline: none; }
.chat-input:disabled { background: var(--bg); color: var(--grey); }
.chat-send {
  min-height: 44px;
  padding: 0 16px;
  font: inherit;
  font-weight: 700;
  color: #fff;
  background: var(--navy);
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}
.chat-send:hover, .chat-send:focus-visible { background: var(--navy2); }

/* The way to a person, always there, never behind a question. */
.chat-foot {
  margin: 0;
  padding: 10px 16px 14px;
  font-size: .8rem;
  color: var(--grey);
  border-top: 1px solid var(--line);
}

/* At phone widths the panel is a full-screen sheet: a 380px floating card on a
   375px screen is a card with nowhere to go. */
@media (max-width: 560px) {
  .chat-panel {
    right: 0;
    left: 0;
    bottom: 0;
    top: 0;
    width: auto;
    max-height: none;
    border: 0;
    border-radius: 0;
  }
  /* The launcher would otherwise sit on top of the sheet it opened. */
  .chat-open .chat-launcher { display: none; }
  /* Stop the page behind the sheet from scrolling under it. */
  .chat-open, .chat-open body { overflow: hidden; }
  .chat-bubble, .chat-starter { font-size: 1rem; }
  .chat-status, .chat-foot { font-size: .88rem; }
}

@media print { .chat-launcher, .chat-panel { display: none !important; } }
