/*
 * Stroud Events design system — stroud.css
 *
 * Written the CUBE CSS way (Andy Bell): the cascade does most of the work.
 *
 *   global       elements styled as themselves: h1, p, a, button, input,
 *                details, dl, table… Most markup needs no class at all.
 *   composition  layout primitives that arrange whatever is inside them
 *                (flow, cluster, grid, sidebar, switcher, wrapper, region).
 *   utility      one job each, driven by tokens (visually-hidden, text-step-*).
 *   block        the few things that are genuinely components (site-head,
 *                tag, notice, poster-card, queue, scanner…).
 *   exception    state and variation as attributes, not modifier classes:
 *                data-variant, data-tone, data-state, aria-current,
 *                aria-pressed, aria-invalid.
 *
 * No prefixes, no margin utilities: space between things comes from flow,
 * set by the container. Visual language: paper and ink, Playfair Display
 * for display sizes, GOV.UK functional colours and focus states. WCAG 2.2
 * AA; a high-contrast mode for e-ink and low vision.
 *
 * Guide: docs/design-system.md. Check: design-system/check.py.
 *
 * Rules for editing:
 * - Colours, borders and shadows are tokens. Never a literal colour outside
 *   the token blocks: that is what makes high contrast a pure token swap.
 * - Spacing and type sizes are fluid (clamp) tokens, so almost nothing
 *   needs a media query. The one breakpoint, 40.0625em (641px), is written
 *   literally because custom properties cannot be used in media queries.
 */

@layer reset, global, composition, utility, block, exception;

/* ─ Display face ─────────────────────────────────────────────────────
 * Playfair Display (SIL Open Font License, fonts/OFL.txt), self-hosted as a
 * variable font. Display sizes only (28px and up), weight 400: hierarchy
 * comes from size. font-synthesis: none, so nothing is faked.
 */

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/playfair-display-latin.ef05b30d6e5c.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Playfair Display";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("fonts/playfair-display-latin-ext.319e6b2aad48.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ─ Tokens ─────────────────────────────────────────────────────────── */

:root {
  /* Functional colours — GOV.UK Design System values, 2025 refresh. */
  --color-text: #0b0c0c;
  --color-secondary-text: #484949;
  --color-inverse-text: #ffffff;
  --color-link: #1a65a6;
  --color-link-hover: #0f385c;
  --color-link-visited: #54319f;
  --color-link-active: #0b0c0c;
  --color-focus: #ffdd00;
  --color-focus-text: #0b0c0c;
  --color-error: #ca3535; /* 4.66:1 on paper; never on the darker paper tone */
  --color-success: #0f7a52;
  /* Paper and ink: a warm card-stock page, near-black type. */
  --color-body-background: #f7f3ea;
  --color-template-background: #efe9dc;
  --color-surface-background: #efe9dc;
  /* Form fields stay white, so they read as fields. */
  --color-input-background: #ffffff;
  /* Decorative lines only, never the edge of a control. */
  --color-border: #d6cfc0;
  --color-rule: #d6cfc0;
  /* The masthead: ink on paper with a heavy rule, like a newspaper. */
  --color-masthead-background: #f7f3ea;
  --color-masthead-text: #0b0c0c;
  --color-masthead-rule: #0b0c0c;
  /* Edges of controls and anything that must be seen: 3:1 or more. */
  --color-input-border: #0b0c0c;
  --color-hover: #cecece;
  /* Behind a modal dialog: the page shows through, dimmed. */
  --color-backdrop: #0b0c0cb3;
  /* Stroud green (GOV.UK palette green shade-25): important banners. */
  --color-brand: #0b5c3e;
  --color-brand-text: #ffffff;

  /* Buttons */
  --color-button: #0f7a52;
  --color-button-hover: #0b5c3e;
  --color-button-shadow: #083d29;
  --color-button-text: #ffffff;
  /* Secondary: white with a green edge, so its outline stands out from
     the paper (a grey fill was 1:1 against it). */
  --color-button-secondary: #ffffff;
  --color-button-secondary-hover: #cfe4dc;
  --color-button-secondary-shadow: #083d29;
  --color-button-secondary-text: #0b5c3e;
  --color-button-secondary-border: #0b5c3e;
  --color-button-warning: #ca3535;
  --color-button-warning-hover: #982828;
  --color-button-warning-shadow: #651b1b;
  --color-button-warning-text: #ffffff;
  --color-button-border: transparent;

  /* Tags: tint-80 backgrounds with shade-50 text (GOV.UK tag convention). */
  --color-tag-grey-background: #f3f3f3;
  --color-tag-grey-text: #0b0c0c;
  --color-tag-green-background: #cfe4dc;
  --color-tag-green-text: #083d29;
  --color-tag-blue-background: #d2e2f1;
  --color-tag-blue-text: #0f385c;
  --color-tag-yellow-background: #fff8cc;
  --color-tag-yellow-text: #594d00;
  --color-tag-red-background: #f4d7d7;
  --color-tag-red-text: #651b1b;
  --color-tag-purple-background: #ddd6ec;
  --color-tag-purple-text: #2a1950;
  --color-tag-border: transparent;

  /* Focus: how a focused link looks (fill, text, thick lower edge), and an
     outline that only shows in high contrast, where fills are black. */
  --color-focus-link-background: var(--color-focus);
  --color-focus-link-text: var(--color-focus-text);
  --color-focus-link-edge: var(--color-focus-text);
  --color-focus-outline: transparent;
  --focus-outline-offset: 0px;

  /* Lines */
  --border-thin: 1px;
  --border-form: 2px;
  --border-emphasis: 5px;
  --focus-width: 3px;
  --button-shadow: 2px;
  --underline: max(1px, 0.0625rem);
  --underline-hover: max(3px, 0.1875rem);

  /* Type. Helvetica or Arial for everything readable; Playfair Display
     for display sizes (28px and up) only. */
  --font-base: "Helvetica Neue", helvetica, arial, sans-serif;
  --font-display: "Playfair Display", georgia, serif;
  --font-mono: ui-monospace, "SFMono-Regular", menlo, consolas, monospace;

  /* Type scale: fluid between 320px and 1280px wide. */
  --step--2: 0.875rem; /* 14px: kickers */
  --step--1: 1rem; /* 16px: small print */
  --step-0: 1.1875rem; /* 19px: body */
  --step-1: clamp(1.3125rem, 1.25rem + 0.3125vw, 1.5rem); /* 21→24 */
  --step-2: clamp(1.75rem, 1.5833rem + 0.8333vw, 2.25rem); /* 28→36 */
  --step-3: clamp(2.25rem, 2rem + 1.25vw, 3rem); /* 36→48 */
  --step-4: 2.5rem; /* 40px: the date flourish */

  /* Space scale: fluid, so nothing needs a breakpoint to breathe. */
  --space-3xs: 0.3125rem; /* 5px */
  --space-2xs: 0.625rem; /* 10px */
  --space-xs: 0.9375rem; /* 15px */
  --space-s: clamp(0.9375rem, 0.8333rem + 0.5208vw, 1.25rem); /* 15→20 */
  --space-m: clamp(1.25rem, 1.0417rem + 1.0417vw, 1.875rem); /* 20→30 */
  --space-l: clamp(1.5625rem, 1.25rem + 1.5625vw, 2.5rem); /* 25→40 */
  --space-xl: clamp(1.875rem, 1.4583rem + 2.0833vw, 3.125rem); /* 30→50 */
  --space-2xl: clamp(2.5rem, 2.0833rem + 2.0833vw, 3.75rem); /* 40→60 */

  /* Page */
  --gutter: clamp(0.9375rem, 0.625rem + 1.5625vw, 1.875rem); /* 15→30 */
  --wrapper-max: 60rem;
  --wrapper-wide: 80rem;
  --measure: 38em; /* about 75 characters */

  color-scheme: light;
}

/* ─ High contrast (e-ink and low vision) ─────────────────────────────
 *
 * Pure black on white, thicker lines, no tints, no shadows, focus shown by
 * inverting. Applies when the device asks for more contrast, reports a
 * monochrome or slow-refreshing (e-ink) screen, or the person switches it on
 * (html[data-contrast="high"], set by the contrast toggle). They can also
 * switch it off (data-contrast="standard"), which wins over the media query.
 *
 * The two blocks below must stay identical; check.py fails if they differ.
 */

@media (prefers-contrast: more), (monochrome), (update: slow) {
  :root:not([data-contrast="standard"]) {
    --color-text: #000000;
    --color-secondary-text: #000000;
    --color-link: #000000;
    --color-link-hover: #000000;
    --color-link-visited: #000000;
    --color-link-active: #000000;
    --color-focus: #000000;
    --color-focus-text: #ffffff;
    --color-error: #000000;
    --color-success: #000000;
    --color-body-background: #ffffff;
    --color-template-background: #ffffff;
    --color-surface-background: #ffffff;
    --color-input-background: #ffffff;
    --color-rule: #000000;
    --color-masthead-background: #ffffff;
    --color-masthead-text: #000000;
    --color-masthead-rule: #000000;
    --color-border: #000000;
    --color-input-border: #000000;
    --color-hover: #ffffff;
    --color-backdrop: #000000e6;
    --color-brand: #000000;
    --color-brand-text: #ffffff;
    --color-button: #000000;
    --color-button-hover: #000000;
    --color-button-shadow: #000000;
    --color-button-text: #ffffff;
    --color-button-secondary: #ffffff;
    --color-button-secondary-hover: #ffffff;
    --color-button-secondary-shadow: #000000;
    --color-button-secondary-text: #000000;
    --color-button-secondary-border: #000000;
    --color-button-warning: #ffffff;
    --color-button-warning-hover: #ffffff;
    --color-button-warning-shadow: #000000;
    --color-button-warning-text: #000000;
    --color-button-border: #000000;
    --color-tag-grey-background: #ffffff;
    --color-tag-grey-text: #000000;
    --color-tag-green-background: #ffffff;
    --color-tag-green-text: #000000;
    --color-tag-blue-background: #ffffff;
    --color-tag-blue-text: #000000;
    --color-tag-yellow-background: #ffffff;
    --color-tag-yellow-text: #000000;
    --color-tag-red-background: #ffffff;
    --color-tag-red-text: #000000;
    --color-tag-purple-background: #ffffff;
    --color-tag-purple-text: #000000;
    --color-tag-border: #000000;
    --color-focus-link-background: #000000;
    --color-focus-link-text: #ffffff;
    --color-focus-link-edge: #000000;
    --color-focus-outline: #000000;
    --focus-outline-offset: 3px;
    --border-thin: 2px;
    --border-form: 3px;
    --border-emphasis: 6px;
    --focus-width: 4px;
    --button-shadow: 0px;
    --underline: max(2px, 0.125rem);
    --underline-hover: max(4px, 0.25rem);
  }
}

:root[data-contrast="high"] {
  --color-text: #000000;
  --color-secondary-text: #000000;
  --color-link: #000000;
  --color-link-hover: #000000;
  --color-link-visited: #000000;
  --color-link-active: #000000;
  --color-focus: #000000;
  --color-focus-text: #ffffff;
  --color-error: #000000;
  --color-success: #000000;
  --color-body-background: #ffffff;
  --color-template-background: #ffffff;
  --color-surface-background: #ffffff;
  --color-input-background: #ffffff;
  --color-rule: #000000;
  --color-masthead-background: #ffffff;
  --color-masthead-text: #000000;
  --color-masthead-rule: #000000;
  --color-border: #000000;
  --color-input-border: #000000;
  --color-hover: #ffffff;
  --color-backdrop: #000000e6;
  --color-brand: #000000;
  --color-brand-text: #ffffff;
  --color-button: #000000;
  --color-button-hover: #000000;
  --color-button-shadow: #000000;
  --color-button-text: #ffffff;
  --color-button-secondary: #ffffff;
  --color-button-secondary-hover: #ffffff;
  --color-button-secondary-shadow: #000000;
  --color-button-secondary-text: #000000;
  --color-button-secondary-border: #000000;
  --color-button-warning: #ffffff;
  --color-button-warning-hover: #ffffff;
  --color-button-warning-shadow: #000000;
  --color-button-warning-text: #000000;
  --color-button-border: #000000;
  --color-tag-grey-background: #ffffff;
  --color-tag-grey-text: #000000;
  --color-tag-green-background: #ffffff;
  --color-tag-green-text: #000000;
  --color-tag-blue-background: #ffffff;
  --color-tag-blue-text: #000000;
  --color-tag-yellow-background: #ffffff;
  --color-tag-yellow-text: #000000;
  --color-tag-red-background: #ffffff;
  --color-tag-red-text: #000000;
  --color-tag-purple-background: #ffffff;
  --color-tag-purple-text: #000000;
  --color-tag-border: #000000;
  --color-focus-link-background: #000000;
  --color-focus-link-text: #ffffff;
  --color-focus-link-edge: #000000;
  --color-focus-outline: #000000;
  --focus-outline-offset: 3px;
  --border-thin: 2px;
  --border-form: 3px;
  --border-emphasis: 6px;
  --focus-width: 4px;
  --button-shadow: 0px;
  --underline: max(2px, 0.125rem);
  --underline-hover: max(4px, 0.25rem);
}

/* ─ Reset ──────────────────────────────────────────────────────────── */

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

  /* No default margins: space between things comes from flow. */
  :where(body, h1, h2, h3, h4, h5, h6, p, ul, ol, dl, dd, figure, blockquote, pre, fieldset) {
    margin: 0;
  }

  /* A list given role="list" is a list for layout, not reading: no
     bullets. (The role also keeps Safari announcing it as a list.) */
  :where(ul, ol)[role="list"] {
    padding: 0;
    list-style: none;
  }

  img,
  video,
  svg,
  canvas {
    display: block;
    max-inline-size: 100%;
    block-size: auto;
  }

  :where(input, button, select, textarea) {
    font: inherit;
  }

  [hidden] {
    display: none !important;
  }
}

/* ─ Global ─────────────────────────────────────────────────────────── */

@layer global {
  html {
    /* 100%, never px: people's browser text size must still apply (1.4.4). */
    font-size: 100%;
    background-color: var(--color-template-background);
    text-size-adjust: 100%;
    overflow-y: scroll;
  }

  body {
    color: var(--color-text);
    background-color: var(--color-body-background);
    font-family: var(--font-base);
    font-size: var(--step-0);
    line-height: 1.3158;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Long words and URLs wrap rather than widen the page (1.4.10). */
    overflow-wrap: break-word;
  }

  /* Headings. h1 and h2 are display headings (Playfair Display, 28px and
     up, weight 400); h3 and h4 are the sans in bold. */
  h1,
  h2,
  h3,
  h4 {
    text-wrap: balance;
  }

  h1,
  h2 {
    font-family: var(--font-display);
    font-weight: 400;
    font-synthesis: none;
    letter-spacing: -0.01em;
    line-height: 1.05;
    overflow-wrap: anywhere;
    hyphens: auto;
  }

  h1 {
    font-size: var(--step-3);
  }

  h2 {
    font-size: var(--step-2);
  }

  h3 {
    font-size: var(--step-1);
    line-height: 1.25;
  }

  h4 {
    font-size: var(--step-0);
  }

  :is(h3, h4, strong, b, dt, th, legend, label) {
    font-weight: 700;
  }

  /* Running text stays readable: about 75 characters at most. Items of
     role="list" lists are components (queues, listings), not prose, so they
     take the full width. */
  :where(p, dd, legend, label, :is(ul, ol):not([role="list"]) > li) {
    max-inline-size: var(--measure);
  }

  :where(ul, ol):not([role="list"]) {
    padding-inline-start: 1.25em;
  }

  :where(ul, ol):not([role="list"]) > li + li {
    margin-block-start: var(--space-3xs);
  }

  a {
    color: var(--color-link);
    text-decoration-thickness: var(--underline);
    text-underline-offset: 0.1578em;
  }

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

  a:hover {
    color: var(--color-link-hover);
    text-decoration-thickness: var(--underline-hover);
    text-decoration-skip-ink: none;
  }

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

  /* GOV.UK focus for every link and disclosure: yellow fill, ink text, a
     thick lower edge. The transparent outline appears in forced colours. */
  :is(a, summary):focus-visible {
    outline: var(--focus-width) solid transparent;
    color: var(--color-focus-link-text);
    background-color: var(--color-focus-link-background);
    box-shadow:
      0 -2px var(--color-focus-link-background),
      0 4px var(--color-focus-link-edge);
    text-decoration: none;
    box-decoration-break: clone;
  }

  hr {
    border: 0;
    border-block-start: var(--border-thin) solid var(--color-border);
  }

  figcaption {
    font-size: var(--step--1);
    line-height: 1.25;
    font-weight: 700;
  }

  figure > figcaption:first-child {
    margin-block-end: var(--space-2xs);
  }

  figure img {
    inline-size: 100%;
    border: var(--border-thin) solid var(--color-border);
    background-color: var(--color-template-background);
  }

  /* Raw data a person may need to read. Scrolls inside itself; give the
     <pre> tabindex="0" so a keyboard can scroll it. */
  pre {
    max-block-size: 20rem;
    overflow: auto;
    padding: var(--space-xs);
    border: var(--border-thin) solid var(--color-input-border);
    background-color: var(--color-input-background);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    line-height: 1.4;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
  }

  /* Summary list: key and value pairs, one row per <div>. */
  dl > div {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--space-s);
    padding-block: var(--space-2xs);
    border-block-end: var(--border-thin) solid var(--color-border);
  }

  dt {
    flex: 1 1 12rem;
  }

  dd {
    flex: 999 1 0;
    min-inline-size: 60%;
  }

  table {
    inline-size: 100%;
    border-collapse: collapse;
  }

  caption {
    margin-block-end: var(--space-xs);
    font-weight: 700;
    text-align: start;
  }

  :is(th, td) {
    padding: var(--space-2xs) var(--space-s) var(--space-2xs) 0;
    border-block-end: var(--border-thin) solid var(--color-border);
    text-align: start;
    vertical-align: top;
  }

  :is(th, td):last-child {
    padding-inline-end: 0;
  }

  /* Disclosure (details/summary): a link-like summary with a triangle,
     the content indented behind a rule. */
  summary {
    display: inline-block;
    position: relative;
    padding-inline-start: 1.5625rem;
    color: var(--color-link);
    text-decoration: underline;
    text-decoration-thickness: var(--underline);
    text-underline-offset: 0.1578em;
    cursor: pointer;
    list-style: none;
  }

  summary::-webkit-details-marker {
    display: none;
  }

  summary::before {
    content: "";
    position: absolute;
    inset-block-start: 0.2em;
    inset-inline-start: 0;
    border-style: solid;
    border-width: 0.4375em 0 0.4375em 0.7em;
    border-color: transparent transparent transparent currentcolor;
  }

  details[open] > summary::before {
    inset-block-start: 0.35em;
    border-width: 0.7em 0.4375em 0 0.4375em;
    border-color: currentcolor transparent transparent transparent;
  }

  summary:hover {
    color: var(--color-link-hover);
    text-decoration-thickness: var(--underline-hover);
  }

  details > summary + * {
    padding: var(--space-xs) var(--space-xs) var(--space-xs) var(--space-s);
    border-inline-start: var(--border-emphasis) solid var(--color-border);
  }

  /* Forms. Labels and legends are bold; hints and errors are blocks. */
  label,
  legend {
    display: block;
  }

  fieldset {
    min-inline-size: 0;
    padding: 0;
    border: 0;
  }

  legend {
    padding: 0;
  }

  :is(input:not([type="checkbox"], [type="radio"], [type="file"], [type="hidden"]), select, textarea) {
    display: block;
    inline-size: 100%;
    min-block-size: 2.5rem;
    padding: var(--space-3xs);
    border: var(--border-form) solid var(--color-input-border);
    border-radius: 0;
    color: var(--color-text);
    background-color: var(--color-input-background);
    line-height: 1.3158;
    appearance: none;
  }

  textarea {
    min-block-size: 7.5rem;
    resize: vertical;
  }

  :is(input[type="date"], input[type="time"]) {
    inline-size: auto;
    min-inline-size: 11em;
  }

  select {
    inline-size: auto;
    min-inline-size: 11.5em;
    max-inline-size: 100%;
    padding-inline-end: 2.25em;
    /* Chevron drawn with gradients so it follows the text colour token. */
    background-image:
      linear-gradient(45deg, transparent 50%, var(--color-text) 50%),
      linear-gradient(135deg, var(--color-text) 50%, transparent 50%);
    background-position:
      calc(100% - 1.05em) 55%,
      calc(100% - 0.7em) 55%;
    background-size: 0.35em 0.35em;
    background-repeat: no-repeat;
  }

  :is(input, select, textarea):focus-visible {
    outline: var(--focus-width) solid var(--color-focus);
    outline-offset: 0;
    box-shadow: inset 0 0 0 var(--border-form) var(--color-input-border);
  }

  input[type="file"] {
    display: block;
    max-inline-size: 100%;
    padding: var(--space-3xs);
  }

  input[type="file"]:focus-visible {
    outline: var(--focus-width) solid var(--color-focus);
    outline-offset: var(--focus-outline-offset);
    box-shadow: inset 0 0 0 4px var(--color-input-border);
  }

  input[type="file"]::file-selector-button {
    margin-inline-end: var(--space-xs);
    padding: var(--space-3xs) var(--space-2xs);
    border: var(--border-form) solid var(--color-input-border);
    color: var(--color-button-secondary-text);
    background-color: var(--color-button-secondary);
    font: inherit;
    cursor: pointer;
  }

  /* Buttons: every <button>, and a link given class="button". Full width
     on small screens (an easy target), their own width from tablet up. */
  :is(button, .button) {
    display: inline-block;
    position: relative;
    inline-size: 100%;
    margin-block-end: var(--button-shadow);
    padding: 0.4375rem 0.625rem 0.375rem;
    border: var(--border-form) solid var(--color-button-border);
    border-radius: 0;
    color: var(--color-button-text);
    background-color: var(--color-button);
    box-shadow: 0 var(--button-shadow) 0 var(--color-button-shadow);
    font-size: var(--step-0);
    line-height: 1.1875;
    text-align: center;
    text-decoration: none;
    vertical-align: top;
    cursor: pointer;
    appearance: none;
  }

  @media (min-width: 40.0625em) {
    :is(button, .button) {
      inline-size: auto;
    }
  }

  :is(button, .button):is(:link, :visited, :hover, :active) {
    color: var(--color-button-text);
    text-decoration: none;
  }

  :is(button, .button):hover {
    background-color: var(--color-button-hover);
  }

  :is(button, .button):active {
    inset-block-start: var(--button-shadow);
  }

  /* Extends the click area over the shadow, as GOV.UK does. */
  :is(button, .button)::before {
    content: "";
    position: absolute;
    inset: calc(-1 * var(--border-form));
    inset-block-end: calc(-1 * (var(--border-form) + var(--button-shadow)));
  }

  :is(button, .button):focus-visible {
    outline: var(--focus-width) solid var(--color-focus-outline);
    outline-offset: var(--focus-outline-offset);
    border-color: var(--color-focus);
    color: var(--color-focus-text);
    background-color: var(--color-focus);
    box-shadow:
      inset 0 0 0 1px var(--color-focus),
      0 var(--button-shadow) 0 var(--color-focus-text);
  }

  :is(button, .button):is(:disabled, [aria-disabled="true"]) {
    opacity: 0.5;
    cursor: not-allowed;
  }

  :is(button, .button):is(:disabled, [aria-disabled="true"]):active {
    inset-block-start: 0;
  }
}

/* ─ Composition ────────────────────────────────────────────────────── */

@layer composition {
  /* Flow (Andy Bell): space between siblings comes from their container,
     never from the elements themselves. 1em of the element's own size by
     default, so bigger type gets more room above it.

     On by default inside page content, so most markup needs no class:
     main, its sections, articles and forms, disclosure content, and the
     blocks that hold running content. Add .flow anywhere else.

     "Preceded by a visible sibling" rather than "* + *", so a hidden
     CSRF input or a [hidden] element first in a form doesn't push the
     first visible thing down. */
  :where(
    .flow,
    main,
    main :is(section, article, form, fieldset),
    details > summary + *,
    .notice > :last-child,
    .inset,
    .panel,
    .field,
    dialog
  ) > :not([hidden], [type="hidden"], template, script, legend) ~ * {
    margin-block-start: var(--flow-space, 1em);
  }

  /* A section in main is a new part of the page: more room above it than
     between paragraphs. A margin, not --flow-space, which would be
     inherited by everything inside the section. */
  :where(main > :not([hidden]) ~ section) {
    margin-block-start: var(--space-xl);
  }

  /* Flow space: how much room an element wants above it. Headings sit
     closer to what they introduce than to what came before. */
  :where(h1, h2) {
    --flow-space: 1.25em;
  }

  :where(h3, h4) {
    --flow-space: 1.5em;
  }

  :where(.field, .cluster, .grid, .listing, .task-list, .notice, .error-summary, table, dl) {
    --flow-space: var(--space-m);
  }

  :where(.kicker, p.color-soft) + :where(h1, h2, h3, .event-title) {
    --flow-space: var(--space-3xs);
  }

  :where(h1, h2, .event-title) + :where(.note) {
    --flow-space: var(--space-2xs);
  }

  :where(.field) > * {
    --flow-space: var(--space-3xs);
  }

  /* Cluster: items in a row that wrap, with even gaps. */
  .cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cluster-space, var(--space-s));
  }

  /* Grid: as many columns as fit, each at least --grid-min wide; one
     column when there is no room for two. No media queries. */
  .grid[data-min="narrow"] {
    --grid-min: 9rem;
  }

  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--grid-min, 14rem), 100%), 1fr));
    gap: var(--grid-gap, var(--space-m));
  }

  /* Sidebar: a narrow item beside a wide one; they stack when the wide one
     would drop below half the width. The first child is the side, unless
     data-side="end". */
  .sidebar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sidebar-gap, var(--space-m));
  }

  .sidebar > * {
    flex-grow: 1;
    min-inline-size: 0;
  }

  .sidebar:not([data-side="end"]) > :first-child,
  .sidebar[data-side="end"] > :last-child {
    flex-basis: var(--sidebar-width, 18rem);
  }

  .sidebar:not([data-side="end"]) > :last-child,
  .sidebar[data-side="end"] > :first-child {
    flex-basis: 0;
    flex-grow: 999;
    min-inline-size: 55%;
  }

  /* Switcher: children side by side in equal columns, all stacking at
     once below --switcher-threshold. */
  .switcher {
    display: flex;
    flex-wrap: wrap;
    gap: var(--switcher-gap, var(--space-m));
  }

  .switcher > * {
    flex-grow: 1;
    flex-basis: calc((var(--switcher-threshold, 40rem) - 100%) * 999);
    min-inline-size: 0;
  }

  /* Wrapper: the page column. */
  .wrapper {
    max-inline-size: var(--wrapper-max);
    margin-inline: auto;
    padding-inline: var(--gutter);
  }

  .wrapper[data-width="wide"] {
    max-inline-size: var(--wrapper-wide);
  }

  /* Region: vertical breathing room for a page's main content. */
  .region {
    padding-block: var(--space-m) var(--space-xl);
  }
}

/* ─ Utilities ──────────────────────────────────────────────────────── */

@layer utility {
  .visually-hidden {
    position: absolute !important;
    inline-size: 1px !important;
    block-size: 1px !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: hidden !important;
    clip-path: inset(50%) !important;
    border: 0 !important;
    white-space: nowrap !important;
  }

  .text-step--1 {
    font-size: var(--step--1);
    line-height: 1.25;
  }

  .text-step-1 {
    font-size: var(--step-1);
    line-height: 1.25;
  }

  .color-soft {
    color: var(--color-secondary-text);
  }
}

/* ─ Blocks ─────────────────────────────────────────────────────────── */

@layer block {
  .skip-link {
    position: absolute;
    inset-inline-start: -9999em;
    padding: var(--space-2xs) var(--space-xs);
    color: var(--color-focus-link-text);
    background-color: var(--color-focus-link-background);
    font-size: var(--step--1);
  }

  .skip-link:focus {
    position: static;
    display: block;
    outline: var(--focus-width) solid var(--color-focus-link-edge);
    outline-offset: 0;
  }

  /* The masthead: the service name in the display face, the menu beside
     it, a heavy rule beneath within the page column. */
  .site-head {
    color: var(--color-masthead-text);
    background-color: var(--color-masthead-background);
  }

  .site-head__bar {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2xs) var(--space-m);
    padding-block: var(--space-s) var(--space-xs);
    border-block-end: 3px solid var(--color-masthead-rule);
  }

  .site-head__name {
    font-family: var(--font-display);
    font-size: 1.75rem;
    font-weight: 400;
    font-synthesis: none;
    line-height: 1.05;
    letter-spacing: -0.01em;
  }

  .site-head :is(a, a:visited) {
    color: var(--color-masthead-text);
    text-decoration: none;
  }

  .site-head a:hover,
  .site-head a[aria-current="page"] {
    text-decoration: underline;
    text-decoration-thickness: var(--underline-hover);
    text-underline-offset: 0.25em;
  }

  .site-head nav ul {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2xs) var(--space-s);
    font-size: var(--step--1);
    line-height: 1.25;
    font-weight: 700;
  }

  /* The phase banner sits in the header, on the page background. Anything
     after the text (the identity form) goes to the end of the row. */
  .phase {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2xs);
    padding-block: var(--space-2xs);
    border-block-end: var(--border-thin) solid var(--color-border);
    color: var(--color-text);
    font-size: var(--step--1);
    line-height: 1.25;
  }

  /* Who is editing, at the end of the phase banner: its own row on small
     screens. */
  .identity {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2xs);
    flex-basis: 100%;
  }

  @media (min-width: 40.0625em) {
    .identity {
      flex-basis: auto;
      margin-inline-start: auto;
    }
  }

  .identity input {
    inline-size: 10em;
    min-block-size: 2.25rem;
    padding-block: 0.1875rem;
    font-size: var(--step--1);
  }

  .site-foot {
    margin-block-start: var(--space-xl);
    padding-block: var(--space-m);
    border-block-start: var(--border-thin) solid var(--color-border);
    background-color: var(--color-template-background);
    font-size: var(--step--1);
    line-height: 1.25;
  }

  .site-foot > .wrapper {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-xs) var(--space-m);
  }

  /* Links between views of one list. The current view is
     aria-current="page" with a heavy ink rule, never colour alone. */
  .subnav {
    border-block-end: var(--border-thin) solid var(--color-rule);
  }

  .subnav ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--space-s);
    font-size: var(--step--1);
    line-height: 1.25;
  }

  .subnav a {
    display: inline-block;
    padding-block: var(--space-2xs);
    border-block-end: 4px solid transparent;
  }

  .subnav a:visited {
    color: var(--color-link);
  }

  .subnav a[aria-current="page"] {
    border-block-end-color: var(--color-text);
    color: var(--color-text);
    font-weight: 700;
    text-decoration: none;
  }

  .back-link {
    display: inline-block;
    position: relative;
    padding-inline-start: 0.875em;
    font-size: var(--step--1);
    line-height: 1.25;
  }

  .back-link:is(:link, :visited) {
    color: var(--color-text);
  }

  .back-link::before {
    content: "";
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0.1875em;
    inline-size: 0.4375em;
    block-size: 0.4375em;
    margin-block: auto;
    border: solid;
    border-width: 1px 0 0 1px;
    transform: rotate(-45deg);
  }

  /* A field: label, hint, error message and control, stacked closely.
     With an invalid control inside, a red rule marks the whole field. */
  .field:has([aria-invalid="true"]) {
    padding-inline-start: var(--space-xs);
    border-inline-start: var(--border-emphasis) solid var(--color-error);
  }

  .hint {
    color: var(--color-secondary-text);
  }

  .error-message {
    color: var(--color-error);
    font-weight: 700;
  }

  /* Checkbox and radio: a 44px target, a 40px drawn box, and the native
     input on top (transparent) so every assistive technology sees a real
     control. <div class="checkbox"><input type="checkbox"><label>… */
  :is(.checkbox, .radio) {
    display: flex;
    flex-wrap: wrap;
    position: relative;
  }

  :is(.checkbox, .radio) > input {
    position: absolute;
    z-index: 1;
    inset-block-start: -2px;
    inset-inline-start: -2px;
    inline-size: 44px;
    block-size: 44px;
    margin: 0;
    opacity: 0;
    cursor: pointer;
  }

  :is(.checkbox, .radio) > label {
    position: relative;
    padding: 8px 15px 5px 55px;
    font-weight: 400;
    cursor: pointer;
    touch-action: manipulation;
  }

  :is(.checkbox, .radio) > label::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    inline-size: 40px;
    block-size: 40px;
    border: var(--border-form) solid currentcolor;
    background: var(--color-input-background);
  }

  .radio > label::before {
    border-radius: 50%;
  }

  .checkbox > label::after {
    content: "";
    position: absolute;
    inset-block-start: 11px;
    inset-inline-start: 9px;
    inline-size: 23px;
    block-size: 12px;
    transform: rotate(-45deg);
    border: solid;
    border-width: 0 0 5px 5px;
    border-block-start-color: transparent;
    opacity: 0;
  }

  .radio > label::after {
    content: "";
    position: absolute;
    inset-block-start: 10px;
    inset-inline-start: 10px;
    border: 10px solid currentcolor;
    border-radius: 50%;
    opacity: 0;
  }

  :is(.checkbox, .radio) > input:checked + label::after {
    opacity: 1;
  }

  :is(.checkbox, .radio) > input:focus-visible + label::before {
    border-width: 4px;
    outline: var(--focus-width) solid transparent;
    outline-offset: 1px;
    box-shadow: 0 0 0 var(--focus-width) var(--color-focus);
  }

  :is(.checkbox, .radio) > .hint {
    flex-basis: 100%;
    padding-inline: 55px 15px;
  }

  :is(.checkbox, .radio) + :is(.checkbox, .radio) {
    margin-block-start: var(--space-2xs);
  }

  /* Status text written by stroud.js while a slow submission runs. */
  .busy-status {
    font-weight: 700;
  }

  /* Empty until a submission starts: take no room, so forms sitting side
     by side keep their buttons in line. Kept displayed (not display: none)
     so it still works as a live region. */
  .busy-status:empty {
    margin-block-start: 0;
  }

  .error-summary {
    padding: var(--space-xs);
    border: var(--border-emphasis) solid var(--color-error);
  }

  .error-summary:focus {
    outline: var(--focus-width) solid var(--color-focus);
  }

  .error-summary h2 {
    margin-block-end: var(--space-s);
    font-family: var(--font-base);
    font-size: var(--step-1);
    font-weight: 700;
    letter-spacing: normal;
    line-height: 1.25;
  }

  .error-summary a {
    color: var(--color-error);
    font-weight: 700;
  }

  /* Notification banner: a heading strip in the brand colour, then the
     content on paper. <div class="notice" role="region" aria-labelledby>
     <h2 id>Important</h2><div>…</div></div> */
  .notice {
    border: var(--border-emphasis) solid var(--color-brand);
    background-color: var(--color-brand);
  }

  .notice[data-tone="success"] {
    border-color: var(--color-success);
    background-color: var(--color-success);
  }

  .notice > :first-child:not(:last-child) {
    padding: 2px var(--space-xs) var(--space-3xs);
    color: var(--color-inverse-text);
    font-family: var(--font-base);
    font-size: var(--step--1);
    font-weight: 700;
    letter-spacing: normal;
    line-height: 1.25;
  }

  .notice > :last-child {
    padding: var(--space-xs);
    color: var(--color-text);
    background-color: var(--color-body-background);
  }

  .inset {
    padding: var(--space-xs);
    border-inline-start: calc(2 * var(--border-emphasis)) solid var(--color-border);
  }

  /* Warning text: an ink circle with "!" (aria-hidden) and bold words that
     start "Warning" for screen readers (visually hidden). */
  .warning {
    display: flex;
    gap: var(--space-xs);
    align-items: flex-start;
    font-weight: 700;
  }

  .warning > [aria-hidden] {
    flex: none;
    display: grid;
    place-items: center;
    inline-size: 2.1875rem;
    block-size: 2.1875rem;
    border-radius: 50%;
    color: var(--color-inverse-text);
    background-color: var(--color-text);
    font-size: 1.875rem;
    line-height: 1;
  }

  .warning > :last-child {
    padding-block-start: 0.3em;
  }

  .panel {
    padding: var(--space-m);
    border: var(--border-emphasis) solid transparent;
    color: var(--color-inverse-text);
    background-color: var(--color-success);
    text-align: center;
  }

  .panel h1 {
    color: inherit;
  }

  /* Tag: a status in words; the colour only repeats it. */
  .tag {
    display: inline-block;
    max-inline-size: 100%;
    padding: 2px 8px 3px;
    border: var(--border-thin) solid var(--color-tag-border);
    color: var(--color-tag-blue-text);
    background-color: var(--color-tag-blue-background);
    font-size: var(--step--1);
    font-weight: 400;
    line-height: 1.25;
  }

  /* Status message: the result of something just done, in words, with a
     rule whose weight (not colour) marks it. Hidden when empty. */
  .status {
    padding-block: var(--space-2xs);
    padding-inline-start: var(--space-xs);
    border-inline-start: var(--border-emphasis) solid var(--color-border);
    font-size: var(--step--1);
    line-height: 1.25;
  }

  .status:empty {
    display: none;
  }

  .status img {
    max-block-size: 20rem;
    inline-size: auto;
    margin-block: var(--space-2xs);
    border: var(--border-thin) solid var(--color-border);
  }

  /* A list of things to do, each with its status (places due a check). */
  .task-list {
    border-block-start: var(--border-thin) solid var(--color-border);
  }

  .task-list > li {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-2xs) var(--space-s);
    padding-block: var(--space-xs);
    border-block-end: var(--border-thin) solid var(--color-border);
  }

  .task-list > li > :first-child {
    flex: 1 1 18rem;
    min-inline-size: 0;
  }

  .task-list > li > :first-child > a {
    font-weight: 700;
  }

  .task-list > li > :first-child > a:visited {
    color: var(--color-link);
  }

  .task-list .hint {
    margin-block-start: var(--space-3xs);
    font-size: var(--step--1);
    line-height: 1.25;
  }

  /* Quick actions on a task, across the whole row. */
  .task-list .cluster {
    flex-basis: 100%;
    font-size: var(--step--1);
  }

  /* Kicker: date and venue in letter-spaced caps above an event title.
     Written in sentence case; CSS sets the caps, so screen readers don't
     spell it out. */
  .kicker {
    font-size: var(--step--2);
    line-height: 1.4;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  /* An event title, whatever its heading level: the display face at 28px
     and up. */
  .event-title {
    font-family: var(--font-display);
    font-size: 1.75rem;
    font-weight: 400;
    font-synthesis: none;
    letter-spacing: -0.01em;
    line-height: 1.05;
    overflow-wrap: anywhere;
    hyphens: auto;
  }

  .event-title a:is(:link, :visited) {
    color: inherit;
    text-decoration: none;
  }

  .event-title a:hover {
    text-decoration: underline;
    text-decoration-thickness: var(--underline-hover);
  }

  /* Supporting detail beside a title: sans italic. */
  .note {
    color: var(--color-secondary-text);
    font-size: var(--step--1);
    font-style: italic;
    line-height: 1.25;
  }

  /* Poster card, after Inclusive Components "Cards" (Heydon Pickering):
   - the heading comes first in source; the poster and the kicker are moved
     above it visually with `order`, which does not change reading order;
   - one real link, on the title; the card is never wrapped in a link;
   - stroud.js (data-module="card") makes the whole card clickable without
     breaking text selection, and marks it data-clickable.
     <li class="poster-card" data-module="card"><div><h2 class="event-title">
     <a>…</a></h2><p class="kicker">…</p></div><img …></li> */
  .poster-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    position: relative;
    min-inline-size: 0;
    padding-block-start: var(--space-s);
    border-block-start: 1px solid var(--color-rule);
  }

  .poster-card > div {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
  }

  .poster-card :is(img, .no-image, .kicker) {
    order: -1;
  }

  .poster-card img {
    inline-size: 100%;
    border: var(--border-thin) solid var(--color-border);
    background-color: var(--color-template-background);
  }

  .poster-card:focus-within {
    outline: var(--focus-width) solid var(--color-focus-link-edge);
    outline-offset: var(--space-2xs);
  }

  /* Other controls in a card stay clickable in their own right. */
  .poster-card :is(a, button) {
    position: relative;
  }

  /* Shown when there is no poster or it fails to load: words, never an
     empty box, so the gallery works without images. */
  .no-image {
    display: grid;
    place-items: center;
    place-content: center;
    aspect-ratio: 3 / 4;
    padding: var(--space-xs);
    border: var(--border-thin) dashed var(--color-input-border);
    color: var(--color-secondary-text);
    background-color: var(--color-template-background);
    font-size: var(--step--1);
    line-height: 1.25;
    text-align: center;
  }

  /* Listings separated by thin rules, like a newspaper column. A listing
     that starts with a date flourish puts it in a column of its own. */
  .listing > li {
    padding-block: var(--space-s);
    border-block-start: 1px solid var(--color-rule);
  }

  .listing > li:last-child {
    border-block-end: 1px solid var(--color-rule);
  }

  .listing > li:has(> .date-flourish:first-child) {
    display: grid;
    grid-template-columns: minmax(5.5rem, max-content) minmax(0, 1fr);
    gap: 0 var(--space-xs);
    align-items: start;
  }

  /* Date flourish: the day large in the display face, the month in small
     caps. Always aria-hidden beside a full readable date.
     <span class="date-flourish" aria-hidden="true"><span>9</span>
     <span>Oct</span></span> */
  .date-flourish {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-3xs);
  }

  .date-flourish > :first-child {
    font-family: var(--font-display);
    font-size: var(--step-4);
    font-weight: 400;
    font-synthesis: none;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }

  .date-flourish > :last-child {
    font-size: var(--step--2);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
  }

  /* Capture app: the queue of posters on the phone. */
  .queue > li {
    display: flex;
    gap: var(--space-xs);
    padding-block: var(--space-xs);
    border-block-end: 1px solid var(--color-rule);
  }

  .queue > li > :last-child {
    flex: 1;
    min-inline-size: 0;
  }

  .queue__thumb {
    flex: none;
    inline-size: 4.5rem;
    block-size: 6rem;
    object-fit: contain;
    border: var(--border-thin) solid var(--color-border);
    background-color: var(--color-template-background);
  }

  span.queue__thumb {
    display: grid;
    place-items: center;
    color: var(--color-secondary-text);
    font-size: var(--step--1);
  }

  .queue :is(time, .queue__source) {
    display: block;
    color: var(--color-secondary-text);
    font-size: var(--step--1);
    line-height: 1.25;
    overflow-wrap: anywhere;
  }

  .queue .error-message:empty,
  .queue__reading:empty {
    display: none;
  }

  .queue .error-message,
  .queue__reading {
    display: block;
  }

  .queue__reading {
    font-size: var(--step--1);
    line-height: 1.25;
  }

  .queue__reading strong {
    display: block;
    font-size: var(--step-0);
  }

  /* Crop: the picture with the poster's four corners outlined and the rest
     dimmed. <div class="crop" data-module="crop"><div class="crop__image">
     <img></div><form>…tl_x, tl_y … bl_y fields…</form></div>. stroud.js
     adds an SVG overlay (.crop__overlay) with draggable corners; without it
     the fields do the same job. */
  .crop {
    display: grid;
    gap: var(--space-m);
  }

  .crop__image {
    position: relative;
    inline-size: fit-content;
    max-inline-size: 100%;
    overflow: hidden;
  }

  .crop__image img {
    display: block;
    max-inline-size: 100%;
    max-block-size: 70vh;
    inline-size: auto;
  }

  /* Dragging moves corners, so it must not scroll the page. */
  .crop__overlay {
    position: absolute;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    cursor: crosshair;
    touch-action: none;
  }

  .crop__shade {
    fill: var(--color-backdrop);
    fill-rule: evenodd;
  }

  .crop__outline {
    fill: transparent;
    stroke: var(--color-focus);
    stroke-width: 3;
    cursor: move;
  }

  /* Drawn small, grabbed large: the transparent ring is the touch target. */
  .crop__handle {
    fill: var(--color-focus);
    stroke: var(--color-text);
    stroke-width: 2;
  }

  .crop__grip {
    fill: transparent;
    cursor: grab;
  }

  /* Modal dialog (TetraLogical: native <dialog>, labelled by its heading,
     focus into the dialog on open and back to the trigger on close). */
  .dialog {
    inline-size: min(100% - 2 * var(--space-xs), 32rem);
    max-block-size: calc(100dvh - 2 * var(--space-xs));
    padding: var(--space-m);
    border: var(--border-form) solid var(--color-input-border);
    color: var(--color-text);
    background-color: var(--color-body-background);
  }

  :is(.dialog, .scanner)::backdrop {
    background-color: var(--color-backdrop);
  }

  /* The scanner: a full-screen dialog holding the live camera, then the
     check of what was taken. The picture fills the space; the controls sit
     at the bottom within thumb reach. Each view is a <section>. */
  .scanner {
    inline-size: 100%;
    max-inline-size: 100%;
    block-size: 100dvh;
    max-block-size: 100dvh;
    margin: 0;
    padding: 0;
    border: 0;
    color: var(--color-text);
    background-color: var(--color-body-background);
  }

  .scanner > section {
    display: flex;
    flex-direction: column;
    block-size: 100%;
  }

  .scanner__stage {
    position: relative;
    flex: 1;
    min-block-size: 0;
    overflow: auto;
    background-color: var(--color-template-background);
  }

  .scanner__stage > :is(video, canvas) {
    inline-size: 100%;
    block-size: 100%;
    object-fit: contain;
  }

  .scanner__stage > canvas:only-child {
    inline-size: auto;
    block-size: auto;
    max-block-size: 100%;
    margin-inline: auto;
    border: var(--border-thin) solid var(--color-border);
  }

  /* The live detection outline and the draggable corners sit on top. */
  .scanner__stage > canvas + canvas,
  .scanner__stage > video + canvas {
    position: absolute;
    inset: 0;
    touch-action: none;
  }

  .scanner__stage > canvas + canvas:focus-visible {
    outline: var(--focus-width) solid var(--color-focus);
    outline-offset: calc(-1 * var(--focus-width));
  }

  .scanner__controls {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
    padding: var(--space-xs);
    border-block-start: var(--border-form) solid var(--color-masthead-rule);
  }

  .scanner__controls h2 {
    font-family: var(--font-base);
    font-size: var(--step-0);
    font-weight: 700;
    letter-spacing: normal;
    line-height: 1.3158;
  }

  /* Detection state as a symbol and words, never colour alone. */
  .scanner [data-state] {
    font-weight: 700;
  }
}

/* ─ Exceptions ─────────────────────────────────────────────────────── */

@layer exception {
  /* The device already asks for high contrast: no switch to offer. Someone
     who has made a choice on this site (data-contrast) keeps it, so they
     can switch back. */
  @media (prefers-contrast: more), (monochrome), (update: slow) {
    :root:not([data-contrast]) [data-module~="contrast-toggle"] {
      display: none;
    }
  }

  /* Button variants. */
  :is(button, .button)[data-variant="secondary"] {
    border-color: var(--color-button-secondary-border);
    color: var(--color-button-secondary-text);
    background-color: var(--color-button-secondary);
    box-shadow: 0 var(--button-shadow) 0 var(--color-button-secondary-shadow);
  }

  :is(button, .button)[data-variant="secondary"]:hover {
    background-color: var(--color-button-secondary-hover);
  }

  :is(button, .button)[data-variant="warning"] {
    color: var(--color-button-warning-text);
    background-color: var(--color-button-warning);
    box-shadow: 0 var(--button-shadow) 0 var(--color-button-warning-shadow);
  }

  :is(button, .button)[data-variant="warning"]:hover {
    background-color: var(--color-button-warning-hover);
  }

  :is(button, .button)[data-variant]:focus-visible {
    border-color: var(--color-focus);
    color: var(--color-focus-text);
    background-color: var(--color-focus);
    box-shadow:
      inset 0 0 0 1px var(--color-focus),
      0 var(--button-shadow) 0 var(--color-focus-text);
  }

  /* The one main action on a screen: taller, larger, easy to hit
     one-handed outdoors. */
  :is(button, .button)[data-variant="start"] {
    min-block-size: 3.5rem;
    padding-block: var(--space-xs);
    font-size: 1.5rem;
    line-height: 1;
    font-weight: 700;
  }

  /* A small button for secondary actions in lists. 24px minimum target
     (WCAG 2.5.8). */
  :is(button, .button)[data-size="small"] {
    inline-size: auto;
    padding: 0.25rem 0.5rem 0.1875rem;
    font-size: var(--step--1);
    line-height: 1.25;
  }

  /* A toggle that is on: filled ink, so its state shows in shape and tone. */
  :is(button, .button)[aria-pressed="true"],
  :is(button, .button)[aria-pressed="true"]:hover {
    border-color: var(--color-text);
    color: var(--color-inverse-text);
    background-color: var(--color-text);
    box-shadow: 0 var(--button-shadow) 0 var(--color-text);
  }

  /* Invalid controls. */
  :is(input, select, textarea)[aria-invalid="true"] {
    border-color: var(--color-error);
  }

  :is(input, select, textarea)[aria-invalid="true"]:focus-visible {
    border-color: var(--color-input-border);
  }

  /* Input widths by characters expected: data-width="2|3|4|5|10|20|30". */
  input[data-width="2"] { max-inline-size: 5.4ex; }
  input[data-width="3"] { max-inline-size: 7.2ex; }
  input[data-width="4"] { max-inline-size: 9ex; }
  input[data-width="5"] { max-inline-size: 10.8ex; }
  input[data-width="10"] { max-inline-size: 23ex; }
  input[data-width="20"] { max-inline-size: 41ex; }
  input[data-width="30"] { max-inline-size: 59ex; }

  /* Tag tones. */
  .tag[data-tone="grey"] {
    color: var(--color-tag-grey-text);
    background-color: var(--color-tag-grey-background);
  }
  .tag[data-tone="green"] {
    color: var(--color-tag-green-text);
    background-color: var(--color-tag-green-background);
  }
  .tag[data-tone="yellow"] {
    color: var(--color-tag-yellow-text);
    background-color: var(--color-tag-yellow-background);
  }
  .tag[data-tone="red"] {
    color: var(--color-tag-red-text);
    background-color: var(--color-tag-red-background);
  }
  .tag[data-tone="purple"] {
    color: var(--color-tag-purple-text);
    background-color: var(--color-tag-purple-background);
  }

  /* Status message states; the words must say which it is. */
  .status[data-state="ok"] {
    border-inline-start-color: var(--color-success);
  }

  .status[data-state="pending"] {
    border-inline-start-color: var(--color-input-border);
  }

  .status[data-state="error"] {
    border-inline-start-color: var(--color-error);
    color: var(--color-error);
    font-weight: 700;
  }

  /* Camera detection: a symbol as well as the words. found, weak and
     searching are reserved for this; .status uses ok, pending and error. */
  [data-state="found"]::before {
    content: "✓ ";
  }

  [data-state="weak"]::before {
    content: "~ ";
  }

  [data-state="searching"]::before {
    content: "… ";
  }

  /* Set by stroud.js when the whole card is clickable. */
  .poster-card[data-clickable] {
    cursor: pointer;
  }

  .poster-card[data-clickable]:hover .event-title a {
    text-decoration: underline;
    text-decoration-thickness: var(--underline-hover);
  }

  /* A page that opens with a back link needs less room above. */
  main:has(> .back-link:first-child) {
    padding-block-start: var(--space-s);
  }
}

/* ─ Windows high contrast / forced colours ───────────────────────── */

@media (forced-colors: active) {
  :is(button, .button, .tag, .notice, .panel, .dialog, .scanner__controls) {
    border-color: ButtonText;
  }

  :is(.no-image, .checkbox > label, .radio > label)::before {
    border-color: CanvasText;
  }

  :is(a, button, .button, summary):focus-visible {
    outline-color: Highlight;
  }

  /* Backgrounds are dropped here; keep the warning circle as a shape. */
  .warning > [aria-hidden] {
    border: var(--border-thin) solid CanvasText;
  }
}

/* ─ Motion: nothing ever needs it to be understood. ──────────────── */

@media (prefers-reduced-motion: reduce), (update: slow) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ─ Print ──────────────────────────────────────────────────────────── */

@media print {
  .site-head nav,
  .site-foot,
  .skip-link,
  .phase,
  .back-link,
  button,
  .button {
    display: none !important;
  }

  body {
    color: #000000;
    background: none;
  }

  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 90%;
  }

  details > summary + * {
    display: block;
  }
}
