/* Highways Transparency Reporter — Infin8 Digital design system.
 *
 * Palette, type scale and component shapes are taken from infin8.digital so the
 * product reads as part of the same family: navy #0F2A3A, flame #F64121, teal
 * #008080, Space Grotesk headings at -0.02em tracking over Inter body text.
 *
 * Everything here is served from this origin. The Content-Security-Policy sets
 * default-src 'self' with no exceptions, so there are no web fonts, icon fonts
 * or stylesheets loaded from anywhere else.
 */

/* ------------------------------------------------------------------ tokens */

:root {
  --ink: #0f2a3a;
  --ink-90: rgba(15, 42, 58, 0.9);
  --ink-70: rgba(15, 42, 58, 0.7);
  --ink-55: rgba(15, 42, 58, 0.55);
  --ink-12: rgba(15, 42, 58, 0.12);
  --ink-06: rgba(15, 42, 58, 0.06);

  --flame: #f64121;
  --flame-dark: #d63515;
  --flame-08: rgba(246, 65, 33, 0.08);
  --flame-16: rgba(246, 65, 33, 0.16);

  --teal: #008080;
  --teal-dark: #006666;
  --teal-10: rgba(0, 128, 128, 0.1);
  --teal-15: rgba(0, 128, 128, 0.15);

  --paper: #ffffff;
  --mist: #fafafa;
  --line: #e6e9ec;
  --slate: #4a5568;
  --muted: #7d878d;

  --green: #1a7f4b;
  --green-bg: #e8f5ee;
  --amber: #a86200;
  --amber-bg: #fdf3e2;
  --red: #b3261e;
  --red-bg: #fdecea;
  /* Waiting on a person, rather than wrong. Distinct from the teal used for
     links and from the navy of the chrome. */
  --blue: #1f5fbf;
  --blue-dark: #17499a;

  --sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  --display: "Space Grotesk", var(--sans);
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --radius: 12px;
  --radius-lg: 18px;
  --pill: 999px;

  --shadow-sm: 0 1px 2px rgba(15, 42, 58, 0.06), 0 1px 3px rgba(15, 42, 58, 0.04);
  --shadow: 0 4px 6px -1px rgba(15, 42, 58, 0.07), 0 2px 4px -2px rgba(15, 42, 58, 0.05);
  --shadow-lg: 0 20px 40px -12px rgba(15, 42, 58, 0.18);

  --wrap: 1180px;
  --gutter: 24px;
}

/* Both families ship as variable fonts — one file each covers every weight
 * this stylesheet uses, so intermediate weights (500 chips, 600 labels) render
 * true instead of being synthesised from 400 and 700. Served from this origin;
 * the CSP allows nothing else, and nobody's IP goes to a font CDN. */
@font-face {
  font-family: "Space Grotesk";
  src: url("/static/fonts/space-grotesk.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: Inter;
  src: url("/static/fonts/inter.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* -------------------------------------------------------------- foundation */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* The browser's own [hidden] rule lives in the user-agent stylesheet, which is
   the weakest origin in the cascade — so any author rule setting `display`,
   including an inline one, silently defeats it. Every list this application
   filters sets element.hidden and trusts it to work. Making it !important here
   is the standard remedy and closes the whole class of bug at once. */
[hidden] {
  display: none !important;
}

h1,
h2,
h3,
h4 {
  font-family: var(--display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.15;
  margin: 0 0 0.5em;
}

h1 {
  font-size: clamp(2rem, 1.4rem + 2.6vw, 3.4rem);
}
h2 {
  font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2.25rem);
}
h3 {
  font-size: 1.25rem;
}
h4 {
  font-size: 1.05rem;
}

p {
  margin: 0 0 1em;
}

a {
  color: var(--teal-dark);
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
}
a:hover {
  color: var(--flame);
}

/* A visible, high-contrast focus ring on every interactive element. This is
 * public sector software: keyboard operation is not optional. */
:focus-visible {
  outline: 3px solid var(--flame);
  outline-offset: 2px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: #fff;
  padding: 12px 20px;
  z-index: 200;
  border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus {
  left: 0;
  color: #fff;
}

/* Both names are in common use, and a label that silently renders because a
   template reached for the other one is a visible defect rather than a
   harmless one. They are the same rule. */
.sr-only,
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.wrap {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 var(--gutter);
}
.wrap-narrow {
  max-width: 780px;
}
/* The preview, a step wider than a reading column.
 *
 * It is not prose — it is two documents full of eight-column spend tables, and
 * at 780px every one of them scrolled sideways inside its own box. Wide enough
 * for the tables, still narrow enough to read. */
.wrap-preview {
  max-width: 920px;
}
/* Two documents in a row, so the second does not look like a continuation of
 * the first. More than the eighteen pixels between ordinary cards, because the
 * gap here is a boundary between two things a council publishes separately.
 *
 * Qualified by the parent on purpose: `.card + .card` sets that eighteen, ties
 * on specificity, and comes later in this file — so a plainer selector loses
 * on source order alone. */
.wrap-preview > .preview-doc + .preview-doc {
  margin-top: 36px;
}

/* ------------------------------------------------------------------ buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: var(--display);
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1;
  padding: 14px 32px;
  border-radius: var(--pill);
  border: 1px solid transparent;
  background: var(--flame);
  color: #fff;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
  white-space: nowrap;
}
.btn:hover {
  background: var(--flame-dark);
  color: #fff;
  transform: translateY(-1px);
  box-shadow: var(--shadow);
}
.btn:active {
  transform: translateY(0);
}
.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.btn-secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--ink-12);
}
.btn-secondary:hover {
  background: var(--ink-06);
  color: var(--ink);
  border-color: var(--ink-12);
}

.btn-ghost {
  background: transparent;
  color: var(--teal-dark);
  padding: 10px 16px;
}
.btn-ghost:hover {
  background: var(--teal-10);
  color: var(--teal-dark);
  box-shadow: none;
}

.btn-danger {
  background: var(--red);
}
.btn-danger:hover {
  background: #8e1e17;
}

.btn-sm {
  padding: 9px 18px;
  font-size: 0.85rem;
}
.btn-block {
  width: 100%;
}

.btn-inverse {
  background: #fff;
  color: var(--ink);
}
.btn-inverse:hover {
  background: rgba(255, 255, 255, 0.88);
  color: var(--ink);
}

/* ------------------------------------------------------------------- forms */

label,
.label {
  display: block;
  font-weight: 600;
  font-size: 0.92rem;
  margin-bottom: 6px;
  color: var(--ink);
}

/* The small print under a control.
 *
 * The negative top margin is for a hint that follows a label, where it is a
 * continuation of the label and wants to sit close under it. Following an
 * actual control it is a different sentence about a different thing, and
 * pulling it two pixels up left it touching the box above — which is what made
 * the Start a report form read as one dense block. */
.hint {
  font-size: 0.85rem;
  color: var(--slate);
  margin: -2px 0 8px;
}
input + .hint,
select + .hint,
textarea + .hint,
.input-affix + .hint,
.pick-list + .hint {
  margin-top: 7px;
}
/* And the list a hint introduces is a thing in its own right, not a third line
 * of the same paragraph. */
.hint + .pick-list {
  margin-top: 12px;
}

input[type="text"],
input[type="email"],
input[type="tel"],
input[type="url"],
input[type="number"],
input[type="date"],
input[type="month"],
input[type="search"],
input[type="password"],
select,
textarea {
  width: 100%;
  font-family: inherit;
  font-size: 1rem;
  color: var(--ink);
  background: #fff;
  padding: 11px 14px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
input:hover,
select:hover,
textarea:hover {
  border-color: rgba(15, 42, 58, 0.24);
}
input:focus,
select:focus,
textarea:focus {
  border-color: var(--teal);
  box-shadow: 0 0 0 3px var(--teal-15);
  outline: none;
}

/* The file picker, dressed as the field it is. Left native it renders as a
 * 21px text control that sits beside this application's 45px buttons looking
 * like something forgot to load. */
input[type="file"] {
  width: 100%;
  font-family: inherit;
  font-size: 0.92rem;
  color: var(--slate);
  background: #fff;
  padding: 6px 10px;
  border: 1px solid var(--ink-12);
  border-radius: var(--radius);
}
input[type="file"]::file-selector-button {
  font-family: var(--display);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink);
  background: var(--ink-06);
  border: 0;
  border-radius: var(--pill);
  padding: 6px 14px;
  margin-right: 12px;
  cursor: pointer;
  transition: background 0.15s ease;
}
input[type="file"]::file-selector-button:hover {
  background: var(--ink-12);
}
/* In a control row the picker shares the line with its button, instead of
 * filling the row and pushing the button underneath. */
.row > input[type="file"] {
  width: auto;
  flex: 1 1 260px;
}

/* A button sharing a row with a text field matches the field's height. A
 * 33px pill centred against a 45px input still reads as misaligned, because
 * the eye lines the button up with the field, not with the row. */
.row:has(> input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"])) > .btn {
  min-height: 45px;
}
textarea {
  resize: vertical;
  min-height: 96px;
  line-height: 1.6;
}
input[aria-invalid="true"],
textarea[aria-invalid="true"],
select[aria-invalid="true"] {
  border-color: var(--red);
}
input[readonly] {
  background: var(--mist);
  color: var(--slate);
}

.field {
  margin-bottom: 22px;
}
/* The buttons that end a form, set apart from the last question rather than
 * running straight on from it. */
.form-actions {
  margin-top: 28px;
}
.field-row {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
.field-row > * {
  flex: 1 1 200px;
}

.input-affix {
  display: flex;
  align-items: stretch;
}
.input-affix .affix {
  display: flex;
  align-items: center;
  padding: 0 12px;
  background: var(--mist);
  border: 1px solid var(--ink-12);
  color: var(--slate);
  font-size: 0.9rem;
  white-space: nowrap;
}
.input-affix .affix:first-child {
  border-right: 0;
  border-radius: var(--radius) 0 0 var(--radius);
}
.input-affix .affix:last-child {
  border-left: 0;
  border-radius: 0 var(--radius) var(--radius) 0;
}
.input-affix input {
  border-radius: 0;
}
/* An input with an addon beside it is squared off on the side the addon
 * joins. The :only-child exclusions are load-bearing: without them a lone
 * input matches both the first-child and the last-child rule, the later one
 * wins, and the field renders rounded on the right and square on the left.
 * Most fields in the builder are lone inputs — an affix only appears for a
 * money field or one with a unit — so this was the common case, not the edge
 * case. Excluding :only-child here rather than trying to out-specify these
 * rules afterwards is what makes the intent survive the next edit. */
.input-affix > :first-child:not(.affix):not(:only-child) {
  border-radius: var(--radius) 0 0 var(--radius);
}
.input-affix > :last-child:not(.affix):not(:only-child) {
  border-radius: 0 var(--radius) var(--radius) 0;
}
.input-affix > :only-child:not(.affix) {
  border-radius: var(--radius);
}

/* Segmented yes/no and enum choices, which read better than a select for the
 * ~90 binary questions in the technical annex. */
.choice {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 6px;
}
/* .hint carries a negative top margin so it sits tight under a text input.
   Under a row of pills that reads as a collision, so undo it here. */
.choice + .hint {
  margin-top: 10px;
}
.choice input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.choice label {
  margin: 0;
  padding: 9px 20px;
  border: 1px solid var(--ink-12);
  border-radius: var(--pill);
  cursor: pointer;
  font-weight: 500;
  font-size: 0.92rem;
  background: #fff;
  transition: all 0.12s ease;
}
.choice label:hover {
  border-color: var(--teal);
  background: var(--teal-10);
}
.choice input:checked + label {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}
.choice input:focus-visible + label {
  outline: 3px solid var(--flame);
  outline-offset: 2px;
}

/* A long, filterable radio list — 153 local highway authorities. The rows are a
 * class rather than an inline style because a filter hides them with the
 * `hidden` attribute, and an inline `display` would win over that. */
.pick-list {
  max-height: 320px;
  overflow-y: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-top: 8px;
}
.pick-row {
  display: flex;
  gap: 11px;
  align-items: center;
  padding: 10px 14px;
  margin: 0;
  border-bottom: 1px solid var(--line);
  cursor: pointer;
  font-weight: 400;
}
.pick-row:last-of-type {
  border-bottom: 0;
}
.pick-row input {
  width: auto;
  margin: 0;
  flex: none;
}
.pick-row:hover {
  background: var(--teal-10);
}
.pick-row:has(input:checked) {
  background: var(--teal-15);
  font-weight: 500;
}
.pick-row:has(input:focus-visible) {
  outline: 3px solid var(--flame);
  outline-offset: -3px;
}
.pick-empty {
  padding: 20px;
  text-align: center;
}

.code-input {
  font-family: var(--mono);
  font-size: 1.8rem;
  letter-spacing: 0.4em;
  text-align: center;
  text-transform: uppercase;
  padding: 16px 12px;
}

/* -------------------------------------------------------------- components */

.card {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 26px;
}
.card + .card {
  margin-top: 18px;
}
/* A card's title is a title, not a section heading. The h2 scale is set for
 * the marketing page, where a heading carries a whole screen; on a card it
 * comes out nearly the size of the page's own h1. */
.card h2,
.card h3 {
  font-size: 1.15rem;
  line-height: 1.3;
  margin-bottom: 12px;
}
.card-flush {
  padding: 0;
  overflow: hidden;
}

.eyebrow {
  font-family: var(--display);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--flame);
  margin-bottom: 12px;
}
.eyebrow-teal {
  color: var(--teal);
}

.lede {
  font-size: 1.15rem;
  line-height: 1.7;
  color: var(--slate);
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.76rem;
  font-weight: 600;
  padding: 4px 11px;
  border-radius: var(--pill);
  background: var(--ink-06);
  color: var(--slate);
  white-space: nowrap;
}
.tag-official {
  background: var(--teal-15);
  color: var(--teal-dark);
}
.tag-flame {
  background: var(--flame-16);
  color: var(--flame-dark);
}
.tag-green {
  background: var(--green-bg);
  color: var(--green);
}
.tag-amber {
  background: var(--amber-bg);
  color: var(--amber);
}
.tag-red {
  background: var(--red-bg);
  color: var(--red);
}

.notice {
  border-left: 4px solid var(--teal);
  background: var(--teal-10);
  padding: 14px 18px;
  border-radius: 0 var(--radius) var(--radius) 0;
  margin-bottom: 18px;
}
.notice-warn {
  border-color: var(--amber);
  background: var(--amber-bg);
}
.notice-error {
  border-color: var(--red);
  background: var(--red-bg);
}
.notice-success {
  border-color: var(--green);
  background: var(--green-bg);
}
.notice p:last-child {
  margin-bottom: 0;
}

.setup-banner {
  background: var(--red);
  color: #fff;
  padding: 10px var(--gutter);
  font-size: 0.88rem;
  font-weight: 600;
  text-align: center;
}
.setup-banner a {
  color: #fff;
}

/* The cookie notice.
 *
 * In the flow of the document rather than fixed over it: a bar pinned to the
 * bottom of the viewport covers content on a short screen, and this one has
 * nothing urgent to say. It appears once, is dismissed by a real form
 * submission, and never comes back. */
.cookie-notice {
  background: var(--navy);
  color: rgba(255, 255, 255, 0.9);
  padding: 12px var(--gutter);
  font-size: 0.86rem;
}
.cookie-notice-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}
.cookie-notice p {
  margin: 0;
  max-width: 80ch;
}
.cookie-notice a {
  color: #fff;
  text-decoration: underline;
}
.cookie-notice form {
  flex: none;
}

/* The published policy pages. Long documents meant to be read end to end and
 * quoted from, so: generous line height, headings that separate rather than
 * decorate, and every section anchored so a procurement officer can link to
 * the paragraph they are asking about. */
.legal h2 {
  margin-top: 42px;
  margin-bottom: 14px;
  scroll-margin-top: 20px;
}
.legal h3 {
  margin-top: 26px;
  margin-bottom: 10px;
  font-size: 1.02rem;
}
.legal p,
.legal li {
  line-height: 1.75;
}
.legal ul {
  margin: 14px 0 14px 22px;
}
.legal li {
  margin-bottom: 8px;
}
.legal table.data {
  margin: 18px 0;
}
.legal table.data th {
  vertical-align: top;
  white-space: normal;
}
.legal-meta {
  margin-top: 10px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.legal-rule {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 46px 0 30px;
}
.legal-siblings ul {
  list-style: none;
  margin-left: 0;
}
.legal-siblings li {
  margin-bottom: 12px;
}

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.92rem;
}
table.data th,
table.data td {
  padding: 11px 14px;
  text-align: left;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
table.data thead th {
  font-family: var(--display);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--slate);
  border-bottom: 2px solid var(--ink-12);
  white-space: nowrap;
}
table.data tbody tr:hover {
  background: var(--mist);
}
table.data td.numeric,
table.data th.numeric {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* A table whose cells hold controls rather than prose.
 *
 * Top alignment is right for wrapping text — a figure should sit level with the
 * first line of the label it belongs to. It is wrong for a row made of a
 * progress bar, a status pill and a button, because those are three different
 * heights and aligning their tops leaves the row visibly ragged. Only the body
 * moves; the headings stay put. */
table.data.has-controls tbody th,
table.data.has-controls tbody td {
  vertical-align: middle;
}
/* The last column carries the row's action, under a heading that is empty by
 * design. Left-aligned it drifts away from the table's edge and away from the
 * right-aligned column before it. */
table.data td.actions,
table.data th.actions {
  text-align: right;
  white-space: nowrap;
}
/* Wide enough for the bar and its percentage without the percentage wrapping. */
table.data th.col-progress {
  width: 190px;
}
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
/* A menu inside one of these was cut off at the table's edge, so the note form
 * on the licences page and the document actions on the prepare step opened into
 * a sliver you could not read or reach.
 *
 * The cause is not z-index and raising it does nothing. Setting overflow on one
 * axis makes the element a clipping box on BOTH — CSS computes the other axis
 * from visible to auto — and an ancestor clips before stacking order is
 * consulted at all. The only escape is to stop clipping, which is safe to do
 * only while a panel is actually open.
 *
 * The table can spill past its container for the few seconds a menu is open.
 * That is the trade: a table that is briefly too wide against a form nobody can
 * use. */
.table-scroll:has(.menu[open]) {
  overflow: visible;
}
@supports not selector(:has(*)) {
  /* Without :has there is no way to know a panel is open, so it stops trying to
     float: it opens in the flow and makes the row taller. Ugly, and legible. */
  .table-scroll .menu[open] > .menu-items {
    position: static;
    margin-top: 8px;
  }
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
}

/* Cards side by side, all the same height.
 *
 * The three charts on the admin overview were an inline grid of .card, and the
 * second and third came out eighteen pixels shorter than the first — because
 * `.card + .card` gives every card after the first a top margin, which inside a
 * grid cell pushes the card down and, with the default stretch alignment,
 * shortens it by exactly that. Resetting the margin here rather than weakening
 * `.card + .card`, which is right everywhere else. */
.chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  align-items: stretch;
  gap: 20px;
}
.chart-grid > .card {
  margin-top: 0;
}
.stat {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 20px;
}
.stat-value {
  font-family: var(--display);
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
  color: var(--ink);
}
.stat-label {
  font-size: 0.85rem;
  color: var(--slate);
  margin-top: 6px;
}

.empty {
  text-align: center;
  padding: 60px 20px;
  color: var(--slate);
}
.empty h3 {
  color: var(--ink);
}

.muted {
  color: var(--slate);
}
.small {
  font-size: 0.85rem;
}
.mono {
  font-family: var(--mono);
  font-size: 0.88em;
}
.nowrap {
  white-space: nowrap;
}
/* A span that wants its own line. Six of these were in the admin templates and
 * this rule was in none of the stylesheets, so a licence's financial year, its
 * contact phone and its billing address all ran together on one line — three
 * facts reading as one sentence, in the column an administrator uses to tell
 * one order from another. */
.block {
  display: block;
}
.stack > * + * {
  margin-top: 14px;
}
.row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.row-between {
  justify-content: space-between;
}
.grow {
  flex: 1;
}
.mt-0 {
  margin-top: 0;
}
.mb-0 {
  margin-bottom: 0;
}
.mt-2 {
  margin-top: 20px;
}
.mt-3 {
  margin-top: 32px;
}

/* --------------------------------------------------------------- app shell */

.app-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--ink);
  color: #fff;
}
.app-header a {
  color: rgba(255, 255, 255, 0.82);
  text-decoration: none;
}
.app-header a:hover {
  color: #fff;
}
.app-header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0 24px;
  min-height: 62px;
  padding: 8px 0;
}

.brand {
  display: flex;
  align-items: baseline;
  gap: 9px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.02rem;
  letter-spacing: -0.02em;
  color: #fff !important;
}
/* The wordmark is one line always. Left to wrap it becomes three clipped
 * lines beside a squeezed byline, which is the first thing on every page. */
.brand > span:first-child {
  white-space: nowrap;
}
.brand-mark {
  color: var(--flame);
}
.brand-sub {
  font-family: var(--sans);
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0;
  color: rgba(255, 255, 255, 0.55);
}

.app-nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 2px 4px;
  margin-left: auto;
  align-items: center;
}
.app-nav a {
  padding: 8px 14px;
  border-radius: var(--pill);
  font-size: 0.92rem;
  font-weight: 500;
}
.app-nav a:hover {
  background: rgba(255, 255, 255, 0.1);
}
.app-nav a[aria-current="page"] {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--flame);
  color: #fff !important;
  font-size: 0.78rem;
  font-weight: 700;
  font-family: var(--display);
  flex: none;
}
.avatar-teal {
  background: var(--teal);
}

.page {
  padding: 36px 0 80px;
  background: var(--mist);
  min-height: calc(100vh - 62px);
}
.page-header {
  margin-bottom: 26px;
}
.page-header h1 {
  font-size: clamp(1.6rem, 1.3rem + 1.2vw, 2.2rem);
  margin-bottom: 6px;
}

.breadcrumb {
  font-size: 0.85rem;
  color: var(--slate);
  margin-bottom: 10px;
}
.breadcrumb a {
  color: var(--slate);
}

/* ---------------------------------------------------------------- footer */

.site-footer {
  background: var(--ink);
  color: rgba(255, 255, 255, 0.65);
  padding: 48px 0 32px;
  font-size: 0.9rem;
}
.site-footer a {
  color: rgba(255, 255, 255, 0.75);
  text-decoration: none;
}
.site-footer a:hover {
  color: #fff;
  text-decoration: underline;
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(auto-fit, minmax(150px, 1fr));
  gap: 32px;
  margin-bottom: 32px;
}
.footer-grid h4 {
  color: #fff;
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 12px;
}
.footer-grid ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.footer-grid li {
  margin-bottom: 8px;
}
.footer-bottom {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  padding-top: 24px;
  display: flex;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 0.82rem;
}

/* ------------------------------------------------------------ progress ring */

.ring {
  display: block;
  transform: rotate(-90deg);
  flex: none;
}
.ring-track {
  stroke: var(--ink-12);
  fill: none;
}
.ring-fill {
  stroke: var(--teal);
  fill: none;
  stroke-linecap: round;
  transition: stroke-dasharray 0.4s ease;
}
.ring-fill.is-complete {
  stroke: var(--green);
}
.ring-fill.is-blocked {
  stroke: var(--flame);
}
.ring-label {
  font-family: var(--display);
  font-size: 0.7rem;
  font-weight: 700;
  fill: var(--ink);
  transform: rotate(90deg);
  transform-origin: center;
  text-anchor: middle;
  dominant-baseline: central;
}

.bar {
  height: 8px;
  background: var(--ink-12);
  border-radius: var(--pill);
  overflow: hidden;
}
.bar > span {
  display: block;
  height: 100%;
  background: var(--teal);
  border-radius: var(--pill);
  transition: width 0.4s ease;
}
.bar.is-complete > span {
  background: var(--green);
}

/* ---------------------------------------------------------------- charts */

.chart {
  display: block;
  width: 100%;
  height: auto;
}
.chart .axis {
  stroke: var(--line);
  stroke-width: 1;
}
.chart .bar-rect {
  fill: var(--teal);
}
.chart .bar-rect:hover {
  fill: var(--teal-dark);
}
.chart .label {
  font-size: 10px;
  fill: var(--muted);
  font-family: var(--sans);
}

/* -------------------------------------------------------------- responsive */

@media (max-width: 900px) {
  .footer-grid {
    grid-template-columns: 1fr 1fr;
  }
}

@media (max-width: 880px) {
  /* The byline earns its place beside the wordmark only where there is room
   * for both on one line. */
  .brand-sub {
    display: none;
  }
}

@media (max-width: 720px) {
  :root {
    --gutter: 18px;
  }
  .brand {
    font-size: 0.98rem;
  }
  /* Brand on the first row, navigation on its own row beneath — nothing
   * clipped, nothing pushed off the edge of the screen. */
  .app-nav {
    row-gap: 0;
  }
  .app-nav a {
    padding: 7px 11px;
    font-size: 0.88rem;
  }
  .footer-grid {
    grid-template-columns: 1fr;
  }
  .card {
    padding: 20px;
  }
}

@media print {
  .app-header,
  .site-footer,
  .no-print {
    display: none !important;
  }
  .page {
    background: #fff;
    padding: 0;
  }
  .card {
    border: 0;
    padding: 0;
  }
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.8em;
    color: #555;
  }
}

/* ---------------------------------------------------- HTTPS: running state
 *
 * What the server is actually doing, above the boxes that describe what it
 * should do. These are the only settings on that page that do not take effect
 * when they are saved, so the difference between the two has to be visible
 * without reading anything.
 */

.tls-state {
  margin: 14px 0 18px;
  padding: 12px 16px;
  border: 1px solid var(--line, #e3e6e8);
  border-left: 4px solid var(--ink, #0f2a3a);
  border-radius: 8px;
  background: var(--mist, #fafafa);
}
.tls-state h4 {
  margin: 0 0 8px;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #5a6672);
}
.tls-state ul { list-style: none; margin: 0; padding: 0; }
.tls-state li {
  padding: 3px 0;
  font-size: 0.9rem;
  /* Long addresses and certificate names must wrap rather than push the card
     wider than the page on a narrow screen. */
  overflow-wrap: anywhere;
}
.tls-state li strong { margin-right: 6px; }

/* ------------------------------------------- drafting connection test */

.ai-test {
  margin-top: 12px;
  padding: 14px 16px;
  border-radius: 8px;
  border: 1px solid var(--line, #e3e6e8);
  background: var(--mist, #fafafa);
  font-size: 0.9rem;
}
.ai-test.good { border-left: 4px solid var(--green, #2e8b57); }
.ai-test.bad { border-left: 4px solid var(--red, #c0392b); }
.ai-test.testing { border-left: 4px solid var(--muted, #9ba4a3); }

.ai-test-summary { font-weight: 600; margin: 0 0 6px; }
.ai-test-advice { margin: 0 0 10px; }

.ai-test-steps { list-style: none; margin: 0; padding: 0; }
.ai-test-steps li {
  display: grid;
  grid-template-columns: 18px auto minmax(0, 1fr);
  gap: 2px 6px;
  padding: 3px 0;
  align-items: baseline;
}
.ai-test-mark { font-weight: 700; }
.ai-test-steps li.ok .ai-test-mark { color: var(--green, #2e8b57); }
.ai-test-steps li.warn .ai-test-mark { color: var(--amber, #d68910); }
.ai-test-steps li.bad .ai-test-mark { color: var(--red, #c0392b); }

.ai-test-reply {
  margin: 10px 0 0;
  font-style: italic;
}
.ai-test-meta {
  margin: 8px 0 0;
  font-size: 0.78rem;
  color: var(--muted, #9ba4a3);
  word-break: break-all;
}

/* ------------------------------------------------------------------- steps */

/* The five steps of a report, on every page of one.
 *
 * Boxes joined by arrows, because the order is a recommendation worth making —
 * most of a report can be filled in before anybody types, and nobody discovers
 * that from a wall of questions. Each box carries its own progress: a number
 * that becomes a tick, a status line, and a meter along the bottom. The arrow
 * into a step takes the colour of the step behind it, so a finished run reads
 * as one green thread and the strip fills from the left as the work is done.
 *
 * Every box is a link, and none of them is ever disabled. */

.steps {
  margin: 0 0 20px;
}

/* One sentence under the strip saying what this step is for. Every step page
 * has exactly one, in the same place, so it reads as part of the furniture
 * rather than as something a particular page decided to say. */
/* Full width, like the stepper above it and the cards below. A measure-capped
 * paragraph between two full-width elements does not read as careful
 * typography, it reads as something that failed to load. */
.page-intro {
  margin: 0 0 20px;
  color: var(--slate);
}

/* An actions menu: one trigger, a short list under it.
 *
 * A <details> rather than a scripted dropdown. The rows on the prepare step
 * carried four buttons each, which is a wall of grey against a column of prose;
 * this is one control that opens on click or Enter, closes on Escape, and needs
 * no JavaScript at all.
 *
 * position: relative on the row is not available — a <td> is not a containing
 * block in every browser — so the panel is positioned against the details
 * itself, which is inline-block. */
.menu {
  position: relative;
  display: inline-block;
}
.menu > summary {
  list-style: none;
  cursor: pointer;
}
.menu > summary::-webkit-details-marker {
  display: none;
}
.menu > summary::after {
  content: " \25BE";
  font-size: 0.85em;
  opacity: 0.7;
}
.menu[open] > summary::after {
  content: " \25B4";
}
.menu-items {
  position: absolute;
  right: 0;
  z-index: 30;
  min-width: 210px;
  margin-top: 6px;
  padding: 6px;
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  text-align: left;
}
.menu-items a,
.menu-items button {
  display: block;
  width: 100%;
  padding: 9px 12px;
  border: 0;
  border-radius: 6px;
  background: none;
  font: inherit;
  font-size: 0.9rem;
  color: var(--ink);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
}
.menu-items a:hover,
.menu-items button:hover {
  background: var(--mist);
}
.menu-items form {
  margin: 0;
}
.menu-items .menu-danger {
  color: var(--red);
}
/* The last item is the destructive one, so it is fenced off rather than sitting
 * flush against "download the file". */
.menu-items .menu-danger {
  margin-top: 4px;
  border-top: 1px solid var(--line);
  border-radius: 0 0 6px 6px;
  padding-top: 10px;
}

/* A label-and-value list, the shape a sidebar card of facts wants to be.
 * Was five copies of the same inline grid declaration. */
.detail-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 8px 16px;
  margin: 0;
  font-size: 0.9rem;
}
.detail-list dt {
  color: var(--muted);
}
.detail-list dd {
  margin: 0;
}

/* What the download contains, said plainly rather than offered as a choice. */
.what-you-get {
  margin: 0;
  padding-left: 20px;
  font-size: 0.9rem;
  color: var(--slate);
}
.what-you-get li + li {
  margin-top: 6px;
}

/* Publishing, as the sequence it is.
 *
 * The Publish step used to be two cards side by side — export on the left, buy
 * publish rights on the right — which is an accurate description of the two
 * things and says nothing about the order they happen in. Buying is a finance
 * process with a purchase order in the middle of it, and an officer doing it for
 * the first time could not tell from two cards that the invoice arrives after
 * the download, or that no card is taken at any point. So: a numbered list, down
 * the page, one thing per step.
 *
 * A real <ol>, with the numbers drawn rather than listed, so the sequence
 * survives with CSS off and a screen reader still announces "list, 5 items". */
.publish-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: none;
}
/* No connecting thread down the left. There was one — a two-pixel rule from
 * each number to the next — and with five cards already numbered, stacked and
 * stated in order, it was drawing a relationship the page had already made
 * plain. */
.publish-step {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 18px;
  padding: 22px 24px 24px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  margin-bottom: 14px;
}
/* A grey disc with the number in it, so a step that is not the one to do next
 * still reads as a numbered step. It was --mist, which is #fafafa on a #ffffff
 * page: the disc was drawn and nobody could see it, so steps 4 and 5 looked
 * like bare numerals sitting beside three that were flame. */
.publish-step-num {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--ink-12);
  color: var(--slate);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.publish-step-body {
  min-width: 0;
}
.publish-step-body h2 {
  margin: 6px 0 4px;
  font-size: 1.15rem;
}
.publish-step-blurb {
  margin: 0 0 16px;
  color: var(--slate);
  font-size: 0.92rem;
}
.publish-step .row,
.publish-step form {
  margin-bottom: 0;
}
/* Every fold on this page sits under the step's buttons, and the rule above
 * leaves that row with no bottom margin — so the gap has to come from the fold
 * or the two touch. */
.publish-step-body > .card-fold {
  margin-top: 18px;
}

/* Where you are. One step is emphasised and the rest are not, because a page on
 * which everything is emphasised has told you nothing. */
.publish-step.is-now .publish-step-num {
  background: var(--flame);
  color: #fff;
}
.publish-step.is-done .publish-step-num {
  background: var(--green-bg);
  color: var(--green);
}
/* A tick instead of the number once a step is behind you: the number's job was
 * to say what order to do things in, and that question is settled. */
.publish-step.is-done .publish-step-num::after {
  content: "\2713";
  font-size: 1.1rem;
}
.publish-step.is-done .publish-step-num {
  font-size: 0;
}
/* Ahead of you: still readable, so the whole process can be planned for, but
 * plainly not the thing to do next. */
.publish-step.is-later {
  background: transparent;
  border-style: dashed;
}
.publish-step.is-later .publish-step-body h2 {
  color: var(--slate);
}

/* A card that folds.
 *
 * For a card worth keeping on the page but not worth its full height every
 * time you arrive: the figures already applied, the documents already
 * attached, the log of what a run did. Two rules make it safe to fold by
 * default — the summary carries the count, so shutting one never hides whether
 * it has anything in it, and anything still waiting on the user opens itself.
 *
 * The native disclosure triangle is replaced rather than hidden: a chevron
 * that rotates is the same affordance drawn to match the rest of the page. */
/* A closed fold must actually be closed.
 *
 * The browser hides a closed details' content itself, but an author rule
 * setting `display` on a direct child overrides that hiding — and these folds
 * contain plenty: .readiness-item is a grid, .row is a flex. The check step
 * rendered all 191 of its findings while every group reported itself shut.
 * Restating the rule as an author rule of higher specificity settles it for
 * every fold, including ones added later. */
.card-fold:not([open]) > *:not(summary) {
  display: none;
}
.card-fold > summary {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  list-style: none;
  font-family: var(--display);
  font-size: 1.15rem;
  font-weight: 500;
}
.card-fold > summary::-webkit-details-marker {
  display: none;
}
.card-fold > summary::before {
  content: "";
  flex: none;
  width: 7px;
  height: 7px;
  margin-left: 2px;
  border-right: 2px solid var(--slate);
  border-bottom: 2px solid var(--slate);
  transform: rotate(-45deg);
  transition: transform 0.15s ease;
}
.card-fold[open] > summary::before {
  transform: rotate(45deg);
}
.card-fold[open] > summary {
  margin-bottom: 14px;
}
.card-fold > summary:focus-visible {
  outline: 2px solid var(--blue);
  outline-offset: 4px;
  border-radius: 6px;
}
/* The heading keeps its level for anyone navigating by headings; the summary
 * supplies the size, so it does not shout. */
.card-fold > summary > h2 {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}
/* A quieter fold, for reference text inside something that already has a
 * heading of its own.
 *
 * Two of the Publish steps end in a paragraph of background — what procurement
 * will ask for before it raises an order, what DfT wants done with the files.
 * Worth having on the page; not worth a second display-face heading inside a
 * step whose title is already one, which is what the card fold's 1.15rem made
 * of it. */
.card-fold-sm > summary,
.card-fold-sm > summary > h4 {
  font-family: var(--sans);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--slate);
  letter-spacing: 0;
}
.card-fold-sm > summary::before {
  width: 6px;
  height: 6px;
}
.card-fold-sm[open] > summary {
  margin-bottom: 10px;
}
/* A disclosure whose trigger is a button.
 *
 * "I have raised a purchase order" was a fold, drawn like the other folds on
 * the page: a chevron and a line of grey text among four paragraphs of grey
 * text. It is not one of the page's records, it is the act that licenses the
 * report — so it is drawn as what it does.
 *
 * Still a <details> underneath, and deliberately: the form is in the page
 * either way, so this needs no script and cannot leave anyone unable to reach
 * it. A real <button> opening a dialog would.
 *
 * The marker has to go three ways because summary is display: list-item and
 * setting display alone does not remove the marker in every engine. */
.reveal > summary {
  display: inline-block;
  list-style: none;
  cursor: pointer;
}
.reveal > summary::marker,
.reveal > summary::-webkit-details-marker {
  display: none;
  content: "";
}
.reveal[open] > summary {
  margin-bottom: 20px;
}
/* Closed means closed — the same author-rule problem .card-fold has above, and
 * this one holds a .stack, which sets display. */
.reveal:not([open]) > *:not(summary) {
  display: none;
}

/* What is inside, said on the outside. */
.fold-count {
  margin-left: auto;
  font-family: var(--sans);
  font-size: 0.85rem;
  font-weight: 400;
  color: var(--slate);
}

/* The single action that ends a step.
 *
 * Right-aligned, because it is the one thing on the page that moves you
 * forward and every other control on these pages is left-aligned — putting it
 * where the eye finishes rather than where it starts is what makes it read as
 * "and then" instead of as one more button in the stack. */
.next-step {
  margin: 28px 0 0;
  text-align: right;
}

/* A checkbox with a sentence beside it. Three of these in a row were three
 * copies of the same five inline styles. */
/* What the run is about to do, before it does it. A numbered list rather than
   a paragraph, because the four steps are the whole explanation and somebody
   deciding whether to press the button should be able to count them. */
.run-plan {
  margin: 14px 0 12px;
  padding-left: 22px;
  color: var(--slate);
  font-size: 0.94rem;
}
.run-plan li {
  margin-bottom: 8px;
  padding-left: 4px;
}
.run-plan li:last-child {
  margin-bottom: 0;
}

.opt {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  margin-bottom: 10px;
  font-weight: 400;
  line-height: 1.45;
}
.opt:last-child {
  margin-bottom: 0;
}
.opt input {
  width: auto;
  flex: none;
  margin-top: 3px;
}

/* -------------------------------------------------------------------- tabs */

/* Sections of one screen. The underline marks the current one, so the tab you
 * are on cannot be mistaken for the page's primary button. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 24px;
  border-bottom: 1px solid var(--line);
}
.tabs a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  margin-bottom: -1px;
  border: 1px solid transparent;
  border-bottom: 2px solid transparent;
  border-radius: 8px 8px 0 0;
  color: var(--slate);
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
}
.tabs a:hover {
  background: var(--ink-06);
  color: var(--ink);
}
.tabs a.is-active {
  color: var(--ink);
  border-bottom-color: var(--flame);
  font-weight: 600;
}
.tabs a.is-active:hover {
  background: none;
}

/* Settings is as wide as the rest of the admin console — it used to be the one
 * page in there at a different width — but a text input the full width of that
 * is not something anyone can read an API key back from. The card matches its
 * siblings; the controls inside it stay a sane measure. */
.settings-form .field > input,
.settings-form .field > select,
.settings-form .field > textarea,
.settings-form .field > .input-affix {
  max-width: 620px;
}
.settings-form .hint,
.settings-form .field > p {
  max-width: 86ch;
}
/* A line between two things inside one card. The email card holds the settings
 * and the send-a-test form, which are one subject and two separate forms — a
 * form inside a form is not markup a browser accepts — so the boundary has to
 * be drawn rather than implied by a gap. */
.rule {
  height: 0;
  margin: 26px 0;
  border: 0;
  border-top: 1px solid var(--line);
}

/* A main column with a narrower one beside it. This lives here rather than in
 * builder.css because the publish page uses it and does not load builder.css:
 * when it did, the two columns fell back to plain blocks and the second one
 * sat flush against the bottom of the first with no gap at all. */
.two-col {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: 24px;
  align-items: start;
}
@media (max-width: 900px) {
  .two-col {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Adding a document sits under the list of documents already there, inside the
 * same card — a rule between them rather than a second card asking the same
 * question in a different place. */
.upload {
  margin-top: 20px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.upload .field-row {
  margin-bottom: 14px;
}
.steps ol {
  /* The arrows are drawn in this gap, so the two measurements are kept in one
   * place and cannot drift apart. */
  --step-gap: 30px;
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--step-gap);
  list-style: none;
  margin: 0;
  padding: 0;
}
.step {
  position: relative;
  display: flex;
}

/* The arrow into a step: two sides of a square stood on its corner. */
.step + .step::before {
  content: "";
  position: absolute;
  top: 50%;
  left: calc((var(--step-gap) / -2) - 5px);
  width: 10px;
  height: 10px;
  margin-top: -5px;
  border-top: 2px solid var(--ink-12);
  border-right: 2px solid var(--ink-12);
  transform: rotate(45deg);
}
/* Only finished work colours the thread. A blocked step is already marked as
 * one, and a fresh report has blockers everywhere — a strip of red arrows
 * would raise an alarm about nothing more than not having started. Ordered
 * weakest first: what the step behind you came to outranks standing on it. */
.step.is-current + .step::before {
  border-color: var(--ink-55);
}
.step.is-done + .step::before {
  border-color: var(--green);
}

.step a {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
  padding: 12px 14px 13px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.12s ease, background 0.12s ease,
    box-shadow 0.12s ease;
}
.step a:hover {
  border-color: var(--teal);
  background: var(--teal-10);
  color: var(--ink);
  box-shadow: var(--shadow-sm);
}

.step-head {
  display: flex;
  gap: 9px;
  align-items: center;
  min-width: 0;
}
.step-num {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--pill);
  background: var(--ink-06);
  color: var(--slate);
  font-family: var(--display);
  font-weight: 700;
  font-size: 0.85rem;
  line-height: 1;
}
.step-title {
  font-family: var(--display);
  font-weight: 500;
  font-size: 0.95rem;
  line-height: 1.25;
  letter-spacing: -0.01em;
}
/* Pushed to the bottom of the box so the status lines sit level across all
 * five however many lines a title runs to. */
.step-detail {
  /* Two lines at most: five boxes of different heights reads worse than a
   * status line that stops. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  margin-top: auto;
  padding-top: 7px;
  overflow: hidden;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--muted);
}
.step-meter {
  display: block;
  height: 4px;
  margin-top: 9px;
  border-radius: var(--pill);
  background: var(--ink-06);
  overflow: hidden;
}
.step-meter > span {
  display: block;
  height: 100%;
  border-radius: var(--pill);
  background: var(--teal);
  transition: width 0.4s ease;
}

.step.is-done a {
  border-color: rgba(26, 127, 75, 0.3);
}
.step.is-done .step-num {
  background: var(--green-bg);
  color: var(--green);
}
.step.is-done .step-meter > span {
  background: var(--green);
}

.step.is-blocked a {
  border-color: rgba(179, 38, 30, 0.35);
}
.step.is-blocked .step-num {
  background: var(--red-bg);
  color: var(--red);
}
.step.is-blocked .step-detail {
  color: var(--red);
}
.step.is-blocked .step-meter > span {
  background: var(--red);
}

/* A live AI Prefill run. Its length is nobody's to predict, so the meter
   slides rather than fills — a stuck-empty bar reads as a dead one. */
.step.is-running .step-meter::after {
  content: "";
  display: block;
  height: 100%;
  width: 35%;
  border-radius: var(--pill);
  background: var(--ink-55);
  animation: step-running 1.4s ease-in-out infinite;
}
.step.is-current.is-running .step-meter::after {
  background: var(--paper);
}
@keyframes step-running {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(385%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .step.is-running .step-meter::after {
    animation: none;
    width: 50%;
  }
}

/* Last of the three states, so that the step you are on wins over whatever
 * else is true of it. */
.step.is-current a,
.step.is-current a:hover {
  border-color: var(--ink);
  background: var(--ink);
  color: var(--paper);
  box-shadow: var(--shadow);
}
.step.is-current .step-num {
  background: rgba(255, 255, 255, 0.16);
  color: var(--paper);
}
.step.is-current .step-detail {
  color: rgba(255, 255, 255, 0.72);
}
.step.is-current.is-blocked .step-detail {
  /* --red is unreadable on navy. Same warning, lifted until it is legible. */
  color: #ffb3ac;
}
.step.is-current .step-meter {
  background: rgba(255, 255, 255, 0.18);
}
.step.is-current .step-meter > span {
  background: var(--paper);
}

/* Narrower than this, five boxes at full size stop fitting. They shrink rather
 * than scroll: seeing all five at once is the whole point of the strip, and a
 * fifth step sliced off at the right edge reads as a fault rather than as an
 * invitation to scroll. */
@media (max-width: 1040px) {
  .steps ol {
    --step-gap: 18px;
  }
  .step a {
    padding: 10px 12px 11px;
  }
  .step-head {
    gap: 8px;
  }
  .step-num {
    width: 24px;
    height: 24px;
    font-size: 0.8rem;
  }
  .step-title {
    font-size: 0.88rem;
  }
  .step-detail {
    -webkit-line-clamp: 1;
    padding-top: 6px;
    font-size: 0.74rem;
  }
  .step-meter {
    margin-top: 8px;
  }
}

/* Below a tablet there is no arrangement of five boxes across that anyone can
 * read, so they stack: name on the left, status on the right, and the meter
 * along the bottom edge of each row. */
@media (max-width: 760px) {
  .steps ol {
    --step-gap: 10px;
    grid-template-columns: minmax(0, 1fr);
  }
  .step a {
    position: relative;
    flex-direction: row;
    align-items: center;
    gap: 12px;
    padding: 11px 14px 13px;
    overflow: hidden;
  }
  /* The arrow turns to point the way the strip now runs. */
  .step + .step::before {
    top: calc((var(--step-gap) / -2) - 5px);
    left: 24px;
    margin-top: 0;
    transform: rotate(135deg);
  }
  .step-detail {
    -webkit-line-clamp: 2;
    margin: 0 0 0 auto;
    padding-top: 0;
    text-align: right;
  }
  .step-meter {
    position: absolute;
    right: 0;
    bottom: 0;
    left: 0;
    margin: 0;
    border-radius: 0;
  }
}

/* The first modal in the application.
 *
 * A native <dialog>, so focus, Escape and the inert background come from the
 * browser rather than from three hundred lines of JavaScript that would get one
 * of them wrong. It ships empty: app.js moves the form for the authority being
 * edited into it and moves it back on close, so there is one dialog on the page
 * rather than one per row, and the forms stay reachable in their fold with
 * JavaScript off. */
.modal {
  width: min(640px, calc(100vw - 32px));
  max-height: min(84vh, 900px);
  padding: 26px;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: #fff;
  color: var(--ink);
  overflow-y: auto;
}
.modal::backdrop {
  background: rgba(16, 22, 33, 0.45);
}
.modal .auth-form-title {
  margin: 0 0 18px;
  font-family: var(--display);
  font-size: 1.25rem;
  font-weight: 500;
}

/* The same forms in their fold, where they are a list rather than a window. */
.auth-forms > .auth-form {
  padding: 22px 0;
  border-top: 1px solid var(--line);
}
.auth-forms > .auth-form:first-child {
  border-top: 0;
  padding-top: 0;
}
.auth-form-title {
  margin: 0 0 12px;
  font-size: 1rem;
  font-weight: 600;
}
.auth-form input[readonly] {
  background: var(--paper);
  color: var(--slate);
}

/* ---------------------------------------------------------------- help panel

   Talking to support, from the corner of every signed-in page.

   A <details> rather than a scripted panel, which is the choice .menu already
   makes: it opens on click or Enter, closes on Escape, and needs no JavaScript
   to exist. The script only makes replies arrive without a reload.

   Fixed bottom-right. The cookie notice is in the flow of the document rather
   than pinned (see .cookie-notice), so there is nothing down here to collide
   with, and z-index sits below the sticky header at 60 and above the table
   menus at 30. */
.help-dock {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 50;
  max-width: calc(100vw - 40px);
}
.help-bubble {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 18px;
  border-radius: var(--pill);
  background: var(--ink);
  color: #fff;
  font-size: 0.9rem;
  font-weight: 600;
  box-shadow: var(--shadow);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.help-bubble::-webkit-details-marker {
  display: none;
}
.help-bubble:hover {
  background: var(--flame);
}
.help-dock[open] > .help-bubble {
  background: var(--flame);
}
.help-bubble-label::before {
  /* A speech mark rather than an image: one character, no request, and it
     survives a stylesheet that loads without its icons. */
  content: "\1F4AC\00a0";
}
.help-unread {
  display: inline-grid;
  place-items: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: var(--pill);
  background: #fff;
  color: var(--flame);
  font-size: 0.75rem;
  font-weight: 700;
}
.help-unread.is-hidden,
.help-unread[hidden] {
  display: none;
}

.help-panel {
  position: absolute;
  right: 0;
  bottom: calc(100% + 10px);
  width: 360px;
  max-width: calc(100vw - 40px);
  padding: 16px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.help-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.help-open {
  font-size: 0.8rem;
}

/* The indicator. Never colour alone: the dot carries the state and the words
   beside it say the same thing, because a green circle means nothing to
   somebody who cannot see the difference between it and a grey one. */
.help-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 0.85rem;
  font-weight: 600;
}
.help-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--muted);
  flex: none;
}
.help-status.is-online {
  color: var(--green);
}
.help-status.is-online .help-dot {
  background: var(--green);
  box-shadow: 0 0 0 3px rgba(26, 127, 75, 0.18);
}
.help-status.is-offline {
  color: var(--muted);
}

.help-log {
  max-height: 300px;
  overflow-y: auto;
  margin-bottom: 12px;
}
/* On the full page there is room, and the conversation is the point of it. */
.help-log-page {
  max-height: none;
  margin-bottom: 20px;
}
.help-empty {
  margin: 0;
  padding: 12px 0;
  font-size: 0.85rem;
  color: var(--muted);
}
.help-msg {
  margin-bottom: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--mist);
  font-size: 0.88rem;
}
.help-msg.is-support {
  background: var(--teal-10);
}
.help-msg.is-mine {
  background: var(--mist);
}
.help-msg-who {
  margin-bottom: 4px;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--slate);
}
.help-msg-when {
  margin-left: 6px;
  font-weight: 400;
  color: var(--muted);
}
.help-msg-body {
  /* Newlines a person typed are newlines they meant. */
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.help-msg-page {
  margin-top: 6px;
  font-size: 0.75rem;
  color: var(--muted);
}
.help-offline-note {
  margin: 0 0 12px;
  font-size: 0.82rem;
  color: var(--slate);
}
.help-form textarea {
  width: 100%;
  margin-bottom: 10px;
}
.help-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Narrow screens: full width along the bottom, because a 360px panel with 20px
   either side does not fit a 375px phone and half of it would be off-screen. */
@media (max-width: 480px) {
  .help-dock {
    right: 12px;
    bottom: 12px;
  }
  .help-panel {
    width: calc(100vw - 24px);
    right: -6px;
  }
}

/* Somebody printing a report step does not want a support panel in the corner
   of it. */
@media print {
  .help-dock {
    display: none;
  }
}
