/* ============================================================
   BASE — applies the tokens to real elements.

   tokens.css decides what the colours are; this file decides
   what they land on. Components reference semantic aliases
   only, never a palette token and never a raw hex.
   ============================================================ */

/* ---- Fonts ----------------------------------------------------------------
   Self-hosted, not loaded from a font CDN. These pages are meant to run under
   a council's own .gov.uk domain, and a third-party request on every page view
   sends a visitor's IP to that third party — a controller-side disclosure the
   council would have to account for, to no benefit. Latin subsets only, all
   three under the SIL Open Font Licence.

   Variable fonts, so one file covers every weight we use.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: 'Fraunces';
  src: url('/fonts/fraunces-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'JetBrains Mono';
  src: url('/fonts/jetbrains-mono-latin.woff2') format('woff2');
  font-weight: 400 600;
  font-style: normal;
  font-display: swap;
}

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

body,
h1, h2, h3, h4,
p, figure, blockquote, dl, dd { margin: 0; }

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

/* Author display rules beat the UA sheet's [hidden]; without this a hidden
   .button stays on screen. */
[hidden] { display: none !important; }

body {
  min-height: 100vh;
  background: var(--surface-sunken);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--size-base);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

/* ---- Type ----------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  color: var(--text);
}

h1 {
  font-size: var(--size-3xl);
  font-weight: 500;
  line-height: var(--leading-display);
  letter-spacing: var(--tracking-display);
}

h2 {
  font-size: var(--size-xl);
  font-weight: 600;
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
}

h3 {
  font-size: var(--size-lg);
  font-weight: 600;
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
}

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

a { color: var(--brand-text); }
a:hover { color: var(--brand-hover); }

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

/* Statutory citations, dates and codes. The vocabulary is part of the
   product's credibility, and mono is how we mark it as quotable. */
.ref {
  font-family: var(--font-mono);
  font-size: var(--size-sm);
  font-weight: 500;
}

/* ---- Layout --------------------------------------------------------------- */
.page {
  max-width: var(--page-width);
  margin-inline: auto;
  padding: var(--size-xl) var(--size-lg) var(--size-4xl);
}

.stack > * + * { margin-top: var(--size-lg); }

/* More room under a heading than between ordinary siblings, and more than the
   metric gap suggests: Fraunces sits high in its line box, so a 20px margin
   reads as about 13px of daylight above a bordered panel. Optical spacing,
   not metric. */
.stack > :is(h1, h2, h3) + * { margin-top: var(--size-2xl); }

/* The same optical argument on the other side of a bordered panel. A notice's
   border reads as a container edge, so ordinary content set the sibling gap
   below it reads as crowding the box rather than following it. Two notices in
   a row keep the sibling gap: both carry a border, so they already read as
   separate things. */
.stack > .alert + :not(.alert) { margin-top: var(--size-2xl); }
.stack-tight > * + * { margin-top: var(--size-sm); }

/* Sections breathe more than the lines inside them do. The page reads as a
   column of separate sheets only if the gap between sheets is visibly larger
   than any gap within one. */
main.stack > * + * { margin-top: var(--size-2xl); }

/* ---- Skip link ------------------------------------------------------------
   WCAG 2.4.1. Visible on focus, never a display:none trap for screen readers.
   -------------------------------------------------------------------------- */
.skip-link {
  position: absolute;
  left: var(--size-sm);
  top: var(--size-sm);
  z-index: 10;
  padding: var(--size-sm) var(--size-base);
  background: var(--brand-fill);
  color: var(--brand-fill-text);
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}

.skip-link:focus { transform: translateY(0); }

/* ---- Wordmark -------------------------------------------------------------
   Always "Quorate." with the full stop, always in the accent. The dot is a
   text character, so the token depends on the size it is drawn at: at 24px and
   above --wordmark-dot clears the large-text threshold, below it does not.
   -------------------------------------------------------------------------- */
.wordmark {
  font-family: var(--font-display);
  font-weight: 500;
  letter-spacing: var(--tracking-display);
  color: var(--text);
}

.wordmark--lg { font-size: var(--size-2xl); }
.wordmark--lg .wordmark__dot { color: var(--wordmark-dot); }

/* Below 24px the stronger tint is the only one that clears 4.5:1. */
.wordmark--sm { font-size: var(--size-base); }
.wordmark--sm .wordmark__dot { color: var(--wordmark-dot-strong); }

/* ---- Site header ----------------------------------------------------------
   The chrome: wordmark, navigation and page head in one element, one gradient.
   Two stacked bars each start their own ramp and meet at a visible step.
   -------------------------------------------------------------------------- */
.site-header {
  background: linear-gradient(135deg, var(--brand-field), var(--brand-field-hi));
}

.site-header__inner {
  max-width: var(--page-width);
  margin-inline: auto;
  padding: var(--size-base) var(--size-lg);
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-base) var(--size-lg);
  align-items: center;
  justify-content: space-between;
  /* The containing block for the Appearance panel, so the panel is measured
     against the page track and can never hang off the side of the viewport. */
  position: relative;
}

/* ---- Utility row -----------------------------------------------------------
   The session's own controls, beside the wordmark: the account, the way out,
   and the theme. Session things live with the identity row and place things
   live in the navigation (#205). Drawn as quiet links, never buttons: a lift up
   here would compete with the page's real buttons and lose.
   ---------------------------------------------------------------------------- */
.utility {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-base);
  align-items: center;
}

.utility__link {
  display: block;
  /* Padding plus the line box clears the 24px minimum of WCAG 2.2 SC 2.5.8. */
  padding: 0.55rem 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--size-sm);
  font-weight: 600;
  line-height: 1.4;
  color: var(--brand-field-muted);
  text-decoration: none;
  cursor: pointer;
}

.utility__link:hover { color: var(--brand-field-text); }

.utility__link:focus-visible {
  outline: 3px solid var(--brand-field-text);
  outline-offset: 2px;
}

/* ---- Appearance form -------------------------------------------------------
   Two segmented radio groups, dark mode and colour theme, saved together. The
   input is clipped, not display:none, so it keeps focus and arrow keys; state
   is drawn on the sibling span (:has() is too new for our users' browsers).

   Drawn on the account page's card since #270, so it takes the page's own
   tokens throughout. It used to sit on the purple band, where every colour
   here was a --brand-field-* one; those are unreadable on a white card in
   light mode and on a near-black one in dark.
   ---------------------------------------------------------------------------- */
.appearance-form fieldset {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: baseline;
}

.appearance-form fieldset + fieldset { margin-top: 0.75rem; }

.appearance-form legend {
  /* A legend cannot be a flex item; float keeps it inline where there is room. */
  float: left;
  padding-right: 0.75rem;
  color: var(--text-muted);
  font-size: var(--size-sm);
  font-weight: 600;
}

.segments {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
}

.segment input {
  position: absolute;
  width: 1px;
  height: 1px;
  /* Clipped as well as sized down, the same recipe as .visually-hidden. Size
     and opacity alone do not hold: forced colours put a form control back to
     its intrinsic size and paint it anyway, which drops an 18px radio on top
     of the label it is meant to be hidden behind. Only the clip survives. */
  clip-path: inset(50%);
  opacity: 0;
}

/* Direct child only: the state span, never the colour swatch nested inside it,
   which has a box of its own. */
.segment > span {
  display: block;
  /* 36px: clears the 24px minimum of WCAG 2.2 SC 2.5.8. */
  min-height: 2.25rem;
  padding: 0.4rem 0.75rem;
  /* The form-control spec: 2px --border-strong on --surface, same as every
     text input on the page, so the segments read as controls rather than as
     tags. */
  border: 2px solid var(--border-strong);
  background: var(--surface);
  color: var(--text);
  font-size: var(--size-sm);
  font-weight: 600;
  line-height: 1.4;
  /* A segment breaks as a whole or not at all: with the colour swatch in the
     label, wrapping inside one puts the swatch on its own line above the name
     and reads as a mistake. The row wraps instead. */
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--transition), color var(--transition);
}

.segment > span:hover { border-color: var(--brand); }

.segment input:checked + span {
  /* The chosen one is a filled primary, matched pair, which is the loudest
     thing the page's own palette offers and so reads as chosen in both modes.
     Never --surface with a coloured border: on the card it is the same colour
     as the card and the choice disappears. */
  background: var(--brand-fill);
  border-color: var(--brand-fill);
  color: var(--brand-fill-text);
}

/* On the page rather than the band now, so the standard ring applies. */
.segment input:focus-visible + span {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

.appearance-form__save { margin-top: 0.75rem; }

/* A colour theme's own two colours, drawn at 16px so the choice can be seen
   before it is saved. Decorative and aria-hidden: the name beside it is what
   carries the option, in line with never encoding meaning in colour alone.
   The colours are fixed rather than themed, because all three previews have
   to look like themselves whichever theme the page is currently painted in —
   the one sanctioned exception to components using semantic aliases only.

   currentColor for the border keeps the square delineated in both states: on
   an unchosen segment that is the page's text colour, and on the chosen one,
   which fills with --brand-fill, it is that fill's own text colour. */
.theme-swatch {
  display: inline-block;
  width: 1rem;
  height: 1rem;
  margin-right: 0.4rem;
  vertical-align: -0.175em;
  border: 2px solid currentColor;
}

.theme-swatch--chamber {
  background: linear-gradient(
    135deg,
    var(--swatch-chamber-brand) 0 50%,
    var(--swatch-chamber-accent) 50%
  );
}

.theme-swatch--parchment {
  background: linear-gradient(
    135deg,
    var(--swatch-parchment-brand) 0 50%,
    var(--swatch-parchment-accent) 50%
  );
}

.theme-swatch--ledger {
  background: linear-gradient(
    135deg,
    var(--swatch-ledger-brand) 0 50%,
    var(--swatch-ledger-accent) 50%
  );
}

/* ---- The appearance control in forced colours -------------------------------
   Windows High Contrast replaces every author background, border and text
   colour with the reader's own palette and drops background images. The chosen
   segment is marked by --brand-fill and nothing else, so under that
   replacement all six options collapse to the same empty box and a clerk
   running High Contrast cannot see which dark mode or which colour theme is in
   force. The state was being carried by colour alone (SC 1.4.1), in a mode no
   theme's tokens can reach, which is why the contrast work on the fill pair
   never caught it.

   The fix is to stop hiding the radio rather than to restate the fill in
   system colours. Painting the chosen span in Highlight/HighlightText looks
   like the obvious answer and does not survive: where Highlight is not fully
   opaque the browser puts its own solid backplate behind the text to protect
   contrast, the backplate lands on Canvas, and HighlightText on Canvas is the
   same colour — the label disappears into a solid block. A real radio is drawn
   by the platform, so its checked state is guaranteed legible in whatever
   palette the reader is running, which no author colour can promise. The
   control degrades to an ordinary radio group, which is the right shape for
   this mode anyway.

   The swatch opts out instead: it previews a colour theme, so the colour is
   the content rather than decoration, and flattened it is three identical
   squares that preview nothing.
   ---------------------------------------------------------------------------- */
@media (forced-colors: active) {
  .segment {
    display: flex;
    align-items: center;
    gap: 0.4rem;
  }

  .segment input {
    position: static;
    width: auto;
    height: auto;
    clip-path: none;
    opacity: 1;
  }

  .theme-swatch { forced-color-adjust: none; }
}

/* The wordmark is a link home but must not read as underlined body-link
   chrome; the pointer and focus outline carry the affordance. The outline
   uses the field text token: --focus-ring is purple on the purple band. */
.site-header__home { text-decoration: none; }

.site-header__home:focus-visible {
  outline: 3px solid var(--brand-field-text);
  outline-offset: 2px;
}

.site-header .wordmark { color: var(--brand-field-text); }
/* Display size only — the accent clears 3:1, not 4.5:1, over the light end. */
.site-header .wordmark__dot { color: var(--brand-field-accent); }

/* ---- Brand plate ----------------------------------------------------------
   The full-bleed brand surface: the whole panel IS the brand.
   -------------------------------------------------------------------------- */
.plate {
  background: linear-gradient(135deg, var(--brand-field), var(--brand-field-hi));
  color: var(--brand-field-text);
  padding: var(--size-2xl) var(--size-xl);
}

.plate .wordmark { color: var(--brand-field-text); }
/* Display size only: the accent is 4.0:1 over the light end of the gradient,
   which is the large-text threshold, not the body one. */
.plate .wordmark__dot { color: var(--brand-field-accent); }
.plate p { color: var(--brand-field-muted); max-width: 46ch; }

.section-head {
  padding-bottom: 0.4rem;
  border-bottom: 4px solid var(--brand);
}

/* ---- Panel: a sheet on the board. Flat; only buttons lift. ------------------ */
.card {
  background: var(--surface);
  border: 2px solid var(--border-strong);
  padding: var(--size-xl);
}

/* ---- Notice ---------------------------------------------------------------
   A panel with a heavier edge in the family colour: found by shape, not by a
   tinted wash. Only the lead-in takes the family text token; a whole
   paragraph in it reads as alarm.
   -------------------------------------------------------------------------- */
.alert {
  /* Two columns: the days-remaining count, then everything said about it.
     Floated, the count let the heading and the body wrap beside it while the
     button fell below the float and snapped back to the padding edge, so one
     component had three left edges (#207). As a grid track the count holds a
     column of its own and everything after it shares one edge.

     The gutter is the count's own margin and padding rather than a grid gap,
     because a notice without a count would otherwise be indented by a gap it
     has no column to fill. */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: 0;
  row-gap: var(--size-base);
  align-items: start;
  background: var(--surface);
  border: 3px solid var(--border-strong);
  /* The button draws its 3px offset outside its own box and moves into it
     when pressed, so the bottom needs that much again or the pressed state
     crosses the notice's border. */
  padding: var(--size-lg) var(--size-lg) calc(var(--size-lg) + 3px);
}

/* Everything except the count reads in the second column. */
.alert > * { grid-column: 2; }

.alert__count {
  grid-column: 1;
  grid-row: 1;
  /* The rule divides the whole notice, not just the figure. */
  align-self: stretch;
  margin-right: var(--size-lg);
  padding-right: var(--size-lg);
  border-right: 3px solid;
  text-align: center;
}

/* Everything the count is about, in one column so heading, body and button
   share a left edge. Wrapped rather than laid out as three grid rows because
   a negative row line counts back from the *explicit* grid, and a grid with
   no explicit rows has none to count: the count cannot be told to span rows
   it did not create. */
.alert__body { grid-row: 1; }

.alert__body > * + * { margin-top: var(--size-base); }

.alert__count b {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--size-4xl);
  line-height: 0.85;
  letter-spacing: var(--tracking-display);
}

.alert__count span {
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* The notice's own headline sits between the count and the body in size: the
   count says how urgent, the headline says what, the body says why. */
.alert :is(h2, h3) { font-size: var(--size-xl); }

/* The count's rule and figure take the family colour, as the heading does: the
   3px rule is a UI component (3:1), the figure is text on --surface and uses
   the same token the heading beside it already uses. */
.alert--success { border-color: var(--success); }

.alert--success :is(h2, h3, .alert__count b) { color: var(--success-text); }

.alert--success .alert__count { border-right-color: var(--success); }

.alert--urgent { border-color: var(--urgent); }

.alert--urgent :is(h2, h3, .alert__count b) { color: var(--urgent-text); }

.alert--urgent .alert__count { border-right-color: var(--urgent); }

.alert--brand {
  border-color: var(--brand);
}

.alert--brand :is(h2, h3, .alert__count b) { color: var(--brand-text); }

.alert--brand .alert__count { border-right-color: var(--brand); }

/* ---- Buttons --------------------------------------------------------------
   Every filled variant uses its matched fill/fill-text pair.
   -------------------------------------------------------------------------- */
.button {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  /* 44px, the target size WCAG 2.2 SC 2.5.8 asks for. A lot of clerks work on
     tablets, where a 32px button is a missed tap and a lost evening. */
  min-height: 2.75rem;
  padding: 0.5rem var(--size-lg);
  border: 2px solid transparent;
  font-family: var(--font-body);
  font-size: var(--size-sm);
  font-weight: 700;
  line-height: 1.4;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transition), border-color var(--transition),
    box-shadow var(--transition), transform var(--transition);
}

/* Pressed is the button moving into its own shadow. It reads as a physical
   press rather than a colour change, so it survives greyscale and does not
   depend on anyone noticing a hue shift. */
.button:active { box-shadow: none !important; transform: translate(3px, 3px); }

.button--primary {
  background: var(--brand-fill);
  color: var(--brand-fill-text);
  border-color: var(--brand-fill);
  box-shadow: var(--elevation-raised);
}

.button--primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); color: var(--text-on-brand); }

/* Secondary carries no fill, so its border is the only thing identifying it —
   --border-strong, not --border. */
.button--secondary {
  background: var(--surface);
  border-color: var(--border-strong);
  color: var(--brand-text);
  box-shadow: var(--elevation-raised);
}

.button--secondary:hover { border-color: var(--brand); }

/* Two or more buttons on one line. Left as inline elements they are separated
   only by the whitespace between the tags, about 4px, and the button draws its
   3px offset shadow outside its own box into that space. Row and column gap
   both apply, so a row that wraps on a small screen keeps the same breathing
   room down the page. */
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-lg);
  align-items: center;
}

/* ---- Forms ----------------------------------------------------------------
   Control boundaries use --border-strong: WCAG 2.2 SC 1.4.11 wants 3:1 for
   anything that identifies a control, and --border is decorative at ~1.2:1.
   -------------------------------------------------------------------------- */
fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}

legend {
  padding: 0;
  font-weight: 600;
}

.field-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-lg);
  align-items: center;
}

.field-row label {
  display: inline-flex;
  gap: 0.5rem;
  align-items: center;
  cursor: pointer;
}

input[type='radio'] {
  width: 1.15rem;
  height: 1.15rem;
  margin: 0;
  accent-color: var(--brand);
}

/* Visible in both themes, and not by colour change alone — the offset ring
   changes shape as well as hue. */
:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* ---- Table ----------------------------------------------------------------- */
.table-scroll { overflow-x: auto; }

table {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

caption {
  text-align: left;
  padding-bottom: var(--size-sm);
  color: var(--text-muted);
  font-size: var(--size-sm);
}

th, td {
  padding: 0.5rem var(--size-base) 0.5rem 0;
  text-align: left;
  border-bottom: 1px solid var(--border);
}

thead th {
  font-size: var(--size-sm);
  font-weight: 600;
  color: var(--text-muted);
  white-space: nowrap;
}

tbody tr:last-child td { border-bottom: 0; }

/* Substitute days and other notes — a label, never colour alone. */
/* A fact about an item rather than its state: outlined, so it sits below a
   filled status stamp in the reading order. */
.tag {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border: 2px solid var(--brand);
  color: var(--brand-text);
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ---- Swatches, for proving the theme ---------------------------------------- */
.swatches {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  gap: var(--size-base);
  padding: 0;
  margin: 0;
  list-style: none;
}

.swatch {
  border: 1px solid var(--border-strong);
  overflow: hidden;
}

.swatch__chip {
  display: block;
  height: 3rem;
}

.swatch__label {
  display: block;
  padding: 0.5rem 0.75rem;
  background: var(--surface);
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  color: var(--text-muted);
}

/* ---- Print ------------------------------------------------------------------
   Clerks print meeting packs. Drop the furniture, keep the record.

   The furniture-hiding block itself lives at the END of this file: it fights
   the components' own display rules at equal specificity, so whichever is
   written later wins, and a component defined below a mid-file print block
   printed anyway. That is how the navigation got onto paper once.
   ---------------------------------------------------------------------------- */
@media print {
  body { background: var(--surface); }
  .page { max-width: none; padding: 0; }

  /* Paper cannot be pressed, and dotted rules print badly. */
  .button { box-shadow: none; }

  .card,
  .tile { border: 1px solid var(--border-strong); }

  .row { border-bottom: 1px solid var(--border-strong); }
}

/* ============================================================
   PATTERNS — the pieces the clerk's daily pages are built from.
   Every colour here is a semantic alias whose pairing is already
   asserted in tokens.test.ts.
   ============================================================ */

/* ---- Page header ----------------------------------------------------------- */
.page-head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-base);
  align-items: baseline;
  justify-content: space-between;
}

/* ---- Page head: the title block is chrome, painted with the band. ----------- */
.site-head {
  padding: var(--size-xl) 0 var(--size-2xl);
  color: var(--brand-field-text);
}

/* With a trail the head hangs from the nav row: the crumb is wayfinding, and
   space above it reads as the trail belonging to the title, not the nav. */
.site-head--crumbed { padding-top: 0.5rem; }

.site-head__inner {
  max-width: var(--page-width);
  margin-inline: auto;
  padding-inline: var(--size-lg);
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-lg) var(--size-xl);
  align-items: flex-end;
  justify-content: space-between;
}

.site-head__title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--size-4xl);
  line-height: 0.95;
  letter-spacing: var(--tracking-display);
  color: var(--brand-field-text);
  text-wrap: balance;
}

/* Facts, never a standfirst. Mono because they are data. */
.site-head__facts {
  margin-top: 0.5rem;
  font-family: var(--font-mono);
  font-size: var(--size-sm);
  color: var(--brand-field-muted);
  font-variant-numeric: tabular-nums;
}

/* The only orange in the chrome: its one time-sensitive thing. */
.site-head__aside {
  border-left: 3px solid var(--brand-field-accent);
  padding-left: var(--size-base);
}

.site-head__label {
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  /* Not the accent: at 12px this is body-size text and the accent only
     clears the 3:1 display threshold over the light end of the band, where
     this aside sits. The muted field text clears 4.5:1 on both ends in both
     themes; the accent stays on the aside's rule, where it is decoration. */
  color: var(--brand-field-muted);
}

.site-head__when {
  margin-top: 0.3rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--size-xl);
  line-height: 1.1;
  letter-spacing: var(--tracking-heading);
}

.site-head__where {
  margin-top: 0.3rem;
  font-size: var(--size-sm);
  color: var(--brand-field-muted);
}

/* ---- The trail back up, above the title on the band -------------------------
   On a page reached through a tab rather than being one. It wraps as whole
   crumbs on a narrow screen: the separator is part of the item it precedes, so
   a break never leaves a chevron stranded at the end of a line.

   The way back is underlined rather than carried by colour alone, and the last
   crumb is not a link, because it is the page the reader is already on.
   ---------------------------------------------------------------------------- */
.crumbs { margin-bottom: var(--size-base); }

.crumbs ol {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: var(--size-sm);
}

.crumbs li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.crumbs__sep { color: var(--brand-field-muted); }

.crumbs a {
  display: inline-block;
  /* With the 14px line box this clears a 24px target: SC 2.5.8. */
  padding: 0.35rem 0;
  color: var(--brand-field-text);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* The colour is held, not changed: the global link hover is a purple that
   disappears into the band. The underline thickening is the whole signal. */
.crumbs a:hover {
  color: var(--brand-field-text);
  text-decoration-thickness: 2px;
}

/* --focus-ring is purple on purple over the band, as on the tabs; the field
   text token is the one guaranteed visible on both ends of the gradient. */
.crumbs a:focus-visible { outline-color: var(--brand-field-text); }

.crumbs__here {
  padding: 0.35rem 0;
  color: var(--brand-field-text);
  font-weight: 700;
}

/* ---- The record's state, stamped above the title on the band ----------------
   Above the title rather than beside it: a reader who is going to mistake a
   draft for the record is not going to read past the first line, so the first
   line is where the state goes. Mono and uppercase like every other stamp we
   draw, and the word is the whole of the signal, so greyscale, a screen reader
   and a printed page all carry it.
   ---------------------------------------------------------------------------- */
.stamp {
  display: inline-block;
  margin-bottom: var(--size-base);
  padding: 0.2rem 0.7rem;
  border: 3px solid var(--brand-field-text);
  font-family: var(--font-mono);
  font-size: var(--size-base);
  font-weight: 600;
  letter-spacing: 0.22em;
  /* The tracking is on the right of the last letter too, which pushes the word
     off centre in its own box without this. */
  text-indent: 0.22em;
  text-transform: uppercase;
  line-height: 1.4;
}

/* The plate inverted: the loudest thing the band can say without spending the
   accent, which belongs to the one time-sensitive fact per page. */
.stamp--draft {
  background: var(--brand-field-text);
  color: var(--brand-field);
}

/* Settled, so it is stated rather than announced. Two thirds of the weight of
   the draft stamp, and the difference a reader acts on is the word. */
.stamp--signed {
  border-width: 2px;
  border-color: var(--brand-field-muted);
  color: var(--brand-field-muted);
}

/* Printers drop background colours by default, which would leave the draft
   stamp as white ink on white paper, in the one place the marking matters
   most. Drawn as an outline on paper, where the ink is the page's only
   colour. */
@media print {
  .stamp--draft {
    background: none;
    color: var(--brand-field-text);
  }
}

/* The band already gave the page its title, so the canvas starts closer. */
.page { padding-top: var(--size-lg); }

/* The page signs off the way a document does: a rule, some air, the small
   wordmark. Without the rule the strapline reads as the last row of whatever
   card happens to sit above it. */
.page > footer {
  margin-top: var(--size-3xl);
  padding-top: var(--size-base);
  border-top: 1px solid var(--border);
  font-size: var(--size-sm);
}

/* Privacy, accessibility and the provider's details: three pages the law says
   a reader must be able to find from wherever she is standing. A wrapping row,
   so 320px stacks them rather than scrolling sideways, and vertical padding so
   each link clears the 24px minimum target (SC 2.5.8) that footer type at 14px
   would otherwise miss. */
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--size-lg);
}

.footer-links a { padding-block: 0.25rem; }

/* ---- Status: a stamp. Words first, colour second. Fills use the matched
   --*-fill pairs asserted in tokens.test.ts. ---------------------------------- */
.status {
  display: inline-flex;
  gap: 0.375rem;
  align-items: center;
  padding: 0.15rem 0.5rem;
  border: 2px solid transparent;
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1.4;
}

.status svg {
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
}

.status--done {
  background: var(--success-fill);
  color: var(--success-fill-text);
}

.status--due {
  background: var(--urgent-fill);
  color: var(--urgent-fill-text);
}

.status--waiting {
  border-color: var(--border-strong);
  color: var(--text-muted);
}

/* ---- Rows -------------------------------------------------------------------
   One repeating record: an agenda item, an application, an action.
   ---------------------------------------------------------------------------- */
.rows {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Ruled, never boxed. Ten agenda items are one sheet with ten lines on it, not
   ten cards: a card per row is what makes a long list unreadable, and it is
   why a board of pinned panels usually falls apart at the tenth one. */
.row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.25rem var(--size-lg);
  align-items: start;
  padding: var(--size-base) 0;
  /* A hairline, like the rules in a ledger: the separation only has to be
     found, and the heavy dotted rule this used to be was the loudest thing on
     a page of ten rows. --border is decorative by design; nothing depends on
     seeing it. */
  border-bottom: 1px solid var(--border);
}

.row:last-child { border-bottom: 0; }

.row__title {
  font-weight: 600;
  margin: 0;
}

.row__meta {
  grid-column: 1;
  color: var(--text-muted);
  font-size: var(--size-sm);
  margin: 0;
  max-width: var(--measure);
}

.row__aside {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  justify-content: flex-end;
  text-align: right;
}

/* The one fact about an application that only the clerk holds: the date on the
   letter the authority posted her. It goes under the row's own text rather
   than in the aside, because it is something to fill in, not a status, and a
   date field is the widest control we have. */
.row__record {
  grid-column: 1;
  margin-top: 0.375rem;
}

.letter-date {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

/* Folded away once a date is recorded: the row already says the date and the
   deadline, so the form is there to correct them rather than to be read. Native
   <details>, so keyboard operation, the screen reader announcement and the
   no-JavaScript behaviour all come with the element. */
.letter-date__change > summary {
  cursor: pointer;
  font-size: var(--size-sm);
  color: var(--text-muted);
  padding: 0.25rem 0;
}

.letter-date__change > summary + .letter-date { margin-top: 0.5rem; }

@media print {
  /* Nothing on paper can be filled in, and the deadline is printed in the row
     above it either way. */
  .row__record { display: none; }
}

/* Anything that prints in order carries its number down a mono column, so the
   printed order is legible without reading a word of the items. */
.row--numbered {
  grid-template-columns: 2.5rem minmax(0, 1fr) auto;
}

.row--numbered .row__no {
  grid-row: 1 / span 2;
  font-family: var(--font-mono);
  font-size: var(--size-lg);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  line-height: 1.4;
}

.row--numbered .row__title,
.row--numbered .row__meta { grid-column: 2; }
.row--numbered .row__aside { grid-column: 3; }

/* On a phone the aside column would starve the title into a word a line, so
   the row stacks instead: what it is, then what can be done about it, the
   same shape the attendance rows use at every width. 40rem covers phones
   and the narrow half of a split tablet screen. */
@media (max-width: 40rem) {
  .row,
  .row--numbered {
    grid-template-columns: minmax(0, 1fr);
  }

  .row__aside,
  .row--numbered .row__aside {
    grid-column: 1;
    grid-row: auto;
    justify-content: flex-start;
    text-align: left;
    margin-top: 0.25rem;
  }

  .row--numbered .row__no,
  .row--numbered .row__title,
  .row--numbered .row__meta { grid-column: 1; }

  .row--numbered .row__no { grid-row: auto; }
}

/* A row the address named takes focus, so the clerk is put back on the item
   she changed and not merely scrolled to it. Marked on :focus and not only on
   :focus-visible, because focus arrived without a key being pressed and the
   ring is what says where it went. */
.row:focus,
.card:focus {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

/* What the last write did, or why it was refused, on the row it happened to.
   The clerk is put back on the item she changed, so a banner at the top of a
   page of seventeen items is a confirmation she never sees (#285). */
.row__outcome {
  grid-column: 1 / -1;
  margin-top: 0.75rem;
}

/* Editing replaces the line rather than sitting beside it. */
.agenda-edit {
  grid-column: 1 / -1;
  margin-top: 0.75rem;
  padding: var(--size-base);
  background: var(--surface-sunken);
  border: 2px solid var(--brand);
}

.agenda-edit input[type="text"] { max-width: none; }

.agenda-edit__actions {
  display: flex;
  flex-wrap: wrap;
  /* Tighter than .button-row because this is a dense inline editor, but still
     wide enough that the 3px offset shadow does not reach its neighbour. */
  gap: var(--size-xs);
  margin-top: 0.75rem;
  /* The buttons draw a 3px offset outside their box; without this the line
     below is printed through it. */
  margin-bottom: 0.75rem;
}

/* A row that needs the clerk to do something is marked on the line itself, by
   a printer's square set before the title. Decorative only: every one of these
   rows also says in words what is wanted, so the square adds findability and
   never carries the meaning on its own.

   It replaces a 4px rule down the left of the row. Two reasons: the rail is
   the most-copied component on the web and made us look like everybody else,
   and at the left edge of a full-width row it sat too far from the words it
   was about to be read with them. */
.row--needs-attention .row__title::before,
.minute--outstanding .minute__head::before,
.agenda__item--not-ready .agenda__title::before,
.tile--due .tile__label::before {
  content: '';
  display: inline-block;
  width: 0.6rem;
  height: 0.6rem;
  margin-right: 0.5rem;
  background: var(--urgent);
}

/* ---- What is already done ----------------------------------------------------
   Finished work is reassurance rather than work, so it is counted in the line
   above and folded away underneath it. Native <details>: keyboard operation,
   the screen reader announcement and the no-JavaScript behaviour all come with
   the element, and the disclosure triangle is the affordance rather than
   something drawn on top of one. Nothing here lifts; it cannot be pressed in
   the sense a button can.
   ---------------------------------------------------------------------------- */
.done-set {
  padding-top: var(--size-sm);
  border-top: 1px solid var(--border);
}

.done-set__summary {
  cursor: pointer;
  font-weight: 600;
  color: var(--text-muted);
  padding: var(--size-sm) 0;
}

.done-set__rows { margin-top: var(--size-sm); }

@media print {
  /* A printed page cannot be opened, so the done actions print with the rest:
     they are what a clerk reports at the next meeting. */
  .done-set::details-content {
    content-visibility: visible;
    block-size: auto;
  }

  .done-set__summary { cursor: auto; }
}

/* ---- Parish boundary check ---------------------------------------------------
   The shape of a parish beside the facts about it, so a clerk confirming a
   boundary has something she can actually check (#229). The drawing wraps
   above the facts on a narrow screen rather than shrinking beside them: a
   parish outline at 8rem tells nobody anything.
   ------------------------------------------------------------------------------ */
.parish-check {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-lg);
  align-items: flex-start;
}

.parish-check__shape { flex: 0 0 auto; }
.parish-check__facts { flex: 1 1 16rem; }

.outline {
  display: block;
  width: min(100%, 16rem);
  height: auto;
  /* The interior is a tint and the line does the work, so the line is the part
     that has to clear 3:1 (SC 1.4.11) against both the tint and the page.
     --brand-text does, in every theme and both modes. */
  fill: var(--brand-surface);
  stroke: var(--brand-text);
}

/* On the path, not the svg: vector-effect applies to the shape being drawn.
   Without it the stroke is scaled by the viewBox, which is in metres, and a
   2px line becomes invisible on a large parish and a blot on a small one. */
.outline path {
  vector-effect: non-scaling-stroke;
  stroke-width: 2;
  stroke-linejoin: round;
}

/* ---- Key/value list ---------------------------------------------------------- */
.meta {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.375rem var(--size-base);
  margin: 0;
  font-size: var(--size-sm);
}

.meta dt {
  color: var(--text-muted);
  white-space: nowrap;
}

.meta dd {
  margin: 0;
  font-weight: 600;
}

/* Data shown under somebody else's licence says whose it is, beside the data
   rather than on a page nobody opens. Regular weight and muted: the value is
   the fact a clerk is checking, the licence line is provenance under it. */
.meta .attribution {
  margin: 0.25rem 0 0;
  font-weight: 400;
  color: var(--text-muted);
}

/* On a phone the label column starves the value: the labels do not wrap, and
   "Where this agenda came from" leaves the value about 13px to sit in, so its
   longest word runs off the page (SC 1.4.10, #280). Stacked, the list is read
   down instead of across and the labels are free to wrap. Same 40rem as .row,
   which stacks for the same reason. */
@media (max-width: 40rem) {
  .meta {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0;
  }

  .meta dt {
    white-space: normal;
  }

  /* Each label sits directly above its own value, with the gap between pairs. */
  .meta dd + dt {
    margin-top: var(--size-xs);
  }
}

/* A stamp and the words it qualifies: a date and its standing, an agenda item
   and the ground stated for closing it. The gap belongs to the container
   because a stamp is mono, uppercase and letter-spaced, and a single collapsed
   text space leaves it touching the words. Wrapping is what keeps it off the
   page width at 320px: the stamp cannot break mid-word, so it has to be able
   to drop onto a line of its own instead (SC 1.4.10). The words beside it have
   to be able to wrap, which is why a stamp never holds a sentence. */
.date-standing,
.agenda__stated-reason {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--size-xs);
}

/* ---- Countdown tiles ---------------------------------------------------------
   The three or four dates a clerk is actually anxious about.
   ------------------------------------------------------------------------------ */
.tiles {
  display: grid;
  /* 14rem lets all four tiles share one row at 1024px, the width the page is
     most often read at; at 15rem the fourth wrapped alone, and an orphan tile
     on the first thing a clerk scans read as a mistake. The dates still fit
     on one line at the width a 14rem track actually resolves to there. */
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--size-base);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* A plate: a dated notice, a panel one size down.

   Each tile is a subgrid over three shared rows, so the labels, dates and
   notes of one row of tiles sit on common baselines however many lines each
   label wraps to: a scan across the dates reads as one line. Browsers
   without subgrid drop that declaration and each tile stacks its own rows,
   which is the layout this had before and still correct. */
.tile {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  align-content: start;
  background: var(--surface);
  border: 2px solid var(--border-strong);
  padding: var(--size-base);
}

/* The urgent border is findability, never the meaning: the due state also
   carries the printer's square (above) and says "Due" in words, so it survives
   greyscale, a colour-blind reader and a printed pack (WCAG 1.4.1). */
.tile--due { border-color: var(--urgent); }

.tile__due {
  margin-left: 0.375rem;
  color: var(--urgent-text);
}

.tile__label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.tile__value {
  display: block;
  font-family: var(--font-display);
  /* One size down from where this started: at --size-2xl the longer dates
     wrapped inside a four-across row, and a date broken over two lines is
     harder to scan than a slightly smaller one on one line. Still display
     type, still the largest thing in the tile by far. */
  font-size: var(--size-xl);
  /* If it must wrap, wrap evenly rather than leaving one word stranded. */
  text-wrap: balance;
  font-weight: 600;
  line-height: var(--leading-heading);
  letter-spacing: var(--tracking-heading);
  margin-top: 0.25rem;
}

.tile__note {
  display: block;
  margin-top: 0.25rem;
  font-size: var(--size-sm);
  color: var(--text-muted);
}

/* ---- Quorum ------------------------------------------------------------------
   A live property during a meeting, not a check at the start.
   ------------------------------------------------------------------------------ */
.quorum {
  display: flex;
  flex-wrap: wrap;
  gap: var(--size-lg);
  align-items: center;
}

.quorum__figure {
  font-family: var(--font-display);
  font-size: var(--size-2xl);
  font-weight: 600;
  line-height: 1;
}

/* ---- Search evidence ---------------------------------------------------------
   What was searched and over what period. "Nothing found" has to be evidenced,
   or it is indistinguishable from a broken integration.
   ------------------------------------------------------------------------------ */
.evidence {
  background: var(--surface-sunken);
  padding: var(--size-base);
  font-size: var(--size-sm);
  color: var(--text-muted);
}

.evidence .ref { color: var(--text); }

/* ---- Nav -----------------------------------------------------------------------
   The service navigation, inside the brand band. These are links, not buttons:
   a button does something, one of these takes the reader somewhere and belongs
   to the page it leads to. So nothing here lifts, and lifting is left to mean
   one thing only.
   -------------------------------------------------------------------------------- */
.nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  /* The same track as .site-header__inner, so the tabs line up under the
     wordmark rather than starting at the edge of the viewport. */
  max-width: var(--page-width);
  margin-inline: auto;
  padding: 0 var(--size-lg);
  list-style: none;
  font-size: var(--size-sm);
  font-weight: 600;
}

.nav a {
  display: block;
  padding: 0.7rem var(--size-base) 0.75rem;
  color: var(--brand-field-muted);
  text-decoration: none;
}

.nav a:hover { color: var(--brand-field-text); }

/* --focus-ring is purple on purple over the band (2.4:1 on the dark end in
   light theme, worse on the light end). As with the theme control, the field
   text token is the one guaranteed visible on both ends of the gradient. */
.nav a:focus-visible {
  outline-color: var(--brand-field-text);
}

/* The current tab is the page itself, poking up into the band: the canvas
   colour, touching the canvas, with no edge between the two. Three signals
   carry it and none of them is colour alone — a different surface, heavier
   type, and being the only tab joined to the page. */
.nav a[aria-current='page'] {
  background: var(--surface-sunken);
  color: var(--text);
  font-weight: 700;
}

/* A meeting's own pages are reached through Meeting dates and have no tab of
   their own, so that tab is marked as the section rather than as the page:
   full-strength text and heavier type, never the cut-out. The cut-out means
   "this tab is this page", and claiming it here would be a lie about where
   the reader is. */
.nav a[aria-current='true'] {
  color: var(--brand-field-text);
  font-weight: 700;
}

/* The council switcher is not one of this council's pages, so it is pushed to
   the far end away from the tabs. The selector marks the first switch only,
   the one that follows a tab; any others sit alongside it. */
.nav li:not(.nav__switch) + .nav__switch { margin-left: auto; }

/* Signed-out pages hold one form each, so the card narrows to the form's
   natural measure rather than stretching a lone email field across the page. */
.sign-in { max-width: 34rem; }

/* The browser's default email field is a stub; both fields fill the measure. */
.sign-in input[type='email'] { width: 100%; }

/* A password field with its show-password button alongside. The button only
   exists once script runs; without it the input simply takes the whole row. */
.password-row {
  display: flex;
  gap: var(--size-xs);
  align-items: stretch;
}

.password-row input {
  flex: 1;
  min-width: 0;
}

/* ---- Form fields -------------------------------------------------------------
   GOV.UK Frontend is the reference: label above the control, hint between the
   two, generous targets. Our users already know these forms from every other
   government service they use, and they are the ones that have been tested
   with the people we are building for.
   ------------------------------------------------------------------------------ */
.field {
  margin: 0;
  padding: 0;
  border: 0;
}

.field__label {
  display: block;
  font-weight: 600;
  font-size: var(--size-base);
  padding: 0;
}

.field__hint {
  margin: 0.125rem 0 0.5rem;
  color: var(--text-muted);
  font-size: var(--size-sm);
  max-width: var(--measure);
}

input[type='text'],
input[type='email'],
input[type='password'],
input[type='search'],
textarea {
  display: block;
  width: 100%;
  max-width: 34rem;
  padding: 0.625rem 0.75rem;
  font-family: var(--font-body);
  /* Never below 16px: smaller text also makes iOS zoom the page on focus. */
  font-size: var(--size-base);
  line-height: var(--leading-body);
  color: var(--text);
  background: var(--surface);
  /* The boundary is the only thing identifying the control, so it takes the
     token that clears WCAG 2.2 SC 1.4.11 rather than the decorative one. */
  border: 2px solid var(--border-strong);
}

textarea {
  resize: vertical;
}

input[type='text']:focus-visible,
input[type='email']:focus-visible,
input[type='password']:focus-visible,
input[type='search']:focus-visible,
textarea:focus-visible {
  border-color: var(--focus-ring);
}

/* Chrome paints autofilled fields its own yellow, which reads as a fault
   against the brand surfaces and ignores dark mode entirely. The inset
   shadow is the one sanctioned way to repaint it; the text colour comes
   with it or Chrome leaves near-black on dark. Autofill still announces
   itself through the browser's own icon and menu, so nothing accessible
   is lost by calming the wash. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px var(--surface) inset;
  -webkit-text-fill-color: var(--text);
  caret-color: var(--text);
}

[aria-invalid='true'] {
  border-color: var(--urgent);
}

/* A form that is only a button should not inherit the stack's spacing. */
.row__aside form {
  margin: 0;
}

/* ---- Agenda -------------------------------------------------------------------
   The document as it will print, not a list of records. Clerks issue these on
   paper and pin them to a noticeboard, so the on-screen version keeps the
   shape of the printed one — the notice above, the standing lines, numbered
   business, the clerk's own notes clearly set apart from all of it.
   ------------------------------------------------------------------------------- */
.agenda {
  border: 1px solid var(--border-strong);
  padding: var(--size-lg);
  background: var(--surface);
}

.agenda__notice,
.agenda__standing {
  max-width: var(--measure);
  margin-bottom: var(--size-sm);
}

/* Muted at 14px until #315, when these lines were boilerplate of ours. They are
   the council's own standing orders now, and one of the things a council
   usually prints here is the deadline a resident has to meet to speak, on an
   agenda that goes on a noticeboard. That is body copy, at body size: below
   16px it is the smallest text on the page and the reader it is aimed at is
   standing in front of it. */
.agenda__standing {
  font-size: var(--size-base);
}

.agenda__heading {
  margin: var(--size-lg) 0 var(--size-sm);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--size-base);
}

.agenda__items {
  margin: var(--size-base) 0 0;
  padding: 0;
  list-style: none;
}

.agenda__item + .agenda__item {
  margin-top: var(--size-base);
  padding-top: var(--size-base);
  border-top: 1px solid var(--border);
}

/* Marked by a square on the title line — see .row--needs-attention, which
   carries the shared rule and the reasoning. */

.agenda__title {
  margin: 0;
  max-width: var(--measure);
}

/* Marks an item the council must resolve on. Spelled out for a screen reader
   rather than left as two characters in brackets. */
.agenda__r {
  font-weight: 600;
  color: var(--brand-text);
  text-decoration: none;
  cursor: help;
}

.agenda__parts {
  margin: 0.375rem 0 0 var(--size-lg);
  padding: 0;
  max-width: var(--measure);
}

.agenda__parts li {
  margin-top: 0.25rem;
}

/* The clerk's working notes. Never part of the document. */
.agenda__note {
  margin: 0.375rem 0 0;
  color: var(--text-muted);
  font-size: var(--size-sm);
  max-width: var(--measure);
}

@media print {
  .agenda { border: 0; padding: 0; }
  .agenda__note { display: none; }
}

/* The ground for closing an item, stated on the item itself as s.1(2) Public
   Bodies (Admission to Meetings) Act 1960 requires. Part of the document, so
   unlike the clerk's note it prints. Laid out by the stamp-and-words rule
   above. */
.agenda__stated-reason {
  margin: 0.375rem 0 0;
  max-width: var(--measure);
}

/* Business taken after the public have been asked to leave. Marked by a rule
   and a label, never by colour alone, and it stays visible on the public
   agenda — the item is announced, only its substance is not. */
.agenda__exclusion,
.agenda__item--closed {
  border-left: 3px solid var(--brand);
  margin-left: calc(var(--size-base) * -1);
  padding-left: calc(var(--size-base) - 3px);
}

/* Available to a screen reader, absent from the page. Used for a label whose
   purpose is obvious from context to a sighted reader and invisible to one
   who is not. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The compact controls: a date, a time, a count, a choice. Never below 16px,
   which is also the size below which iOS zooms the page on focus. */
input[type='date'],
input[type='time'],
input[type='number'],
select {
  padding: 0.5rem 0.625rem;
  font-family: var(--font-body);
  font-size: var(--size-base);
  color: var(--text);
  background: var(--surface);
  border: 2px solid var(--border-strong);
  /* A select takes its width from its longest option, so a control offering a
     sentence is wider than a 320px screen and scrolls the page sideways
     (SC 1.4.10). The option text is then clipped rather than the page
     broken. */
  max-width: 100%;
}

input[type='date']:focus-visible,
input[type='time']:focus-visible,
input[type='number']:focus-visible,
select:focus-visible {
  border-color: var(--focus-ring);
}

/* What went wrong with one field, said beside that field. Urgent text on the
   ordinary surface rather than a tinted panel: it belongs to the control above
   it, and a wash here would compete with the section's own alert. The pairing
   is asserted at body-text contrast in both modes and all three themes. */
.field__error {
  margin: 0.375rem 0 0;
  color: var(--urgent-text);
  font-size: var(--size-sm);
  font-weight: 500;
  max-width: var(--measure);
}

/* Picking your parish out of the ones that match what you typed (#238).

   A radio group and not a list of buttons: it is one question with one answer,
   arrow keys walk it, and a single button at the foot keeps the page from
   growing twenty raised things. Each line is a full-width target, because this
   is done on a small screen as often as not, and the touch area is the whole
   label rather than the 18px control. */
.choices {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
}

.choice {
  border-bottom: 1px solid var(--border);
}

.choice__pick {
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  /* Comfortably past the 24px minimum target of SC 2.5.8 without needing a
     fixed height, which would clip a name that wraps on a narrow screen. */
  padding: 0.75rem 0.5rem;
  cursor: pointer;
}

/* The whole line highlights, so it is obvious which name the control belongs
   to before it is chosen, on a screen where the radio itself is small. */
.choice__pick:hover {
  background: var(--surface-sunken);
}

/* The outline goes round the line, not round the 18px control, because the
   line is what is being chosen (SC 2.4.11). */
.choice__pick:has(:focus-visible) {
  outline: 3px solid var(--focus-ring);
  outline-offset: -3px;
}

.choice__label {
  font-weight: 500;
}

/* The postcode question that a shared name asks, indented under its own line
   so it reads as part of that choice and not as a question about the list. */
.choice__more {
  padding: 0 0.5rem 0.75rem 2.4rem;
  max-width: var(--measure);
}

/* Recording who is in the room, a member to a line. The controls sit on one
   wrapping row so a nine-member roster is nine lines rather than nine panels,
   and they wrap rather than shrink: a clerk doing this on a phone in a village
   hall gets a taller list, never a smaller target. */
.attendance__controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin: 0;
}

/* The controls go under the member rather than beside them. A time field, a
   choice and a button in the right-hand column would squeeze the name into a
   few characters on a small screen, and the name is the thing being read off a
   room. */
.row--recording {
  grid-template-columns: minmax(0, 1fr);
}

.row--recording .row__aside {
  grid-column: 1;
  grid-row: auto;
  flex-wrap: wrap;
  justify-content: flex-start;
  text-align: left;
  margin-top: 0.25rem;
}

/* An agenda item that leads somewhere. The agenda is a list of things to be
   done, so every item with somewhere to do it is a way in — underlined rather
   than coloured alone, so it reads as a link on paper and in greyscale too. */
.agenda__link {
  color: var(--text);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

.agenda__link:hover {
  color: var(--brand-text);
  text-decoration-thickness: 2px;
}

/* Anchored sections must not land under the brand band. */
[id] {
  scroll-margin-top: var(--size-lg);
}

@media print {
  .agenda__link { text-decoration: none; color: var(--text); }
}

/* Editing an agenda item in place. Structured fields, not a document — the
   clerk changes the wording of an item, not the layout of a page. */
.agenda__controls {
  display: flex;
  flex-wrap: wrap;
  /* These buttons keep their offset shadow, so 0.5rem left them nearly
     touching. Same value as .agenda-edit__actions. */
  gap: var(--size-xs);
  align-items: center;
  margin-top: 0.5rem;
}

.agenda__controls input[type='text'] {
  flex: 1 1 20rem;
  max-width: none;
  /* 16px, not the --size-sm of the buttons beside it. Safari on iOS zooms the
     page when a control below 16px takes focus and does not zoom back out, and
     this is the most-typed control in the product: a clerk building an agenda
     on a tablet would fight the zoom on every item. Matching the neighbours
     here would cost more than the inconsistency saves (#263). */
  font-size: var(--size-base);
  padding: 0.375rem 0.5rem;
}

.agenda__controls .button {
  padding: 0.375rem 0.625rem;
  font-size: var(--size-xs);
}

.button[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Row actions drop the offset: one per line is noise. */
.button--small {
  min-height: 2.25rem;
  padding: 0.3rem 0.75rem;
  font-size: var(--size-xs);
  box-shadow: none;
}

/* A row can hold several single-button forms side by side. */
.row__aside form { margin: 0; display: contents; }

/* Says why there is no "take off" here, rather than leaving a gap where the
   other items have a button. */
.agenda__standing-note {
  color: var(--text-muted);
  font-size: var(--size-xs);
  font-style: italic;
}

/* Where an item came from. A clerk should not have to remember why each line
   is on the agenda, and the answer differs in kind: the bones of the agenda
   are quiet, and the things that arrived since the last meeting are not. */
.agenda__origin {
  display: inline-block;
  margin-right: 0.5rem;
  padding: 0.0625rem 0.4rem;
  border: 1px solid var(--border-strong);
  font-size: var(--size-xs);
  font-weight: 600;
  white-space: nowrap;
}

/* Already there before anyone did anything: stated, not highlighted. */
.agenda__origin--standing,
.agenda__origin--recurring {
  color: var(--text-muted);
}

/* Arrived since the last meeting, so worth the eye stopping on. */
.agenda__origin--carried,
.agenda__origin--raised,
.agenda__origin--added {
  border-color: var(--brand);
  color: var(--brand-text);
}

/* ---- Minutes -------------------------------------------------------------------
   Written against the agenda, item by item. Each minute is a small form rather
   than a page of prose: minutes record decisions, and a resolution's wording is
   legally operative text, so it is captured as itself and never as a paragraph
   somebody has to parse later.
   --------------------------------------------------------------------------------- */
.minutes {
  margin: 0;
  padding: 0;
  list-style: none;
}

.minute + .minute {
  margin-top: var(--size-lg);
  padding-top: var(--size-lg);
  border-top: 1px solid var(--border);
}

.minute__head {
  font-weight: 600;
  margin: 0 0 var(--size-base);
  max-width: var(--measure);
}

/* Nothing recorded yet. Findable by shape as well as by reading, because the
   thing a clerk needs at 10pm is the list of what is still blank. The mark is
   a square on the heading line — see .row--needs-attention. */

.input--tiny { max-width: 5rem; }

/* Signed minutes are not editable, and a readonly field should look it. */
[readonly] {
  background: var(--surface-sunken);
  cursor: default;
}

/* ---- The signed record --------------------------------------------------------
   Once signed, a minute is printed rather than put in a field. A readonly
   textarea still reads as something a clerk failed to edit; this is a record.
   `pre-wrap` because the wording is legally operative text and the clerk's own
   line breaks are part of it.
   ------------------------------------------------------------------------------- */
.minute__text {
  margin: 0 0 var(--size-sm);
  max-width: var(--measure);
  white-space: pre-wrap;
}

.minute__meta {
  margin: 0;
  max-width: var(--measure);
  color: var(--text-muted);
  font-size: var(--size-sm);
}

/* ---- Corrections --------------------------------------------------------------
   A signed minute is never edited. A correction is a resolution passed at a
   later meeting, and it sits beside the wording it corrects rather than over
   it, so the record can never quietly read as though it had always said the
   corrected thing.

   Found by shape, like the notices: a heavier edge in the brand colour on the
   ordinary surface, not a tinted wash that would glow in dark mode. Purple
   rather than orange, because a correction is a fact about the record and not
   something the clerk has to act on today.
   ------------------------------------------------------------------------------- */
.correction {
  margin-top: var(--size-base);
  padding: var(--size-base);
  background: var(--surface);
  border: 3px solid var(--brand);
  max-width: var(--measure);
}

.correction__label {
  margin: 0;
  font-family: var(--font-mono);
  font-size: var(--size-xs);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--brand-text);
}

.correction__meta {
  margin: 0.25rem 0 0;
  color: var(--text-muted);
  font-size: var(--size-sm);
}

.correction__text {
  margin: var(--size-sm) 0 0;
  white-space: pre-wrap;
}

/* ---- Handed back after a save we could not complete ----------------------------
   `pre-wrap` because the clerk's own line breaks are part of what she wrote and
   she is about to copy it back into the form she wrote it in.
   ------------------------------------------------------------------------------- */
.kept-text {
  margin: 0;
  max-width: var(--measure);
  white-space: pre-wrap;
}

/* ---- The minute book ----------------------------------------------------------
   Every set of signed minutes the council holds, as a book rather than a page.
   A council's book runs to years, our readers are on old hardware and rural
   broadband, and a clerk opening it has nearly always been asked about one
   meeting. So a set stays closed until it is asked for, and the newest is open
   because it is the one most often wanted.

   Native <details>: keyboard, screen reader and no-JavaScript behaviour all
   come with it, and the disclosure triangle is the affordance rather than
   something drawn on top of it. Rules between sets, no offsets: nothing here
   can be pressed in the sense a button can.
   ------------------------------------------------------------------------------- */
.book__set {
  border-top: 1px solid var(--border);
  padding: var(--size-sm) 0;
}

.book__set:first-child {
  border-top: 0;
  padding-top: 0;
}

.book__summary {
  cursor: pointer;
  padding: var(--size-sm) 0;
}

.book__when {
  display: block;
  margin: 0;
}

/* The facts about the set, as data: kind of meeting, how many minutes, and
   how many of them a later meeting has corrected. */
.book__facts {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--font-mono);
  font-size: var(--size-sm);
  color: var(--text-muted);
}

.book__body {
  margin-top: var(--size-base);
  padding-left: var(--size-base);
  border-left: 2px solid var(--border-strong);
}

@media print {
  /* An inspection copy is the whole book, not the sets a clerk happened to
     have open. Honoured where ::details-content is supported; the Word export
     is the reliable way to produce the book on paper, which is why it exists. */
  .book__set::details-content {
    content-visibility: visible;
    block-size: auto;
  }

  .book__summary { cursor: auto; }
}

/* ---- Print: drop the furniture --------------------------------------------
   LAST IN THE FILE ON PURPOSE. These hides fight the components' own display
   rules at equal specificity, so source order decides: anything defined after
   this block would print regardless of it. Add new chrome to this list AND
   keep the block at the end.

   The utility row is chrome too: Account and Sign out are session controls,
   and a session cannot be signed out of on paper.
   -------------------------------------------------------------------------- */
@media print {
  .skip-link,
  .utility,
  .nav,
  .no-print { display: none; }
}
