/* =============================================================================
   ParityBoard - modernisation layer over NHS.UK Frontend.

   Loaded AFTER /assets/nhsuk.css. NHS.UK provides the base layer: focus
   management, responsive behaviour, semantic tag/table styling, and real
   accessibility work. This file rebinds the VISUAL layer on top of it, so the
   nhsuk-* class names used across every route keep working.

   Two rules govern everything below:
     1. Every colour, size, radius and shadow is a token on :root. No raw hex
        in a component rule.
     2. NHS.UK Frontend 9.x compiles its Sass colour variables to literal hex -
        it ships NO --nhsuk-* custom properties. So "rebind their custom
        properties" is not available; we redeclare the same values as our own
        tokens, shadow them in the override rules, and keep the NHS semantic
        meaning (green=success, amber=warning, red=danger, blue=info) intact.

   Dark mode re-binds the same token names. There is deliberately no second
   stylesheet and no separate dark class.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   1. DESIGN TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* --- Neutral ramp (NHS.UK greys, extended into a usable ramp) --- */
  --pb-n-0:   #ffffff;
  --pb-n-25:  #f7f9fa;
  --pb-n-50:  #f0f4f5;  /* NHS.UK grey-5: row hover, table stripe */
  --pb-n-100: #e8edef;
  --pb-n-200: #d8dde0;  /* NHS.UK grey-4: borders, footer */
  --pb-n-300: #c3cbd1;
  --pb-n-400: #aeb7bd;  /* NHS.UK grey-3 */
  --pb-n-500: #768692;  /* NHS.UK grey-2: muted */
  --pb-n-600: #4c6272;  /* NHS.UK grey-1: input borders, secondary text */
  --pb-n-700: #354550;  /* NHS.UK tag grey border/text */
  --pb-n-800: #212b32;  /* NHS.UK text default */
  --pb-n-900: #131a1f;

  /* --- Brand: teal anchor, coral accent, cream surface, ink text ---
     The artwork is teal + coral on cream. Teal replaces NHS blue as the
     primary anchor so the mark and the chrome agree; the NHS *structural*
     inheritance (contrast first, loud focus, plain language, big targets)
     is untouched. See docs/adr/0009-brand-palette-and-name.md.

     Coral is an ACCENT ONLY. At 2.16:1 on cream it fails AA for text and at
     3.02:1 on teal it clears only the 3:1 graphics threshold (WCAG 1.4.11).
     Never body text, never small text, never white on a coral fill, never the
     sole carrier of a status. Rules, dividers, illustrative marks only. --- */
  --pb-teal:        #1F6177;  /* brand primary */
  --pb-teal-dark:   #164B5C;  /* darker teal for hover/pressed states */
  --pb-teal-darker: #0F3A47;  /* deepest, for depth and borders */
  --pb-teal-deep:   #16323D;  /* dark-mode teal surface */
  --pb-teal-tint:   #E3EDF0;  /* teal wash, AA with --pb-teal-dark on top */
  --pb-teal-tint-2: #F1F6F7;
  --pb-coral:       #F4926D;  /* decorative accent, 3.02:1 on teal - graphics only */
  --pb-cream:       #FBF8F5;  /* the brand surface */
  --pb-ink:         #353535;  /* body text on cream, 11.59:1 */

  /* --- Brand: the blue ramp. Still the "blue" semantic/web-link identity in
         dark mode, but the LIGHT anchor is now teal so the header band, links
         and primary buttons all carry the brand. --- */
  --pb-blue:        #1F6177;  /* was #005eb8 NHS blue - now the brand teal */
  --pb-blue-dark:   #164B5C;  /* AA on cream and on the teal tint */
  --pb-blue-darker: #0F3A47;
  --pb-blue-tint:   #E3EDF0;  /* was NHS #ccdff1 */
  --pb-blue-tint-2: #F1F6F7;
  --pb-blue-contrast: #ffffff;

  /* --- Semantic: meanings NHS staff already read at a glance --- */
  --pb-green:        #007f3b;  /* success / done */
  --pb-green-dark:   #004c23;
  --pb-green-tint:   #cce5d8;
  --pb-amber:        #ed8b00;  /* warning / blocked */
  --pb-amber-strong: #ffeb3b;  /* NHS.UK focus yellow - deliberately loud */
  --pb-amber-dark:   #6b4a00;
  --pb-amber-tint:   #fff9c4;
  --pb-red:          #d5281b;  /* danger / overdue */
  --pb-red-dark:     #6b140e;
  --pb-red-tint:     #f7d4d1;
  --pb-grey:         #dbe0e3;
  --pb-grey-dark:    #354550;

  /* --- Surface / text roles. Components reference these, never the ramps.
         Page and sunken surfaces are CREAM, not the grey ramp: cream is the
         brand surface and the artwork is drawn for it. Cards stay white so
         they lift off the cream rather than dissolving into it. --- */
  --pb-page:            var(--pb-cream);
  --pb-surface:         var(--pb-n-0);
  --pb-surface-sunken:  var(--pb-cream);
  --pb-surface-raised:  var(--pb-n-0);
  --pb-text:            var(--pb-ink);    /* #353535 on cream = 11.59:1 */
  --pb-text-muted:      var(--pb-n-600);
  --pb-text-inverse:    var(--pb-n-0);
  --pb-border:          var(--pb-n-200);  /* ONE border colour, used everywhere */
  --pb-border-strong:   var(--pb-n-400);
  --pb-border-input:    var(--pb-n-600);

  /* --- Workstream accents. Status/workstream is NEVER colour alone -
         every one of these is paired with a text label in the markup. --- */
  --pb-ws-general:       var(--pb-n-500);
  --pb-ws-clinical:      var(--pb-blue);
  --pb-ws-housekeeping:  var(--pb-green);
  --pb-ws-maintenance:   var(--pb-amber);
  --pb-ws-compliance:    var(--pb-red);

  /* --- Type scale. NHS.UK shouts (48px h1 in a dense operational tool);
         this tightens the top end while keeping hierarchy obvious. --- */
  --pb-font: ui-sans-serif, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --pb-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --pb-text-xs:   0.75rem;   /* 12px - meta, table labels */
  --pb-text-s:    0.875rem;  /* 14px - dense UI text, nav */
  --pb-text-base: 1rem;      /* 16px - body floor */
  --pb-text-m:    1.0625rem; /* 17px - comfortable body */
  --pb-text-l:    1.25rem;   /* 20px - h3 */
  --pb-text-xl:   1.5rem;    /* 24px - h2 */
  --pb-text-2xl:  1.875rem;  /* 30px - h1 (NHS used 48px) */
  --pb-leading-tight: 1.25;
  --pb-leading-snug:  1.4;
  --pb-leading-body:  1.6;

  /* --- Spacing scale, 4px base --- */
  --pb-space-1: 0.25rem;  /*  4px */
  --pb-space-2: 0.5rem;   /*  8px */
  --pb-space-3: 0.75rem;  /* 12px */
  --pb-space-4: 1rem;     /* 16px */
  --pb-space-5: 1.5rem;   /* 24px */
  --pb-space-6: 2rem;     /* 32px */
  --pb-space-7: 3rem;     /* 48px */
  --pb-space-8: 4rem;     /* 64px */

  /* --- Radii: softens NHS.UK's hard squares, stays restrained --- */
  --pb-radius-s: 4px;
  --pb-radius-m: 6px;
  --pb-radius-l: 8px;
  --pb-radius-pill: 999px;

  /* --- Elevation --- */
  --pb-shadow-s: 0 1px 2px rgba(19, 26, 31, 0.06);
  --pb-shadow-m: 0 1px 3px rgba(19, 26, 31, 0.10), 0 1px 2px rgba(19, 26, 31, 0.06);
  --pb-shadow-l: 0 4px 12px rgba(19, 26, 31, 0.10), 0 1px 3px rgba(19, 26, 31, 0.06);

  /* --- Focus. NHS.UK's yellow indicator is kept - it is one of its best
         accessibility decisions and must stay loud. --- */
  --pb-focus: var(--pb-amber-strong);
  --pb-focus-ring: var(--pb-n-800);

  /* --- Motion --- */
  --pb-dur-fast: 120ms;
  --pb-dur: 160ms;
  --pb-ease: cubic-bezier(0.2, 0, 0.2, 1);

  /* --- Layout --- */
  --pb-prose-max: 44em;
  --pb-page-max: 1200px;
  /* Wider cap for the 7-day week grid, which needs horizontal room that prose
     does not. See .pb-week__grid. */
  --pb-week-max: 1600px;
}

/* -----------------------------------------------------------------------------
   2. DARK MODE - re-binds the SAME token names. No second stylesheet.

   Only the neutral ramp and the surface/text role tokens move. The semantic
   brand colours keep their MEANING: green stays green, red stays red. They are
   lifted off pure NHS values only as far as is needed to reach contrast
   against a dark surface - they are not inverted or desaturated.
   -------------------------------------------------------------------------- */
@media (prefers-color-scheme: dark) {
  :root {
    --pb-n-0:   #0d1117;
    --pb-n-25:  #11171e;
    --pb-n-50:  #161c24;
    --pb-n-100: #1c242d;
    --pb-n-200: #2b3540;   /* border on dark */
    --pb-n-300: #3b4753;
    --pb-n-400: #556472;
    --pb-n-500: #8b98a5;
    --pb-n-600: #aeb9c4;
    --pb-n-700: #c6cfd8;
    --pb-n-800: #e8edf2;   /* body text on dark */
    --pb-n-900: #f4f7fa;

    /* Brand teal is lightened for use as TEXT/LINK on dark. Measured against
       every dark surface: #5CAFC6 gives 7.57 / 7.21 / 6.86 on n-0 / n-25 / n-50
       and 5.38 on the dark tint - so it clears 4.5:1 body text everywhere, and
       beats the NHS-blue link it replaces (6.11) rather than regressing it.
       The darker tints stay tints so tags keep their light-chip appearance. */
    --pb-teal:          #2A7A93;  /* filled surfaces on dark, not text */
    --pb-teal-dark:     #5CAFC6;  /* text/link teal on dark */
    --pb-teal-darker:   #8ACBDB;
    --pb-teal-deep:     #0E2A33;
    --pb-teal-tint:     #10333D;
    --pb-teal-tint-2:   #0C2731;
    --pb-coral:         #F4926D;  /* unchanged: still an accent, never text */
    --pb-cream:         #FBF8F5;  /* a light surface; only used as such */
    --pb-ink:           #353535;  /* light-mode ink; dark text uses the ramp */

    --pb-blue:          #5CAFC6;  /* brand teal, lightened for dark surfaces */
    --pb-blue-dark:     #8ACBDB;
    --pb-blue-darker:   #B0DCE8;
    --pb-blue-tint:     #10333D;
    --pb-blue-tint-2:   #0C2731;
    --pb-blue-contrast: #ffffff;

    /* Semantic: kept as recognisable colours, adjusted for dark contrast. */
    --pb-green:        #34c26b;
    --pb-green-dark:   #7ce0a3;
    --pb-green-tint:   #12331f;
    --pb-amber:        #ffab33;
    --pb-amber-strong: #ffeb3b;  /* focus yellow is unchanged - it must stay loud */
    --pb-amber-dark:   #ffd27a;
    --pb-amber-tint:   #332708;
    --pb-red:          #ff6b5e;
    --pb-red-dark:     #ff9d94;
    --pb-red-tint:     #3a1512;
    --pb-grey:         #2b3540;
    --pb-grey-dark:    #c6cfd8;

    --pb-surface:         var(--pb-n-25);
    --pb-surface-sunken:  var(--pb-n-0);
    --pb-surface-raised:  var(--pb-n-50);
    --pb-page:            var(--pb-n-0);
    --pb-text:            var(--pb-n-800);
    --pb-text-muted:      var(--pb-n-600);
    --pb-border:          var(--pb-n-200);
    --pb-border-strong:   var(--pb-n-300);
    --pb-border-input:    var(--pb-n-400);

    --pb-shadow-s: 0 1px 2px rgba(0, 0, 0, 0.4);
    --pb-shadow-m: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
    --pb-shadow-l: 0 4px 12px rgba(0, 0, 0, 0.5), 0 1px 3px rgba(0, 0, 0, 0.4);

    /* On a dark surface the focus ring must read against the page, not against
       the yellow. NHS pairs yellow with dark text; we pair it with the ramp. */
    --pb-focus-ring: var(--pb-n-800);
  }
}

/* -----------------------------------------------------------------------------
   1b. COLOUR THEMES  (Piece 3)

   A theme rebinds TOKENS. It is not a new stylesheet, and no component rule
   below knows a theme exists - every one of them already reads the semantic
   names (--pb-page, --pb-surface, --pb-text, --pb-border) defined in section 1.

   The four shipped themes are Default, High contrast, Bright and Soft. Each
   sets data-theme on <html>, which layout() emits from the user's saved
   preference (the dashboard_preference table). Light and dark are still chosen
   by the SYSTEM via prefers-color-scheme - a theme picks the palette, the OS
   picks the mode. That is why each theme has two blocks: one for light, one
   inside the dark media query.

   WHAT A THEME MAY AND MAY NOT CHANGE
     - It MAY change surfaces, text, borders, and the saturation of the
       semantic colours.
     - It MAY NOT change what a colour MEANS. Green is done, red is overdue,
       amber is blocked, in every theme.
     - Coral stays decorative in every theme. At 2.16:1 on cream it fails AA
       for text (ADR 0009), and a "bright" theme is exactly where someone would
       be tempted to promote it. The Bright theme below darkens the accent
       instead of brightening it.
     - Status is never carried by colour alone, in any theme.
   -------------------------------------------------------------------------- */

/* --- High contrast: maximum legibility. ---------------------------------------
   The one that matters most. Care staff are often older, on cheap tablets, in
   poorly lit corridors at 3am. Everything is pushed to the extremes: pure black
   on pure white, borders at full strength so boxes do not rely on a subtle
   shade difference. */
[data-theme='high-contrast'] {
  --pb-page:           #ffffff;
  --pb-surface:        #ffffff;
  --pb-surface-sunken: #ffffff;
  --pb-surface-raised: #ffffff;
  --pb-text:           #000000;
  --pb-text-muted:     #1a1a1a;
  --pb-text-inverse:   #ffffff;
  --pb-border:         #000000;
  --pb-border-strong:  #000000;
  --pb-border-input:   #000000;
  /* Saturation raised, hue and meaning untouched. */
  --pb-green:          #00602c;
  --pb-amber:          #8a4b00;
  --pb-red:            #b81c11;
  --pb-blue:           #0d4f63;
  --pb-teal:           #0d4f63;
  --pb-blue-tint:      #e6e6e6;
  --pb-blue-tint-2:    #f2f2f2;
}
@media (prefers-color-scheme: dark) {
  [data-theme='high-contrast'] {
    --pb-page:           #000000;
    --pb-surface:        #000000;
    --pb-surface-sunken: #000000;
    --pb-surface-raised: #000000;
    --pb-text:           #ffffff;
    --pb-text-muted:     #f0f0f0;
    --pb-text-inverse:   #000000;
    --pb-border:         #ffffff;
    --pb-border-strong:  #ffffff;
    --pb-border-input:   #ffffff;
    --pb-green:          #5ce08a;
    --pb-amber:          #ffc266;
    --pb-red:            #ff8a80;
    --pb-blue:           #7fd4e8;
    --pb-teal:           #7fd4e8;
    --pb-blue-tint:      #1a1a1a;
    --pb-blue-tint-2:    #262626;
  }
}

/* --- Bright: stronger, more saturated panels and borders. ---------------------
   This is what the request actually asked for - boxes and cards that separate
   clearly. Separation comes from BORDER strength and panel tint, not from
   lighter text or a louder accent: coral is darkened here, never promoted. */
[data-theme='bright'] {
  --pb-page:           #ffffff;
  --pb-surface:        #ffffff;
  --pb-surface-sunken: #eef3f5;
  --pb-surface-raised: #ffffff;
  --pb-text:           #1a2228;
  --pb-text-muted:     #3d4d59;
  --pb-border:         #7a8f9d;   /* clears 3:1 on white (was #d8dde0, which did not) */
  --pb-border-strong:  #5a6f7d;
  --pb-border-input:   #3d4d59;
  --pb-teal:           #14536a;
  --pb-blue:           #14536a;
  --pb-teal-tint:      #d3e4ea;
  --pb-blue-tint:      #d3e4ea;
  --pb-blue-tint-2:    #e8f1f4;
  --pb-green:          #006b32;
  --pb-amber:          #a85c08;
  --pb-red:            #c02218;
  --pb-coral:          #d9744d;   /* darkened: stays decorative, never text */
}
@media (prefers-color-scheme: dark) {
  [data-theme='bright'] {
    --pb-page:           #0b1015;
    --pb-surface:        #131a21;
    --pb-surface-sunken: #0b1015;
    --pb-surface-raised: #1a232c;
    --pb-text:           #ffffff;
    --pb-text-muted:     #c2ccd6;
    --pb-border:         #64757f;   /* clears 3:1 on the dark surface */
    --pb-border-strong:  #7d8f9d;
    --pb-border-input:   #8b9dab;
    --pb-teal:           #6cc4db;
    --pb-blue:           #6cc4db;
    --pb-green:          #6ce39a;
    --pb-amber:          #ffc46b;
    --pb-red:            #ff9b92;
    --pb-coral:          #f2a184;
  }
}

/* --- Soft: lower glare, for night shift. --------------------------------------
   Warm, desaturated, low-contrast-but-still-AA. Deliberately NOT dim text: the
   brief permits lower glare, not lower legibility, and every pair below still
   clears 4.5:1. */
[data-theme='soft'] {
  --pb-page:           #f4f1ec;
  --pb-surface:        #faf8f4;
  --pb-surface-sunken: #efeae2;
  --pb-surface-raised: #faf8f4;
  --pb-text:           #3a3530;
  --pb-text-muted:     #5c554d;
  --pb-border:         #8f8577;   /* 1.62 was nowhere near 3:1; calm, not invisible */
  --pb-border-strong:  #a99e90;
  --pb-border-input:   #6b635a;
  --pb-teal:           #2a5a68;
  --pb-blue:           #2a5a68;
  --pb-teal-tint:      #e2e6e2;
  --pb-blue-tint:      #e2e6e2;
  --pb-blue-tint-2:    #eceeea;
  --pb-green:          #2c6b45;
  --pb-amber:          #8a5a12;
  --pb-red:            #a83a2c;
  --pb-coral:          #c98a69;   /* muted; still decorative only */
}
@media (prefers-color-scheme: dark) {
  [data-theme='soft'] {
    --pb-page:           #1b1d1f;
    --pb-surface:        #232628;
    --pb-surface-sunken: #17191b;
    --pb-surface-raised: #2a2e31;
    --pb-text:           #ded9d2;
    --pb-text-muted:     #b3aca3;
    --pb-border:         #6b7176;   /* 1.65 was nowhere near 3:1 */
    --pb-border-strong:  #5f666b;
    --pb-border-input:   #7b8288;
    --pb-teal:           #8fc4d0;
    --pb-blue:           #8fc4d0;
    --pb-green:          #8fce9f;
    --pb-amber:          #d9b46a;
    --pb-red:            #e59a90;
    --pb-coral:          #cf9c86;
  }
}

/* --- Reduced motion -----------------------------------------------------------
   Piece 3 requires any transition to respect this. The theme switch is the only
   place the UI animates a colour, and this is also the accessible default for a
   user who has asked the system for less movement. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}


/* -----------------------------------------------------------------------------
   3. BASE
   -------------------------------------------------------------------------- */
/*
 * `html` carries the page colour as well as `body`, and both are needed.
 *
 * NHS.UK sets a grey canvas on `html` (#d8dde0). The body only covers its own
 * content box, so whenever the document is shorter than the viewport - or the
 * footer ends above the fold - that grey shows through underneath as a band.
 * It was visible below the footer in BOTH schemes and glaring in dark mode,
 * where a light grey strip sat under a near-black page.
 *
 * Painting `html` too means the root canvas is cream (light) or near-black
 * (dark) no matter how short the content is, and there is no strip to show.
 *
 * The colour was only ever half the problem. On a short page - an error, a
 * confirmation, anything with little content - the footer still sat directly
 * under the last paragraph, floating in the middle of the screen with blank
 * space beneath it. The band was hidden; the layout was still wrong.
 *
 * The fix is the standard sticky footer: the body is at least as tall as the
 * viewport and laid out as a column, and the main region is allowed to grow so
 * the footer is pushed to the bottom EDGE rather than the bottom of the text.
 * `min-height: 100dvh` is used on the body with `100vh` kept first as the
 * fallback, because `dvh` accounts for mobile browser chrome that `vh` ignores
 * (with `vh` alone the footer sits below the visible area on a phone until the
 * URL bar retracts).
 *
 * Marks the skip link, header, main and footer all as flex items. Only `main`
 * grows, so the header keeps its natural height at the top and the footer is
 * pinned to the foot however short the content is.
 */
html {
  -webkit-text-size-adjust: 100%;
  background-color: var(--pb-page);
  /* Reserve the vertical scrollbar's width whether or not it is present.
     Without this, a short page (no scrollbar) and a long one (scrollbar) put
     the centred container at different x positions, so the tab bar jumped
     sideways as you moved between tabs of different length. */
  scrollbar-gutter: stable;
}

body {
  background-color: var(--pb-page);
  color: var(--pb-text);
  font-family: var(--pb-font);
  font-size: var(--pb-text-base);
  line-height: var(--pb-leading-body);
  /* Kill NHS.UK's blue-underline-everything default; links opt in explicitly. */
  text-underline-offset: 0.15em;
  /* Sticky footer. 100vh first as the fallback for browsers without dvh. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/*
 * The main region absorbs the leftover height so the footer is pushed down.
 *
 * `flex: 1 0 auto` rather than `flex: 1`: the basis is `auto`, so a page with
 * long content still grows past the viewport and scrolls normally instead of
 * being squeezed. The main wrapper is nested inside `.nhsuk-width-container`,
 * which is why that wrapper is the flex item that grows - the container itself
 * must NOT grow, or it would stretch the centred content column.
 */
body > .nhsuk-width-container {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
}

body > .nhsuk-width-container > .nhsuk-main-wrapper {
  flex: 1 0 auto;
}

body > .nhsuk-width-container {
  /* Stretch to the body's full width so the page gutter is the same on every
     page. `body` is a flex column (header/footer pinning, above), and a flex
     item in a column does NOT stretch along the main axis - it takes its width
     from its content. `.nhsuk-grid-row` carries a -16px side margin, so any page
     containing one measured ~32px wider than its parent and pulled the centred
     container off-centre: Today sat at x=120, Compliance at x=237 and Staff at
     x=203 for byte-identical tab markup. The tab bar looked like it moved
     between tabs; in fact the whole page did. */
  width: 100%;
}

body > .nhsuk-width-container > .nhsuk-main-wrapper {
  width: 100%;
}

.nhsuk-width-container {
  max-width: var(--pb-page-max);
  padding-left: var(--pb-space-4);
  padding-right: var(--pb-space-4);
  /* NHS.UK centres the container with `margin: 0 auto` only from 1024px up, and
     the app.css max-width/padding override above re-declares the properties
     without the margin. Stating it here keeps the page gutter at every width -
     without it a widened container (the week grid, see below) lost its side
     margins and content touched the left edge. */
  margin-left: auto;
  margin-right: auto;
}

.nhsuk-main-wrapper {
  padding-top: var(--pb-space-6);
  padding-bottom: var(--pb-space-7);
  /* Fill the container rather than shrink-wrapping the widest child.
     The container is a flex column, so without this the wrapper took its width
     from its content - and `.nhsuk-grid-row`, which carries a -16px side margin,
     made itself 32px wider than its parent. Pages containing a grid row
     therefore ended up with a wider wrapper than pages without one, and because
     the container centres it, EVERYTHING on the page - including the tab bar -
     sat at a different x. That is why the tabs appeared to jump between Today
     and Compliance even though the bar itself was identical. */
  width: 100%;
  min-width: 0;
}

/* Prose keeps a readable measure. */
.nhsuk-body,
.nhsuk-body-l,
.nhsuk-body-s,
.nhsuk-lede-text,
.nhsuk-inset-text > p {
  max-width: var(--pb-prose-max);
}

/* -----------------------------------------------------------------------------
   4. FOCUS - one consistent treatment on EVERY interactive element.
   Never removed. WCAG 2.2 AA (2.4.7/2.4.11).
   -------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 3px solid var(--pb-focus);
  outline-offset: 2px;
  border-radius: var(--pb-radius-s);
}

/* NHS.UK's own control focus uses a 4px yellow outline with a dark inner
   border, which is excellent and stays. Below only unifies the offset/radius. */
.nhsuk-input:focus,
.nhsuk-select:focus,
.nhsuk-textarea:focus {
  outline: 3px solid var(--pb-focus);
  outline-offset: 0;
  border-color: var(--pb-text);
  box-shadow: inset 0 0 0 2px var(--pb-text);
}

/* Skip link: NHS.UK styles it, but it must also work on our page background. */
.nhsuk-skip-link:focus {
  outline: 3px solid var(--pb-focus-ring);
  outline-offset: 0;
}

/* -----------------------------------------------------------------------------
   5. TYPOGRAPHY
   -------------------------------------------------------------------------- */
h1, h2, h3, h4,
.nhsuk-heading-xl,
.nhsuk-heading-l,
.nhsuk-heading-m,
.nhsuk-heading-s {
  color: var(--pb-text);
  font-weight: 700;
  letter-spacing: -0.01em;
}

h1, .nhsuk-heading-xl {
  font-size: var(--pb-text-2xl);
  line-height: var(--pb-leading-tight);
  margin-bottom: var(--pb-space-5);
}

h2, .nhsuk-heading-l {
  font-size: var(--pb-text-xl);
  line-height: var(--pb-leading-tight);
  margin-bottom: var(--pb-space-4);
}

h3, .nhsuk-heading-m {
  font-size: var(--pb-text-l);
  line-height: var(--pb-leading-snug);
  margin-bottom: var(--pb-space-3);
}

h4, .nhsuk-heading-s {
  font-size: var(--pb-text-m);
  line-height: var(--pb-leading-snug);
  margin-bottom: var(--pb-space-2);
}

.nhsuk-body, p {
  font-size: var(--pb-text-m);
  line-height: var(--pb-leading-body);
  margin-bottom: var(--pb-space-4);
}

.nhsuk-body-s {
  font-size: var(--pb-text-s);
  line-height: var(--pb-leading-snug);
}

.nhsuk-body-l {
  font-size: var(--pb-text-xl);
  line-height: var(--pb-leading-snug);
}

.nhsuk-hint,
.nhsuk-u-secondary-text-colour {
  color: var(--pb-text-muted);
}

.nhsuk-caption-m {
  color: var(--pb-text-muted);
  font-size: var(--pb-text-s);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

code {
  font-family: var(--pb-font-mono);
  font-size: 0.9em;
  background-color: var(--pb-surface-sunken);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-s);
  padding: 0.1em 0.35em;
  overflow-wrap: anywhere;
}

/* Links: NHS blue, underlined, with a visible non-colour cue on hover. */
a,
.nhsuk-link {
  color: var(--pb-blue);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  transition: color var(--pb-dur-fast) var(--pb-ease);
}

a:hover,
.nhsuk-link:hover {
  color: var(--pb-blue-darker);
  text-decoration-thickness: 3px;
}

a:active,
.nhsuk-link:active {
  color: var(--pb-blue-dark);
}

a:visited {
  color: var(--pb-blue-dark);
}

/* -----------------------------------------------------------------------------
   6. HEADER / BRAND / PRIMARY NAV
   The band is the brand teal and carries the supplied artwork. Only the
   identity layer changed: the nav treatment, focus indicator and touch
   targets are the ones that were already there.
   -------------------------------------------------------------------------- */
.nhsuk-header {
  background-color: var(--pb-teal);
  border-bottom: 4px solid var(--pb-teal-darker);
  position: relative;
}

.nhsuk-header__container {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--pb-space-3) var(--pb-space-5);
  padding-top: var(--pb-space-3);
  padding-bottom: var(--pb-space-3);
  max-width: var(--pb-page-max);
}

/* Brand lockup - sized to the artwork's own intrinsic ratio. The NHS logo box
   (100x40) is deliberately NOT applied: the lockup is a different shape and
   forcing it into that box is what caused the original overflow. */
.nhsuk-header__logo {
  flex: 0 0 auto;
}

.nhsuk-header__link {
  display: inline-flex;
  align-items: center;
  gap: var(--pb-space-2);
  width: auto;
  height: auto;
  max-width: none;
  text-decoration: none;
}

.nhsuk-header__link:hover,
.nhsuk-header__link:focus {
  text-decoration: none;
}

/* The lockup image. Height is fixed and width is driven by the intrinsic
   ratio, so the mark never distorts and never overflows its container.

   The header uses the LOCKUP variant (mark + wordmark, no tagline) from 481px up.
   The full tagline lockup is not used anywhere in the chrome, because its tagline
   renders at under 4px at header and footer sizes and is unreadable. See the
   comment on `brand()` in ui.ts for the reasoning. */
.pb-brand__logo {
  display: block;
  height: 30px;
  width: auto;
}

/* The mark + wordmark lockup, no tagline - the desktop header's default.
   The mark is the distinctive part of the brand, and it previously appeared
   nowhere above 480px while the phone layout showed it with no name. The lockup
   is ~1.6x the wordmark's width for the same text height, which desktop has room
   for. `logo-wordmark` remains for placements too tight for it. */
.pb-brand__logo--lockup {
  height: 36px;
  width: auto;
}

/* Below ~480px the wordmark shrinks past legibility, so the square mark is
   shown instead. Both are in the DOM; only one is ever displayed, and only the
   displayed one is exposed to assistive technology (see the markup comment). */
.pb-brand__logo--mark {
  display: none;
  height: 40px;
  width: auto;
}

@media (max-width: 480px) {
  .pb-brand__logo--lockup { display: none; }
  .pb-brand__logo--wordmark { display: none; }
  .pb-brand__logo--mark { display: block; }
}

/* Small lockup in the footer. The footer sits on the cream page surface, so it
   takes the light artwork and swaps to -dark in dark mode via <picture>.
   Also the wordmark variant: at 32px the full tagline lockup put the tagline at
   under 3px, the least readable instance of it anywhere in the app. */
.pb-footer__brand {
  margin: 0 0 var(--pb-space-3);
}

.pb-brand__logo--on-light {
  display: block;
  height: 32px;
  width: auto;
}

/* Optional service name slot (NHS.UK convention), used on the board pages. */
.pb-header__service {
  color: var(--pb-cream);
  font-size: var(--pb-text-s);
  font-weight: 600;
  border-left: 1px solid rgba(255, 255, 255, 0.35);
  padding-left: var(--pb-space-3);
  margin-left: var(--pb-space-1);
  align-self: center;
}

/* --- Primary nav --- */
.nhsuk-header__navigation {
  flex: 1 1 auto;
  display: flex;
  justify-content: flex-end;
}

.nhsuk-header__navigation-list {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-1);
  margin: 0;
  padding: 0;
}

.nhsuk-header__navigation-item {
  margin: 0;
}

.nhsuk-header__navigation-link,
.nhsuk-footer a,
.nhsuk-skip-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;              /* accessible 44px tap target */
  padding: var(--pb-space-2) var(--pb-space-3);
  line-height: 1.3;
  color: var(--pb-text-inverse);
  font-size: var(--pb-text-s);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--pb-radius-m);
  border-bottom: 0;
  transition: background-color var(--pb-dur-fast) var(--pb-ease);
}

.nhsuk-header__navigation-link:hover,
.nhsuk-header__navigation-link:focus {
  /* Darken, never lighten. A translucent WHITE overlay lightens the blue and
     drops white text from 6.38:1 to 4.42:1, below the 4.5:1 AA threshold
     (measured - see scripts/contrast-check.ts). A translucent BLACK overlay
     moves the background the other way, so contrast improves. */
  background-color: rgba(0, 0, 0, 0.22);
  color: var(--pb-text-inverse);
  text-decoration: none;
}

/* Current page: a dark background plus a yellow underline bar, so the state is
   carried by more than colour alone and keeps white text above 4.5:1. */
.nhsuk-header__navigation-link[aria-current="page"] {
  background-color: rgba(0, 0, 0, 0.28);
  font-weight: 700;
  box-shadow: inset 0 -3px 0 0 var(--pb-amber-strong);
}

.nhsuk-header__navigation-link[aria-current="page"]:hover {
  background-color: rgba(0, 0, 0, 0.36);
}

/* Focus inside the blue header keeps the loud yellow indicator, offset so it
   does not disappear into the band. */
.nhsuk-header__navigation-link:focus-visible,
.nhsuk-header__link:focus-visible {
  outline: 3px solid var(--pb-amber-strong);
  outline-offset: -3px;
}

/* --- Mobile header: a real treatment, not wrapped tiny links --- */
@media (max-width: 48.0525em) {
  .nhsuk-header__container {
    flex-direction: column;
    align-items: stretch;
    gap: var(--pb-space-2);
    padding-top: var(--pb-space-3);
    padding-bottom: var(--pb-space-2);
  }

  .nhsuk-header__logo {
    align-self: flex-start;
  }

  .pb-header__service {
    border-left: 0;
    padding-left: 0;
    margin-left: 0;
  }

  .nhsuk-header__navigation {
    width: 100%;
    justify-content: flex-start;
    /* A flex item defaults to min-width: auto, which means it refuses to shrink
       below its content. With the scroll rail below (overflow-x: auto) that makes
       the RAIL as wide as its items, so the header - and with it the whole page -
       scrolls sideways instead of the rail scrolling inside it. min-width: 0 lets
       the rail be the width of the viewport, which is what makes overflow-x
       actually take effect. */
    min-width: 0;
  }

  /* Horizontal snap-scroll rail: every item stays full-size and tappable and
     the page itself never scrolls sideways. */
  .nhsuk-header__navigation-list {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
    gap: var(--pb-space-1);
    padding-bottom: var(--pb-space-1);
    scroll-snap-type: x proximity;
    /* `width: 100%` and NOT only `max-width`. A scroll container with a
       max-width still lays its flex items out at their natural size and lets them
       extend past its own box - they are clipped visually but their geometry
       still counts towards the document's scrollWidth, which is what the overflow
       check measures (`li.nhsuk-header__navigation-item` measured right=423 in a
       rail whose right edge is 359, and the page reported 143px of overflow).
       width: 100% makes the rail's box the authority, so the items are laid out
       inside a box the width of the header and the rail scrolls within it. */
    width: 100%;
    max-width: 100%;
    min-width: 0;
  }

  .nhsuk-header__navigation-item {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }

  .nhsuk-header__navigation-link {
    font-size: var(--pb-text-s);
    padding: var(--pb-space-2) var(--pb-space-3);
    white-space: nowrap;
  }
}

/* -----------------------------------------------------------------------------
   7. FOOTER
   -------------------------------------------------------------------------- */
.nhsuk-footer {
  background-color: var(--pb-surface-sunken);
  border-top: 1px solid var(--pb-border);
  color: var(--pb-text-muted);
  margin-bottom: 0;
  padding-top: var(--pb-space-5);
  padding-bottom: var(--pb-space-5);
}

.nhsuk-footer__copyright,
.nhsuk-footer p {
  color: var(--pb-text-muted);
  font-size: var(--pb-text-s);
  line-height: var(--pb-leading-snug);
  max-width: var(--pb-prose-max);
  margin-bottom: var(--pb-space-2);
}

.nhsuk-footer a {
  color: var(--pb-blue);
  padding: 0;
  min-height: 0;
  display: inline;
  text-decoration: underline;
  border-radius: 0;
}

.nhsuk-footer a:hover {
  color: var(--pb-blue-darker);
  text-decoration-thickness: 3px;
}

/* -----------------------------------------------------------------------------
   8. CARDS - one border token, one radius token, subtle elevation.
   Fixes: doubled borders where a table sat inside a card, mismatched 1px
   borders, rules stopping short of the container edge.
   -------------------------------------------------------------------------- */
.nhsuk-card {
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-l);
  box-shadow: var(--pb-shadow-s);
  color: var(--pb-text);
  margin-bottom: var(--pb-space-5);
  padding: 0;
  position: relative;
  width: 100%;
  overflow: hidden;              /* keeps inner rules flush to the radius */
  transition: box-shadow var(--pb-dur) var(--pb-ease);
}

.nhsuk-card__content {
  padding: var(--pb-space-5);
}

.nhsuk-card__content > :last-child {
  margin-bottom: 0;
}

.nhsuk-card__heading,
.nhsuk-card__heading.nhsuk-heading-m,
.nhsuk-card__heading.nhsuk-heading-s {
  margin-top: 0;
  margin-bottom: var(--pb-space-3);
}

/* Feature card: an accent bar instead of NHS.UK's heavy full-bleed block. */
.nhsuk-card--feature {
  border-top: 4px solid var(--pb-blue);
}

.nhsuk-card--feature > .nhsuk-card__heading-container,
.nhsuk-card--feature > .nhsuk-card__content {
  padding-top: var(--pb-space-5);
}

/* A table inside a card must not draw a second box. */
.nhsuk-card .nhsuk-table-container,
.nhsuk-card .nhsuk-table {
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin-bottom: 0;
}

/* A table inside a card stretches to the card edge so its rules run the full
   width. The cells then have to supply the inset themselves, otherwise the first
   and last cell in each row sit flush against the card border and the text reads
   as if it is running into the edge ("Done by Bethany Cox", "Expected
   Evidence"). The side padding is one step larger than the card's own content
   padding, so the table's outer rule still reaches the card edge while its text
   lines up with the prose above it. */
.nhsuk-card .nhsuk-table-container {
  margin-left: calc(-1 * var(--pb-space-5));
  margin-right: calc(-1 * var(--pb-space-5));
  width: calc(100% + 2 * var(--pb-space-5));
}

.nhsuk-card .nhsuk-table tr > :first-child {
  padding-left: var(--pb-space-5);
}

.nhsuk-card .nhsuk-table tr > :last-child {
  padding-right: var(--pb-space-5);
}

/* -----------------------------------------------------------------------------
   9. STAT CARDS - used on /board/dashboard and /board/my-stats.
   Replaces the inline style="color: #..." that was scattered in the route.
   -------------------------------------------------------------------------- */
/* A row of stat cards.
   Staff Management renders four of these in a .pb-stat-grid, but the class was
   never defined, so each block-level .pb-stat stacked full-width down the page
   instead of sitting in a row. */
.pb-stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--pb-space-4);
  margin-bottom: var(--pb-space-4);
}

.pb-stat-grid > .pb-stat {
  margin-bottom: 0;
}

.pb-stat {
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-left: 4px solid var(--pb-n-400);
  border-radius: var(--pb-radius-l);
  box-shadow: var(--pb-shadow-s);
  padding: var(--pb-space-4) var(--pb-space-5);
  margin-bottom: var(--pb-space-4);
  height: 100%;
}

.pb-stat__value {
  display: block;
  font-size: var(--pb-text-2xl);
  font-weight: 700;
  line-height: var(--pb-leading-tight);
  color: var(--pb-text);
  margin-bottom: var(--pb-space-1);
  font-variant-numeric: tabular-nums;
}

.pb-stat__label {
  display: block;
  font-size: var(--pb-text-s);
  font-weight: 600;
  color: var(--pb-text-muted);
  margin: 0;
}

.pb-stat--done      { border-left-color: var(--pb-green); }
.pb-stat--done .pb-stat__value { color: var(--pb-green-dark); }
.pb-stat--overdue   { border-left-color: var(--pb-red); }
.pb-stat--overdue .pb-stat__value { color: var(--pb-red-dark); }
.pb-stat--blocked   { border-left-color: var(--pb-amber); }
.pb-stat--blocked .pb-stat__value { color: var(--pb-amber-dark); }
.pb-stat--total     { border-left-color: var(--pb-blue); }
.pb-stat--total .pb-stat__value { color: var(--pb-blue-dark); }

/* -----------------------------------------------------------------------------
   9b. SECTOR NEWS
   Headlines from third-party feeds. Kept deliberately plain: the content is
   untrusted, so it is rendered as ordinary text in ordinary blocks, never as
   rich markup.
   -------------------------------------------------------------------------- */
.pb-news-list {
  list-style: none;
  padding-left: 0;
  margin-bottom: 2rem;
}

.pb-news-item {
  border-bottom: 1px solid var(--pb-border);
  padding: 1rem 0;
}

.pb-news-item:last-child {
  border-bottom: 0;
}

.pb-news-item__title {
  margin-bottom: 0.25rem;
}

.pb-news-item__title a {
  text-decoration: underline;
}

/* -----------------------------------------------------------------------------
   10. TAGS / BADGES
   Semantic vocabulary kept exactly (green/amber/red/blue/grey). Softened into
   chips: tinted fill, coloured text and border, so contrast stays high and the
   meaning never depends on colour alone (the label is always in the markup).
   -------------------------------------------------------------------------- */
.nhsuk-tag {
  background-color: var(--pb-grey);
  border: 1px solid var(--pb-grey-dark);
  border-radius: var(--pb-radius-pill);
  color: var(--pb-grey-dark);
  display: inline-block;
  font-size: var(--pb-text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1.4;
  padding: 2px var(--pb-space-2);
  text-decoration: none;
  white-space: nowrap;
  overflow-wrap: normal;
  vertical-align: middle;
}

.nhsuk-tag--blue {
  background-color: var(--pb-blue-tint);
  border-color: var(--pb-blue-dark);
  color: var(--pb-blue-dark);
}

.nhsuk-tag--green {
  background-color: var(--pb-green-tint);
  border-color: var(--pb-green-dark);
  color: var(--pb-green-dark);
}

.nhsuk-tag--red {
  background-color: var(--pb-red-tint);
  border-color: var(--pb-red-dark);
  color: var(--pb-red-dark);
}

.nhsuk-tag--amber,
.nhsuk-tag--yellow {
  background-color: var(--pb-amber-tint);
  border-color: var(--pb-amber-dark);
  color: var(--pb-amber-dark);
}

.nhsuk-tag--grey {
  background-color: var(--pb-grey);
  border-color: var(--pb-grey-dark);
  color: var(--pb-grey-dark);
}

/* -----------------------------------------------------------------------------
   11. BUTTONS
   NHS.UK's 4px hard bottom shadow becomes a soft elevation. The yellow focus
   fill is kept - it is the single most recognisable NHS interaction state.
   -------------------------------------------------------------------------- */
/*
 * Primary action button: TEAL, per ADR 0009.
 *
 * ADR 0009 makes teal the primary brand token - "header band, links, primary
 * buttons, brand chrome" - and keeps green reserved for success/done semantics.
 * The button inherited NHS.UK's green, which meant the most prominent action on
 * every page was wearing the colour this system uses to mean "this worked". A
 * primary button is not a success state, and having the two look identical
 * dilutes both.
 *
 * Green is NOT removed; it stays exactly where it carries meaning (success
 * callouts, "done" stats, valid matrix cells). Only the primary action moves.
 *
 * Contrast improves: white on teal is 6.92:1 against 5.13:1 on the old green,
 * so this is a WCAG gain as well as a correctness fix.
 */
.nhsuk-button {
  background-color: var(--pb-teal);
  border: 2px solid transparent;
  border-radius: var(--pb-radius-m);
  box-shadow: var(--pb-shadow-s);
  color: var(--pb-n-0);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  box-sizing: border-box;
  font-family: inherit;
  font-size: var(--pb-text-base);
  font-weight: 700;
  line-height: 1.5;
  margin: 0 var(--pb-space-2) var(--pb-space-3) 0;
  padding: var(--pb-space-2) var(--pb-space-5);
  text-align: center;
  text-decoration: none;
  vertical-align: top;
  transition: background-color var(--pb-dur-fast) var(--pb-ease),
              box-shadow var(--pb-dur-fast) var(--pb-ease),
              transform var(--pb-dur-fast) var(--pb-ease);
}

.nhsuk-button:hover {
  background-color: var(--pb-teal-dark);
  box-shadow: var(--pb-shadow-m);
  color: var(--pb-n-0);
}

.nhsuk-button:active {
  background-color: var(--pb-teal-darker);
  box-shadow: none;
  transform: translateY(1px);
}

.nhsuk-button:focus,
.nhsuk-button:focus:not(:active) {
  background-color: var(--pb-amber-strong);
  border-color: var(--pb-text);
  box-shadow: 0 0 0 3px var(--pb-focus-ring);
  color: var(--pb-text);
  outline: 3px solid var(--pb-amber-strong);
  outline-offset: 0;
}

/* Secondary: an outline button, not a transparent one - it needs a visible
   edge against a card surface in both colour schemes. */
.nhsuk-button--secondary {
  background-color: var(--pb-surface-raised);
  border-color: var(--pb-blue);
  box-shadow: var(--pb-shadow-s);
  color: var(--pb-blue-dark);
}

.nhsuk-button--secondary:hover {
  background-color: var(--pb-blue-tint-2);
  border-color: var(--pb-blue-darker);
  color: var(--pb-blue-darker);
}

.nhsuk-button--secondary:active {
  background-color: var(--pb-blue-tint);
  transform: translateY(1px);
}

.nhsuk-button--warning {
  background-color: var(--pb-red);
  box-shadow: var(--pb-shadow-s);
}

.nhsuk-button--warning:hover {
  background-color: var(--pb-red-dark);
}

/* -----------------------------------------------------------------------------
   12. FORMS
   -------------------------------------------------------------------------- */
.nhsuk-form-group {
  margin-bottom: var(--pb-space-5);
}

.nhsuk-label,
.nhsuk-label--m {
  color: var(--pb-text);
  display: block;
  font-size: var(--pb-text-m);
  font-weight: 600;
  margin-bottom: var(--pb-space-1);
}

.nhsuk-label--m {
  font-size: var(--pb-text-l);
}

.nhsuk-hint {
  color: var(--pb-text-muted);
  display: block;
  font-size: var(--pb-text-s);
  margin-bottom: var(--pb-space-2);
}

.nhsuk-input,
.nhsuk-select,
.nhsuk-textarea {
  background-color: var(--pb-surface-raised);
  border: 2px solid var(--pb-border-input);
  border-radius: var(--pb-radius-m);
  box-sizing: border-box;
  color: var(--pb-text);
  font-family: inherit;
  font-size: var(--pb-text-base);
  line-height: 1.5;
  margin-top: 0;
  max-width: 100%;
  padding: var(--pb-space-2) var(--pb-space-3);
  transition: border-color var(--pb-dur-fast) var(--pb-ease);
}

.nhsuk-input,
.nhsuk-select {
  min-height: 44px;             /* generous touch target */
}

.nhsuk-textarea {
  min-height: 96px;
  resize: vertical;
  width: 100%;
}

.nhsuk-input:hover,
.nhsuk-select:hover,
.nhsuk-textarea:hover {
  border-color: var(--pb-text-muted);
}

.nhsuk-input--width-4 { max-width: 6em; }
.nhsuk-input--width-10 { max-width: 12em; }

.nhsuk-checkboxes__item {
  margin-bottom: var(--pb-space-2);
}

.nhsuk-checkboxes__input {
  accent-color: var(--pb-blue);
}

.nhsuk-checkboxes__label {
  font-size: var(--pb-text-base);
  line-height: var(--pb-leading-snug);
}

/* Error summary: kept NHS.UK's red block treatment, softened to match. */
.nhsuk-error-summary {
  border: 2px solid var(--pb-red);
  border-left-width: 6px;
  border-radius: var(--pb-radius-m);
  background-color: var(--pb-surface-raised);
  margin-bottom: var(--pb-space-5);
  padding: var(--pb-space-4);
}

.nhsuk-error-summary__title {
  color: var(--pb-text);
}

/* -----------------------------------------------------------------------------
   13. INSET TEXT / FLASH MESSAGES
   Was an 8px NHS blue slab; now a tinted notice with an accent bar.
   -------------------------------------------------------------------------- */
.nhsuk-inset-text {
  background-color: var(--pb-blue-tint-2);
  border-left: 4px solid var(--pb-blue);
  border-radius: 0 var(--pb-radius-m) var(--pb-radius-m) 0;
  color: var(--pb-text);
  margin: var(--pb-space-5) 0;
  max-width: var(--pb-prose-max);
  padding: var(--pb-space-4) var(--pb-space-5);
}

.nhsuk-inset-text > :last-child {
  margin-bottom: 0;
}

.nhsuk-warning-callout {
  background-color: var(--pb-amber-tint);
  border: 1px solid var(--pb-amber);
  border-left-width: 4px;
  border-radius: var(--pb-radius-m);
  color: var(--pb-text);
  padding-top: 0;
}

.nhsuk-warning-callout__label {
  color: var(--pb-amber-dark);
}

/* -----------------------------------------------------------------------------
   14. TABLES
   NHS.UK 9 no longer ships the row-stacking rules that used to come with
   .nhsuk-table-responsive, so our table() helper emits a per-cell label and
   we stack on that. Behaviour preserved through the restyle.
   -------------------------------------------------------------------------- */
.nhsuk-table-container {
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-l);
  box-shadow: var(--pb-shadow-s);
  margin-bottom: var(--pb-space-5);
  /* Scroll rather than clip. `overflow: hidden` was cutting the last column off
     at the rounded edge - the Compliance "Completion" column and its action
     button were being sliced by the card border. The container radius is kept
     visually by the table's own rounded corners below. */
  overflow-x: auto;
  overflow-y: hidden;
  width: 100%;
}

.nhsuk-table-container > .nhsuk-table {
  border-radius: var(--pb-radius-l);
}

.nhsuk-table {
  border: 0;
  border-collapse: collapse;
  color: var(--pb-text);
  margin-bottom: 0;
  width: 100%;
}

.nhsuk-table__head {
  background-color: var(--pb-surface-sunken);
}

.nhsuk-table__header {
  border-bottom: 2px solid var(--pb-border);
  color: var(--pb-text);
  font-size: var(--pb-text-s);
  font-weight: 700;
  letter-spacing: 0.01em;
  padding: var(--pb-space-3) var(--pb-space-4);
  text-align: left;
  vertical-align: bottom;
}

.nhsuk-table__cell {
  border-bottom: 1px solid var(--pb-border);
  color: var(--pb-text);
  padding: var(--pb-space-3) var(--pb-space-4);
  vertical-align: top;
}

.nhsuk-table__body tr:last-child .nhsuk-table__cell,
.nhsuk-table__body tr:last-child .nhsuk-table__header {
  border-bottom: 0;
}

.nhsuk-table__row {
  transition: background-color var(--pb-dur-fast) var(--pb-ease);
}

.nhsuk-table__row:hover {
  background-color: var(--pb-surface-sunken);
}

.nhsuk-table__caption {
  color: var(--pb-text);
  font-size: var(--pb-text-m);
  font-weight: 700;
  padding: var(--pb-space-4) var(--pb-space-4) 0;
  text-align: left;
}

/* On desktop, keep words whole.
   `overflow-wrap: anywhere` used to apply everywhere, which let a short header
   break mid-word whenever the column was slightly too narrow - "mainten / ance",
   "Frequen / cy", "Responsib / le Role". It also makes the element report a tiny
   min-content width, so the table stopped pushing its container to scroll and
   preferred to shatter the words instead. Break only between words here; the
   phone layout below re-enables anywhere, where it is genuinely needed to let a
   long unbroken email shrink to the column (break-word does not affect flex
   min-content sizing). Badges are short and must never break mid-word
   ("activ / e"), so opt them out. */
.nhsuk-table__cell,
.nhsuk-table__header {
  overflow-wrap: normal;
  word-break: normal;
}

.nhsuk-table-responsive__heading {
  overflow-wrap: normal;
  word-break: normal;
}

.nhsuk-table td,
.nhsuk-table th {
  vertical-align: top;
}

/* --- Stacked (phone) layout --- */
.nhsuk-table-responsive__heading {
  display: none;
}

@media (max-width: 48.0525em) {
  /* In the stacked phone layout the cells are flex items, so a long unbroken
     value (an email) needs anywhere to be allowed to shrink; it is also the
     width at which breaking a word is far better than overflowing the screen. */
  .nhsuk-table__cell,
  .nhsuk-table__header {
    overflow-wrap: anywhere;
  }

  .nhsuk-table-responsive thead {
    display: none;
  }

  .nhsuk-table-responsive,
  .nhsuk-table-responsive tbody,
  .nhsuk-table-responsive tr,
  .nhsuk-table-responsive th,
  .nhsuk-table-responsive td {
    display: block;
    width: auto;
  }

  .nhsuk-table-container {
    border-radius: var(--pb-radius-m);
  }

  .nhsuk-table-responsive tr {
    padding: var(--pb-space-3) var(--pb-space-4);
    border-bottom: 1px solid var(--pb-border);
  }

  .nhsuk-table-responsive tr:last-child {
    border-bottom: 0;
  }

  .nhsuk-table-responsive td {
    display: flex;
    gap: var(--pb-space-3);
    border: 0;
    padding: var(--pb-space-1) 0;
    /* The value must be allowed to shrink and wrap. Without min-width:0 a flex
       item refuses to go below its content width, so a long unbroken value
       (a URL, a staff name, "GP / Visiting professional") overflows the cell
       and is clipped rather than wrapped. Measured on /diary at 390px: the
       value needed 299px inside a 292px cell before this line. */
    min-width: 0;
  }

  /* The label column keeps its width; the value takes the rest and wraps. */
  .nhsuk-table-responsive td > *:not(.nhsuk-table-responsive__heading) {
    min-width: 0;
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  .nhsuk-table-responsive th[scope='row'] {
    border: 0;
    padding: 0 0 var(--pb-space-2);
    font-size: var(--pb-text-m);
  }

  .nhsuk-table-responsive__heading {
    display: block;
    flex: 0 0 auto;
    min-width: 7.5rem;
    font-size: var(--pb-text-s);
    font-weight: 700;
    color: var(--pb-text-muted);
  }
}

/* R9: Action column padding and button-style action link */
.pb-table__action-cell,
td.pb-table__action-cell,
th.pb-table__action-cell {
  padding-right: var(--pb-space-4) !important;
  min-width: 100px;
  text-align: right;
  white-space: nowrap;
}

.pb-table__action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: var(--pb-space-1) var(--pb-space-3);
  font-size: var(--pb-text-s);
  font-weight: 700;
  color: var(--pb-blue-dark);
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border-strong);
  border-radius: var(--pb-radius-m);
  text-decoration: none;
  transition: background-color var(--pb-dur-fast) var(--pb-ease),
              border-color var(--pb-dur-fast) var(--pb-ease),
              color var(--pb-dur-fast) var(--pb-ease);
}

.pb-table__action-btn:hover {
  background-color: var(--pb-blue-tint-2);
  border-color: var(--pb-blue);
  color: var(--pb-blue-darker);
  text-decoration: none;
}

/* -----------------------------------------------------------------------------
   15. LISTS
   -------------------------------------------------------------------------- */
.nhsuk-list {
  color: var(--pb-text);
  font-size: var(--pb-text-m);
  line-height: var(--pb-leading-body);
  max-width: var(--pb-prose-max);
}

.nhsuk-list li {
  margin-bottom: var(--pb-space-1);
}

/* -----------------------------------------------------------------------------
   16. ACTION LINK
   -------------------------------------------------------------------------- */
.nhsuk-action-link {
  margin-bottom: var(--pb-space-4);
}

.nhsuk-action-link__link {
  align-items: center;
  color: var(--pb-blue);
  display: inline-flex;
  font-size: var(--pb-text-m);
  font-weight: 700;
  gap: var(--pb-space-2);
  text-decoration: underline;
  text-decoration-thickness: 1px;
}

.nhsuk-action-link__link:hover {
  color: var(--pb-blue-darker);
  text-decoration-thickness: 3px;
}

.nhsuk-action-link__link::before {
  content: "+";
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5em;
  height: 1.5em;
  flex: 0 0 auto;
  border-radius: var(--pb-radius-pill);
  background-color: var(--pb-blue);
  color: var(--pb-n-0);
  font-size: var(--pb-text-s);
  font-weight: 700;
  text-decoration: none;
}

/* -----------------------------------------------------------------------------
   17. DETAILS / SUMMARY
   -------------------------------------------------------------------------- */
.nhsuk-details {
  border-left: 4px solid var(--pb-border-strong);
  border-radius: 0 var(--pb-radius-m) var(--pb-radius-m) 0;
  background-color: var(--pb-surface-raised);
  font-size: var(--pb-text-m);
  margin-bottom: var(--pb-space-5);
  padding: var(--pb-space-3) var(--pb-space-4);
}

.nhsuk-details__summary {
  color: var(--pb-blue);
  cursor: pointer;
  font-weight: 700;
}

.nhsuk-details__text {
  border-left: 0;
  padding: var(--pb-space-3) 0 0;
}

/* -----------------------------------------------------------------------------
   18. BOARD NAV (in-page sub-navigation, styled as a scrollable chip rail)
   -------------------------------------------------------------------------- */
.pb-subnav {
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-l);
  box-shadow: var(--pb-shadow-s);
  margin-bottom: var(--pb-space-5);
  padding: var(--pb-space-2);
}

.pb-subnav__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-1);
  /* flex-start, not space-between: with space-between the gaps grow to fill the
     row, so every tab's x position depended on the width of its neighbours'
     text. That is what made the bar shuffle between pages. */
  justify-content: flex-start;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Every tab occupies the SAME box regardless of state.
   The active tab used to be told apart by a box-shadow underline, which does
   not change the box - but the font weight and padding differed between the
   header rail and the section rail, so the two rendered at different sizes and
   the row reflowed. Fixing padding, weight and border here means the active
   state can only be expressed by colour. */
.pb-subnav__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: var(--pb-space-2) var(--pb-space-3);
  border: 2px solid transparent;
  border-radius: var(--pb-radius-m);
  color: var(--pb-text);
  font-size: var(--pb-text-s);
  font-weight: 600;
  text-decoration: none;
  transition: background-color var(--pb-dur-fast) var(--pb-ease);
}

.pb-subnav__link:hover {
  background-color: var(--pb-surface-sunken);
  color: var(--pb-blue-dark);
  text-decoration: none;
}

/* The active tab is shown by COLOUR and an underline only. It must not change
   the box: same border width and weight as the rest, so no neighbour shifts. */
.pb-subnav__link[aria-current="page"] {
  background-color: var(--pb-blue-tint);
  color: var(--pb-blue-dark);
  border-color: transparent;
  box-shadow: inset 0 -2px 0 0 var(--pb-blue);
}

/* A single date picker above a page's content (Compliance checklist date). */
.pb-day-picker {
  align-items: flex-end;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-3);
  margin-bottom: var(--pb-space-4);
}
.pb-day-picker .nhsuk-form-group {
  margin-bottom: 0;
}

/* A tab bar, under the global header navigation.
   Deliberately quieter than the header: no card, no shadow, and a left rule
   that reads as "these belong to the section you are in". The header nav is the
   product; this is a filter on it.

   The link BOX is deliberately identical to .pb-subnav__link above - same
   padding, border width and weight - so the tabs do not resize when the active
   one moves, and the same bar can be reused on a page outside this section. */
.pb-subnav--section {
  background: none;
  border: 0;
  border-left: 2px solid var(--pb-border);
  border-radius: 0;
  box-shadow: none;
  margin-bottom: var(--pb-space-4);
  padding: var(--pb-space-1) 0 var(--pb-space-1) var(--pb-space-3);
}
.pb-subnav--section .pb-subnav__link {
  font-size: var(--pb-text-s);
  min-height: 40px;
  padding: var(--pb-space-2) var(--pb-space-3);
  border-width: 2px;
}

/* A section's own links, as a modest row of BODY links.
   Deliberately not a tab bar: the Dashboard tabs are the navigation, and a
   second row of tabs made it ambiguous which control changed section. */
.pb-section-links {
  color: var(--pb-text-muted);
  margin-bottom: var(--pb-space-4);
}
.pb-section-links a {
  font-weight: 600;
}

/* -----------------------------------------------------------------------------
   19. WEEK GRID  (the rebuilt GET /board)
   -------------------------------------------------------------------------- */
.pb-week {
  margin-bottom: var(--pb-space-5);
}

.pb-week__toolbar {
  align-items: center;
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-l);
  box-shadow: var(--pb-shadow-s);
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-3);
  justify-content: space-between;
  margin-bottom: var(--pb-space-4);
  padding: var(--pb-space-3) var(--pb-space-4);
}

.pb-week__nav {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-2);
}

/* The week view's h1. Carries the page's single top-level heading, so it must
   stay an <h1>; sized down here because it sits inside the toolbar rather than
   at the top of the content column. */
.pb-week__title {
  font-size: var(--pb-text-l);
  font-weight: 700;
  margin: 0;
}

h1.pb-week__title {
  font-size: var(--pb-text-l);
  line-height: var(--pb-leading-snug);
  margin-bottom: 0;
}

.pb-week__range {
  color: var(--pb-text-muted);
  display: block;
  font-size: var(--pb-text-s);
  font-weight: 600;
}

/* Week navigation uses real links styled as buttons so they work with JS off. */
.pb-week__navlink {
  align-items: center;
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border-strong);
  border-radius: var(--pb-radius-m);
  color: var(--pb-text);
  display: inline-flex;
  font-size: var(--pb-text-s);
  font-weight: 700;
  min-height: 44px;
  padding: var(--pb-space-2) var(--pb-space-3);
  text-decoration: none;
  transition: background-color var(--pb-dur-fast) var(--pb-ease),
              border-color var(--pb-dur-fast) var(--pb-ease);
}

.pb-week__navlink:hover {
  background-color: var(--pb-surface-sunken);
  border-color: var(--pb-blue);
  color: var(--pb-blue-dark);
  text-decoration: none;
}

.pb-week__navlink[aria-current="page"] {
  background-color: var(--pb-blue-tint);
  border-color: var(--pb-blue);
  color: var(--pb-blue-dark);
}

/* "Jump to week": label and hint sit ABOVE, then a row holding the date input
   and the Go button.

   The button used to be a sibling of the whole form-group, so `align-items:
   flex-end` aligned it to the bottom of the GROUP - which included the hint
   line under the input. The button therefore sat a line lower than the field it
   submits. The row now contains only the input and the button, so flex-end has
   nothing but the two controls to align, and both are given one shared height
   so they end up the same height as well as on the same baseline. */
.pb-week__jump {
  margin: 0;
}

.pb-week__jump-label {
  font-size: var(--pb-text-s);
  margin-bottom: 2px;
}

/* Both controls are given the SAME explicit height, not just the same
   min-height. `min-height: 44px` on each left them 2px apart, because the
   NHS.UK input carries a 2px border on top of its own box sizing while the
   button does not, so the input resolved to 46px and the button to 44px and
   their tops did not line up. A single shared height removes the difference at
   the source rather than padding one of them to match by luck. 46px is the
   input's natural height, so the taller of the two sets the row and the button
   grows to meet it. */
.pb-week__jump-row {
  align-items: flex-end;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-2);
}

.pb-week__jump-row .nhsuk-input,
.pb-week__jump-row .nhsuk-button {
  height: 46px;
  margin-bottom: 0;
}

.pb-week__jump-row .nhsuk-input {
  width: auto;
}

/* The responsible-role control on a template page. The select is kept to its
   content width rather than NHS.UK's full-width default, because a six-option
   list of short role names stretched across a desktop page reads as an empty
   text field. The hint is capped so it stays readable at 1440px. */
.pb-responsible-form {
  margin-top: var(--pb-space-3);
}

.pb-responsible-form .nhsuk-select {
  max-width: 22rem;
  width: 100%;
}

.pb-responsible-form .nhsuk-hint {
  max-width: 44rem;
}

/* Secondary text. `--pb-text-muted` (n-600) rather than `--pb-n-500`: grey-2 at
   this size fails the WCAG AA 4.5:1 ratio against the page background, which the
   axe gate reports. Kept as a `.pb-muted` override of the later shorthand rule so
   the two definitions agree instead of one silently winning. */
.pb-muted {
  color: var(--pb-text-muted);
}

/* The "Edit schedule" form. Its five detail selects are always in the DOM (so
   the form works without JavaScript and `buildChoice` can ignore the ones that
   do not belong to the chosen kind), so they are laid out in a wrapping row -
   otherwise five full-width selects would make the card taller than the page.
   Each keeps a readable minimum width so a role or month name is not clipped. */
.pb-schedule-form {
  margin-top: var(--pb-space-3);
}

.pb-schedule-form__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-3);
}

.pb-schedule-form__row .nhsuk-form-group {
  flex: 1 1 12rem;
  margin-bottom: var(--pb-space-3);
  min-width: 12rem;
}

.pb-schedule-form .nhsuk-select {
  width: 100%;
}

/* The Export Data form. The two date inputs sit side by side on desktop and
   stack on a phone, and the checkbox list keeps NHS.UK's own spacing. */
.pb-export-form {
  margin-top: var(--pb-space-4);
}

.pb-export-form .nhsuk-date-input__item {
  margin-right: var(--pb-space-3);
}

/* --- Rota: role column, grouped dividers and the cell popovers --- */

/* The role, in plain words, beside the name. Muted rather than bold: the name
   is what the eye scans for, and the role is the qualifier. */
.pb-rota-role {
  color: var(--pb-text-muted);
  font-size: 0.85rem;
  white-space: nowrap;
}

/* ── The rota must not make the PAGE scroll sideways ──────────────────────────
   Only the grid inside .pb-rota-scroll may scroll. Measured at 390px, the page
   was carrying 143px of horizontal scroll on /staff/rota.

   THE CAUSE IS NOT IN THIS PAGE'S MARKUP, which is why the obvious fixes did not
   work. Bisection from the served HTML:

     - removing <main> (the whole 649KB grid) still left 533px;
     - emptying the rota card still left 533px;
     - removing the header, the footer AND the skip link still left 533px;
     - on a page containing ONLY the footer (27 elements) EVERY element's right
       edge measured <= 375 and the widest was 375 - yet documentElement
       .scrollWidth was still 533.

   So no element overflows the viewport, and no rule that resizes one can clear
   it: the scroll extent is stuck at 390 + 143 and survived every layout change,
   including forcing the whole document to display:block. `body { overflow-x:
   hidden }` is the only thing that clears it, and it is the right mechanism here
   regardless of the cause: the requirement is that the page never scrolls
   sideways and that .pb-rota-scroll is the one thing that does.

   `hidden` on body rather than html, because setting it on html alone left the
   value at 533 - the propagation has to start at the body. The inner grid is
   unaffected: it has its own overflow-x:auto and keeps its own scrollbar. */
body {
  overflow-x: hidden;
}

/* The divider between role groups. A border on the first cell of the row, so it
   spans the table without a spacer row that would break the column alignment -
   and it is a real border, not a colour cue, so it survives a monochrome print
   and does not depend on colour perception. */
.pb-rota-group-start {
  border-top: 2px solid var(--pb-border-strong);
  display: block;
  margin-bottom: var(--pb-space-1);
}

/* Sort control: inline, quiet, and next to the week navigation. */
.pb-rota-sort {
  margin: var(--pb-space-3) 0 0;
}

.pb-rota-sort__label {
  color: var(--pb-text-muted);
  margin-right: var(--pb-space-1);
}

/* The current sort is marked by weight as well as colour, so the state is not
   conveyed by colour alone (WCAG 1.4.1). */
.pb-rota-sort--current {
  font-weight: 700;
  text-decoration: none;
  color: var(--pb-text);
}

/* The shift-cell popover. <details> gives keyboard opening, Escape and focus
   handling natively; these rules only position and style the panel. */
.pb-rota-menu {
  display: inline-block;
  position: relative;
}

.pb-rota-menu__summary {
  cursor: pointer;
  list-style: none;
  display: inline-block;
}

/* Remove the default disclosure triangle: the cell badge is the affordance. */
.pb-rota-menu__summary::-webkit-details-marker {
  display: none;
}

.pb-rota-menu__summary::marker {
  content: '';
}

.pb-rota-menu__panel {
  background: var(--pb-surface);
  border: 2px solid var(--pb-border-strong);
  border-radius: var(--pb-radius-m);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
  left: 0;
  min-width: 11rem;
  /* The panel must never make the page wider than the screen.
     `left: 0` is relative to a cell that sits near the right edge on a narrow
     screen, so the panel overhangs by its own width and, once it does, the page
     scrolls sideways - which the axe gate reports as horizontal overflow and which
     on a real phone puts the actions off-screen.
     `position: fixed` is deliberately NOT used: the details element is inside a
     scrolling table, and a fixed panel would detach from its cell and float over
     unrelated rows. Instead the panel is bounded by the viewport and allowed to
     shrink, and the narrow-screen rule below anchors it to the right edge so it
     grows leftwards into the page rather than out of it. */
  max-width: calc(100vw - 1.5rem);
  padding: var(--pb-space-3);
  position: absolute;
  text-align: left;
  top: calc(100% + 0.25rem);
  z-index: 20;
}

/* Narrow screens: anchor the panel's RIGHT edge to the cell's right edge, so it
   extends leftwards into the page. `right: 0` with `left: auto` is the whole
   fix - the overhang was caused by left-anchoring a wide panel near the right
   edge, not by the panel being too wide. */
@media (max-width: 48.0525em) {
  .pb-rota-menu__panel {
    left: auto;
    right: 0;
    min-width: 0;
    width: max-content;
  }

  /* A long staff name in the title must wrap rather than widen the panel. */
  .pb-rota-menu__panel,
  .pb-rota-menu__option,
  .pb-rota-menu__title {
    overflow-wrap: anywhere;
  }
}

.pb-rota-menu__title {
  margin: 0 0 var(--pb-space-2);
  font-weight: 700;
}

/* Each shift is a submit button, so choosing one is a real form POST that works
   without JavaScript and is not a link that a crawler or prefetch could fire. */
.pb-rota-menu__option {
  background: none;
  border: 0;
  border-radius: var(--pb-radius-m);
  cursor: pointer;
  display: block;
  font: inherit;
  padding: var(--pb-space-2);
  text-align: left;
  width: 100%;
}

.pb-rota-menu__option:hover,
.pb-rota-menu__option:focus {
  background: var(--pb-surface-sunken);
}

.pb-rota-menu__current {
  color: var(--pb-text-muted);
  font-size: 0.85rem;
}

.pb-rota-menu__fallback {
  display: inline-block;
  margin-top: var(--pb-space-2);
  font-size: 0.9rem;
}

/* The staff-name actions. Same popover treatment, but always hidden until the
   toggle is pressed - there is no summary element to open it. */
.pb-rota-name__menu {
  position: relative;
}

.pb-rota-name__toggle {
  background: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  padding: 0;
  text-decoration: underline;
}

.pb-rota-name__panel {
  background: var(--pb-surface);
  border: 2px solid var(--pb-border-strong);
  border-radius: var(--pb-radius-m);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
  display: flex;
  flex-direction: column;
  gap: var(--pb-space-1);
  left: 0;
  min-width: 14rem;
  padding: var(--pb-space-3);
  position: absolute;
  text-align: left;
  top: calc(100% + 0.25rem);
  z-index: 20;
}

.pb-rota-name__panel[hidden] {
  display: none;
}

.pb-rota-name__panel a,
.pb-rota-name__panel button {
  background: none;
  border: 0;
  cursor: pointer;
  font: inherit;
  padding: 0;
  text-align: left;
  text-decoration: underline;
}

/* The destructive action in the menu. --pb-red is the house danger token and is
   already contrast-checked by scripts/contrast-check.ts in every theme. */
.pb-rota-name__danger {
  color: var(--pb-red) !important;
}

/* The Load-models and Remove-key forms sit under the main AI form, on their own
   lines, and are visually secondary to Save. */
.pb-settings-ai-actions {
  margin-top: var(--pb-space-3);
  display: flex;
  align-items: center;
  gap: var(--pb-space-2);
}

/* --- Custom forms --- */

/* The notice on the create page. It states a fact about ParityBoard rather than
   warning about anything, so it is not styled as an error or a danger - an
   over-emphatic box would read as a caution about the form's legality. */
.pb-forms-notice {
  margin-bottom: var(--pb-space-5);
}

/* One field row in the builder. A left rule separates the rows, because a
   fieldset's own border is too faint to follow once there are ten of them. */
.pb-form-field {
  border-left: 4px solid var(--pb-border-strong);
  padding-left: var(--pb-space-4);
  margin-bottom: var(--pb-space-5);
}

.pb-form-field .nhsuk-form-group:last-child {
  margin-bottom: 0;
}

/* --- AI for rota drafting: the questions panel --- */

/* A left border and a tinted background mark this as a prompt for the manager
   rather than a record. It asks for input, and a panel that looks like every
   other card is one people scroll past. */
.pb-ai-questions {
  border-left: 4px solid var(--pb-blue);
  background-color: var(--pb-surface-sunken);
}

/* The question text sits above its input and is the thing to read; the person's
   name is the label and is already bold. Cap the measure so a long question does
   not run the full width of a wide screen. */
.pb-ai-questions .nhsuk-body {
  max-width: 44rem;
}

/* --- The 7-column grid ---

   Seven columns need horizontal room that a normal prose page max-width does
   not give them: at the 1024px page max each column was 131px and card titles
   wrapped to 4-5 lines. The week view is a data view, not prose, so the GRID is
   allowed to use the viewport up to a wider cap.

   ── WHY THE WIDE CAP IS ON THE GRID, NOT THE PAGE CONTAINER ─────────────────
   This used to widen `.nhsuk-width-container` itself, via
   `body:has(.pb-week__grid) > .nhsuk-width-container`. That container also
   holds the header, the section tab bar and the back link, so switching to
   Week View made all of them wider — and therefore made the TAB BAR SHIFT
   sideways relative to every other tab. The one thing this branch existed to
   stop was the tab bar moving between pages, and this reintroduced it.

   So the page container now keeps its normal width on Week View, and only the
   element that actually needs the room breaks out of it. `.pb-week__wide` is a
   wrapper the route puts around the grid alone; it is centred on the viewport
   and capped, so the header and tab bar above it are untouched. */
.pb-week__wide {
  margin-left: auto;
  margin-right: auto;
}

@media (min-width: 1680px) {
  .pb-week__wide {
    max-width: var(--pb-week-max);
  }
}

.pb-week__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--pb-space-3);
  align-items: start;
}

@media (min-width: 75em) {
  .pb-week__grid {
    gap: var(--pb-space-4);
  }
}

/* R4 Today: Day column on left (narrow), task detail panel on right (remaining width). Stacked on mobile. */
.pb-today,
.pb-today-split {
  display: grid;
  grid-template-columns: minmax(260px, 320px) 2fr;
  gap: 24px;
  align-items: flex-start;
  margin-top: var(--pb-space-4);
}

.pb-today__list,
.pb-today-split__left {
  min-width: 0;
}

.pb-today__detail,
.pb-today-split__right {
  min-width: 0;
}

@media (max-width: 768px) {
  .pb-today,
  .pb-today-split {
    grid-template-columns: 1fr;
  }
}

.pb-day {
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-l);
  box-shadow: var(--pb-shadow-s);
  display: flex;
  flex-direction: column;
  min-width: 0;
  overflow: hidden;
}

.pb-day__head {
  background-color: var(--pb-surface-sunken);
  border-bottom: 1px solid var(--pb-border);
  padding: var(--pb-space-3);
}

/* Day columns are real headings, not styled divs. */
.pb-day__name {
  display: block;
  font-size: var(--pb-text-s);
  font-weight: 700;
  letter-spacing: 0.02em;
  margin: 0;
  text-transform: uppercase;
  color: var(--pb-text);
}

.pb-day__date {
  color: var(--pb-text-muted);
  display: block;
  font-size: var(--pb-text-s);
  font-weight: 600;
  margin-top: 1px;
}

.pb-day__count {
  color: var(--pb-text-muted);
  display: block;
  font-size: var(--pb-text-xs);
  font-weight: 600;
  margin-top: var(--pb-space-1);
}

/* Today: an accent treatment, not just bold. */
.pb-day--today {
  border-color: var(--pb-blue);
  box-shadow: 0 0 0 2px var(--pb-blue), var(--pb-shadow-m);
}

.pb-day--today .pb-day__head {
  background-color: var(--pb-blue-tint);
  border-bottom-color: var(--pb-blue);
}

.pb-day--today .pb-day__name,
.pb-day--today .pb-day__date,
.pb-day--today .pb-day__count {
  color: var(--pb-blue-dark);
}

/* Past days are de-emphasised but stay legible. */
.pb-day--past {
  background-color: var(--pb-surface-sunken);
}

.pb-day--past .pb-day__head {
  background-color: var(--pb-surface-sunken);
}

.pb-day--past .pb-day__name {
  color: var(--pb-text-muted);
}

.pb-day__body {
  display: flex;
  flex-direction: column;
  gap: var(--pb-space-2);
  flex: 1 1 auto;
  padding: var(--pb-space-3);
}

.pb-day__empty {
  color: var(--pb-text-muted);
  font-size: var(--pb-text-s);
  margin: 0;
  padding: var(--pb-space-2) 0;
}

/* "+N more" - the overflow link when a column is capped at MAX_TASKS_PER_DAY.
   Styled as a link, not a muted footnote: it is the only way to reach the
   remaining tasks from this view, so it must look actionable and be an obvious
   target on a tablet. */
.pb-day__more {
  color: var(--pb-blue-dark);
  display: block;
  font-size: var(--pb-text-s);
  font-weight: 600;
  padding: var(--pb-space-2) var(--pb-space-1);
  text-align: center;
}

.pb-day__more:hover {
  background-color: var(--pb-teal-tint);
  text-decoration: underline;
}

/* "+ Add" affordance per column. A real link so it works with JS disabled. */
.pb-day__add {
  align-items: center;
  border: 1px dashed var(--pb-border-strong);
  border-radius: var(--pb-radius-m);
  color: var(--pb-blue-dark);
  display: flex;
  font-size: var(--pb-text-s);
  font-weight: 700;
  justify-content: center;
  margin-top: auto;
  min-height: 44px;
  padding: var(--pb-space-2);
  text-decoration: none;
  transition: background-color var(--pb-dur-fast) var(--pb-ease),
              border-color var(--pb-dur-fast) var(--pb-ease);
}

.pb-day__add:hover {
  background-color: var(--pb-blue-tint-2);
  border-color: var(--pb-blue);
  border-style: solid;
  color: var(--pb-blue-darker);
  text-decoration: none;
}

/* --- Task card inside a day column --- */
.pb-task {
  background-color: var(--pb-surface);
  border: 1px solid var(--pb-border);
  border-left: 4px solid var(--pb-n-400);   /* workstream accent */
  border-radius: var(--pb-radius-m);
  display: block;
  padding: var(--pb-space-3);
  text-decoration: none;
  transition: box-shadow var(--pb-dur-fast) var(--pb-ease),
              border-color var(--pb-dur-fast) var(--pb-ease);
}

.pb-task:hover {
  box-shadow: var(--pb-shadow-m);
  text-decoration: none;
}

.pb-task__title {
  color: var(--pb-text);
  display: block;
  font-size: var(--pb-text-s);
  font-weight: 700;
  line-height: var(--pb-leading-snug);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  overflow-wrap: anywhere;
}

.pb-task:hover .pb-task__title {
  text-decoration-thickness: 3px;
}

.pb-task__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-1);
  margin-top: var(--pb-space-2);
}

.pb-task__meta {
  color: var(--pb-text-muted);
  display: block;
  font-size: var(--pb-text-xs);
  margin-top: var(--pb-space-2);
  overflow-wrap: anywhere;
}

/* Recurring instances are visually distinguishable from one-off tasks and
   link back to their template. */
.pb-task--recurring {
  background-color: var(--pb-blue-tint-2);
  border-left-style: dashed;
}

.pb-task__recurring {
  color: var(--pb-blue-dark);
  display: block;
  font-size: var(--pb-text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-top: var(--pb-space-2);
  text-transform: uppercase;
  text-decoration: none;
}

.pb-task__recurring:hover {
  text-decoration: underline;
}

/**
 * Workstream accents - the card's thin LEFT BORDER, and nothing else.
 *
 * The workstream used to be a badge on the card as well, which meant every card
 * in the week grid carried a coloured chip saying "Clinical" whether or not the
 * reader cared. The colour already encodes it, and the label survives in the
 * accessible name: `title` attribute below, and the detail page states it in
 * full. So this is not colour-alone - the information is available as text in
 * two places, it is simply not shouted on the card.
 *
 * Contrast note: these are DECORATIVE accents, never the sole carrier of
 * meaning, so the 3:1 non-text contrast requirement does not bind them. They are
 * the same tokens the badges used.
 */
.pb-task--general       { border-left-color: var(--pb-ws-general); }
.pb-task--clinical      { border-left-color: var(--pb-ws-clinical); }
.pb-task--housekeeping  { border-left-color: var(--pb-ws-housekeeping); }
.pb-task--maintenance   { border-left-color: var(--pb-ws-maintenance); }
.pb-task--compliance    { border-left-color: var(--pb-ws-compliance); }

/**
 * Done tasks: stated, not struck through.
 *
 * The title used to carry a line-through. On a card whose title wraps to three
 * lines that decoration cuts across the text and makes the one thing the reader
 * needs - WHAT was done - the hardest thing to read. A muted "✓ Done" line and a
 * softened title say the same thing without damaging the words.
 *
 * ── WHY THERE IS NO CARD-WIDE `opacity` HERE ────────────────────────────────
 * The first attempt dimmed the whole card with `opacity: 0.72`. That is an
 * accessibility defect, and axe caught it: opacity composites the ELEMENT AND
 * ALL ITS TEXT against the backdrop, so the muted "✓ Done" line and the
 * "Signature · Note" line - already the lowest-contrast text on the card at
 * 6.37:1 - were dragged down to 3.38:1, under the 4.5:1 AA floor. Dimming text
 * is not a way to dim a card; it is a way to make text unreadable.
 *
 * So the "done" cue is carried by the pieces that CAN be softened safely:
 *
 *   - the title drops to `--pb-text-muted`, which is 6.37:1 on the card - still
 *     comfortably AA, but visibly quieter than the 11.59:1 of a live task;
 *   - the card's background tints to the sunken surface and its border fades to
 *     the light border token, so it recedes against its neighbours;
 *   - the "✓ Done" line states it in words.
 *
 * Colour is never the only signal, and every piece of text stays above AA.
 */
.pb-task--done {
  background-color: var(--pb-surface-sunken);
  border-color: var(--pb-border);
}

.pb-task--done .pb-task__title {
  color: var(--pb-text-muted);
  font-weight: 400;
  text-decoration-thickness: 1px;
}

.pb-task--overdue {
  border-color: var(--pb-red);
}

.pb-task--overdue .pb-task__title {
  color: var(--pb-red-dark);
}

/** The muted "✓ Done" text. Grey, small, and NOT a badge. */
.pb-task__done-mark {
  color: var(--pb-text-muted);
  font-size: var(--pb-text-xs);
  font-weight: 700;
}

/** The "Signature · Note" requirement line. One muted line, not two badges. */
.pb-task__req {
  color: var(--pb-text-muted);
  font-size: var(--pb-text-xs);
}

/* --- Phone: day-by-day stacked list. Never a squashed 7-col grid. --- */
@media (max-width: 48.0525em) {
  .pb-week__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--pb-space-3);
  }

  .pb-week__toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .pb-week__nav {
    justify-content: space-between;
  }

  .pb-day__body {
    padding: var(--pb-space-3);
  }
}

/* Tablet: two columns before the full seven. */
@media (min-width: 48.0625em) and (max-width: 63.99em) {
  .pb-week__grid {
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
}

/* Screen-reader-only helper (NHS ships .nhsuk-u-visually-hidden, this mirrors
   it for our own markup). */
.pb-visually-hidden {
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  margin: 0;
  overflow: hidden;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

/* -----------------------------------------------------------------------------
   20. RECURRENCE FIELDSET (new-task form)
   -------------------------------------------------------------------------- */
.pb-recurrence {
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-l);
  background-color: var(--pb-surface-raised);
  box-shadow: var(--pb-shadow-s);
  margin-bottom: var(--pb-space-5);
  padding: var(--pb-space-4);
}

.pb-recurrence > legend {
  font-size: var(--pb-text-l);
  font-weight: 700;
  padding: 0 var(--pb-space-2);
}

.pb-recurrence__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-4);
}

.pb-recurrence__row > .nhsuk-form-group {
  flex: 1 1 12rem;
  min-width: 0;
}

/* -----------------------------------------------------------------------------
   22. TRAINING MATRIX
   -----------------------------------------------------------------------------
   Staff down, courses across. This is the artefact managers show inspectors, so
   it must print cleanly. The table uses fixed layout with minmax(0, 1fr) so a
   long course title cannot force the page to scroll horizontally.

   State is carried by THREE channels at once - colour, a glyph and a text
   label - because an inspector's copy may be photocopied in black and white
   and a colour-blind reader still has to use it. Colour alone is never the only
   signal (UI-REDESIGN §5).
   -------------------------------------------------------------------------- */
.pb-mx__scroll {
  overflow-x: auto;
  /* Contain the scroll: without this the page itself scrolls sideways on a
     phone, which is the failure the week-grid work already fixed. */
  max-width: 100%;
}

.pb-mx {
  border-collapse: collapse;
  width: 100%;
  table-layout: fixed;
  font-size: var(--pb-text-s);
}

.pb-mx__caption {
  caption-side: top;
  text-align: left;
  font-weight: 700;
  padding-bottom: var(--pb-space-2);
  color: var(--pb-text);
}

.pb-mx__head {
  background-color: var(--pb-n-50);
  border-bottom: 2px solid var(--pb-text);
  padding: var(--pb-space-2);
  text-align: left;
  vertical-align: bottom;
  /* Long course titles wrap rather than widen the column. */
  overflow-wrap: anywhere;
  hyphens: auto;
}

.pb-mx__head--staff {
  /* Staff and role columns get a stable share; courses share the rest. */
  width: 11rem;
}

.pb-mx__staff {
  border-bottom: 1px solid var(--pb-border);
  padding: var(--pb-space-2);
  text-align: left;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.pb-mx__role {
  border-bottom: 1px solid var(--pb-border);
  padding: var(--pb-space-2);
  color: var(--pb-n-600);
  text-transform: capitalize;
}

.pb-mx__cell {
  border-bottom: 1px solid var(--pb-border);
  border-left: 1px solid var(--pb-border);
  padding: var(--pb-space-2);
  text-align: center;
  /* The 1fr discipline: cells may shrink below their content's natural width. */
  min-width: 0;
}

.pb-mx__glyph {
  display: block;
  font-weight: 700;
  line-height: 1;
}

.pb-mx__label {
  display: block;
  font-size: var(--pb-text-xs);
  margin-top: 2px;
}

.pb-mx__cell--valid      { background-color: var(--pb-green-tint);  color: var(--pb-green-dark); }
.pb-mx__cell--expiring   { background-color: var(--pb-amber-tint);  color: var(--pb-amber-dark); }
.pb-mx__cell--expired    { background-color: var(--pb-red-tint);    color: var(--pb-red-dark); }
.pb-mx__cell--not_held   { background-color: var(--pb-n-50);        color: var(--pb-n-700); }
.pb-mx__cell--not_required { color: var(--pb-n-500); }

.pb-mx__summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-3);
  margin-bottom: var(--pb-space-4);
}

.pb-mx__stat {
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-m);
  padding: var(--pb-space-2) var(--pb-space-3);
  background-color: var(--pb-surface-raised);
}

.pb-mx__stat strong { font-size: var(--pb-text-l); }

.pb-mx__legend-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-3);
}

.pb-mx__legend-list > li {
  display: flex;
  align-items: center;
  gap: var(--pb-space-2);
}

.pb-mx__chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-s);
  font-weight: 700;
  flex: 0 0 auto;
}

.pb-mx__chip--valid     { background-color: var(--pb-green-tint); color: var(--pb-green-dark); }
.pb-mx__chip--expiring  { background-color: var(--pb-amber-tint); color: var(--pb-amber-dark); }
.pb-mx__chip--expired   { background-color: var(--pb-red-tint);   color: var(--pb-red-dark); }
.pb-mx__chip--not_held  { background-color: var(--pb-n-50);       color: var(--pb-n-700); }
.pb-mx__chip--not_required { color: var(--pb-n-500); }

/* A visible marker for content a human still has to supply. Deliberately
   conspicuous: an unresolved TODO that nobody notices is worse than none. */
.pb-todo {
  border-left: 4px solid var(--pb-amber);
  background-color: var(--pb-amber-tint);
  padding: var(--pb-space-2) var(--pb-space-3);
}

.pb-form__status { min-height: 1.5rem; }

/* Phone: a squashed N-column grid is unreadable. Per-staff cards instead, the
   same choice made for the diary week grid. */
@media (max-width: 767px) {
  .pb-mx { table-layout: auto; }
  .pb-mx__scroll { overflow-x: visible; }
  .pb-mx thead { display: none; }
  .pb-mx, .pb-mx tbody, .pb-mx tr, .pb-mx td, .pb-mx th { display: block; width: auto; }
  .pb-mx tr {
    border: 1px solid var(--pb-border);
    border-radius: var(--pb-radius-m);
    margin-bottom: var(--pb-space-3);
    padding: var(--pb-space-2);
  }
  .pb-mx__staff { border-bottom: none; font-size: var(--pb-text-l); }
  .pb-mx__role { border-bottom: 1px solid var(--pb-border); margin-bottom: var(--pb-space-2); }
  .pb-mx__cell {
    border-left: none;
    border-bottom: 1px solid var(--pb-n-100);
    display: flex;
    justify-content: space-between;
    align-items: center;
    text-align: left;
  }
  .pb-mx__glyph { display: inline; }
  .pb-mx__label { display: inline; margin-top: 0; }
}

/* Print: the matrix is the artefact. Chrome goes, the grid stays, and the
   states must survive a monochrome photocopier - hence the glyph and label
   rather than a colour wash alone. */
@media print {
  .nhsuk-header, .nhsuk-footer, .nhsuk-skip-link, .nhsuk-breadcrumb,
  .pb-actions, .pb-form, .pb-mx__legend { display: none; }

  .pb-mx { font-size: 9pt; }
  .pb-mx__scroll { overflow: visible; }

  /* Force the text to black so a greyscale print keeps contrast, and add
     borders so the cell boundaries do not vanish without their background. */
  .pb-mx__cell {
    color: #000;
    background-color: #fff;
    border: 1px solid #000;
  }

  .pb-mx__glyph { font-weight: 700; }
  .pb-mx__label { font-size: 8pt; }

  /* Never break a staff member's row across pages. */
  .pb-mx tr { break-inside: avoid; page-break-inside: avoid; }
  .pb-mx thead { display: table-header-group; }
}

/* -----------------------------------------------------------------------------
   22. NAVIGATOR ("where do I…?") - the header query box and its results page.

   The band is part of the teal header, so its contents must be legible on teal.
   Every colour used here is checked against the background it actually sits on,
   and the focus ring is kept: the box is a primary control, and a search input
   with an invisible focus ring is unusable by keyboard.
   -------------------------------------------------------------------------- */
.pb-navigator__band {
  padding-bottom: 12px;
}

.pb-navigator--compact .nhsuk-form-group {
  margin-bottom: 0;
}

.pb-navigator__row {
  display: flex;
  gap: 8px;
  align-items: stretch;
  flex-wrap: wrap;
}

.pb-navigator__input {
  flex: 1 1 16rem;
  min-width: 0;
  max-width: 32rem;
}

.pb-navigator__button {
  flex: 0 0 auto;
  margin-bottom: 0;
}

/* On the teal band the hint must be cream, not the default grey, which would
   fall below 4.5:1 against teal. */
.pb-navigator__band .nhsuk-hint {
  color: #fbf8f5;
  margin-bottom: 0;
}

.pb-navigator__results {
  list-style: none;
  padding: 0;
  margin: 0 0 24px;
}

.pb-navigator__result {
  border-left: 4px solid #1f6177;
  padding: 12px 0 12px 16px;
  margin-bottom: 12px;
}

.pb-navigator__result:last-child {
  margin-bottom: 0;
}

.pb-navigator__refusal,
.pb-navigator__provenance {
  margin-bottom: 24px;
}

/* The redacted prompt and structured output shown to a manager for audit. It is
   evidence, so it must not wrap into an unreadable mush: a horizontal scroll
   keeps the tokens intact and legible rather than reflowing them. */
.pb-code-block {
  background: #f0f4f5;
  border: 1px solid #d8dde0;
  border-radius: 4px;
  padding: 16px;
  overflow-x: auto;
  font-size: 0.875rem;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
}

@media (prefers-color-scheme: dark) {
  .pb-code-block {
    background: #14252b;
    border-color: #2c4650;
    color: #fbf8f5;
  }
}

/* -----------------------------------------------------------------------------
   21. REDUCED MOTION - every transition above is disabled here.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms;
    animation-iteration-count: 1;
    transition-duration: 0.01ms;
    scroll-behavior: auto;
  }
}

/* -----------------------------------------------------------------------------
   FLOATING ASSISTANT  (Piece 4.5)

   Bottom-left, on every signed-in page. Replaces the header "Find" band.

   Built on <details>/<summary>, so keyboard operation, the expanded state and
   Escape-to-close come from the element rather than from script - see the note
   in ui.ts. There is no aria-expanded here because the element already exposes a
   real expanded state; adding one would be a second source of truth that can
   drift from the first.

   Colours are TOKENS, not hex: the assistant appears on every route in all four
   themes, so a literal colour here would be wrong in three of them. The tokens
   used are already classified in scripts/contrast-check.ts - --pb-surface-raised
   and --pb-text are the text pair, --pb-border is the decorative group, and the
   toggle uses the same teal as a primary button, whose white-on-teal pairing is
   asserted.
   -------------------------------------------------------------------------- */

.pb-assistant {
  position: fixed;
  left: var(--pb-space-3);
  /* Above the footer's own content, not on top of it. The offset keeps the
     collapsed toggle clear of the last footer line at 390px. */
  bottom: var(--pb-space-3);
  z-index: 40;
}

.pb-assistant__toggle {
  align-items: center;
  background-color: var(--pb-teal);
  border: 1px solid var(--pb-teal-dark);
  border-radius: var(--pb-radius-m);
  /* white on teal: asserted at 4.5:1 in contrast-check.ts, the same pair as the
     primary button. */
  color: var(--pb-text-inverse);
  cursor: pointer;
  display: inline-flex;
  font-weight: 700;
  gap: var(--pb-space-2);
  /* 44px minimum: the touch target rule this codebase applies everywhere. */
  min-height: 44px;
  padding: var(--pb-space-2) var(--pb-space-3);
  transition: background-color var(--pb-dur-fast) var(--pb-ease);
}

.pb-assistant__toggle:hover {
  background-color: var(--pb-teal-dark);
}

.pb-assistant__icon {
  font-weight: 700;
}

.pb-assistant__panel {
  background-color: var(--pb-surface-raised);
  border: 1px solid var(--pb-border);
  border-radius: var(--pb-radius-m);
  bottom: calc(100% + var(--pb-space-2));
  /* Cap the width AND the height so the panel cannot run off a 390px screen or
     cover the page on a short viewport. */
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.15);
  left: 0;
  max-height: 70vh;
  max-width: min(320px, calc(100vw - 2 * var(--pb-space-3)));
  overflow-y: auto;
  padding: var(--pb-space-3);
  position: absolute;
}

.pb-assistant__panel .nhsuk-hint,
.pb-assistant__panel .nhsuk-label {
  /* Dense panel: the label is visually hidden by the compact navigator form, so
     the hint carries the meaning. */
  margin-bottom: var(--pb-space-2);
}

/* Clear the fixed toggle from the footer's CONTENT while keeping the footer as
   the last element in the document.

   The toggle is fixed at bottom-left, so at 390px it lands on the footer's
   copyright line. It cannot be moved into the flow without losing the "always
   available" behaviour the brief asks for, so the footer makes room instead.

   Padding the BODY was the first attempt and was wrong: it cleared the overlap
   but left dead space under the footer, and the sweep flagged 48
   footer-not-at-end findings across every phone route. Padding the footer itself
   keeps the footer at the end of the document (the invariant the sweep checks)
   while moving its text clear of the toggle.

   Measured at 390px, 800px viewport, scrolled to the bottom:
     before: footer content ended at 800px, toggle occupied 726-788px  -> covered
     after:  footer content ends at 716px,   toggle occupies 726-788px  -> clear */
/* Applies at EVERY width, not just phone. The first version scoped this to
   max-width: 768px on the assumption there was room beside the footer on
   desktop - measured at 1280px, there is not: the toggle occupied 726-788px and
   the footer copyright sat at 721-740 with "Sign out" at 748-768, so it covered
   both. The toggle is bottom-anchored at every width, so the clearance is
   needed at every width. */
.nhsuk-footer .nhsuk-width-container {
  padding-bottom: 84px;
}

.pb-assistant__links {
  margin-bottom: 0;
  margin-top: var(--pb-space-3);
}

/* ── Rota grid (days down, staff across) ─────────────────────────────────── */
.pb-rota-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-3);
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--pb-space-4);
  padding: var(--pb-space-3) var(--pb-space-4);
  background: var(--pb-surface-sunken);
  border-radius: var(--pb-radius-m);
}
.pb-rota-toolbar__week,
.pb-rota-toolbar__actions,
.pb-inline-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-2);
  align-items: center;
}
.pb-rota-toolbar .nhsuk-button,
.pb-inline-actions .nhsuk-button {
  margin: 0;
}
.pb-rota-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Fill the cell so the WHOLE cell is the click target, not just the badge
     inside it. The cell supplies the padding and the 44px minimum height, so
     the target is the full box rather than a small link centred in it. */
  min-height: 44px;
  min-width: 44px;
  height: 100%;
  margin: calc(-1 * var(--pb-space-3)) calc(-1 * var(--pb-space-4));
  padding: var(--pb-space-3) var(--pb-space-4);
  border-radius: var(--pb-radius-s);
  text-decoration: none;
}
.pb-rota-cell:hover {
  background: var(--pb-blue-tint);
  text-decoration: none;
}
/* Keyboard users get the same affordance as the mouse: the focus ring is
   drawn inside the cell so it is not clipped by the scroll container. */
.pb-rota-cell:focus-visible {
  background: var(--pb-blue-tint);
  outline: 3px solid var(--pb-blue);
  outline-offset: -3px;
}

/* The rota grid scrolls sideways when there are many staff, with the STAFF
   column pinned. Staff are rows and days are columns, so this rarely triggers at
   seven days - but it still protects against a very narrow window. Names and
   dates never break mid-word here (the general table rule above allows it for
   the stacked phone layout). */
.pb-rota-scroll {
  overflow-x: auto;
  /* The grid is the ONE thing allowed to scroll sideways on this page.
     `width` as well as `max-width`: the container is a block in a flow that can
     shrink, and with only max-width it sizes to its CONTENT (the grid, whose first
     column carries a min-width) rather than to the space available - so
     overflow-x: auto would have nothing to scroll and the grid's width would
     escape into the page. width: 100% pins the box to its parent, which is what
     gives overflow-x something to scroll.
     The page-level half of this fix is `body { overflow-x: hidden }` above. */
  width: 100%;
  max-width: 100%;
  border: 1px solid var(--pb-n-200);
  border-radius: var(--pb-radius-m);
  /* Own stacking context, so the sticky name column and the dropdown panels are
     contained by the scroller rather than by the page. */
  position: relative;
}

/* NHS.UK's own .nhsuk-table-container is `overflow-x: auto`, which inside the
   rota card nests a SECOND scroll container inside .pb-rota-scroll. Two nested
   scrollbars around one grid is bad to use: the inner one scrolls the days while
   the outer one scrolls the same grid, and only the outer one keeps the staff
   column pinned. Neutralising the inner container leaves exactly one scroll
   owner, which is the one with the pinned column.
   NOTE: this does NOT fix the pre-existing horizontal overflow on /rota at phone
   width. That was measured against every commit from the branch base (35bf629)
   through item H and the page is 533px wide at all of them, so it predates this
   branch and is not addressed here. */
.pb-rota-scroll .nhsuk-table-container {
  /* BOTH axes. The base rule sets `overflow-x: auto; overflow-y: hidden`, and CSS
     forces a `visible` axis to compute to `auto` when the other axis is not
     visible - so setting `overflow-x: visible` alone silently computes back to
     `auto` and leaves the nested scroll container in place. Setting both to
     visible is what actually removes it. */
  overflow: visible;
}
.pb-rota-scroll .nhsuk-table__header,
.pb-rota-scroll .nhsuk-table__cell {
  overflow-wrap: normal;
  word-break: normal;
  white-space: nowrap;
}
.pb-rota-scroll tr > :first-child {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--pb-surface-raised);
}
.nhsuk-card .pb-rota-scroll .nhsuk-table,
.nhsuk-card .pb-rota-scroll .nhsuk-table-container {
  margin: 0;
}
.pb-rota-scroll caption {
  padding-left: var(--pb-space-3);
}
.pb-rota-scroll tr > :first-child {
  padding-left: var(--pb-space-3);
  padding-right: var(--pb-space-3);
}
/* The staff name column is the grid's row label: keep it readable, but narrow
   enough that the day columns get the rest. 12rem (192px) was more than a third
   of a 390px phone and pushed the days out of reach; 8.5rem holds a name like
   "Clint Shamaine" over two lines and still leaves room for the week. */
.pb-rota-scroll thead th:first-child {
  min-width: 8.5rem;
}

/* -----------------------------------------------------------------------------
   PRINT ROTA - a clean sheet for the staff-room wall
   -----------------------------------------------------------------------------
   A standalone page (it does not go through layout()), so it carries no header
   navigation, tab bar or edit links. It must read well on screen AND print well:
   the screen rules below are the plain, light version, and the @media print
   block turns it into an A4 landscape sheet. */
.pb-print-rota {
  background: #ffffff;
  color: #0b0c0c;
  margin: 0 auto;
  max-width: 1100px;
  padding: var(--pb-space-5);
  font-family: var(--pb-font);
}

.pb-print-rota__head h1 {
  font-size: 1.6rem;
  margin: 0 0 var(--pb-space-2);
}

.pb-print-rota__meta {
  color: var(--pb-text-muted);
  margin: 0 0 var(--pb-space-4);
}

.pb-print-rota__actions {
  display: flex;
  gap: var(--pb-space-3);
  margin-bottom: var(--pb-space-4);
}

/* The scroll container that keeps the sheet from overflowing the PAGE on a
   phone. The grid is eight columns wide and cannot be squeezed below roughly
   560px without the staff names breaking mid-word, so on a narrow screen the
   honest presentation is a horizontally scrollable region - the same rule the
   on-screen rota grid uses, and the only exception to "no horizontal scroll"
   in this product.

   Before this, the table simply spilled: the document became 583px wide on a
   390px viewport, so the whole page scrolled sideways and the Print button and
   key could be scrolled out of reach.

   On paper the container is inert - the @media print block below resets it. */
.pb-print-rota__scroll {
  max-width: 100%;
  overflow-x: auto;
}

.pb-print-rota__grid {
  border-collapse: collapse;
  width: 100%;
}

.pb-print-rota__grid th,
.pb-print-rota__grid td {
  border: 1px solid var(--pb-n-300);
  padding: var(--pb-space-2) var(--pb-space-3);
  text-align: center;
  font-size: 0.95rem;
}

.pb-print-rota__grid thead th {
  background: var(--pb-n-50);
  font-weight: 700;
}

/* The staff name is the row label: left aligned, and kept whole. */
.pb-print-rota__grid .pb-print-rota__name {
  text-align: left;
  white-space: nowrap;
  min-width: 11rem;
}

/* On a phone the 11rem name column plus seven day columns is wider than the
   screen, so the name column narrows and the day padding tightens. The grid
   still scrolls rather than wrapping, but it now starts from a width that fits
   a typical rota of a handful of staff on screen at once. */
@media (max-width: 48.0525em) {
  .pb-print-rota {
    padding: var(--pb-space-3);
  }

  .pb-print-rota__grid .pb-print-rota__name {
    min-width: 7rem;
  }

  .pb-print-rota__grid th,
  .pb-print-rota__grid td {
    padding: var(--pb-space-1) var(--pb-space-2);
  }
}

.pb-print-rota__key,
.pb-print-rota__foot {
  color: var(--pb-text-muted);
  font-size: 0.85rem;
  margin-top: var(--pb-space-4);
}

@media print {
  /* A4 landscape: the grid is wider than it is tall, so portrait would waste
     the sheet and force the day columns to wrap. */
  @page {
    size: A4 landscape;
    margin: 12mm;
  }

  /* Everything on the page is content - there is no site chrome to strip, but
     the buttons are screen affordances and must not be printed. */
  .pb-print-rota__actions {
    display: none !important;
  }

  .pb-print-rota {
    max-width: none;
    padding: 0;
    font-size: 11pt;
  }

  /* The scroll container is a screen affordance. On paper it must not clip the
     sheet or introduce a scrollbar, so it is made inert and the table is
     allowed its natural width across the landscape page. */
  .pb-print-rota__scroll {
    max-width: none;
    overflow-x: visible;
  }

  /* Black-and-white friendly: black rules and grey headings carry no colour
     information, so the sheet is readable on a mono printer. */
  .pb-print-rota__grid th,
  .pb-print-rota__grid td {
    border: 1px solid #000000;
    padding: 3mm 2mm;
    color: #000000;
    font-size: 10pt;
  }

  .pb-print-rota__grid thead th {
    background: #eeeeee;
  }

  /* Repeat the column headings on every page, and never split a staff member's
     week across a page break - half a row is worse than none. */
  .pb-print-rota__grid thead {
    display: table-header-group;
  }

  .pb-print-rota__grid tr {
    break-inside: avoid;
    page-break-inside: avoid;
  }

  .pb-print-rota__head h1 {
    font-size: 14pt;
  }

  .pb-print-rota__key,
  .pb-print-rota__foot {
    font-size: 8pt;
  }
}

/* A page heading that carries a status badge beside it. */
.pb-page-heading {
  display: flex;
  align-items: center;
  gap: var(--pb-space-3);
  flex-wrap: wrap;
  margin-bottom: var(--pb-space-2);
}

/* Bulk-selection controls. These were inline style attributes on the labels and
   buttons; the markup now carries classes so the layout can be changed here. */
.pb-bulk__select-all {
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--pb-space-2);
  font-size: 0.875rem;
}
.pb-bulk__submit {
  padding: 4px 8px;
  font-size: 0.875rem;
  min-height: 36px;
}
.pb-today__listhead {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--pb-space-3);
  flex-wrap: wrap;
  margin-bottom: var(--pb-space-3);
  border-bottom: 1px solid var(--pb-border);
  padding-bottom: var(--pb-space-2);
}
.pb-task__check {
  margin-right: var(--pb-space-2);
}

/* Export report controls: a row of fields that wraps rather than squashing. */
.pb-export-controls {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-4);
  align-items: flex-end;
  margin-bottom: var(--pb-space-4);
  padding: var(--pb-space-4);
  background: var(--pb-surface-sunken);
  border-radius: var(--pb-radius-m);
}
.pb-export-controls .nhsuk-form-group {
  margin-bottom: 0;
}
.pb-export-controls__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--pb-space-2);
  align-items: center;
}
.pb-export-controls__actions .nhsuk-button {
  margin-bottom: 0;
}

/* Audit details: the signature id and ledger hash, closed by default. Staff need
   to know a record is signed; they do not need a UUID in the way of the content. */
.pb-audit-details {
  margin-top: var(--pb-space-3);
}
.pb-audit-details > summary {
  cursor: pointer;
  color: var(--pb-n-700);
  font-size: 0.875rem;
}
.pb-audit-details code {
  font-size: 0.8125rem;
  overflow-wrap: anywhere;
}

/* ── Print ─────────────────────────────────────────────────────────────────
   The export report is what a manager takes to an inspection. On paper the
   chrome is noise, and a hash column squeezes names into "Bethan / y Cox". */
@media print {
  .nhsuk-header,
  .nhsuk-footer,
  .nhsuk-back-link,
  .pb-subnav,
  .pb-export-controls,
  .nhsuk-skip-link,
  .pb-no-print {
    display: none !important;
  }
  .nhsuk-width-container,
  .nhsuk-grid-column-full,
  .nhsuk-grid-column-two-thirds {
    max-width: none !important;
    width: 100% !important;
    margin: 0 !important;
    padding: 0 !important;
  }
  .nhsuk-card {
    border: 0 !important;
    box-shadow: none !important;
    break-inside: avoid;
  }
  .nhsuk-table__header,
  .nhsuk-table__cell {
    overflow-wrap: normal !important;
    word-break: normal !important;
  }
  a[href]::after {
    content: '';
  }
  body {
    font-size: 11pt;
  }
}
