/* Public business register — institutional styling.
   ---------------------------------------------------------------------------
   Brand family: civic / government (docs/vl/public-search/brand.md).
   Calibrated against GOV.UK (structure, focus mechanics, tag construction),
   Companies House (a statutory register states its facts, it does not decorate
   them), and the two-band government masthead that ACRA/BizFile and the New
   Zealand Companies Register also use — an organisation band above a service
   band, so the reader learns WHOSE register this is before reading anything.
   BIPA's orange is the seam between the two, and the only saturated accent.

   The organisation band carries BIPA's REAL logo (static/brand/bipa-mark.png)
   on the white ground that lockup is drawn for, with the statutory name beside
   it as live text. Nothing asserts a seal, an Act number or a ministry — none
   are established in this repository. See templates/search/base.html.

   Constraints this file is bound by, none of them aesthetic:
     - Mobile first, on a cheap phone over a slow Namibian connection. No web
       fonts, ONE image (3.0 KB, the logo), no layout shift. The display face
       is a SYSTEM serif — Georgia on Apple/Windows, Noto Serif on Android — so
       the institutional voice costs zero bytes and zero requests.
     - Works with JavaScript disabled. HTMX is progressive enhancement.
     - Every colour pair carrying text is >= 4.5:1; interactive borders and the
       non-text data fills are >= 3:1. Verified, not eyeballed — the ratios are
       recorded beside each token below.
     - No gradients, no icons, no emoji, no shadows-as-decoration, no motion
       beyond an instant state change.
   --------------------------------------------------------------------------- */

:root {
  /* Ink and ground -------------------------------------------------------- */
  /* Sampled from BIPA's own lockup: the wordmark's black is #15171A (97,235
     px of it), so the register's ink IS the brand's ink rather than a near
     miss. 18.0:1 on white. */
  --color-text: #15171a;            /* 18.0:1 on white */
  --color-muted: #52585c;           /*  7.2:1 on white, 6.6:1 on sunken/paper */
  --color-bg: #ffffff;
  --color-sunken: #f3f5f6;

  /* "Paper" — a warm off-white for the regions that frame the page (the search
     hero, a record's identity header). Warm rather than grey so the page reads
     as a printed document rather than as a disabled form. Ink on it is 16.4:1,
     muted 6.6:1, link 7.7:1. */
  --color-paper: #f7f5f2;

  /* Ink used as a GROUND, not just as text: the organisation band, the
     statistics panel and the footer. This is what gives the page regions a
     reader can orient in without a single decorative device. */
  --color-ink-rule: #3d4348;        /* hairline on an ink ground (decorative) */
  --color-on-dark: #ffffff;         /* 17.8:1 on ink */
  --color-on-dark-muted: #c2c9ce;   /* 10.6:1 on ink */
  --color-on-dark-link: #9ccbf0;    /* 10.4:1 on ink */
  --color-on-dark-negative: #f3a6a1;/*  9.1:1 on ink */
  --color-on-dark-positive: #6fc3a0;/*  8.5:1 on ink */

  /* Rules. The decorative divider is deliberately light; anything that bounds
     an interactive control uses --color-border-strong (4.95:1). */
  --color-border: #c9ced1;
  --color-border-strong: #6b7175;

  /* Links keep the register's existing deep blue (8.4:1) — a conventional,
     highly legible link colour that does not compete with the brand accent. */
  --color-link: #0b4f8a;
  --color-link-hover: #073760;

  /* BIPA accent — ONE orange, measured off the authority's own logo rather
     than approximated. Decoding static/brand/bipa-logo-linear.png, 94.9% of
     its orange pixels (59,596 of 62,831) are exactly #F79448: hue 26.1deg,
     saturation 91.6%. The file previously carried three unrelated oranges
     (#f26a1b, #FF993D, #b8450c) at three different hues; the two below are now
     the SAME hue and saturation as the band, darkened until they carry text.

     #F79448 is 2.26:1 on white, so it can never be a text colour or a UI
     boundary — it is the solid seam between the two masthead bands and
     nothing else. That is a deliberate ceiling, not an oversight. */
  --color-brand-band: #f79448;      /* the real BIPA orange; band only, never text */
  --color-brand: #b05108;           /* 5.2:1 — carries white text (buttons) */
  --color-brand-deep: #893f06;      /* 7.6:1 — brand as a text colour, and hover */

  /* Focus: two channels, per GOV.UK. Yellow carries contrast on dark grounds
     (12.4:1 on ink), the black bar carries it on light ones, so focus is
     visible anywhere on a page that now has both. */
  --color-focus: #ffd400;
  --color-focus-ink: #16181a;

  /* Status tints — dark shade on light tint, so the pairs stay legible in
     greyscale and in print. Ratios: 8.9 / 9.4 / 7.3 / 9.8 : 1. */
  --color-positive-bg: #d6e8df;
  --color-positive-text: #08432c;
  --color-negative-bg: #f7dcdc;
  --color-negative-text: #6b1414;
  --color-warning-bg: #fbeccd;
  --color-warning-text: #6b4506;
  --color-neutral-bg: #e6eaec;
  --color-neutral-text: #31383d;
  --color-guidance-bg: #fdf4e0;     /* ink on this is 16.3:1 */

  /* The compliance bar sits on the ink panel, so its fills are lightened until
     they clear 3:1 against that ground (3.4:1 and 4.4:1) — the non-text
     contrast minimum. The dark table shades used on white would vanish. */
  --color-bar-negative: #c0403a;
  --color-bar-positive: #1f8f66;

  /* Type ------------------------------------------------------------------ */
  /*
   * REVERSED 2026-08-02, at Flip's direction, having been shown the trade-off.
   *
   * This file previously set every heading in Georgia, on the reasoning that a
   * system serif "is what makes this read as a document of record rather than
   * as an app". That reasoning is sound and the result was still dated: Georgia
   * at large sizes is the single strongest signal of a mid-2000s website, and
   * the client's reaction to the rendered page — not to the rationale — is the
   * measurement that matters for whether an institution looks credible.
   *
   * What did NOT change, because it is a constraint rather than a preference:
   * there is still no web font. Both stacks are system faces, so the page costs
   * zero extra bytes and zero requests on a cheap Android over slow Namibian
   * data. The modern grotesques below are present on every current phone and
   * fall back cleanly on old ones.
   */
  --font-body: ui-sans-serif, system-ui, -apple-system, "Segoe UI Variable Text",
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  /* Display: the same family, differentiated by WEIGHT and TRACKING rather than
     by a second typeface. One family set confidently reads as more designed
     than two families set timidly, and it removes a whole class of pairing
     mistakes. */
  --font-display: var(--font-body);
  /* Figures in tables and statistics.
   *
   * The BODY face with tabular figures, NOT a monospace family. A monospace
   * stack was tried first and rejected on sight: most system mono faces draw a
   * SLASHED zero, and the officer dashboard is mostly zeros — "0 filings to
   * review, 0 names to decide" rendered as a row of Ø, which reads as "null"
   * or "empty set" rather than as "none to do". `tabular-nums` gives the column
   * alignment that was the actual requirement without importing a technical
   * voice into a statutory register. */
  --font-numeric: var(--font-body);

  /* A real modular scale, 1.25. Replaces a set of ad-hoc rem values that had
     no ratio between them, which is most of why the headings never felt
     deliberate. */
  --text-xs: 0.8rem;
  --text-sm: 0.9rem;
  --text-base: 1rem;
  --text-lg: 1.25rem;
  --text-xl: 1.5625rem;
  --text-2xl: 1.953rem;
  --text-3xl: 2.441rem;
  --text-4xl: 3.052rem;

  /* Spacing — an 8pt rhythm. The old 5px base came from GOV.UK and produced
     values (0.3125rem, 0.9375rem) that never landed on a common grid, so
     vertical rhythm drifted a pixel or two everywhere and nothing quite lined
     up. Every value below is a multiple of 4px. */
  --space-1: 0.25rem;   /*  4 */
  --space-2: 0.5rem;    /*  8 */
  --space-3: 0.75rem;   /* 12 */
  --space-4: 1rem;      /* 16 */
  --space-5: 1.5rem;    /* 24 */
  --space-6: 2rem;      /* 32 */
  --space-7: 3rem;      /* 48 */
  --space-8: 4.5rem;    /* 72 */

  /* Width. The shell is wide enough for five-column register tables; prose is
     separately capped near 75 characters, which is the readable measure. */
  --shell: 68rem;
  --measure: 42rem;

  /* Elevation. Four steps, all of them tinted with the page's own ink rather
     than pure black — a neutral-black shadow on a warm ground reads as grime.
     Used for depth, never as decoration: a raised surface must MEAN something
     is interactive or is floating above the page. */
  --shadow-1: 0 1px 2px rgb(21 23 26 / 6%);
  --shadow-2: 0 2px 4px rgb(21 23 26 / 6%), 0 4px 12px rgb(21 23 26 / 6%);
  --shadow-3: 0 4px 8px rgb(21 23 26 / 8%), 0 12px 28px rgb(21 23 26 / 10%);
  --shadow-inset: inset 0 1px 0 rgb(255 255 255 / 60%);

  --radius: 6px;
  --radius-lg: 10px;
  --radius-pill: 999px;

  /* One duration and one curve, so everything on the page moves like one
     thing. 150ms is below the threshold where a transition starts to feel like
     waiting. */
  --ease: cubic-bezier(0.2, 0, 0, 1);
  --fast: 150ms;
}

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

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

body {
  margin: 0;
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.55;
  /* 18px. GOV.UK's research settled on a large body size that does NOT shrink
     on mobile; this is a shade under theirs and still well above the 16px
     default, which matters for older readers on small screens. */
  font-size: 1.125rem;
}

/* Every full-bleed region carries its own ground and holds its content in one
   of these. One rule, so a new region cannot drift out of alignment. */
.shell {
  max-width: var(--shell);
  margin: 0 auto;
  padding-left: var(--space-4);
  padding-right: var(--space-4);
}

/* --- typography ---------------------------------------------------------- */

h1,
h2,
h3,
h4 {
  /* Tighter than the body and tighter as they get bigger — a heading set at
     the body's 1.55 leading is the other half of why this looked unfinished.
     Negative tracking on the large sizes is what makes a system grotesque read
     as typeset rather than as default. */
  line-height: 1.15;
  letter-spacing: -0.011em;
  text-wrap: balance;
  margin: 0 0 var(--space-4);
  font-weight: 650;
}

h1 { font-size: var(--text-3xl); letter-spacing: -0.021em; }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }
h4 { font-size: var(--text-base); }

@media (max-width: 40rem) {
  h1 { font-size: var(--text-2xl); }
}

/* Figures are set in the tabular face wherever they carry meaning, so columns
   align and a statistic does not shift as it changes. */
.stat-card__n,
.office-figure,
.queue-tile__n,
.amount,
td.amount {
  font-family: var(--font-numeric);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}

/* The display face is scoped to the headings that carry the page's voice.
   Small headings inside data (h3, table captions, field labels) stay in the
   body sans, where a serif would only cost legibility at 15-19px. */
h1,
h2 {
  font-family: var(--font-display);
  font-weight: 700;
}

h1 {
  /* Decisive scale contrast: ~3x the body size at desktop. The previous 2.25rem
     ceiling put the page heading barely above a bold paragraph. */
  font-size: clamp(2.125rem, 1.55rem + 2.7vw, 3.25rem);
  letter-spacing: -0.015em;
  margin-bottom: var(--space-4);
}

h2 {
  font-size: clamp(1.4375rem, 1.2rem + 0.95vw, 1.9375rem);
  letter-spacing: -0.01em;
  margin-top: var(--space-7);
}

h3 {
  font-size: 1.125rem;
  font-weight: 700;
  margin-top: var(--space-5);
}

p {
  margin: 0 0 var(--space-3);
}

/* Prose stays near the readable measure (~75 characters, per GOV.UK's line
   length guidance); tables and result lists may use the full shell. */
.main-body > p,
.main-body > h1,
.main-body > h2,
section > p,
.guidance p,
.state,
.provenance,
.search-examples,
.result-count {
  max-width: var(--measure);
}

/* A whole document of prose — the privacy notice — is capped as one block, so
   a paragraph added later inherits the measure instead of running the full
   1024px shell. */
.prose {
  max-width: var(--measure);
}

a {
  color: var(--color-link);
  text-underline-offset: 0.15em;
}

a:hover {
  color: var(--color-link-hover);
}

/* Focus: yellow ground plus a black underline bar for text links, and a
   yellow/black double ring for controls. The transparent outline is what keeps
   focus visible in Windows High Contrast Mode, which discards box-shadows.
   Because the page now has ink grounds as well as white ones, this pair is
   what guarantees focus is visible on both — 12.4:1 yellow-on-ink. */
a:focus-visible {
  outline: 3px solid transparent;
  color: var(--color-focus-ink);
  background: var(--color-focus);
  box-shadow:
    0 -2px var(--color-focus),
    0 4px var(--color-focus-ink);
  text-decoration: none;
}

input:focus-visible,
button:focus-visible,
summary:focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 0;
  box-shadow: inset 0 0 0 2px var(--color-focus-ink);
}

/* Standalone action links — the ones that are a control in their own right
   rather than a word inside a sentence — need a tappable target. WCAG 2.2
   SC 2.5.8 asks for 24x24 CSS px; these were 18px tall. Links inside running
   prose are exempt from that rule and are deliberately left alone, so the
   measure of a paragraph is not disturbed. */
.back-link,
.search-clear a,
.state-actions a,
.result-next-step a,
.footer-links a,
.footer-staff a {
  display: inline-block;
  padding-top: 0.25rem;
  padding-bottom: 0.25rem;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: auto;
  background: var(--color-bg);
  padding: var(--space-2) var(--space-3);
  border: 2px solid var(--color-focus-ink);
  z-index: 20;
}

.skip-link:focus {
  left: var(--space-3);
  top: var(--space-3);
}

/* --- masthead ------------------------------------------------------------ */

/* ONE bar, closed by the BIPA orange seam: the authority's lockup at the left,
   the SERVICE NAME at the right, vertically centred on one ground. It was two
   full-width bands; the second carried one line and no navigation, so it read
   as vertical height rather than as structure. Measured: the masthead went
   from 216px to 106px at 1280 and from 198px to 137px at 375.

   THE GROUND IS FORCED, NOT CHOSEN — one bar has one ground and the logo picks
   it. The "bipa" wordmark is black on transparent, so the bar is white and the
   service name is ink on it (18.0:1) rather than reversed out. The page keeps
   its region structure regardless: white bar, orange seam, paper hero, and the
   ink grounds it lost at the top it still has at the statistics panel and the
   footer. See templates/search/base.html for the rest of the reasoning. */

.masthead-bar {
  background: var(--color-bg);
  /* The single saturated brand gesture on the page, doing structural work as
     the edge that closes the masthead. Decorative by construction: 2.26:1 on
     white means it may never carry text or bound a control. */
  border-bottom: 4px solid var(--color-brand-band);
}

/* Wraps rather than shrinks. Below the measured 730px fitting point the
   service name drops to its own line under the lockup — a deliberate stack,
   not a broken row: both items keep the bar's left margin, the gap between
   them is the bar's own rhythm, and the service name is still the largest text
   in the region. Nothing is hidden and nothing is reordered at any width. */
.masthead-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3) var(--space-5);
  padding-top: var(--space-3);
  padding-bottom: var(--space-3);
}

/* Now an <a>: the lockup is the route home. It must not LOOK like a link —
   a masthead wordmark that turns blue and underlines reads as broken — so the
   affordance is a hover tint and a real focus ring, and the colours are
   inherited rather than taking the link palette. */
.masthead-org {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  min-width: 0;
  padding: var(--space-2);
  margin: calc(var(--space-2) * -1);
  border-radius: var(--radius);
  color: inherit;
  text-decoration: none;
  transition: background var(--fast) var(--ease);
}

.masthead-org:hover { background: rgb(21 23 26 / 4%); }

.masthead-org:hover .org-name { color: var(--color-brand-deep); }

/* Sized by HEIGHT so the bar's rhythm does not depend on the file's aspect
   ratio; the intrinsic width/height attributes in the markup reserve the box,
   a logo being the classic masthead layout shift — and, at 375px, the classic
   cause of horizontal overflow. */
.org-logo {
  display: block;
  height: 3.75rem;
  width: auto;
  flex: 0 0 auto;
}

/* The CAPTION to the mark, not a competitor to the service name: small,
   uppercase, muted (7.2:1 on white). Who runs this is a quieter question than
   which service you are on, and the type sizes say so — 15-16px against the
   service name's 24-32px.

   The hairline between the mark and the statutory name is how an institutional
   lockup is built — and it is how BIPA's own horizontal lockup is built too.
   But it only works while the two sit on one line: on a phone the name wraps
   beneath the mark and the rule is orphaned against the left margin, so it is
   added at the width where the lockup actually holds together. */
.org-name {
  color: var(--color-muted);
  font-size: 0.9375rem;
  line-height: 1.35;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  max-width: 22ch;
}

/* The service name — what the reader came for, and the largest text in the
   bar. Right-alignment places it, it does not rank it: rank is carried by size
   (2rem against the organisation caption's 1rem), by the display serif, and by
   the full ink. */
.site-title {
  display: inline-block;
  font-family: var(--font-display);
  /* Flat across the phone range, then steep. Measured rather than guessed: on
     the old curve the name reached 28px by 768, which with the lockup is 20px
     wider than the shell — so a tablet wrapped to a stack and the bar was 153px
     tall. Held at 24px the pair fits from 730px, and 768 is 106px. */
  font-size: clamp(1.5rem, 0.6rem + 1.9vw, 2rem);
  font-weight: 700;
  letter-spacing: -0.015em;
  line-height: 1.15;
  text-decoration: none;
  color: var(--color-text);
  max-width: 100%;
}

.site-title:hover {
  color: var(--color-text);
  text-decoration: underline;
}

@media (min-width: 34em) {
  .org-logo {
    height: 4.5rem;
  }

  .org-name {
    padding-left: var(--space-4);
    border-left: 1px solid var(--color-border);
    font-size: 1rem;
    /* Narrow enough that the caption and the service name fit the bar together
       from 600px up. It sets to three lines here and drops to two once there is
       room — three lines beside a 72px mark is a lockup; three lines beside an
       empty right half, which is what 26ch gave at 1280, is a mistake. */
    max-width: 20ch;
  }

}

/* Only once the two actually sit side by side — measured at 730px, so this is
   the first standard step above it. On the stacked layout a right rag would
   pull the service name away from the lockup's left margin and make the reflow
   read as a broken row rather than as a deliberate stack. */
@media (min-width: 48em) {
  .site-title {
    text-align: right;
  }
}

@media (min-width: 60em) {
  .org-name {
    /* Two lines, as BIPA's own horizontal lockup sets it. */
    max-width: 26ch;
  }
}

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

/* The band carrying the page's primary matter: the search on the search page,
   a record's identity on an entity page. Paper ground, full width, real
   vertical room — a citizen on a phone meets one obvious thing to do. It is a
   base-template region, not a search-page component, so the two cannot drift. */
.hero {
  background: var(--color-paper);
  border-bottom: 1px solid var(--color-border);
  padding-top: var(--space-6);
  padding-bottom: var(--space-6);
}

/* Not capped to the prose measure: a heading is not prose, and at 42rem
   "Search the business register" broke over two lines on a 1280 screen while
   the region around it stayed empty. A character cap keeps a long entity name
   from running the full shell without forcing a short heading to wrap. */
/* The heading sits close to whatever qualifies it; the air before the primary
   action is put on the action itself (below), so a lede line between them is
   attached to the heading rather than floating between two 30px gaps. A record
   page's heading is not affected — `.record-head h1` sets its own. */
.hero h1 {
  margin-bottom: var(--space-3);
  max-width: 32ch;
}

/* The reassurance that used to be the masthead's second band, relocated to the
   action it is about. Set between the heading and the body scale so it reads
   as the heading's qualifier and not as the page's first paragraph. 6.6:1 on
   the paper ground. */
.hero-lede {
  margin: 0;
  color: var(--color-muted);
  font-size: 1.0625rem;
  max-width: var(--measure);
}

.hero .search-form {
  margin-top: var(--space-5);
}

/* <main> carries no width: each region inside it (the hero, the body) sets its
   own ground and holds its content in a .shell. See base.html for why the hero
   is inside main rather than above it. */
main {
  min-height: 40vh;
}

.main-body {
  padding-top: var(--space-6);
  padding-bottom: var(--space-8);
}

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

/* Ink ground, so the page ends on a deliberate edge rather than trailing off
   into white. The authority is named here in full, as a printed statutory
   document repeats its issuer — but as TEXT, not a second lockup: the logo's
   wordmark is black and would vanish on this ground, and inventing a
   reversed-out version of the client's asset is not ours to do. */
.site-footer {
  background: var(--color-text);
  color: var(--color-on-dark-muted);
  font-size: 0.9375rem;
  padding-top: var(--space-6);
  padding-bottom: var(--space-7);
}

.site-footer-inner {
  display: grid;
  gap: var(--space-5);
}

.site-footer a {
  color: var(--color-on-dark-link);
}

.site-footer a:hover {
  color: var(--color-on-dark);
}

.footer-org {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-ink-rule);
}

.footer-org-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.125rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--color-on-dark);
}

.site-footer p {
  max-width: var(--measure);
}

.footer-links {
  margin-bottom: 0;
}

/* --- staff sign-in / footer navigation ------------------------------------ */

/* Deliberately the quietest region in the footer, and deliberately last. A
   citizen must never form the impression that searching the register needs an
   account — the whole point of the service is that it is open. Smaller than the
   surrounding text and behind a rule. Shared byte-for-byte between the public
   register and /office/ via templates/search/_footer_staff_nav.html, which
   both base.html templates include — see that partial for the routing rules. */
.footer-staff {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-ink-rule);
  font-size: 0.875rem;
  color: var(--color-on-dark-muted);
}

.footer-staff p {
  margin-bottom: 0;
}

/* Signed in, the same strip states WHO — otherwise an officer who wandered onto
   the public page has no way to tell whether they still hold a session. */
.footer-staff strong {
  color: var(--color-on-dark);
}

.linkish {
  background: none;
  border: 0;
  padding: 0.25rem 0;
  font: inherit;
  color: var(--color-link);
  text-decoration: underline;
  text-underline-offset: 0.15em;
  cursor: pointer;
}

.linkish:hover {
  color: var(--color-link-hover);
}

/* Buttons, not a run of inline links: every footer destination (Staff sign-in,
   Back office, Sign out, Django admin) reads as an obviously interactive
   control, but the group stays subordinate to the page — outlined rather than
   filled, sized down from the search form's button, and drawn from tokens
   already in use on this ground (no new accent, per brand.md). A footer is not
   a call to action.

   Sign-out is a <form>, never a plain link (a GET logout is a one-pixel image
   away from firing on any page a signed-in officer visits) — its submit
   button gets the identical class so the row reads as one group of controls
   rather than three links and an odd button out. */
.footer-nav-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.footer-nav-signout {
  margin: 0;
}

/* Contrast, computed: white text (17.96:1) / the muted on-dark border
   (10.72:1) against the footer's ink ground; on hover, the border and text
   stay white (10.02:1) against the ink-rule fill used elsewhere as this
   ground's own hairline. All comfortably clear 4.5:1 text / 3:1 non-text. */
.footer-staff .footer-nav-button {
  display: inline-flex;
  align-items: center;
  /* WCAG 2.2 SC 2.5.8 wants a 24x24 CSS px target; the padding clears it even
     at this reduced footer type size. */
  min-height: 1.75rem;
  padding: 0.4rem 0.9rem;
  border: 1px solid var(--color-on-dark-muted);
  border-radius: var(--radius);
  background: transparent;
  color: var(--color-on-dark);
  font: inherit;
  font-size: 0.875rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}

.footer-staff .footer-nav-button:hover {
  border-color: var(--color-on-dark);
  background: var(--color-ink-rule);
  color: var(--color-on-dark);
}

/* The button's own `color`/`background` above are more specific than the
   sitewide `a:focus-visible` rule (two classes beat one class + one element),
   which would otherwise silently win the specificity war and leave a focused
   button's text white-on-white against the focus yellow. Restated here at
   matching specificity so a footer button gets the exact same visible focus
   as every other link and control on the page. */
.footer-staff .footer-nav-button:focus-visible {
  outline: 3px solid transparent;
  border-color: var(--color-focus-ink);
  color: var(--color-focus-ink);
  background: var(--color-focus);
  box-shadow:
    0 -2px var(--color-focus),
    0 4px var(--color-focus-ink);
  text-decoration: none;
}

/* The one control an anonymous visitor sees. Same shape as every other
   footer button — still obviously interactive — but the lighter weight keeps
   it from reading as a bigger action than the search field above it. */
.footer-nav-button-quiet {
  font-weight: 400;
}

.footer-nav-hint {
  margin-top: var(--space-2);
  font-size: 0.875rem;
}

/* --- search form --------------------------------------------------------- */

/* The primary action, and now sized like one: a 56px field at 19px, not a
   default input. Both controls share a height so the pair reads as one
   instrument. */
.search-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  /* Wider than the prose measure on purpose. This is the page's primary action
     and a register search is often a long registration number; a field that
     stops at the measure reads as a secondary control beside a 52px heading. */
  max-width: 46rem;
}

.search-form label {
  font-weight: 700;
  display: block;
  margin-bottom: var(--space-2);
  font-size: 1.0625rem;
}

.search-form input[type="search"] {
  font: inherit;
  font-size: 1.1875rem;
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--fast) var(--ease),
              border-color var(--fast) var(--ease);
  /* 2px ink border, per GOV.UK's form-element treatment — a 1px light-grey
     input border is the commonest way a government form fails 1.4.11. It is
     the full ink here rather than the mid grey: on the paper ground the field
     has to be the most positive shape in the region. */
  border: 2px solid var(--color-text);
  border-radius: var(--radius);
  padding: 0.7rem 0.85rem;
  width: 100%;
  min-height: 3.5rem;
  background: var(--color-bg);
  color: var(--color-text);
}

.search-form button {
  align-self: flex-start;
  font: inherit;
  font-size: 1.0625rem;
  font-weight: 650;
  letter-spacing: -0.006em;
  padding: 0.7rem 2rem;
  min-height: 3.5rem;
  border: 2px solid var(--color-brand);
  /* `background-color` AND `background-image`, never the `background`
     shorthand. The shorthand resets background-color to transparent, so the
     white label would have had NO guaranteed backing — it looked fine because
     the gradient painted, and would have failed in forced-colours mode, in
     print, and anywhere the image did not render. The accessibility checker
     computes contrast from `backgroundColor` and caught it at 1.09:1, which is
     exactly the reading a screen-reader-adjacent tool would take too. */
  background-color: var(--color-brand);
  background-image: linear-gradient(180deg,
              color-mix(in srgb, var(--color-brand) 92%, white),
              var(--color-brand));
  color: #fff;
  border-radius: var(--radius);
  box-shadow: var(--shadow-1), var(--shadow-inset);
  cursor: pointer;
  transition: transform var(--fast) var(--ease),
              box-shadow var(--fast) var(--ease),
              background var(--fast) var(--ease);
}

.search-form input[type="search"]:hover { box-shadow: var(--shadow-2); }

.search-form button:hover {
  background: var(--color-brand-deep);
  border-color: var(--color-brand-deep);
  box-shadow: var(--shadow-2);
  transform: translateY(-1px);
}

.search-form button:active {
  transform: translateY(0);
  box-shadow: var(--shadow-1);
}

@media (prefers-reduced-motion: reduce) {
  .search-form button { transition: none; }
  .search-form button:hover,
  .search-form button:active { transform: none; }
}

/* Po2 — a way to clear the search and get back to the landing state. A plain
   link, so it works with JavaScript off. */
.search-clear {
  font-size: 0.9375rem;
  margin-bottom: var(--space-3);
}

.search-examples {
  color: var(--color-muted);
  font-size: 0.9375rem;
  margin-bottom: 0;
}

.search-examples code {
  font-size: 0.9em;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: 0.05em 0.35em;
  white-space: nowrap;
}

.htmx-indicator {
  opacity: 0;
  font-size: 0.9375rem;
  color: var(--color-muted);
  transition: opacity 150ms linear;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .htmx-indicator {
    transition: none;
  }
}

/* --- result states -------------------------------------------------------- */

/* States are bordered blocks with a weighted left edge rather than tinted
   cards: the edge survives greyscale and printing, where a pale tint does not. */
.state {
  padding: var(--space-4);
  border: 1px solid var(--color-border);
  border-left: 5px solid var(--color-border-strong);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--color-sunken);
  margin-bottom: var(--space-4);
}

/* Every state block reads as ink on a tint, with the colour carried by the left
   bar rather than by the body text. Tinted text on a tinted ground was legible
   but tiring over a long paragraph, and it made the important states the
   HARDEST to read — the ambiguity notice and the guidance aside are the two
   longest passages on the site. Keeping one rule here also stops the badge
   palette and the block palette drifting apart. */
.state-empty,
.state-truncated {
  color: var(--color-text);
}

.state-error,
.state-stale-cursor {
  background: var(--color-negative-bg);
  border-color: var(--color-negative-text);
  border-left-color: var(--color-negative-text);
  color: var(--color-text);
}

.state-ambiguous {
  background: var(--color-warning-bg);
  border-color: var(--color-warning-text);
  border-left-color: var(--color-warning-text);
  color: var(--color-text);
}

.state-actions {
  margin-bottom: var(--space-4);
}

.result-count {
  color: var(--color-muted);
  font-size: 0.9375rem;
  margin-bottom: var(--space-2);
}

.provenance {
  font-size: 0.9375rem;
  color: var(--color-muted);
  border-left: 3px solid var(--color-border);
  padding-left: var(--space-3);
  margin-bottom: var(--space-5);
}

.provenance strong {
  color: var(--color-text);
}

/* --- results list --------------------------------------------------------- */

/* A hairline-separated list, not a grid of bordered cards: this is a register
   of records, and Companies House's plain result list is the peer precedent.
   The top rule is now the full ink weight, so the list starts on a deliberate
   edge instead of fading in. */
.result-list {
  list-style: none;
  margin: 0 0 var(--space-6);
  padding: 0;
  border-top: 3px solid var(--color-text);
}

.result-card {
  border-bottom: 1px solid var(--color-border);
  padding: var(--space-5) 0;
}

/* Deliberately NOT the display serif, unlike the headings around it. An entity
   name in a result list is a data value, and it is frequently long, upper-case
   and full of abbreviations — the exact material a serif reads worst at. The
   serif is reserved for the page's own voice. */
.result-name {
  display: inline-block;
  font-size: 1.3125rem;
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: -0.01em;
  margin-bottom: var(--space-3);
}

.result-meta {
  margin: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3) var(--space-5);
}

.result-meta > div {
  display: grid;
  /* Label above value on a phone — the previous side-by-side flex row could not
     fit a long compliance label and pushed the whole page into a horizontal
     scroll at 375px and 768px. */
  grid-template-columns: 1fr;
  gap: 0.15rem;
  min-width: 0;
}

/* Field labels across the register: one treatment, so a result card and a
   record header state their fields in the same voice. */
.result-meta dt,
.record-summary dt {
  color: var(--color-muted);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.result-meta dd,
.record-summary dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: break-word;
}

.result-next-step {
  margin: var(--space-3) 0 0;
  font-size: 0.9375rem;
}

.matched-on {
  color: var(--color-muted);
  font-size: 0.875rem;
  margin: var(--space-3) 0 0;
}

/* --- compliance and status badges ---------------------------------------- */

/* The most consequential element on the site: it states a legal position about
   a named business. Four things carry that, in order of reliability —
     1. the full sentence inside the badge (it never abbreviates to a word);
     2. the shape marker, which differs per state and survives greyscale;
     3. the left bar's weight;
     4. colour, last.
   Square-ish corners, not pills: GOV.UK user research found saturated pills
   are read as buttons and clicked. This must not look actionable. */
.badge {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45em;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.35;
  padding: 0.3rem 0.65rem;
  border-radius: var(--radius);
  border-left: 4px solid currentColor;
  /* Must wrap. Fixed the horizontal page overflow caused by nowrap. */
  white-space: normal;
  overflow-wrap: break-word;
  max-width: 100%;
}

/* Shape marker — redundant, non-colour encoding of the state. Decorative to
   assistive tech (the label already says it), meaningful in print and to a
   colour-blind reader. */
/* Sits on the FIRST line's baseline, not centred on the block: a compliance
   label wraps to two lines on a phone, and a centred marker floats away from
   the words it qualifies. */
.badge::before {
  content: "";
  flex: 0 0 auto;
  width: 0.6em;
  height: 0.6em;
  transform: translateY(-0.02em);
  background: currentColor;
}

/* compliant — solid disc */
.badge-positive {
  background: var(--color-positive-bg);
  color: var(--color-positive-text);
}

.badge-positive::before {
  border-radius: 50%;
}

/* not compliant — solid square, the visually heaviest marker */
.badge-negative {
  background: var(--color-negative-bg);
  color: var(--color-negative-text);
}

.badge-negative::before {
  border-radius: 0;
}

/* caution (e.g. on an inactive list) — solid diamond */
.badge-warning {
  background: var(--color-warning-bg);
  color: var(--color-warning-text);
}

.badge-warning::before {
  border-radius: 1px;
  transform: translateY(-0.02em) rotate(45deg);
  width: 0.55em;
  height: 0.55em;
}

/* not yet due / unknown — HOLLOW ring. Deliberately the only unfilled marker:
   "not yet due" and "not on record" are not claims of good standing, and must
   never read as a tick. */
.badge-neutral {
  background: var(--color-neutral-bg);
  color: var(--color-neutral-text);
}

.badge-neutral::before {
  background: transparent;
  border: 2px solid currentColor;
  border-radius: 50%;
}

/* --- register statistics (landing page) ---------------------------------- */

/* Reversed out on ink: a published statistical panel, which is what these
   figures are. 140,564 entities and 58.6% non-compliant are the most arresting
   facts this service holds and on white they were a paragraph with a big number
   in it. Contained rather than full-bleed because `width: 100vw` overflows by
   the scrollbar width — the exact defect class the last pass was fixing. */
.stats-panel {
  background: var(--color-text);
  color: var(--color-on-dark);
  border-radius: var(--radius);
  padding: var(--space-5) var(--space-4) var(--space-6);
  margin-bottom: var(--space-6);
}

.stats-panel h2 {
  margin-top: 0;
  margin-bottom: var(--space-2);
  color: var(--color-on-dark);
}

.stats-panel h3 {
  color: var(--color-on-dark);
  font-size: 0.875rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin-top: 0;
  margin-bottom: var(--space-3);
}

.stats-panel .scope-caveat,
.stats-panel caption,
.stats-panel .stat-total-label,
.stats-panel .compliance-figure-label,
.stats-panel .stat-status-caveat {
  color: var(--color-on-dark-muted);
}

.scope-caveat {
  font-size: 0.9375rem;
  max-width: var(--measure);
  margin-bottom: 0;
}

.stats-panel .scope-caveat strong {
  color: var(--color-on-dark);
}

/* The headline figure. The numbers are the most compelling thing on this page,
   so they are given room and weight — but no count-up animation: these are
   legal facts, not a marketing metric. Set in the body sans, not the display
   serif: a system serif's figures are old-style, which is wrong for a count. */
.stat-total {
  margin: var(--space-6) 0 var(--space-5);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--color-ink-rule);
}

.stat-number {
  display: block;
  font-size: clamp(3rem, 2rem + 5.2vw, 5rem);
  font-weight: 700;
  line-height: 0.95;
  letter-spacing: -0.035em;
  font-variant-numeric: tabular-nums;
}

.stat-total-label {
  display: block;
  font-size: 1rem;
  margin-top: var(--space-3);
}

.stat-bo-compliance {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-ink-rule);
}

/* A plain proportional bar for the compliance split. Solid fills, no gradient;
   aria-hidden because the figures beside it are the accessible statement. Both
   fills clear 3:1 against the ink ground. */
.compliance-bar {
  display: flex;
  height: 1rem;
  margin: 0 0 var(--space-5);
  border-radius: 1px;
  overflow: hidden;
  max-width: var(--measure);
}

.compliance-bar-negative {
  background: var(--color-bar-negative);
}

/* Takes whatever the non-compliant share leaves, so only ONE width in this
   component is data and needs an inline style. */
.compliance-bar-positive {
  background: var(--color-bar-positive);
  flex: 1;
}

.compliance-figures {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-4);
  max-width: var(--measure);
}

/* The two halves of the split are given the bar's own colours so the reader can
   tie a figure to a segment without a legend. Both clear 4.5:1 on ink. */
.compliance-figures li {
  border-left: 4px solid currentColor;
  padding-left: var(--space-3);
}

.compliance-figures li:first-child {
  color: var(--color-on-dark-negative);
}

.compliance-figures li:last-child {
  color: var(--color-on-dark-positive);
}

.compliance-figure-value {
  display: block;
  font-size: clamp(1.875rem, 1.5rem + 1.5vw, 2.5rem);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
}

.compliance-figure-label {
  display: block;
  font-size: 0.9375rem;
  margin-top: var(--space-1);
}

.stat-status-caveat {
  font-size: 0.9375rem;
  margin-top: var(--space-6);
  margin-bottom: 0;
  max-width: var(--measure);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-ink-rule);
}

/* The by-type table, reversed out with the panel. Hairlines in the ink rule
   shade; the header rule keeps the full white weight so the column heads read
   as a header and not as another row. */
.stats-panel table {
  color: var(--color-on-dark);
}

.stats-panel th,
.stats-panel td {
  border-bottom-color: var(--color-ink-rule);
}

.stats-panel thead th {
  border-bottom-color: var(--color-on-dark);
}

/* Counts read as a column of numbers, not as ragged text, and sit against the
   right edge of the panel where a table of figures belongs. */
.stats-panel td {
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.stats-panel thead th:last-child {
  text-align: right;
}

/* The last row's hairline sat a few pixels above the compliance block's own
   top rule, reading as a double rule with an empty band between them. */
.stats-panel tbody tr:last-child th,
.stats-panel tbody tr:last-child td {
  border-bottom: 0;
}

/* --- entity detail -------------------------------------------------------- */

/* A record's identity — name, type, number, standing, as-at date — is lifted
   into the hero band, so the page opens with the thing a citizen came to check
   presented as a record header rather than as the first few lines of a
   document. Everything below it is supporting history. */
.record-head .back-link {
  display: inline-block;
  margin-bottom: var(--space-4);
  font-size: 0.9375rem;
}

.record-head h1 {
  margin-bottom: var(--space-5);
}

/* The record summary: label-above-value pairs, which is how Companies House
   presents the authoritative facts of a company record. */
.record-summary {
  margin: 0 0 var(--space-4);
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4) var(--space-5);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.record-summary > div {
  min-width: 0;
}

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
}

.record-head .provenance {
  margin-bottom: 0;
}

/* Guidance: ink on a pale ground with a heavy left bar, rather than amber text
   on amber. The previous all-amber block was legible but tiring, and the
   heading disappeared into its own background. */
.guidance {
  border: 1px solid var(--color-warning-text);
  border-left: 6px solid var(--color-warning-text);
  background: var(--color-guidance-bg);
  color: var(--color-text);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: var(--space-5);
  margin: 0 0 var(--space-6);
  max-width: var(--measure);
}

.guidance h2 {
  margin-top: 0;
  font-size: 1.375rem;
  color: var(--color-warning-text);
}

.guidance p:last-child {
  margin-bottom: 0;
}

/* Each part of a record is a section a reader can find: a full-weight rule
   above the heading, real room between them. Stacked blocks with a heading
   were what made this page read as one undifferentiated document. */
.entity-detail > section {
  margin-bottom: var(--space-7);
  padding-top: var(--space-5);
  border-top: 3px solid var(--color-text);
}

.entity-detail > section > h2 {
  margin-top: 0;
}

address {
  font-style: normal;
}

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

.detail-table-wrap {
  overflow-x: auto;
}

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.9375rem;
}

caption {
  text-align: left;
  font-weight: 700;
  color: var(--color-muted);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding-bottom: var(--space-3);
}

th,
td {
  text-align: left;
  padding: var(--space-3) var(--space-3) var(--space-3) 0;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
  /* Cells wrap. Blanket `nowrap` forced a horizontal scroll on every register
     table at 375px, which is the width most of this audience will use. */
  white-space: normal;
}

thead th {
  border-bottom: 2px solid var(--color-text);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding-bottom: var(--space-2);
}

tbody th {
  font-weight: 600;
}

td:last-child,
th:last-child {
  padding-right: 0;
}

/* --- wider screens -------------------------------------------------------- */

@media (min-width: 40em) {
  .search-form {
    flex-direction: row;
    align-items: flex-end;
    gap: var(--space-3);
  }

  .search-form .search-field {
    flex: 1;
  }

  .result-meta {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .record-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .compliance-figures {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-5);
  }

  .hero {
    padding-top: var(--space-7);
    padding-bottom: var(--space-7);
  }

  .stats-panel {
    padding: var(--space-6) var(--space-6) var(--space-7);
  }

  /* The lead figure and the register's scope read side by side once there is
     room: the number takes the eye, the caveat sits beside it rather than
     three paragraphs away, which is where a caveat gets skipped. */
  .stat-total {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    gap: var(--space-3) var(--space-5);
  }

  .stat-total-label {
    margin-top: 0;
  }
}

@media (min-width: 60em) {
  .result-meta {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .record-summary {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* A record's sections become heading-beside-content once there is room for
     it — the layout a statutory publication uses, and the reason the desktop
     view no longer reads as a phone page stretched wide. No markup changes:
     the heading takes the first column, everything else stacks in the second. */
  .entity-detail > section {
    display: grid;
    grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
    column-gap: var(--space-6);
  }

  /* Stepped down from the full h2 scale: in a 15rem column the display size
     broke "Registered office address" over three lines, which reads as a
     mistake rather than as a heading. Its authority here comes from the rule
     above it and the space around it, not from its size. */
  .entity-detail > section > h2 {
    grid-column: 1;
    grid-row: 1;
    margin-bottom: 0;
    font-size: 1.375rem;
  }

  .entity-detail > section > :not(h2) {
    grid-column: 2;
  }

  .site-footer-inner {
    grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
    gap: var(--space-7);
  }

  .footer-org {
    padding-bottom: 0;
    border-bottom: 0;
    border-right: 1px solid var(--color-ink-rule);
    padding-right: var(--space-6);
  }
}

/* --- print ---------------------------------------------------------------- */

/* A register page is printed and filed. Tints are commonly dropped by printer
   settings, so the badge's meaning survives without them — the label and marker
   shape do that, and the border is forced back on. The ink grounds are dropped
   outright: reversed-out text is unreadable when colour is dropped, and a solid
   black slab when it is not. */
@media print {
  body {
    font-size: 11pt;
  }

  .masthead-bar,
  .stats-panel,
  .site-footer,
  .hero {
    background: transparent;
    color: #000;
    border-color: #000;
  }

  /* The orange seam is 2.26:1 and prints as an indeterminate grey; the bar
     still needs an edge, so it is redrawn in black. */
  .masthead-bar {
    border-bottom: 2px solid #000;
  }

  .site-title {
    color: #000;
  }

  .stats-panel {
    border: 1px solid #000;
    padding: 0.5cm;
  }

  .stats-panel h2,
  .stats-panel h3,
  .stats-panel table,
  .stats-panel .scope-caveat,
  .stats-panel .stat-total-label,
  .stats-panel .compliance-figure-label,
  .stats-panel .stat-status-caveat,
  .stats-panel .scope-caveat strong,
  .org-name,
  .footer-org-name,
  .compliance-figures li:first-child,
  .compliance-figures li:last-child {
    color: #000;
  }

  .stats-panel th,
  .stats-panel td,
  .stats-panel thead th,
  .stat-total,
  .stat-bo-compliance,
  .stat-status-caveat,
  .footer-org,
  .org-name {
    border-color: #000;
  }

  .search-form,
  .search-examples,
  .search-clear,
  .skip-link,
  .state-actions,
  .htmx-indicator,
  /* A staff sign-in prompt on a printed and filed register page is noise. */
  .footer-staff {
    display: none;
  }

  .badge {
    border: 1px solid #000;
    border-left-width: 4px;
    color: #000;
    background: transparent;
  }

  .provenance,
  .guidance {
    border-color: #000;
  }

  a {
    color: #000;
  }

  .detail-table-wrap {
    overflow: visible;
  }
}

/* --- Messages ------------------------------------------------------------ */

/* The public shell had no renderer for django.contrib.messages, so every
   refusal raised on a /my/ page was discarded before it reached a page. Colours
   match the office's so a refusal reads the same wherever it is met. */
.page-messages {
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
}

.page-message {
  padding: var(--space-3) var(--space-4);
  border-left: 5px solid var(--color-border-strong);
  background: var(--color-sunken);
  margin-bottom: var(--space-2);
  max-width: var(--measure);
  border-radius: var(--radius);
}

.page-message--success {
  border-left-color: var(--color-positive-text);
  background: var(--color-positive-bg);
  color: var(--color-positive-text);
}

.page-message--error {
  border-left-color: var(--color-negative-text);
  background: var(--color-negative-bg);
  color: var(--color-negative-text);
}

/* --- primary navigation --------------------------------------------------- */

/* The second masthead band, and the note above the masthead in base.html
   explains why it is allowed back: it carries navigation, which is the one
   thing the earlier two-band construction lacked.

   Built 2026-08-02 after measuring the deployed site: `<nav>` was absent from
   14 of 16 public page types. The CMS held six navigation items and a context
   processor to serve them; nothing had ever been registered or rendered. */
/* Sticky, so the way out of a 29-item section page or a 3,000-row search is
   always on screen rather than a scroll away. `position: sticky` is CSS only —
   no scroll listener, nothing to fail with JavaScript off. */
.masthead-nav {
  position: sticky;
  top: 0;
  z-index: 30;
  background: rgb(255 255 255 / 92%);
  /* A cheap blur ONLY where it is supported and only 8px: enough that text
     scrolling under the bar reads as behind it rather than through it. The
     opaque background above is the fallback, so nothing depends on this. */
  backdrop-filter: saturate(180%) blur(8px);
  border-bottom: 1px solid var(--color-border);
  box-shadow: var(--shadow-1);
}

.masthead-nav__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-5);
  /* `margin-block`, never `margin`. This element also carries `.shell`, whose
     `margin: 0 auto` is what centres every region on the page — a bare
     `margin: 0` here silently cancelled it, and the navigation sat hard against
     the left edge of the window while the masthead above it and the content
     below it were both centred. One shorthand, one misalignment on every page. */
  margin-block: 0;
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
  list-style: none;
}

.masthead-nav a {
  display: flex;
  align-items: center;
  /* 44px tap target from the padding, no media query needed. */
  min-height: 44px;
  padding: var(--space-3) var(--space-3);
  margin-inline: calc(var(--space-3) * -1);
  border-radius: var(--radius);
  font-size: var(--text-sm);
  font-weight: 550;
  letter-spacing: -0.006em;
  color: var(--color-text);
  text-decoration: none;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}

.masthead-nav a:hover {
  color: var(--color-brand-deep);
  background: rgb(176 81 8 / 7%);
}

/* Where you are. The rule carries it as well as the colour — colour alone
   fails WCAG 1.4.1, and these six labels sit in a row where a shade difference
   is the easiest thing in the world to miss. */
/* Where you are: a filled pill, not an underline. The rule carries it as well
   as the colour — colour alone fails WCAG 1.4.1, and in a row of six labels a
   shade difference is the easiest thing in the world to miss. */
.masthead-nav a[aria-current="page"] {
  color: var(--color-brand-deep);
  background: rgb(176 81 8 / 10%);
  box-shadow: inset 0 0 0 1px rgb(176 81 8 / 22%);
}

/* Under this the six labels wrap to two lines, which is the intended
   behaviour rather than a fallback — see base.html on why there is no
   hamburger. Tightening the gap keeps them to two lines rather than three on a
   360px phone. */
@media (max-width: 40rem) {
  .masthead-nav__inner { gap: 0 var(--space-4); }
  .masthead-nav a { font-size: 0.95rem; }
}

/* --- News and guidance --------------------------------------------------- */

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

/* One column by default. The image column is opt-in via
   `.feed__item--illustrated`, which the template applies under the same
   condition that emits the <img>.

   It used to be an unconditional `200px 1fr`. With no hero image the <img> was
   never rendered, so the TEXT became the first grid child and was laid out in
   the 200px column with the rest of the row empty — headlines wrapping every
   two words down a narrow ribbon on a 1280px page. Every article in the seeded
   corpus is imageless, so that was the entire news page. Found by looking at
   it; no test asserts a column width and none would have. */
.feed__item {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--color-border);
  align-items: start;
}

.feed__item--illustrated {
  grid-template-columns: 200px 1fr;
}

/* Below this the image would be a postage stamp beside two lines of text.
   It goes full width above the words instead. */
@media (max-width: 40rem) {
  .feed__item--illustrated {
    grid-template-columns: 1fr;
  }
}

.feed__image {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
}

.feed__item h2 {
  margin: 0 0 var(--space-2);
  font-size: 1.25rem;
}

.feed__kind {
  margin: 0 0 var(--space-1);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-brand-deep);
}

.feed__standfirst {
  margin: 0 0 var(--space-2);
  max-width: var(--measure);
}

.feed__date {
  margin: 0;
  font-size: 0.875rem;
  color: var(--color-muted);
}

.feed--compact li {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.feed--compact .feed__date {
  display: block;
}

.panel--notices {
  border-left: 4px solid var(--color-brand);
}

.article {
  max-width: var(--measure);
}

.article__standfirst {
  font-size: 1.15rem;
  line-height: 1.5;
  color: var(--color-text);
}

.article__meta {
  font-size: 0.875rem;
  color: var(--color-muted);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

.article__hero {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  margin: var(--space-4) 0;
}

.article__body h2 { margin-top: var(--space-5); }
.article__body p,
.article__body ul { max-width: var(--measure); }

.image-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--space-4);
}

.image-grid img {
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  border: 1px solid var(--color-border);
}

/* --- Cookie choice ------------------------------------------------------- */

/* Anchored to the bottom and NOT modal. A citizen checking whether their
   company is about to be deregistered must not have to make a privacy decision
   before they can read the page. */
.consent {
  position: sticky;
  bottom: 0;
  background: var(--color-text);
  color: var(--color-on-dark);
  border-top: 4px solid var(--color-brand);
  padding: var(--space-4) 0;
}

.consent__inner {
  display: flex;
  gap: var(--space-4);
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
}

.consent__text {
  margin: 0;
  max-width: 60ch;
  font-size: 0.9375rem;
  line-height: 1.5;
}

.consent__actions {
  display: flex;
  gap: var(--space-2);
  flex-shrink: 0;
}

/* Explicitly styled. This sheet has no generic `button` rule — every button is
   styled by its context — so the consent buttons rendered as raw browser
   defaults until this existed. On a government service that reads as a
   third-party widget bolted on, which is exactly what a cookie banner must NOT
   look like: people dismiss those without reading them.

   The tap target is 44px, the accessibility minimum for a touch control, and
   both choices are given equal visual weight. A banner whose "Allow" is a
   button and whose "No thanks" is faint grey text is a dark pattern, and this
   one records a real consent decision. */
.consent__actions button {
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  min-height: 44px;
  padding: 0.55rem 1.4rem;
  border-radius: var(--radius);
  cursor: pointer;
  border: 2px solid var(--color-on-dark);
  background: var(--color-on-dark);
  color: var(--color-text);
}

.consent__actions .button--secondary {
  background: transparent;
  color: var(--color-on-dark);
}

.consent__actions button:hover {
  background: var(--color-on-dark-muted);
  border-color: var(--color-on-dark-muted);
  color: var(--color-text);
}

/* On a phone the banner is the last thing between a reader and the page, so it
   gets tighter and the buttons go full width — a 44px target is only a target
   if it is reachable with a thumb. */
@media (max-width: 34rem) {
  .consent {
    padding: var(--space-3) 0;
  }

  .consent__text {
    font-size: 0.875rem;
  }

  .consent__actions {
    width: 100%;
  }

  .consent__actions button {
    flex: 1;
  }
}

/* --- CSS-only bar chart -------------------------------------------------- */

/* No charting library: a national register should not pull a megabyte of
   JavaScript to draw thirty rectangles, and this works with scripting off. */
.bars {
  list-style: none;
  margin: var(--space-3) 0;
  padding: 0;
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 140px;
}

.bars__bar {
  flex: 1;
  min-width: 3px;
  height: var(--h, 1%);
  background: var(--color-brand);
  border-radius: 2px 2px 0 0;
}

.two-up {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--space-4);
}

/* --- Stat cards and the front page --------------------------------------- */

/* Figures given weight, rather than a definition list. The non-compliant count
   is the fact this service exists about, so it carries its own colour. */
.stat-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-3);
  margin: var(--space-4) 0 var(--space-3);
}

/* Brought onto the same card language as everything else on 2026-08-03. These
   were a warm-grey fill behind a grey `--color-border-strong` bar — the exact
   pairing retired from the section cards and the queue tiles, because a cold
   fill with a grey rule is what a DISABLED control looks like. A statistic is
   the least disabled thing on the page. */
.stat-card {
  padding: var(--space-5);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-1);
}

.stat-card--good { border-left-color: var(--color-positive-text); }
.stat-card--attention { border-left-color: var(--color-brand); }

.stat-card__n {
  display: block;
  font-size: clamp(1.75rem, 1.3rem + 1.8vw, 2.5rem);
  font-weight: 700;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
}

.stat-card__label {
  display: block;
  margin-top: var(--space-2);
  font-size: 0.9375rem;
  color: var(--color-muted);
  line-height: 1.4;
}

.home-news__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  border-bottom: 2px solid var(--color-text);
  padding-bottom: var(--space-2);
  margin-top: var(--space-7);
}

.home-news__head h2 { margin: 0; }

.home-news__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-5);
  padding-top: var(--space-4);
}

.home-news__list h3 {
  margin: 0 0 var(--space-2);
  font-size: 1.125rem;
}

/* Motion, deliberately small. The original brief for this sheet said "no motion
   beyond an instant state change"; that is relaxed here to a 2px lift and a
   border colour on the section cards, because a card that does not respond at
   all reads as an image rather than a link. Anything larger would make a
   statutory register feel like a marketing site, and anyone who has asked for
   less motion gets none. */
.home-section {
  transition: transform 140ms ease, border-color 140ms ease,
    box-shadow 140ms ease;
}

.home-section::after {
  transition: transform 140ms ease;
}

.home-section:hover,
.home-section:focus-visible {
  transform: translateY(-2px);
  border-color: var(--color-brand);
}

@media (prefers-reduced-motion: reduce) {
  .home-section,
  .home-section::after { transition: none; }
  .home-section:hover,
  .home-section:focus-visible { transform: none; }
  /* The chevron keeps its 45deg rotation — that is its SHAPE, not motion. Only
     the 2px slide is dropped, by not overriding the base transform. */
  .home-section:hover::after { transform: rotate(45deg); }
}

/* --- Filter rows --------------------------------------------------------- */

/* The office sheet styles `.filters`; the public one did not, so the news feed's
   period selector and its button rendered as raw browser defaults beside
   carefully-styled everything else. Found by rendering the page at 390px —
   the same class of defect as the consent buttons, and invisible to every
   other check we have. */
.filters {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
  margin: var(--space-4) 0;
}

.filters select {
  font: inherit;
  font-size: 1rem;
  min-height: 44px;
  padding: 0.5rem var(--space-3);
  border: 2px solid var(--color-border-strong);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-text);
}

.filters button {
  font: inherit;
  font-size: 1rem;
  font-weight: 700;
  min-height: 44px;
  padding: 0.5rem var(--space-4);
  border: 2px solid var(--color-brand);
  background: var(--color-brand);
  color: #fff;
  border-radius: var(--radius);
  cursor: pointer;
}

.filters button:hover {
  background: var(--color-brand-deep);
  border-color: var(--color-brand-deep);
}

/* --- the account holder's bar and actions (/my/) ------------------------- */

/* Added 2026-08-02. `/my/` had NO navigation at all: a holder signed in, saw a
   heading and one orphan link floating above the content, and had no way to
   discover anything else the account could do. This is the portal's equivalent
   of the officer bar — deliberately quieter, because a business owner visits
   twice a year and an officer lives here all day. */
.my-bar {
  background: var(--color-paper);
  border-bottom: 1px solid var(--color-border);
}

.my-bar__inner {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: center;
  justify-content: space-between;
}

.my-nav,
.my-identity {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  align-items: center;
}

.my-nav a,
.my-identity a,
.my-identity .linkish {
  display: block;
  min-height: 44px;
  padding: 0.6rem 0;
  line-height: 1.4;
}

/* Where you are. The weight and the rule carry it, not the colour alone —
   WCAG 1.4.1, same reasoning as the office bar. */
.my-nav a[aria-current="page"] {
  font-weight: 600;
  color: var(--color-brand-deep);
  box-shadow: inset 0 -3px 0 var(--color-brand);
}

.my-who { color: var(--color-muted); }

.my-actions {
  margin: var(--space-6) 0 0;
  padding: var(--space-4);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-top: 3px solid var(--color-brand);
  border-radius: var(--radius);
}

.my-actions h2 { margin: 0 0 var(--space-3); }

.my-actions ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

.my-actions li a {
  display: block;
  min-height: 24px;
  font-weight: 600;
}

.my-actions li span {
  display: block;
  margin-top: var(--space-1);
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--color-muted);
}

.my-actions__note {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--color-muted);
}

/* --- /status/ before a search -------------------------------------------- */

/* This page used to render a form above ~120px of empty white and then the
   footer — nothing at all in <main>. The worst possible state for the one page
   a worried business owner arrives at first. */
.status-guide {
  max-width: var(--measure);
}

.status-guide__list {
  margin: 0 0 var(--space-4);
  padding: 0 0 0 var(--space-4);
}

.status-guide__list li + li { margin-top: var(--space-3); }

.status-guide__boundary {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--color-guidance-bg);
  border-left: 3px solid var(--color-brand);
  border-radius: var(--radius);
}

.status-guide__alt { margin: 0; }

/* --- the global reduced-motion backstop ----------------------------------- */

/* Per-component rules are above; this is the net beneath them, so a transition
   added tomorrow without its own escape is still honoured. Not `all: unset` —
   that would break layout. Duration only. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
