/* ==========================================================================
   Pharmacy Dashboard — app.css
   Loaded AFTER bootstrap and dashboard.css, and overrides both.
   See DESIGN.md for the system this implements.

   Section 1 is the Bootstrap 5 migration shims and nothing else. Tokens,
   primitives and the dark palette land here in later phases.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Bootstrap 5 compatibility shims

   Bootstrap 5 removed these two classes. Both are used widely and purely
   presentationally, so they are reimplemented here rather than rewritten out
   of the templates -- renaming them across the app would be churn with no
   functional gain. They keep their Bootstrap 4 definitions.
   -------------------------------------------------------------------------- */

/* Removed in BS5. 21 templates use it as the standard page-block wrapper. */
.jumbotron {
  padding: 2rem 1rem;
  margin-bottom: 2rem;
  background-color: #e9ecef;
  border-radius: .3rem;
}

@media (min-width: 576px) {
  .jumbotron {
    padding: 4rem 2rem;
  }
}

/* The store switcher in the account menu.
 *
 * This replaces `.pre-scrollable`, a BS4 class dropped in BS5 that was shimmed
 * back purely for the old nested "Select another store" submenu -- a pattern
 * Bootstrap has never supported. The stores are now listed inline in the
 * account menu, so the scroll cap belongs to that list.
 *
 * `overflow-y: auto`, not `scroll`: the shim always drew a scrollbar gutter,
 * even for a two-store operator whose list never overflows. */
.store-switch {
  max-height: 15rem;
  overflow-y: auto;
}

.store-switch__item {
  display: flex;
  align-items: center;
  gap: .5rem;
}

.store-switch__tick {
  flex: none;
  font-size: .875rem;
  color: var(--text-faint);
}

/* Which store's money you are looking at. The chrome never said, on an app
   where every figure on the page belongs to exactly one store. DESIGN.md §4. */
.store-switch__item.is-current {
  font-weight: 600;
}

.store-switch__item.is-current .store-switch__tick {
  color: var(--brand-700);
}

.store-switch__only {
  font-weight: 600;
}

/* dashboard.css used to uppercase EVERY input in the app -- passwords, email,
   store name, and every DataTables search box -- with four vendor-prefixed
   rules to keep placeholders readable. Only the seven WinRx code fields need
   it (the ones views.py calls .upper() on), and they now carry Bootstrap's
   .text-uppercase from their widget definition. This is the one part of that
   block still worth keeping: text-transform inherits into the placeholder, so
   "Facility e.g. BP MON or RETAIL" would otherwise shout. */
.text-uppercase::placeholder {
  text-transform: none;
}

/* Bootstrap 5 gave .nav-link `color: var(--bs-nav-link-color)`, where BS4 set no
   colour at all. That variable is only declared on `.nav`, and the navbar links
   in dashboard/base.html sit directly in .navbar-collapse rather than inside a
   .nav / .navbar-nav -- so it resolves to nothing, the declaration is invalid at
   computed-value time, and `color` falls back to *inherited* (near-black
   #212529) on a black navbar. dashboard.css's `nav a { color: #fff }` used to
   cover this, but `.nav-link` (0,1,0) now outranks `nav a` (0,0,2).

   Declaring the variable on .navbar is the idiomatic 5.3 fix: it inherits to
   every descendant .nav-link and drives the hover state too. */
.navbar {
  --bs-nav-link-color: #fff;
  --bs-nav-link-hover-color: #ccc;
}

/* Same trap as .nav-link above. Bootstrap 5 gave .badge
   `color: var(--bs-badge-color)` with `--bs-badge-color: #fff`, where BS4 set no
   colour and the text simply inherited. The KPI delta badges are
   `class="badge rounded-pill"` with no `text-bg-*`, so on the Bootstrap 5
   upgrade they became white text on a white card and the percentages vanished
   from every stat card.

   Inheriting restores the parent's semantic colour (.text-success /
   .text-danger). Badges that DO carry a `text-bg-*` are unaffected -- those set
   their colour with !important. Superseded when .stat-tile lands (DESIGN.md §6),
   which drops the badge and the never-firing tooltip for visible caption text. */
.card .badge {
  color: inherit;
}

/* --------------------------------------------------------------------------
   2. Tokens

   From DESIGN.md section 3. Brand is the WinRx family navy, so this app and
   WinRxDelivery read as one product line.

   `fill` and `ink` are separate because they move in OPPOSITE directions in
   dark mode: fill is a solid background that white text sits on, ink is text
   drawn on a plain surface. One token for both is what makes a dark theme
   impossible to finish later.

   Dark mode keys off Bootstrap 5.3's own `data-bs-theme` attribute rather than
   a separate `data-theme` plus a prefers-color-scheme media query.

   DESIGN.md section 9 called for writing the palette twice, once under the
   media query and once under the attribute. That is the right shape when CSS
   alone can theme the page -- but Bootstrap 5.3 does NOT follow
   prefers-color-scheme on its own; its components only change when the
   attribute is set. Declaring dark tokens under a bare media query would
   therefore give exactly the half-dark app that warning was about: dark tokens
   under light Bootstrap components. So an inline script resolves the choice
   (light / dark / follow the device) and stamps a concrete value before first
   paint, and the palette is written once, here.
   -------------------------------------------------------------------------- */

:root {
  --brand-fill: #1f4f7c;   /* solid fill; white text sits on it -- 8.5:1 on white */
  --brand-700:  #1f4f7c;   /* ink */
  --brand-900:  #143a5c;   /* strongest ink */
  --brand-500:  #2f6fa8;   /* focus rings */
  --brand-050:  #eaf1f8;   /* tint background */

  /* Semantic. These are for STATE -- a delta that is good or bad, an error --
     never for identifying a measure. DESIGN.md section 2: colour encodes the
     measure, never the mood, and red/green here are spent on direction so they
     cannot also be spent on decoration. */
  --accent-700: #006300;   /* positive delta text; 6.4:1 on white */
  --accent-050: #e7f5ee;
  --warn-700:   #8a5200;
  --warn-050:   #fdf1de;
  --danger-700: #a4342c;   /* negative delta text; 6.2:1 on white */
  --danger-050: #fdeceb;

  --surface:        #ffffff;
  --surface-sunken: #f3f5f9;
  --border:         #e2e7f0;
  --control-border: #c3ccdb;

  --text:       #14203a;
  --text-muted: #5b6a83;
  --text-faint: #a9b4c6;

  --radius-lg: 18px;
  --radius-md: 12px;
  --radius-sm: 8px;
  --touch: 44px;
}

/* Dark palette. Note which way each pair moves: --brand-fill gets LIGHTER
   (a solid that white text still sits on at 5.0:1) while --brand-700 ink goes
   much lighter still, because it is now drawn on a dark surface. One token for
   both is what makes a dark theme impossible to finish. */
:root[data-bs-theme="dark"] {
  --brand-fill: #336da8;   /* solid fill; still 5.0:1 under white text */
  --brand-700:  #8fbde8;   /* ink on a dark surface */
  --brand-900:  #b9d2ff;   /* strongest ink */
  --brand-500:  #4d8ff5;
  --brand-050:  #16233c;   /* tint background */

  --accent-700: #5fd39b;
  --accent-050: #10291f;
  --warn-700:   #f0b357;
  --warn-050:   #2e2113;
  --danger-700: #f08c84;
  --danger-050: #2e1614;

  --surface:        #161b26;
  --surface-sunken: #0e121a;
  --border:         #29323f;
  --control-border: #3c4658;

  --text:       #e6ecf7;
  --text-muted: #9aa7bd;
  --text-faint: #6b7a92;
}

/* The chart card is a themed surface here, not an image: figures are recoloured
   to the dark colourway by base.js, so the card follows the theme like anything
   else. See dashboard/charts.py for why the palette has to swap rather than
   just the chrome. */
:root[data-bs-theme="dark"] .chart-card .plotly-graph-div {
  background: transparent;
}

/* Bootstrap's own dark surfaces are a different grey from ours; point its
   variables at our tokens so a .card or a .modal matches the app. */
:root[data-bs-theme="dark"] {
  --bs-body-bg: var(--surface-sunken);
  --bs-body-color: var(--text);
  --bs-border-color: var(--border);
  --bs-secondary-color: var(--text-muted);
}

/* --------------------------------------------------------------------------
   3. The shell

   A real two-column grid. What this replaces: a position:fixed rail sized by
   .col-md-2, and a <main> in a separate div sized by .col-lg-10, aligned to
   each other by ms-sm-auto. Nothing tied the two together, which is why the
   rail could collapse to 53px on the Bootstrap 5 upgrade without the content
   noticing. DESIGN.md section 4.

   The rail is `position: sticky`, not `fixed`: it participates in layout, so
   the grid can size the content column against it instead of guessing.
   -------------------------------------------------------------------------- */

:root {
  --navbar-h: 46px;   /* the fixed-top navbar; the shell offsets itself by this */
  --rail-w: 17.5rem;
}

.app-shell {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin-top: var(--navbar-h);
}

.app-shell__rail {
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border);
}

.app-shell__rail-inner {
  padding: .5rem 1rem 1rem;
}

.app-shell__colophon {
  margin: 1rem 0 0;
  font-size: .75rem;
  text-align: center;
  color: var(--text-faint);
}

.app-shell__content {
  /* min-width: 0 lets a wide table scroll inside this column rather than
     forcing the whole grid wider than the viewport. */
  min-width: 0;
  padding: .5rem 1rem 3rem;
}

@media (min-width: 992px) {
  .app-shell {
    grid-template-columns: var(--rail-w) minmax(0, 1fr);
    min-height: calc(100vh - var(--navbar-h));
    /* A sticky rail is only as tall as its content, so on a short page (an
       empty result, say) its background and divider would stop mid-screen.
       Painting the rail column onto the grid itself keeps both full height
       regardless of what either column contains. */
    background: linear-gradient(to right,
      var(--surface-sunken) 0, var(--surface-sunken) var(--rail-w),
      var(--border) var(--rail-w), var(--border) calc(var(--rail-w) + 1px),
      transparent calc(var(--rail-w) + 1px));
  }

  /* From lg up the rail is permanent, so neutralise .collapse -- including the
     inline height Bootstrap's Collapse plugin leaves behind if the user
     toggled it on a narrow screen and then widened the window. */
  .app-shell__rail.collapse {
    display: block !important;
    height: auto !important;
    visibility: visible !important;
  }

  .app-shell__rail {
    position: sticky;
    top: var(--navbar-h);
    align-self: start;
    max-height: calc(100vh - var(--navbar-h));
    overflow-y: auto;
    border-bottom: 0;
    /* divider comes from the grid background above */
  }

  .app-shell__content {
    padding: .75rem 1.5rem 4rem;
  }
}

/* Cap the reading width. An unconstrained column on a 27" monitor gives
   unreadable line lengths and strands charts in whitespace; the grid still
   takes the full width, the content inside it does not. */
.app-shell__inner {
  max-width: 110rem;
  margin: 0 auto;
}

/* --------------------------------------------------------------------------
   4. Page header and the active-filter summary

   Every screen here is a filtered view of one dataset over one date range, and
   nothing on the page used to say which. DESIGN.md section 5.
   -------------------------------------------------------------------------- */

.page-header {
  padding: .75rem 0 .625rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--border);
}

.page-header__top {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem 1rem;
}

.page-header__title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
  /* Bootstrap sets `h1 { color: var(--bs-heading-color) }`, and under
     [data-bs-theme=dark] that resolves through --bs-body-color. Pinning the
     heading variable as well as `color` keeps this on our token whichever of
     the two a browser resolves first. */
  --bs-heading-color: var(--text);
  color: var(--text);
  line-height: 1.2;
}

/* The store name is context for the numbers below, not a second heading. It
   was the same weight and a competing colour, so the two read as peers. */
.page-header__store {
  font-size: .9375rem;
  font-weight: 500;
  color: var(--text-muted);
}

/* On dark, brand ink is a light blue that reads as a link next to a white
   title. Plain muted ink is quieter and unambiguous. */
:root[data-bs-theme="dark"] .page-header__title {
  color: var(--text);
}

:root[data-bs-theme="dark"] .page-header__store {
  color: var(--text-muted);
}

.filter-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .375rem;
  margin-top: .625rem;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .1875rem .25rem .1875rem .5rem;
  font-size: .8125rem;
  line-height: 1.4;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
}

/* The period is not a filter you can remove -- it is the scope of the whole
   screen -- so it reads as a statement rather than a dismissible chip. */
.filter-chip--period {
  padding-right: .625rem;
  color: var(--brand-900);
  background: var(--brand-050);
  border-color: transparent;
  font-weight: 600;
}

.filter-chip--period .bi {
  opacity: .7;
}

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

.filter-chip__label::after {
  content: ":";
}

.filter-chip__value {
  font-weight: 600;
}

.filter-chip__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.375rem;
  height: 1.375rem;
  padding: 0;
  color: var(--text-muted);
  background: none;
  border: 0;
  border-radius: 999px;
  line-height: 1;
}

.filter-chip__clear:hover,
.filter-chip__clear:focus-visible {
  color: var(--surface);
  background: var(--brand-fill);
}

.filter-clear-all {
  padding: .1875rem .5rem;
  font-size: .8125rem;
  color: var(--text-muted);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.filter-clear-all:hover,
.filter-clear-all:focus-visible {
  color: var(--brand-700);
}

/* Density is right on a monitor; a thumb needs more. DESIGN.md section 2. */
@media (max-width: 767.98px) {
  .filter-chip {
    padding: .3125rem .3125rem .3125rem .625rem;
    font-size: .875rem;
  }

  .filter-chip__clear {
    width: 1.75rem;
    height: 1.75rem;
  }
}

/* --------------------------------------------------------------------------
   5. Period presets

   Chips rather than a dropdown: the available periods should be visible
   without a click. DESIGN.md section 5.
   -------------------------------------------------------------------------- */

.date-presets {
  display: flex;
  flex-wrap: wrap;
  gap: .25rem;
  margin: .75rem 0 .625rem;
}

.date-preset {
  padding: .1875rem .5rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--brand-700);
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: 999px;
  line-height: 1.5;
}

.date-preset:hover,
.date-preset:focus-visible {
  color: var(--surface);
  background: var(--brand-fill);
  border-color: var(--brand-fill);
}

@media (max-width: 767.98px) {
  .date-preset {
    padding: .375rem .75rem;
    font-size: .8125rem;
  }
}

/* --------------------------------------------------------------------------
   6. The filter rail

   Thirteen ungrouped controls meant scanning the whole column to find one
   field. Named groups, with the rarely-used ones collapsed, turn that into
   "scan five labels". DESIGN.md section 5.
   -------------------------------------------------------------------------- */

.filter-rail {
  padding-bottom: 1rem;
}

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

.filter-group__summary {
  display: flex;
  align-items: center;
  gap: .375rem;
  padding: .5rem .125rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
  cursor: pointer;
  list-style: none;
  user-select: none;
}

/* The default disclosure triangle is inconsistent across browsers, so it is
   suppressed and replaced with a chevron that rotates on open. */
.filter-group__summary::-webkit-details-marker {
  display: none;
}

.filter-group__summary::before {
  content: "";
  width: .5rem;
  height: .5rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(-45deg);
  transition: transform .15s ease-in-out;
  flex: 0 0 auto;
}

.filter-group[open] > .filter-group__summary::before {
  transform: rotate(45deg);
}

.filter-group__summary:hover {
  color: var(--brand-700);
}

.filter-group__body {
  padding-bottom: .625rem;
}

.filter-field {
  margin-bottom: .5rem;
}

.filter-label {
  display: block;
  margin-bottom: .1875rem;
  font-size: .75rem;
  font-weight: 600;
  color: var(--text-muted);
}

/* Native date inputs size differently from text inputs across browsers; pin
   them so From and To line up. */
.filter-rail input[type="date"] {
  min-height: 2.25rem;
}

@media (max-width: 767.98px) {
  .filter-group__summary {
    padding: .75rem .125rem;
    font-size: .8125rem;
  }

  .filter-field {
    margin-bottom: .75rem;
  }
}

/* --------------------------------------------------------------------------
   7. Stat tiles

   One measure each. No decorative colour: the old cards were border-left-danger
   for Count and border-left-success for Cost, which spent red and green on
   nothing and left them unavailable for the deltas, where they mean something.
   DESIGN.md sections 2 and 6.
   -------------------------------------------------------------------------- */

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: .75rem;
  margin-bottom: 1.25rem;
}

.stat-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .125rem;
  padding: .875rem 1rem;
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* A tile that discloses detail is a real button. These were divs carrying
   data-bs-toggle: no keyboard access, no role, no aria-expanded. */
.stat-tile--action {
  cursor: pointer;
  transition: border-color .12s ease-in-out, box-shadow .12s ease-in-out;
}

.stat-tile--action:hover,
.stat-tile--action:focus-visible {
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--brand-050);
}

.stat-tile__label {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.stat-tile__value {
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.15;
  color: var(--text);
  /* Tabular figures so digits line up down a column of tiles. This is a
     financial report; misaligned numerals cost real reading time. */
  font-variant-numeric: tabular-nums;
}

/* Secondary value: a service count's billed total. Subordinate to the headline
   but still a figure to be read, so it keeps tabular numerals. */
.stat-tile__sub {
  font-size: 1.0625rem;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

.stat-tile__delta {
  display: inline-flex;
  align-items: center;
  gap: .125rem;
  margin-top: .25rem;
  font-size: .875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Tone is whether the movement is good news for THIS measure, which is not the
   same as which way it moved. Paired with a differing arrow silhouette, so the
   state never rests on colour alone. */
.stat-tile__delta--good { color: var(--accent-700); }
.stat-tile__delta--bad  { color: var(--danger-700); }
.stat-tile__delta--flat { color: var(--text-muted); }

.stat-tile__delta .bi {
  font-size: 1.25rem;
  line-height: 1;
}

.stat-tile__caption {
  font-size: .6875rem;
  color: var(--text-faint);
  line-height: 1.3;
}

@media (max-width: 767.98px) {
  .stat-row {
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  }

  .stat-tile__value {
    font-size: 1.5rem;
  }
}

/* --------------------------------------------------------------------------
   8. Chart and table cards

   Height is the CARD's contract, not the figure's. Every figure now renders
   with autosize plus `responsive: true` (dashboard/charts.py), so it fills
   whatever box it is given -- which is what makes one chart per row on a phone
   and two on a monitor a pure CSS decision. DESIGN.md section 7.
   -------------------------------------------------------------------------- */

.chart-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

/* The height chain matters here. plotly.offline.plot emits
   `<div><div class="plotly-graph-div" style="height:100%; width:100%">` -- an
   UNCLASSED wrapper around a percentage-height graph div. A percentage height
   against an auto-height parent collapses to nothing, so the card has to state
   a height and every link between it and the graph div has to pass it down. */
.chart-card {
  min-width: 0;   /* let a wide figure shrink rather than stretch the grid */
  height: 22rem;
  display: flex;
  flex-direction: column;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* Belt and braces. The customers map declared height=700 in Python, which
     beat the card and painted over the cards below it. The fixed dimensions
     are gone, but a figure must never be able to escape its card again. */
  overflow: hidden;
}

.chart-card > div {
  flex: 1 1 auto;
  min-height: 0;
}

.chart-card .plotly-graph-div {
  width: 100% !important;
  height: 100% !important;
}

/* A map or a bubble chart carries more marks than a line or a bar and never
   shares a row; it earns the extra height. */
.chart-card--tall {
  height: 30rem;
}

@media (min-width: 992px) {
  .chart-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .chart-card {
    height: 24rem;
  }

  .chart-card--tall {
    height: 34rem;
  }
}

/* Below md a chart has the row to itself, so it can afford a little less
   height without competing for width. */
@media (max-width: 767.98px) {
  .chart-card {
    height: 20rem;
    padding: .75rem;
  }
}

.table-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  margin-bottom: 1.25rem;
}

@media (min-width: 992px) {
  .table-grid {
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  }
}

.table-card {
  min-width: 0;
  padding: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  /* A 19-column table scrolls inside its card rather than pushing the page
     sideways. DESIGN.md: the page body must never scroll horizontally. */
  overflow-x: auto;
}

/* One-row grid: a table wide enough to need every column of the content area,
   rather than sharing the row with something narrower. */
.table-grid--single {
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 992px) {
  .table-grid--single {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Column headers on a summary table must not wrap: "Rx Count" and "Compound
   Fee" on two lines each doubles the header height and makes the row hard to
   scan. The card scrolls horizontally if the table outgrows it, which is the
   right trade for a totals row that is read across. */
.table-card table th {
  white-space: nowrap;
}

/* The totals table on productivity, inventory and the two customer screens.
   It is a 3-row summary read down a column, not a data grid read across, so
   the column titles are centred over their numbers rather than ragged left.

   All four pages had drifted apart: inventory carried align="right" on its
   column headers, the others carried nothing, and the row headers were right
   aligned on some pages and not others. The alignment is defined here once and
   the deprecated align attributes are gone from the markup, so there is
   nothing left to contradict it. */
#totalsTable thead th {
  text-align: center;
  vertical-align: middle;
}

/* Figures stay right aligned -- that is what makes a column of currency
   comparable at a glance. */
#totalsTable tbody td {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The scope label is prose, not a figure. */
#totalsTable tbody th[scope="row"] {
  text-align: left;
}

/* A short numeric filter is not a form that needs its own card. */
.field-inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  padding: .625rem .875rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

.field-inline__legend {
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-right: .25rem;
}

.field-inline__label {
  margin: 0;
  font-size: .8125rem;
  color: var(--text-muted);
}

.field-inline__input {
  width: 6rem;
}

.table-card__title {
  margin: 0 0 .625rem;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.table-card > table {
  margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   9. State panel and the query overlay

   .state-panel is the shared shape for loading, empty and error states, so all
   three read as one family rather than three unrelated screens. DESIGN.md §6.
   -------------------------------------------------------------------------- */

.state-panel {
  max-width: 26rem;
  padding: 2rem 1.5rem;
  text-align: center;
  background: var(--surface);
  border-radius: var(--radius-md);
  box-shadow: 0 1px 2px rgba(16, 32, 64, .06), 0 8px 24px rgba(16, 32, 64, .12);
}

/* Centres a state panel in the content column when it is the whole page,
   rather than pinning it to the top like a heading. */
.state-wrap {
  display: flex;
  justify-content: center;
  padding: 3rem 0;
}

.state-panel__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  margin: 0 auto 1rem;
  font-size: 1.5rem;
  color: var(--brand-700);
  background: var(--brand-050);
  border-radius: 50%;
}

/* Shape as well as colour: the two states use different glyphs, so they are
   still distinguishable to anyone who does not perceive the tint.
   DESIGN.md section 2. */
.state-panel__icon--danger {
  color: var(--danger-700);
  background: var(--danger-050);
}

.state-panel__title {
  margin: 0 0 .375rem;
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--text);
}

.state-panel__text {
  margin: 0 0 1rem;
  font-size: .875rem;
  color: var(--text-muted);
}

.state-panel__spinner {
  width: 2rem;
  height: 2rem;
  margin: 0 auto 1rem;
  border: 3px solid var(--brand-050);
  border-top-color: var(--brand-fill);
  border-radius: 50%;
  animation: wrx-spin .8s linear infinite;
}

@keyframes wrx-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .state-panel__spinner {
    animation-duration: 2.4s;
  }
}

/* Actions that must exist but must not compete. Only legible on a raised
   surface -- inside the panel here, never on the page background. */
.btn-quiet {
  padding: .5rem .75rem;
  min-height: var(--touch);
  font-size: .875rem;
  color: var(--text-muted);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
}

.btn-quiet:hover,
.btn-quiet:focus-visible {
  color: var(--brand-700);
  background: var(--brand-050);
}

.query-overlay {
  position: fixed;
  inset: 0;
  z-index: 1080;   /* above Bootstrap's fixed navbar (1030) and modal (1055) */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  background: rgba(16, 32, 64, .38);
}

.query-overlay[hidden] {
  display: none;
}

/* --------------------------------------------------------------------------
   10. Sign-in screens

   One narrow column, one obvious action. The ground is the brand navy rather
   than the generic #333 these pages used, so signing in looks like it belongs
   to the same product as the dashboard.
   -------------------------------------------------------------------------- */

.auth-shell {
  min-height: 100vh;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1rem;
  color: var(--text);
  background: var(--brand-900);
  background-image: linear-gradient(160deg, var(--brand-fill) 0%, var(--brand-900) 100%);
}

.auth-shell__main {
  width: 100%;
  max-width: 26rem;
}

.auth-brand {
  margin: 0 0 1rem;
  text-align: center;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: .01em;
  color: #fff;
}

.auth-card {
  padding: 1.75rem 1.5rem;
  background: var(--surface);
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgba(16, 32, 64, .18), 0 12px 32px rgba(16, 32, 64, .22);
}

.auth-card__title {
  margin: 0 0 .25rem;
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text);
}

/* One line saying what the screen will do. A password-reset form that just
   says "Password Reset" leaves the user guessing whether anything gets sent. */
.auth-card__lead {
  margin: 0 0 1.25rem;
  font-size: .875rem;
  color: var(--text-muted);
}

/* The primary action is the brand colour, not green. Green here was decorative
   -- signing in is not a "success", it is the thing the screen is for, and red
   and green are reserved for state. DESIGN.md section 2. */
.auth-card .btn-primary,
.auth-card input[type="submit"] {
  width: 100%;
  min-height: var(--touch);
  margin-top: .25rem;
  font-weight: 600;
  color: #fff;
  background-color: var(--brand-fill);
  border: 1px solid var(--brand-fill);
  border-radius: var(--radius-sm);
}

.auth-card .btn-primary:hover,
.auth-card input[type="submit"]:hover {
  filter: brightness(.9);
}

/* Actions that must exist but must not compete with the primary. */
.auth-alt {
  margin: 1rem 0 0;
  text-align: center;
  font-size: .8125rem;
}

.auth-alt a {
  color: var(--brand-700);
}

.auth-foot {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: .25rem 1rem;
  margin: 1.5rem 0 .25rem;
  font-size: .8125rem;
}

.auth-foot a {
  color: rgba(255, 255, 255, .75);
  text-decoration: none;
}

.auth-foot a:hover,
.auth-foot a:focus-visible {
  color: #fff;
  text-decoration: underline;
}

.auth-copyright {
  margin: 0;
  text-align: center;
  font-size: .75rem;
  color: rgba(255, 255, 255, .55);
}

/* --------------------------------------------------------------------------
   Signed-in account screens

   The same chrome as the dashboard, because these are part of the app. What
   they replace: the sign-in shell's dark #333 cover and 42em column, which was
   never the right shape for a store-profile form.
   -------------------------------------------------------------------------- */

.acct-body {
  margin: 0;
  min-height: 100vh;
  color: var(--text);
  background: var(--surface-sunken);
}

.acct-nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .5rem 1rem;
  padding: .625rem 1.25rem;
  background: var(--brand-fill);
}

.acct-nav__brand {
  font-size: 1rem;
  font-weight: 600;
  color: #fff;
  text-decoration: none;
}

.acct-nav__actions {
  display: flex;
  align-items: center;
  gap: .25rem;
}

/* A link and a form button side by side; the button is reset so the two match. */
.acct-nav__link {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  padding: .375rem .625rem;
  font-size: .875rem;
  color: rgba(255, 255, 255, .85);
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  text-decoration: none;
}

.acct-nav__link:hover,
.acct-nav__link:focus-visible {
  color: #fff;
  background: rgba(255, 255, 255, .12);
}

.acct-main {
  max-width: 48rem;
  margin: 0 auto;
  padding: 0 1.25rem 3rem;
}

.acct-main--wide {
  max-width: 64rem;
}

.acct-card {
  padding: 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}

/* A row of actions at the foot of a form: one primary, the rest quiet. These
   screens used to stack btn-success on btn-info on btn-warning, all full width,
   which told the user nothing about which one they came for.
   DESIGN.md section 2. */
.acct-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--border);
}

.acct-actions .btn {
  min-height: var(--touch);
}

.acct-actions__spacer {
  flex: 1 1 auto;
}

.acct-section-title {
  margin: 1.5rem 0 .625rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.acct-section-title:first-child {
  margin-top: 0;
}

/* Links that leave the form. Kept out of the field flow and demoted so they do
   not compete with the form's own primary action -- but they still have to look
   like controls, not like a stray line of underlined text. */
.acct-subnav {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin: 0 0 1.25rem;
}

.acct-subnav a {
  display: inline-flex;
  align-items: center;
  gap: .375rem;
  min-height: 2.25rem;
  padding: .375rem .75rem;
  font-size: .875rem;
  font-weight: 500;
  color: var(--brand-700);
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-sm);
  text-decoration: none;
}

.acct-subnav a::after {
  content: "";
  width: .4rem;
  height: .4rem;
  border-top: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  transform: rotate(45deg);
  opacity: .6;
}

.acct-subnav a:hover,
.acct-subnav a:focus-visible {
  color: var(--brand-900);
  background: var(--brand-050);
  border-color: var(--brand-500);
}

/* A count, an age or a number of days does not need the width of an address
   field. Capping these stops a "2" sitting alone in a 900px box.

   Matched on the ids Django generates rather than a class, because the widget
   classes on these forms are assigned in accounts/forms.py by
   bootstrap_widget_class(), which REPLACES the class attribute -- a class set
   in the field definition would not survive. */
.acct-card input[type="number"] {
  max-width: 11rem;
}

/* Short pickers: the appointment interval, and the start/end times in the hours
   grid (id_mondayStart, id_lunchEnd, ...). They should match each other rather
   than stretch to fill their column. */
.acct-card select[id$="Start"],
.acct-card select[id$="End"] {
  max-width: 9rem;
}

.acct-card #id_appointmentInterval {
  max-width: 11rem;
}

/* On a phone a capped field wastes the row instead of using it. */
@media (max-width: 575.98px) {
  .acct-card input[type="number"],
  .acct-card select[id$="Start"],
  .acct-card select[id$="End"],
  .acct-card #id_appointmentInterval {
    max-width: 100%;
  }
}

.acct-lead {
  margin: 0 0 1rem;
  font-size: .9375rem;
  color: var(--text-muted);
}

/* A card whose content supplies its own row padding. */
.acct-card--flush {
  padding: 0;
}

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

.plan {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem 1rem;
  padding: 1rem 1.5rem;
  border-bottom: 1px solid var(--border);
}

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

/* The plan you are on is stated, not just implied by a missing button. */
.plan--current {
  background: var(--brand-050);
}

.plan__info {
  display: flex;
  flex-direction: column;
  gap: .125rem;
  min-width: 0;
}

.plan__name {
  font-weight: 600;
  color: var(--text);
}

.plan__price {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.plan__per {
  margin-left: .25rem;
  font-size: .8125rem;
  font-weight: 400;
  color: var(--text-muted);
}

.plan__note {
  font-size: .8125rem;
  color: var(--brand-700);
}

/* Read-only facts. Not disabled inputs -- those render grey-on-grey and imply
   you could edit them. DESIGN.md section 2. */
.info-list {
  margin: 0 0 1.25rem;
  padding: 0;
  list-style: none;
}

.info-list li {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--border);
}

.info-list li:last-child {
  border-bottom: 0;
}

.info-list__label {
  flex: 0 0 6rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.info-list__value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.info-list__unit {
  margin-left: .25rem;
  font-size: .8125rem;
  font-weight: 400;
  color: var(--text-muted);
}

.acct-card .acct-lead:last-of-type {
  margin-bottom: 0;
}

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

.link-list li + li {
  border-top: 1px solid var(--border);
}

.link-list a {
  display: block;
  padding: .625rem .25rem;
  min-height: var(--touch);
  color: var(--brand-700);
  text-decoration: none;
}

.link-list a:hover,
.link-list a:focus-visible {
  color: var(--brand-900);
  background: var(--brand-050);
}

/* One store per row: a real checkbox, its name as the label, and when it was
   last updated. This was a two-column grid of bare fields with the label after
   the input. */
.store-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.store-list__row {
  display: flex;
  align-items: center;
  gap: .625rem;
  padding: .5rem .25rem;
  border-bottom: 1px solid var(--border);
}

.store-list__row:last-child {
  border-bottom: 0;
}

.store-list__check .form-check-input {
  margin: 0;
}

.store-list__name {
  flex: 1 1 auto;
  margin: 0;
  min-width: 0;
  font-weight: 500;
}

.store-list__meta {
  flex: 0 0 auto;
  font-size: .8125rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Theme control

   Three options rather than a two-way switch: "follow my device" is the default
   and a real choice, and flipping a binary toggle silently opts out of it for
   good.
   -------------------------------------------------------------------------- */

.theme-toggle__legend {
  display: block;
  margin-bottom: .375rem;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.theme-toggle {
  display: inline-flex;
  padding: .1875rem;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.theme-toggle__option {
  display: inline-flex;
  align-items: center;
  gap: .3125rem;
  padding: .3125rem .625rem;
  font-size: .8125rem;
  color: var(--text-muted);
  background: none;
  border: 0;
  border-radius: 999px;
  white-space: nowrap;
}

.theme-toggle__option:hover {
  color: var(--text);
}

/* The selected option is raised onto a surface pill, so the active state does
   not rest on colour alone. */
.theme-toggle__option.is-active {
  color: var(--brand-700);
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(16, 32, 64, .12);
}

/* On the navy account bar the control sits on a solid brand fill, where the
   default light-grey groove disappears. */
.acct-nav .theme-toggle {
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .18);
}

.acct-nav .theme-toggle__option {
  color: rgba(255, 255, 255, .8);
}

.acct-nav .theme-toggle__option:hover {
  color: #fff;
}

.acct-nav .theme-toggle__option.is-active {
  color: var(--brand-fill);
  background: #fff;
  box-shadow: none;
}

/* Below sm the labels are the first thing to go; the icons carry the meaning
   and the row would otherwise wrap the nav onto three lines. */
@media (max-width: 575.98px) {
  .acct-nav .theme-toggle__option {
    font-size: 0;
    padding: .375rem .5rem;
  }

  .acct-nav .theme-toggle__option .bi {
    font-size: 1rem;
  }
}

/* Wider than a sign-in form: prose, not fields. */
.auth-shell__main--wide {
  max-width: 34rem;
}

/* Long documents -- privacy policy, terms. Capped near 70 characters, which is
   the readable measure for continuous text, and left-aligned rather than the
   centred white-on-#333 these used to be. */
.auth-shell__main--doc {
  max-width: 52rem;
}

.auth-card--doc {
  padding: 2rem 2.25rem;
  text-align: left;
}

.doc-body {
  font-size: .9375rem;
  color: var(--text);
}

.doc-body h1 {
  margin: 2rem 0 .5rem;
  font-size: 1.125rem;
  font-weight: 600;
}

.doc-body h2 {
  margin: 1.5rem 0 .375rem;
  font-size: 1rem;
  font-weight: 600;
}

.doc-body p,
.doc-body li {
  line-height: 1.6;
  color: var(--text-muted);
}

.doc-body a {
  color: var(--brand-700);
}

.auth-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
  margin-top: 1.25rem;
}

.auth-actions .btn {
  flex: 1 1 auto;
  min-height: var(--touch);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

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

.contact-list li {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  padding: .5rem 0;
  border-bottom: 1px solid var(--border);
}

.contact-list li:last-child {
  border-bottom: 0;
}

.contact-list__label {
  flex: 0 0 4rem;
  font-size: .75rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.contact-list a {
  color: var(--brand-700);
  word-break: break-word;
}

/* The password rules Django enforces, shown before the field is submitted
   rather than as an error afterwards. */
.auth-rules {
  margin: -.25rem 0 1rem;
  padding-left: 1.1rem;
  font-size: .8125rem;
  color: var(--text-muted);
}

.auth-rules li + li {
  margin-top: .125rem;
}

/* --------------------------------------------------------------------------
  11. DataTables
   -------------------------------------------------------------------------- */

/* The per-column search row survives at every width. base.js mirrors column
   visibility onto its cells so it stays aligned with the table instead of
   overhanging, and each input keeps searching its own column. Per-column search
   is the only way to ask "location is A" -- the global box searches everything
   at once -- so hiding the row on narrow screens would remove a real capability
   rather than tidy one away. DESIGN.md section 8. */
tr.column-search-row th {
  padding-top: .25rem;
  padding-bottom: .25rem;
}

tr.column-search-row .form-control {
  min-width: 4rem;
  font-size: .75rem;
}

/* Expand control. The bootstrap5 Responsive stylesheet draws this as a text
   triangle glyph, not a shape -- so it only wants a colour. An earlier attempt
   here set a background-color, which painted a solid navy block behind that
   small glyph and read as a blue square.

   Selectors mirror the extension's own (.dtr-inline.collapsed) because a
   shorter one loses on specificity. */
table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control::before,
table.dataTable.dtr-inline.collapsed > tbody > tr > th.dtr-control::before {
  color: var(--text-faint);
}

table.dataTable.dtr-inline.collapsed > tbody > tr > td.dtr-control:hover::before {
  color: var(--brand-500);
}

/* Open rows get the stronger ink, so state reads by weight as well as by the
   glyph changing from a right-pointing to a down-pointing triangle. */
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td.dtr-control::before,
table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th.dtr-control::before {
  color: var(--brand-700);
}

ul.dtr-details {
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

ul.dtr-details > li {
  display: flex;
  gap: .5rem;
  padding: .3125rem 0;
  border-bottom: 1px solid var(--border);
  font-size: .8125rem;
}

ul.dtr-details > li:last-child {
  border-bottom: 0;
}

.dtr-title {
  flex: 0 0 9rem;
  font-weight: 600;
  color: var(--text-muted);
}

.dtr-data {
  flex: 1 1 auto;
  min-width: 0;
  font-variant-numeric: tabular-nums;
  word-break: break-word;
}

/* DataTables ships two stylesheets and you load one or the other: the standalone
   `jquery.dataTables.css` or a framework integration. This project previously
   loaded BOTH the standalone and the Bootstrap 4 integration; the upgrade
   correctly kept only `dataTables.bootstrap5.css`.

   What went with the standalone file was `.dataTables_wrapper { clear: both }`.
   The Bootstrap 5 integration styles the wrapper's *children* but never the
   wrapper itself. dashboard.css floats these tables (`table.clear { float:
   left }`), so with no clearfix the wrapper collapses to the height of its
   non-floated parts, the table escapes it, and the NEXT table's wrapper -- its
   button bar and search box -- is laid out on top of the previous table,
   swallowing clicks on that table's sortable headers.

   Symptom to recognise: sorting works on the LAST table on the page and no
   other. */
div.dataTables_wrapper {
  position: relative;
  clear: both;
}

div.dataTables_wrapper::after {
  display: block;
  clear: both;
  content: "";
}

/* Buttons renders the export controls as a Bootstrap .btn-group, which pulls
   adjacent buttons together with a -1px margin and squares off the inner
   corners, so the group reads as one undivided slab. Space them and give each
   its own border back. */
div.dt-buttons.btn-group {
  gap: .375rem;
  flex-wrap: wrap;
}

div.dt-buttons.btn-group > .btn,
div.dt-buttons.btn-group > div.btn-group > .btn {
  margin-left: 0 !important;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius) !important;
}

/* --------------------------------------------------------------------------
   The API reference

   The three API doc pages -- documentation, POS, HangRx -- read by integrators
   writing code against the API rather than by pharmacy staff reading numbers.
   That audience is why this is a documentation layout with a persistent index
   rather than the app shell: the task is "find getPatientProfile", and the old
   pages answered it with 2,480 lines and no table of contents.

   It replaces api.css, the last of the pre-Bootstrap-5 chrome. That file
   centred the whole app (`body { align-items: center }` plus `.text-center`),
   so every page opened with a `.text-start` div to undo it; it hard-coded #333
   on every link and heading; and it had no dark mode at all -- the code samples
   were `bg-dark text-light`, a Bootstrap utility that stays dark-on-dark when
   the theme flips.

   DESIGN.md section 13.
   -------------------------------------------------------------------------- */

:root {
  --docs-rail-w: 16.5rem;
}

.docs-body {
  margin: 0;
  min-height: 100vh;
  color: var(--text);
  background: var(--surface-sunken);
}

/* Keyboard users should not have to tab the whole index to reach the page.
   Off-screen until focused, then a real, visible control. */
.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 100;
  padding: .625rem 1rem;
  font-size: .875rem;
  color: #fff;
  background: var(--brand-fill);
  border-radius: 0 0 var(--radius-sm) 0;
}

.skip-link:focus {
  left: 0;
  top: 0;
  color: #fff;
}

/* ---- shell --------------------------------------------------------------- */

.docs-shell {
  padding: 0 1rem 3rem;
}

/* The index is collapsed behind this below lg and always open from lg up, so
   the button exists only at the widths where it does something. Sticky, so it
   is still reachable after scrolling into the middle of a long reference. */
.docs-rail-toggle {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: .5rem;
  width: 100%;
  min-height: var(--touch);
  padding: .625rem 1rem;
  font-size: .9375rem;
  font-weight: 600;
  color: var(--text);
  background: var(--surface);
  border: 0;
  border-bottom: 1px solid var(--border);
}

.docs-rail {
  display: none;
  padding: 1rem 0;
}

.docs-rail.is-open {
  display: block;
}

@media (min-width: 992px) {
  .docs-rail-toggle {
    display: none;
  }

  .docs-shell {
    display: grid;
    grid-template-columns: var(--docs-rail-w) minmax(0, 1fr);
    gap: 2.5rem;
    align-items: start;
    max-width: 84rem;
    margin: 0 auto;
    padding: 1.75rem 1.5rem 5rem;
  }

  /* Sticky rather than fixed, so the grid sizes the content column against it
     instead of guessing -- the same correction the app shell needed. The bar
     above is static and scrolls away, so the rail pins near the top. */
  .docs-rail {
    display: block;
    position: sticky;
    top: 1rem;
    max-height: calc(100vh - 2rem);
    padding: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

.docs-main {
  max-width: 62rem;
  padding: 1.25rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

@media (min-width: 992px) {
  .docs-main {
    padding: 2rem 2.25rem 3rem;
  }
}

/* ---- the index ----------------------------------------------------------- */

.docs-filter {
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .4375rem .75rem;
  margin-bottom: 1rem;
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-sm);
}

.docs-filter .bi {
  font-size: .875rem;
  color: var(--text-muted);
}

.docs-filter__input {
  width: 100%;
  min-width: 0;
  font-size: .875rem;
  color: var(--text);
  background: none;
  border: 0;
}

.docs-filter__input:focus {
  outline: none;
}

.docs-filter:focus-within {
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--brand-050);
}

.docs-nav__group {
  margin: 1.25rem 0 .375rem;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--text-muted);
}

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

.docs-nav__link {
  display: block;
  padding: .3125rem .625rem;
  font-size: .875rem;
  line-height: 1.35;
  color: var(--text-muted);
  text-decoration: none;
  border-left: 2px solid transparent;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.docs-nav__link:hover,
.docs-nav__link:focus-visible {
  color: var(--text);
  background: var(--surface);
}

/* Where you are in the reference. The bar carries it as well as the colour, so
   it does not rest on hue alone. */
.docs-nav__link.is-current {
  color: var(--brand-700);
  font-weight: 600;
  background: var(--brand-050);
  border-left-color: var(--brand-fill);
}

.docs-nav__empty {
  padding: .5rem .625rem;
  font-size: .8125rem;
  color: var(--text-muted);
}

/* A thumb needs more than a monitor does. DESIGN.md section 2. */
@media (max-width: 991.98px) {
  .docs-nav__link {
    min-height: var(--touch);
    padding: .6875rem .75rem;
  }
}

/* ---- sections ------------------------------------------------------------ */

.api-section__title,
.api-group {
  margin: 0 0 1rem;
  font-size: 1.375rem;
  font-weight: 700;
  color: var(--text);
}

/* A group heading opens a run of endpoints, so it needs air above it and a rule
   to separate it from the run before. */
.api-group {
  margin: 3.5rem 0 1.25rem;
  padding-top: 1.5rem;
  border-top: 2px solid var(--border);
}

.api-section {
  scroll-margin-top: 4.5rem;
}

.api-subhead {
  margin: 1.75rem 0 .625rem;
  font-size: .8125rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.docs-main p {
  max-width: 46rem;
  line-height: 1.6;
}

/* ---- one endpoint -------------------------------------------------------- */

.api-endpoint {
  padding-top: 1.5rem;
  margin-top: 2rem;
  border-top: 1px solid var(--border);
  scroll-margin-top: 4.5rem;
}

@media (min-width: 992px) {
  .api-section,
  .api-endpoint {
    scroll-margin-top: 1.5rem;
  }
}

.api-endpoint__head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .625rem;
}

.api-endpoint__name {
  margin: 0;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--text);
}

.api-endpoint__intro {
  margin: .75rem 0 0;
  color: var(--text-muted);
}

/* Who may call this. Only ever rendered for readers already in the group, so it
   is a note about scope, not a lock. */
.api-tag {
  padding: .125rem .5rem;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--brand-700);
  background: var(--brand-050);
  border-radius: 999px;
}

/* Method and URL are the identity of the endpoint, so they sit together on one
   line under the name. They used to be two <h5> sections of prose. */
.api-route {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin-top: .75rem;
  padding: .5rem .625rem;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.api-route__url {
  flex: 1 1 18rem;
  min-width: 0;
  font-size: .8125rem;
  color: var(--text);
  word-break: break-all;
}

/* Categorical, not a judgement: the word is always present, so the colour is a
   second cue for scanning rather than the only one. Green and red stay spent on
   direction elsewhere in the app. */
.api-method {
  padding: .1875rem .5rem;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .04em;
  border-radius: var(--radius-sm);
}

.api-method--get {
  color: var(--brand-700);
  background: var(--brand-050);
}

.api-method--post {
  color: var(--warn-700);
  background: var(--warn-050);
}

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

/* A five-column parameter table does not fit a phone, and the page must never
   scroll sideways as a whole -- so the table scrolls inside its own box.
   DESIGN.md section 8. */
.table-scroll {
  margin-top: .5rem;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.api-table {
  width: 100%;
  margin: 0;
  border-collapse: collapse;
  font-size: .875rem;
}

.api-table th {
  padding: .5rem .75rem;
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-align: left;
  white-space: nowrap;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border);
}

.api-table td {
  padding: .5rem .75rem;
  vertical-align: top;
  border-top: 1px solid var(--border);
}

.api-table tbody tr:first-child td {
  border-top: 0;
}

.api-table__type {
  white-space: nowrap;
  color: var(--text-muted);
}

/* A max length is a figure, and figures line up. DESIGN.md section 3. */
.api-table__num {
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--text-muted);
}

/* The name is what goes on the wire, so it is set as code and never wraps
   mid-identifier. */
.api-field {
  font-size: .8125rem;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
}

/* "Required" and "Optional" rather than Y and N: the old table made you work
   out which column the letter belonged to before it meant anything. */
.api-req,
.api-opt {
  display: inline-block;
  padding: .125rem .4375rem;
  font-size: .6875rem;
  font-weight: 600;
  white-space: nowrap;
  border-radius: var(--radius-sm);
}

.api-req {
  color: var(--warn-700);
  background: var(--warn-050);
}

.api-opt {
  color: var(--text-muted);
  background: var(--surface-sunken);
}

/* Accepted values for one parameter. It belongs on that row -- these used to be
   asterisks inside the parameter NAME pointing at a footnote under the table,
   so the name you copied was wrong. */
.api-note {
  display: block;
  margin-top: .25rem;
  font-size: .8125rem;
  color: var(--text-muted);
}

/* The fields of a nested object. Preformatted, because the shape is the point. */
.api-shape {
  margin: .375rem 0 0;
  padding: .5rem .625rem;
  font-size: .75rem;
  line-height: 1.5;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  white-space: pre-wrap;
}

.api-footnote {
  margin-top: .75rem;
  font-size: .8125rem;
  color: var(--text-muted);
}

/* ---- code ---------------------------------------------------------------- */

/* What this replaces: <p class="bg-dark text-light px-2"> with <br> at the end
   of every line. Copying one produced a single unindented run of text, and
   `bg-dark` is a fixed Bootstrap utility -- it stayed dark when the theme went
   dark, and put dark body text on it. A themed surface and a real <pre> fix
   both. */
.code-block {
  margin: .5rem 0 1.25rem;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.code-block__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: .375rem .75rem;
  border-bottom: 1px solid var(--border);
}

.code-block__label {
  font-size: .6875rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.code-block__body {
  margin: 0;
  padding: .75rem;
  overflow-x: auto;
  font-size: .8125rem;
  line-height: 1.55;
  color: var(--text);
}

.code-block__copy,
.api-route__copy {
  display: inline-flex;
  align-items: center;
  gap: .3125rem;
  padding: .25rem .5rem;
  font-size: .75rem;
  color: var(--text-muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  white-space: nowrap;
}

.code-block__copy:hover,
.code-block__copy:focus-visible,
.api-route__copy:hover,
.api-route__copy:focus-visible {
  color: var(--text);
  filter: brightness(.97);
}

.code-block__copy.is-copied,
.api-route__copy.is-copied {
  color: var(--accent-700);
  background: var(--accent-050);
  border-color: var(--accent-050);
}

/* Inline code in prose -- a field name, a file path. Not the code blocks, which
   set their own surface. */
.docs-main p > code,
.docs-main li > code {
  padding: .0625rem .3125rem;
  font-size: .8125rem;
  color: var(--brand-700);
  background: var(--brand-050);
  border-radius: var(--radius-sm);
}

/* --------------------------------------------------------------------------
   The patient booking pages

   The only screens in this product a PATIENT sees: book, check, booked, manage.
   Everything else here is a pharmacist reading dense figures on a monitor, so
   this is the one place where DESIGN.md's device priority inverts -- phone
   first, one task per screen, targets sized for a thumb rather than a cursor.

   It replaces form-validation.css, which set `.container { max-width: 960px }`
   for the whole app and drew the date and time cards in hard-coded #0d6efd
   Bootstrap blue on #fff -- so the grid stayed white when the theme went dark,
   and the selected slot was a blue that appears nowhere else in the product.

   DESIGN.md section 14.
   -------------------------------------------------------------------------- */

.booking-body {
  margin: 0;
  min-height: 100vh;
  color: var(--text);
  background: var(--surface-sunken);
}

.booking-shell {
  max-width: 42rem;
  margin: 0 auto;
  padding: 1.25rem 1rem 3rem;
}

/* ---- header -------------------------------------------------------------- */

.booking-head {
  text-align: center;
  padding: .5rem 0 1.5rem;
}

.booking-head__logo {
  display: block;
  margin: 0 auto .75rem;
  max-width: 60%;
  height: auto;
}

.booking-head__logo--default {
  width: 56px;
  opacity: .75;
}

.booking-head__title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text);
}

.booking-head__store {
  margin: .25rem 0 0;
  font-size: .9375rem;
  color: var(--text-muted);
}

.booking-head__lead {
  margin: .75rem 0 0;
  color: var(--text-muted);
}

.booking-alert {
  display: flex;
  gap: .625rem;
  padding: .875rem 1rem;
  margin-bottom: 1.25rem;
  font-size: .9375rem;
  color: var(--warn-700);
  background: var(--warn-050);
  border: 1px solid var(--warn-700);
  border-radius: var(--radius-md);
}

.booking-alert .bi {
  flex: none;
  margin-top: .125rem;
}

/* ---- steps --------------------------------------------------------------- */

.step {
  padding: 1.25rem;
  margin-bottom: 1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

/* A step the patient has not reached yet. Its heading stays visible so the
   shape of the whole task is legible from the first screen -- what is left is
   never a surprise -- but its controls are not reachable. */
.step.is-locked {
  background: none;
  border-style: dashed;
}

.step.is-locked .step__body {
  display: none;
}

.step.is-locked .step__title {
  color: var(--text-faint);
}

.step.is-locked .step__num {
  color: var(--text-faint);
  background: var(--surface-sunken);
}

.step__title {
  display: flex;
  align-items: center;
  gap: .625rem;
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text);
}

.step__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 1.75rem;
  height: 1.75rem;
  font-size: .875rem;
  color: #fff;
  background: var(--brand-fill);
  border-radius: 999px;
}

.step__body {
  margin-top: 1rem;
}

.step__hint {
  margin: 0 0 .75rem;
  font-size: .875rem;
  color: var(--text-muted);
}

/* ---- choosing a service -------------------------------------------------- */

.reason-list {
  display: grid;
  gap: .5rem;
}

/* The radio itself is the control; the card is its label. Keeping a real
   <input type="radio"> means keyboard and screen-reader behaviour come for
   free, which a div with a click handler would have had to reimplement. */
.reason-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: .75rem;
  min-height: 3.25rem;
  padding: .875rem 1rem;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-md);
}

.reason-card__input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.reason-card__body {
  flex: 1 1 auto;
  min-width: 0;
}

.reason-card__name {
  display: block;
  font-weight: 600;
  color: var(--text);
}

.reason-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: .3125rem;
  margin-top: .375rem;
}

/* What this service requires, said before it is chosen rather than after the
   form is filled in. */
.reason-tag {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  padding: .125rem .4375rem;
  font-size: .75rem;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
}

.reason-card__tick {
  flex: none;
  font-size: 1.25rem;
  color: var(--brand-fill);
  opacity: 0;
}

.reason-card:hover {
  border-color: var(--brand-500);
}

.reason-card:focus-within {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

.reason-card:has(.reason-card__input:checked) {
  border-color: var(--brand-fill);
  background: var(--brand-050);
}

.reason-card:has(.reason-card__input:checked) .reason-card__tick {
  opacity: 1;
}

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

.field {
  margin-bottom: 1rem;
}

.field__label {
  display: block;
  margin-bottom: .3125rem;
  font-size: .875rem;
  font-weight: 600;
  color: var(--text);
}

.field__optional {
  font-weight: 400;
  color: var(--text-muted);
}

.field__note,
.field__error {
  margin: .3125rem 0 0;
  font-size: .8125rem;
}

.field__note { color: var(--text-muted); }
.field__error { color: var(--danger-700); }

.field-grid {
  display: grid;
  gap: 0 .75rem;
  grid-template-columns: 1fr;
}

@media (min-width: 576px) {
  .field-grid {
    grid-template-columns: 1fr 1fr;
  }
}

.id-fields {
  display: grid;
  gap: 0 .75rem;
  grid-template-columns: 1fr;
}

@media (min-width: 576px) {
  .id-fields {
    grid-template-columns: 1fr 1fr;
  }
}

/* A patient filling this in on a phone, once, possibly standing up. Bigger
   than the dashboard's controls on purpose -- DESIGN.md section 2 sizes for
   the device the screen is actually used on, and this one is not a monitor. */
.booking-form .form-control,
.booking-form .form-select {
  min-height: 3rem;
  font-size: 1rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-sm);
}

.booking-form .form-control:focus,
.booking-form .form-select:focus {
  border-color: var(--brand-500);
  box-shadow: 0 0 0 3px var(--brand-050);
}

.booking-form textarea.form-control {
  min-height: 4.5rem;
}

.id-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .75rem;
  margin-top: .25rem;
}

.id-actions .btn {
  min-height: 3rem;
  padding-inline: 1.25rem;
}

.id-error {
  margin: .875rem 0 0;
  padding: .75rem .875rem;
  font-size: .875rem;
  color: var(--danger-700);
  background: var(--danger-050);
  border-radius: var(--radius-sm);
}

/* ---- the identified patient ---------------------------------------------- */

/* Ten prefilled fields on a phone is a wall to scroll past. The patient only
   needs to confirm we found the right person; the fields stay one tap away. */
.patient-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  padding: 1rem;
  background: var(--accent-050);
  border: 1px solid var(--accent-700);
  border-radius: var(--radius-md);
}

.patient-card__name {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text);
}

.patient-card__meta {
  margin: .125rem 0 0;
  font-size: .875rem;
  color: var(--text-muted);
}

/* ---- dates and times ----------------------------------------------------- */

.appt-date-grid,
.appt-time-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.5rem, 1fr));
  gap: .5rem;
  margin-bottom: 1rem;
}

.date-card,
.time-slot {
  display: block;
  min-height: var(--touch);
  padding: .5rem .25rem;
  text-align: center;
  cursor: pointer;
  user-select: none;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-sm);
  font-variant-numeric: tabular-nums;
}

.date-card:hover,
.time-slot:hover {
  border-color: var(--brand-500);
}

.date-card:focus-visible,
.time-slot:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

.date-card.selected,
.time-slot.selected {
  color: #fff;
  background: var(--brand-fill);
  border-color: var(--brand-fill);
}

.date-card-dow,
.date-card-month {
  display: block;
  font-size: .75rem;
  color: var(--text-muted);
}

.date-card-dow {
  text-transform: uppercase;
  letter-spacing: .03em;
}

.date-card-day {
  display: block;
  font-size: 1.125rem;
  font-weight: 700;
}

/* On a selected card the muted grey would sit on brand fill at about 2:1. */
.date-card.selected .date-card-dow,
.date-card.selected .date-card-month {
  color: rgba(255, 255, 255, .85);
}

.time-slot {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .9375rem;
}

.date-pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: .75rem;
  margin-bottom: 1rem;
}

.date-pager__label {
  font-size: .8125rem;
  color: var(--text-muted);
}

.health-note {
  padding: .875rem 1rem;
  margin-bottom: 1rem;
  font-size: .875rem;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
}

/* ---- submitting ---------------------------------------------------------- */

.booking-submit {
  padding: 1.25rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

/* What is about to be booked, in words, next to the button that books it. */
.booking-summary {
  margin: 0 0 .875rem;
  padding: .75rem .875rem;
  font-size: .9375rem;
  font-weight: 600;
  color: var(--brand-700);
  background: var(--brand-050);
  border-radius: var(--radius-sm);
  text-align: center;
}

.booking-submit .btn-lg {
  min-height: 3.25rem;
  font-size: 1.0625rem;
}

.booking-submit__alt {
  display: block;
  margin: .875rem 0 0;
  text-align: center;
}

/* ---- existing appointments ----------------------------------------------- */

.appt-list {
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
  display: grid;
  gap: .5rem;
}

.appt-pick {
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  padding: .875rem 1rem;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-md);
}

.appt-pick__input {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  margin-top: .125rem;
}

.appt-pick__when {
  display: block;
  font-weight: 600;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.appt-pick__what {
  display: block;
  margin-top: .1875rem;
  font-size: .875rem;
  color: var(--text-muted);
}

.appt-pick:has(.appt-pick__input:checked) {
  border-color: var(--danger-700);
  background: var(--danger-050);
}

/* ---- the landing page ---------------------------------------------------- */

.manage-choices {
  display: grid;
  gap: .75rem;
}

.manage-choice {
  display: block;
  padding: 1.25rem;
  text-align: center;
  text-decoration: none;
  background: var(--surface);
  border: 1px solid var(--control-border);
  border-radius: var(--radius-lg);
}

.manage-choice:hover,
.manage-choice:focus-visible {
  border-color: var(--brand-fill);
  background: var(--brand-050);
}

.manage-choice__icon {
  display: block;
  font-size: 1.75rem;
  color: var(--brand-700);
}

.manage-choice__label {
  display: block;
  margin-top: .5rem;
  font-size: 1.0625rem;
  font-weight: 700;
  color: var(--text);
}

.manage-choice__hint {
  display: block;
  margin-top: .25rem;
  font-size: .875rem;
  color: var(--text-muted);
}

/* ---- the pharmacy, and the footer ---------------------------------------- */

/* After the task, not before it. This was a col-md-4 that `order-md-2` floated
   right of the form on a monitor and stacked ABOVE it on a phone -- so on the
   device most patients use, the first thing on screen was the opening hours and
   the form started a screen down. */
.booking-store {
  padding: 1.25rem;
  margin-top: 1.5rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.booking-store__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
}

.booking-store__line {
  margin: .25rem 0 .75rem;
  font-size: .875rem;
  color: var(--text-muted);
}

.booking-store__actions {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem;
}

.booking-store__actions .btn-quiet {
  min-height: var(--touch);
}

.booking-hours {
  margin-top: 1rem;
  border-top: 1px solid var(--border);
  padding-top: .75rem;
}

.booking-hours__summary {
  min-height: var(--touch);
  display: flex;
  align-items: center;
  font-size: .875rem;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}

.booking-hours__list {
  margin: .25rem 0 0;
}

.booking-hours__row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: .25rem 0;
  font-size: .8125rem;
  color: var(--text-muted);
}

.booking-hours__row dt,
.booking-hours__row dd {
  margin: 0;
  font-weight: 400;
}

.booking-hours__row dd {
  font-variant-numeric: tabular-nums;
}

.booking-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: .5rem 1rem;
  margin-top: 2rem;
  font-size: .8125rem;
}

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

.booking-foot__copy {
  flex: 1 0 100%;
  margin: 0;
  text-align: center;
  color: var(--text-faint);
}

/* Everything that failed validation, listed once at the top of the booking
   form. On a stepped form the offending field can sit inside a step the
   patient has scrolled past, so a message beside the input is not enough by
   itself. */
.form-errors {
  padding: .875rem 1rem;
  margin-bottom: 1.25rem;
  color: var(--danger-700);
  background: var(--danger-050);
  border: 1px solid var(--danger-700);
  border-radius: var(--radius-md);
}

.form-errors__title {
  margin: 0 0 .375rem;
  font-weight: 700;
}

.form-errors__list {
  margin: 0;
  padding-left: 1.125rem;
  font-size: .875rem;
}

/* A matched patient record with a gap in it. Sits under the confirmation card
   and names what the pharmacy does not have, rather than letting the patient
   submit into a validation error. */
.patient-missing {
  margin: .625rem 0 1rem;
  padding: .625rem .875rem;
  font-size: .875rem;
  color: var(--warn-700);
  background: var(--warn-050);
  border-radius: var(--radius-sm);
}

/* --------------------------------------------------------------------------
   The theme control on the two shells that carry it in a footer rather than a
   nav bar. Both stamped a theme from the device and offered no way to override
   it; on the booking pages theme.js was even loaded with nothing to drive.
   -------------------------------------------------------------------------- */

/* Its own row, above the links, so the pill keeps its own width instead of
   being stretched across the footer by the flex row. */
.booking-foot__theme {
  flex: 1 0 100%;
  display: flex;
  justify-content: center;
  margin-bottom: .375rem;
}

/* The page ground is the same token the control's own groove uses, so on the
   booking pages it is raised onto a surface to read as a control at all. */
.booking-foot .theme-toggle {
  background: var(--surface);
}

/* The sign-in shell is a navy gradient, where the default light-grey groove
   disappears -- the same problem the account nav bar has, and the same fix. */
.auth-shell .theme-toggle {
  display: flex;
  width: fit-content;
  margin: 1.5rem auto 0;
  background: rgba(255, 255, 255, .12);
  border-color: rgba(255, 255, 255, .18);
}

.auth-shell .theme-toggle__option {
  color: rgba(255, 255, 255, .8);
}

.auth-shell .theme-toggle__option:hover {
  color: #fff;
}

.auth-shell .theme-toggle__option.is-active {
  color: var(--brand-fill);
  background: #fff;
  box-shadow: none;
}
