/*
    The app skin.

    Every colour, size, spacing step and radius here comes from a custom property defined in
    css/tokens.css, which is GENERATED from design/tokens.json. Never write a raw hex value in this
    file: the same tokens are generated into design/generated/Tokens.xaml for the MAUI app, and a hex
    value written here is a colour that exists on web and nowhere else.

    THE LOOK, IN ONE PARAGRAPH (owner, 3 October 2026: "everything large, bold, a dashboard with
    cards, not busy"). The reader glances at this on a phone between gates. So the number is the
    biggest thing on every card, then its label, then the detail - three steps, never two things the
    same size. Cards are white on the grey ground with a soft shadow and no hairline border; a status
    is a thick coloured edge on the leading side of a card, never a fill. Teal is the structure: the
    bars, the section badges, buttons and links. Green, amber and red mean safe, watch and breach and
    nothing else.

    Sections:
      0. The typeface
      1. Bootstrap, re-pointed at our tokens
      2. Page basics
      3. Numbers
      4. The shared UI kit  (Components/Ui - StatTile, StatusChip, InfoHint, Disclosure, EmptyState, AppModal)
      5. Bootstrap popovers and modals, re-skinned
      6. Motion, focus and Blazor's own furniture
*/

/* =========================================================================
   0. The typeface

   Atkinson Hyperlegible Next, SIL Open Font License 1.1 - the licence is fonts/OFL.txt, beside the
   file. Made by the Braille Institute for readers with low vision, and chosen for exactly that: its
   look-alike characters are drawn apart on purpose (0 and O, 1 and l and I, 5 and S), which is what
   a figure read in half a second on a phone needs. It also has true tabular figures, which section 3
   depends on.

   ONE file. It is the variable font, so every weight from 200 to 800 comes from these 48 KB and no
   weight is ever faked by the browser.

   SELF-HOSTED, NEVER A CDN. The file is in wwwroot/fonts, the service worker precaches every .woff2
   with the rest of the app, and the CSP is font-src 'self'. font-display: swap is the safety net:
   if the file is ever missing, the text shows at once in the system faces that follow it in
   --font-ui, rather than staying invisible while the browser waits.
   ========================================================================= */
@font-face {
    font-family: 'Atkinson Hyperlegible Next';
    src: url('../fonts/atkinson-hyperlegible-next.woff2') format('woff2');
    font-weight: 200 800;
    font-style: normal;
    font-display: swap;
}

/* =========================================================================
   1. Bootstrap, re-pointed at our tokens

   Mapped with var(), not with copied hex values. --ground and the rest are redefined per theme in
   tokens.css, and var() resolves at use time, so every Bootstrap component follows the theme from
   this one block. ui.js also stamps data-bs-theme on <html> so Bootstrap's own dark variants cover
   anything not listed here.
   ========================================================================= */
:root {
    --bs-body-font-family: var(--font-ui);
    --bs-body-font-size: var(--text-base);
    --bs-body-line-height: var(--leading-normal);
    --bs-body-color: var(--ink);
    --bs-body-bg: var(--ground);
    --bs-emphasis-color: var(--ink);
    --bs-secondary-color: var(--muted);
    --bs-tertiary-bg: var(--sunken);
    --bs-border-color: var(--rule);
    --bs-border-radius: var(--radius-md);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-lg);
    --bs-border-radius-pill: var(--radius-pill);
    --bs-heading-color: var(--ink);

    --bs-primary: var(--accent);
    --bs-link-color: var(--accent);
    --bs-link-hover-color: var(--accent);
    --bs-code-color: var(--accent);

    /* The semantic three, and nothing else, carry meaning. */
    --bs-success: var(--ok);
    --bs-warning: var(--warn);
    --bs-danger: var(--crit);

    --bs-font-monospace: var(--font-mono);

    /* ---- The skin's own derived values. Every one is built from tokens, never from a literal. ----

       --elev-card: how a card stands off the ground. A ring so faint it reads as an edge rather than
       a line, then a short contact shadow and a long soft one. The shadow colour is --shade, which
       is a deep teal in light theme and black in dark, so it never turns into a glow.

       --edge: the width of a status edge. Thick enough to be seen at a glance, which a 3px hairline
       on a rounded card was not. */
    --elev-card:
        0 0 0 1px color-mix(in srgb, var(--rule) 60%, transparent),
        0 1px 2px color-mix(in srgb, var(--shade) 7%, transparent),
        0 12px 32px -18px color-mix(in srgb, var(--shade) 28%, transparent);
    --elev-pop:
        0 0 0 1px var(--rule),
        0 20px 44px -18px color-mix(in srgb, var(--shade) 40%, transparent);
    --edge: 0.375rem;
    --card-pad: var(--space-4);
}

/* Cards breathe more where there is room for it. A phone keeps 16px so the figures keep theirs. */
@media (min-width: 40rem) {
    :root {
        --card-pad: var(--space-5);
    }
}

/* =========================================================================
   2. Page basics
   ========================================================================= */
html {
    /* The reader's own setting. Every size below is rem, so it follows them. */
    font-size: 100%;
}

body {
    /* Explicit, not inherited. A transparent body borrows whatever is behind the page. */
    background-color: var(--ground);
    color: var(--ink);
    font-family: var(--font-ui);
    font-size: var(--text-base);
    line-height: var(--leading-normal);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
    color: var(--ink);
    font-weight: var(--weight-semibold);
    line-height: var(--leading-snug);
    letter-spacing: -0.01em;
}

h1 {
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
}

h2 { font-size: var(--text-lg); }
h3 { font-size: var(--text-md); }
h4, h5, h6 { font-size: var(--text-base); }

h1:focus {
    outline: none;
}

a {
    color: var(--accent);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.18em;
}

a:hover {
    text-decoration-thickness: 2px;
}

code, kbd, samp, pre {
    font-family: var(--font-mono);
}

hr {
    border-color: var(--rule);
    opacity: 1;
}

.content {
    padding-top: var(--space-4);
}

/* A card. The default surface for anything that groups. No hairline border: the shadow and the
   white on grey are the edge. */
.card {
    --bs-card-bg: var(--surface);
    --bs-card-border-width: 0;
    --bs-card-border-color: transparent;
    --bs-card-border-radius: var(--radius-md);
    --bs-card-inner-border-radius: var(--radius-md);
    --bs-card-spacer-y: var(--card-pad);
    --bs-card-spacer-x: var(--card-pad);
    --bs-card-cap-bg: transparent;
    --bs-card-title-spacer-y: var(--space-3);
    box-shadow: var(--elev-card);
}

.card-title {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
}

.card-header,
.card-footer {
    border-color: var(--rule);
}

/* Every button is a comfortable thumb: 44px tall, whatever its size class says. */
.btn {
    --bs-btn-border-radius: var(--radius-sm);
    --bs-btn-padding-x: 1.125rem;
    --bs-btn-padding-y: 0.5rem;
    --bs-btn-font-size: var(--text-base);
    --bs-btn-font-weight: var(--weight-semibold);
    min-height: 2.75rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    font-weight: var(--weight-semibold);
}

.btn-sm {
    --bs-btn-padding-x: var(--space-4);
    --bs-btn-padding-y: 0.375rem;
    --bs-btn-font-size: var(--text-sm);
    --bs-btn-border-radius: var(--radius-sm);
}

.btn-primary {
    --bs-btn-bg: var(--accent);
    --bs-btn-border-color: var(--accent);
    --bs-btn-color: var(--accent-ink);
    --bs-btn-hover-bg: var(--accent);
    --bs-btn-hover-border-color: var(--accent);
    --bs-btn-hover-color: var(--accent-ink);
    --bs-btn-active-bg: var(--accent);
    --bs-btn-active-border-color: var(--accent);
    --bs-btn-active-color: var(--accent-ink);
    --bs-btn-disabled-bg: var(--accent);
    --bs-btn-disabled-border-color: var(--accent);
    --bs-btn-disabled-color: var(--accent-ink);
}

.btn-primary:hover,
.btn-primary:focus-visible {
    filter: brightness(1.12);
}

.btn-outline-secondary {
    --bs-btn-color: var(--ink);
    --bs-btn-bg: var(--surface);
    --bs-btn-border-color: var(--rule);
    --bs-btn-hover-bg: var(--accent-wash);
    --bs-btn-hover-border-color: var(--accent);
    --bs-btn-hover-color: var(--ink);
    --bs-btn-active-bg: var(--accent-wash);
    --bs-btn-active-border-color: var(--accent);
    --bs-btn-active-color: var(--ink);
}

.form-control,
.form-select {
    min-height: 2.75rem;
    background-color: var(--surface);
    border-color: var(--rule);
    color: var(--ink);
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
}

.form-control:focus,
.form-select:focus {
    background-color: var(--surface);
    border-color: var(--accent);
    color: var(--ink);
}

.form-label {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--ink);
}

.form-text {
    font-size: var(--text-sm);
    color: var(--muted);
}

.form-check-input {
    border-color: var(--muted);
}

.form-check-input:checked {
    background-color: var(--accent);
    border-color: var(--accent);
}

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

.small, small {
    font-size: var(--text-sm);
}

.table {
    --bs-table-bg: var(--surface);
    --bs-table-color: var(--ink);
    --bs-table-border-color: var(--rule);
    --bs-table-striped-bg: var(--sunken);
    --bs-table-striped-color: var(--ink);
    --bs-table-hover-bg: var(--sunken);
    --bs-table-hover-color: var(--ink);
}

.table > :not(caption) > * > * {
    padding: var(--space-3) var(--space-3);
}

/* Column heads are labels, not shouting: sentence case, small, muted. */
.table > thead th {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--muted);
    border-bottom-color: var(--rule);
}

/* Wide content scrolls inside its own box. The page body never scrolls sideways. */
.ctt-scroll-x {
    overflow-x: auto;
}

/*
   The one line under a page heading that carries the section 8 disclaimer. Body text, never a muted
   caption: it is the sentence the app is required to keep visible, so it gets full contrast.

   Shared, not scoped per page. Three pages had written their own copy of this rule, which meant a
   fourth page using the same class name silently got no styling at all.
*/
.ctt-page-note {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    color: var(--ink);
    max-width: 68ch;
    margin-bottom: var(--space-4);
}

/* =========================================================================
   3. Numbers

   Tabular figures everywhere a digit lines up under another digit. Proportional digits make a column
   of day counts look ragged, and this app is a column of day counts.
   ========================================================================= */
.ctt-nums,
.ctt-figure,
.ctt-tile__figure,
.ctt-tile__unit,
table td,
table th,
time,
output {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

/* =========================================================================
   4. The shared UI kit

   These class names belong to src/CTT.Web/Components/Ui. Any page may use the components; no page
   should reach past them and restyle these classes.
   ========================================================================= */

/* ---- StatTile ----
   Three steps of size, so the eye has an order to follow: the figure, then the label, then the
   caption. */
.ctt-tile {
    background-color: var(--surface);
    border: 0;
    border-radius: var(--radius-md);
    box-shadow: var(--elev-card);
    padding: var(--card-pad);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    min-width: 0;
}

/* The status colour is a thick leading edge, not a fill. A filled card shouts; an edge marks. Drawn
   as an inset shadow so it follows the card's rounded corners instead of fighting them. */
.ctt-tile--ok { box-shadow: inset var(--edge) 0 0 0 var(--ok), var(--elev-card); }
.ctt-tile--warn { box-shadow: inset var(--edge) 0 0 0 var(--warn), var(--elev-card); }
.ctt-tile--crit { box-shadow: inset var(--edge) 0 0 0 var(--crit), var(--elev-card); }

.ctt-tile__label {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--ink);
    line-height: var(--leading-snug);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.ctt-tile__value {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.ctt-tile__figure {
    font-size: var(--text-figure);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    letter-spacing: -0.02em;
    color: var(--ink);
}

.ctt-tile--sm .ctt-tile__figure {
    font-size: var(--text-figure-sm);
}

/* No answer is not a small number. It is a sentence, in muted type, where the number would be. */
.ctt-tile__figure--none {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: 0;
    color: var(--muted);
    line-height: var(--leading-snug);
}

.ctt-tile__figure--ok { color: var(--ok); }
.ctt-tile__figure--warn { color: var(--warn); }
.ctt-tile__figure--crit { color: var(--crit); }

.ctt-tile__unit {
    font-size: var(--text-md);
    font-weight: var(--weight-medium);
    color: var(--muted);
}

.ctt-tile__caption {
    font-size: var(--text-sm);
    color: var(--muted);
    line-height: var(--leading-snug);
}

.ctt-tile__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

/* ---- StatusChip ----
   Always visible, always short. The chip is the warning; the popover behind it is the explanation.
   Text on a wash is always --ink, so the chip reads at full contrast whatever it means.

   Bigger than it was (owner, 3 October 2026: "need larger text in chips"): the body's small size,
   semibold, with room around it. The status is told three ways - the wash, the ring, the icon's
   colour - and the icon also has its own shape per meaning, so colour is never the only signal. */
.ctt-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    min-height: 2rem;
    padding: 0.25rem 0.75rem 0.25rem 0.625rem;
    border: 0;
    border-radius: var(--radius-pill);
    background-color: var(--sunken);
    box-shadow: inset 0 0 0 1px var(--rule);
    color: var(--ink);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    line-height: var(--leading-snug);
    white-space: nowrap;
    max-width: 100%;
}

/* A chip that explains itself is a button, and must not look like body text got a border.
   The ::after reaches past the chip's own 32px to a 44px tap target without making the chip look
   any taller. */
button.ctt-chip {
    position: relative;
    cursor: help;
    text-align: left;
}

button.ctt-chip::after {
    content: "";
    position: absolute;
    inset: -0.375rem 0;
}

button.ctt-chip:hover {
    filter: brightness(0.97);
}

.ctt-chip__icon {
    font-size: 1.05em;
    line-height: 1;
}

.ctt-chip--ok {
    background-color: var(--ok-wash);
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ok) 55%, transparent);
}

.ctt-chip--ok .ctt-chip__icon { color: var(--ok); }

.ctt-chip--warn {
    background-color: var(--warn-wash);
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--warn) 60%, transparent);
}

.ctt-chip--warn .ctt-chip__icon { color: var(--warn); }

.ctt-chip--crit {
    background-color: var(--crit-wash);
    box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--crit) 60%, transparent);
}

.ctt-chip--crit .ctt-chip__icon { color: var(--crit); }

.ctt-chip--neutral .ctt-chip__icon { color: var(--muted); }

/* ---- InfoHint ----
   The one control that carries prose off the page. Quiet to look at, generous to touch: the icon
   stays small, the ::after gives it a 44px target. */
.ctt-hint {
    appearance: none;
    -webkit-appearance: none;
    position: relative;
    background: none;
    border: 0;
    padding: 0 0.125rem;
    margin: 0;
    color: var(--muted);
    font-size: 1.05em;
    line-height: 1;
    cursor: help;
    border-radius: var(--radius-pill);
    vertical-align: baseline;
}

.ctt-hint::after {
    content: "";
    position: absolute;
    inset: -0.625rem;
}

.ctt-hint:hover {
    color: var(--accent);
}

/* ---- Disclosure ----
   Level 2: the working, one click away. Collapsed on every load; it remembers nothing on purpose,
   because a panel that reopens itself hides the change from the reader who closed it.
   No rule above it: the space does the separating. */
.ctt-disclosure {
    margin-top: var(--space-2);
}

.ctt-disclosure__toggle {
    appearance: none;
    -webkit-appearance: none;
    background: none;
    border: 0;
    min-height: 2.75rem;
    padding: var(--space-2);
    margin: 0 calc(-1 * var(--space-2));
    width: calc(100% + 2 * var(--space-2));
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--accent);
    font-family: inherit;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    text-align: left;
    cursor: pointer;
    border-radius: var(--radius-sm);
}

.ctt-disclosure__toggle:hover {
    background-color: var(--accent-wash);
}

.ctt-disclosure__chevron {
    color: var(--accent);
    font-size: var(--text-xs);
    line-height: 1;
}

.ctt-disclosure__body {
    padding: var(--space-2) 0 var(--space-3);
    font-size: var(--text-sm);
    color: var(--ink);
}

/* ---- CountryFlag ----
   A flag before a country name. 4:3, sized in em so it follows the text it sits in, rounded 2px.
   The thin ring is the --rule token, which follows the theme: it keeps a mostly white flag (Japan,
   Cyprus) visible on a white card in light mode and on a dark card in dark mode. */
.ctt-flag {
    display: inline-block;
    flex: none;
    width: 1.3333em;
    height: 1em;
    object-fit: cover;
    border-radius: 2px;
    box-shadow: 0 0 0 1px var(--rule);
    vertical-align: -0.125em;
    margin-inline-end: 0.4em;
}

.ctt-flag--sm {
    width: 1.12em;
    height: 0.84em;
}

.ctt-flag--lg {
    width: 1.6667em;
    height: 1.25em;
}

/* ---- EmptyState ---- */
.ctt-empty {
    background-color: var(--surface);
    border: 0;
    border-radius: var(--radius-md);
    box-shadow: var(--elev-card);
    padding: var(--space-7) var(--card-pad);
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
}

/* The icon sits in a teal wash: structure, not a verdict - an empty list is not good or bad. */
.ctt-empty__icon {
    display: inline-grid;
    place-items: center;
    width: 4.5rem;
    height: 4.5rem;
    border-radius: var(--radius-pill);
    background-color: var(--accent-wash);
    font-size: var(--text-figure-sm);
    color: var(--accent);
    line-height: 1;
}

.ctt-empty__line {
    color: var(--ink);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    line-height: var(--leading-snug);
    max-width: 34ch;
    margin: 0;
}

/* ---- Layout helpers the kit expects ---- */
.ctt-tile-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
    gap: var(--space-4);
}

.ctt-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
}

/* =========================================================================
   5. Bootstrap popovers and modals, re-skinned

   Level 3. Everything that is prose lives in here, so it has to be readable: full small size, normal
   line height, a real surface behind it, and a measure short enough to read in one pass.
   ========================================================================= */
.popover {
    --bs-popover-bg: var(--surface);
    --bs-popover-border-color: var(--rule);
    --bs-popover-border-radius: var(--radius-md);
    --bs-popover-inner-border-radius: var(--radius-md);
    --bs-popover-header-bg: var(--surface);
    --bs-popover-header-color: var(--ink);
    --bs-popover-header-font-size: var(--text-base);
    --bs-popover-header-padding-x: var(--space-4);
    --bs-popover-header-padding-y: var(--space-3);
    --bs-popover-body-color: var(--ink);
    --bs-popover-body-padding-x: var(--space-4);
    --bs-popover-body-padding-y: var(--space-3);
    --bs-popover-font-size: var(--text-sm);
    --bs-popover-max-width: min(24rem, calc(100vw - 2rem));
    font-family: var(--font-ui);
    box-shadow: var(--elev-pop);
}

.popover-header {
    font-weight: var(--weight-bold);
    border-bottom: 0;
    padding-bottom: 0;
}

.popover-body {
    line-height: var(--leading-normal);
}

.popover-body p:last-child {
    margin-bottom: 0;
}

.modal {
    --bs-modal-bg: var(--surface);
    --bs-modal-color: var(--ink);
    --bs-modal-border-width: 0;
    --bs-modal-border-color: transparent;
    --bs-modal-border-radius: var(--radius-lg);
    --bs-modal-inner-border-radius: var(--radius-lg);
    --bs-modal-header-border-color: transparent;
    --bs-modal-footer-border-color: transparent;
    --bs-modal-padding: var(--space-5);
    --bs-modal-header-padding: var(--space-5) var(--space-5) var(--space-2);
    --bs-modal-box-shadow: var(--elev-pop);
}

.modal-content {
    box-shadow: var(--elev-pop);
}

.modal-title {
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
}

.tooltip {
    --bs-tooltip-bg: var(--ink);
    --bs-tooltip-color: var(--ground);
    --bs-tooltip-border-radius: var(--radius-sm);
    --bs-tooltip-font-size: var(--text-sm);
    --bs-tooltip-opacity: 1;
    font-family: var(--font-ui);
}

.alert {
    --bs-alert-border-radius: var(--radius-md);
    --bs-alert-padding-x: var(--space-4);
    --bs-alert-padding-y: var(--space-3);
}

/* =========================================================================
   6. Motion, focus and Blazor's own furniture
   ========================================================================= */

/* One focus ring for the whole app, on the token colour so it stays visible on an accent button. */
:focus-visible {
    outline: 2px solid var(--focus);
    outline-offset: 2px;
}

.btn:focus,
.btn:active:focus,
.btn-link.nav-link:focus,
.form-control:focus,
.form-check-input:focus {
    /* Bootstrap's own glow, replaced by the ring above. */
    box-shadow: none;
}

/*
    Nothing animates for a reader who asked for no motion. Bootstrap's collapse and fade both animate
    by default, so they are switched off rather than shortened - a 1ms transition still fires events
    in an order that assumes a transition.
*/
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }

    .collapsing {
        transition: none !important;
    }
}

/* An icon that means something is never hidden from a screen reader; a decorative one always is. */
.bi[aria-hidden="true"] {
    speak: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--ok);
}

.invalid {
    outline: 1px solid var(--crit);
}

.validation-message {
    color: var(--crit);
    font-size: var(--text-sm);
}

#blazor-error-ui {
    color-scheme: light only;
    background: #fff8d6;
    color: #12191c;
    bottom: 0;
    box-shadow: 0 -1px 2px rgb(0 0 0 / 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

.blazor-error-boundary {
    background: var(--crit);
    padding: var(--space-4);
    color: #ffffff;
    border-radius: var(--radius-md);
}

.blazor-error-boundary::after {
    content: "An error has occurred.";
}

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

.loading-progress circle {
    fill: none;
    stroke: var(--sunken);
    stroke-width: 0.6rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: var(--weight-semibold);
    color: var(--ink);
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

.form-floating > .form-control-plaintext::placeholder,
.form-floating > .form-control::placeholder {
    color: var(--muted);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder,
.form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* Popover bodies from InfoHint and StatusChip are plain text, set with textContent so rule data
   cannot inject markup. pre-line is what turns the paragraph breaks in that text into real breaks. */
.ctt-popover .popover-body {
    white-space: pre-line;
}

/* EmptyState: one short line under the action, and nothing more. */
.ctt-empty__extra {
    font-size: var(--text-sm);
    color: var(--muted);
    max-width: 46ch;
}

/* =========================================================================
   9. The rest of Bootstrap, pointed at the tokens

   These were written in Layout/MainLayout.razor.css as ::deep rules while app.css was believed to
   have no token wiring in it. It does, so keeping a second copy there meant two files to change and
   two chances for the app to disagree with itself about what amber is. Here they are global, which
   is what an app-wide skin should be.

   Every value is a token. No hex, no font stack, no rule, no threshold, no country.
   ========================================================================= */

.btn-link {
    color: var(--accent);
}

code {
    color: var(--accent);
    font-family: var(--font-mono);
}

.btn-outline-primary {
    border-color: var(--accent);
    color: var(--accent);
    background-color: transparent;
}

.btn-outline-secondary:hover,
.btn-outline-primary:hover {
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
}

.btn-outline-danger {
    border-color: var(--crit);
    color: var(--crit);
    background-color: transparent;
}

.btn-outline-danger:hover {
    background-color: var(--crit);
    color: var(--surface);
}

.list-group {
    --bs-list-group-border-radius: var(--radius-md);
}

.list-group-item {
    background-color: var(--surface);
    border-color: var(--rule);
    color: var(--ink);
    padding: var(--space-3) var(--space-4);
}

.form-control {
    background-color: var(--surface);
    border-color: var(--rule);
    color: var(--ink);
}

/* Alerts: the semantic colours as a wash and a thick leading edge, with ink on top. Never a filled
   block - a card filled red is a card the reader learns to close, and some of these carry the
   warning. */
.alert-secondary,
.alert-info,
.alert-light {
    background-color: var(--sunken);
    border: 0;
    box-shadow: inset var(--edge) 0 0 0 var(--muted);
    color: var(--ink);
}

.alert-success {
    background-color: var(--ok-wash);
    border: 0;
    box-shadow: inset var(--edge) 0 0 0 var(--ok);
    color: var(--ink);
}

.alert-warning {
    background-color: var(--warn-wash);
    border: 0;
    box-shadow: inset var(--edge) 0 0 0 var(--warn);
    color: var(--ink);
}

.alert-danger {
    background-color: var(--crit-wash);
    border: 0;
    box-shadow: inset var(--edge) 0 0 0 var(--crit);
    color: var(--ink);
}

/* Bootstrap's contextual badges. Kept because older screens still use them; the semantic tokens
   mean one thing each, so a bg-success badge can never be the accent by accident. Text on a solid
   semantic fill is --surface: white in light theme, the dark card colour in dark theme, and 5.6:1
   or better on all three fills in both. */
.badge {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    padding: 0.3em 0.65em;
    border-radius: var(--radius-pill);
}

.badge.bg-success,
.text-bg-success {
    background-color: var(--ok) !important;
    color: var(--surface) !important;
}

.badge.bg-warning,
.text-bg-warning {
    background-color: var(--warn) !important;
    color: var(--surface) !important;
}

.badge.bg-danger,
.text-bg-danger {
    background-color: var(--crit) !important;
    color: var(--surface) !important;
}

.badge.bg-secondary,
.text-bg-secondary {
    background-color: var(--muted) !important;
    color: var(--surface) !important;
}

.text-bg-light {
    background-color: var(--sunken) !important;
    color: var(--ink) !important;
}

.text-bg-light.border {
    border-color: var(--rule) !important;
}

/* =========================================================================================
   THE APP MARK
   =========================================================================================
   BrandGlyph renders an <svg> carrying only a viewBox, which has NO intrinsic size inside a
   flex row - so with no width it collapses to zero and the logo is silently absent. That
   already happened once, when a size rule was written without ::deep and matched nothing.

   A default size here means the mark is always visible. A surface that wants a different one
   overrides it, and a surface that forgets still gets a logo rather than a gap. This file is
   global and unscoped, so it reaches the svg wherever BrandGlyph is used.

   Colours are deliberately NOT set here: they come from --brand-deep, --brand-soft and
   --brand-cut, which the component already defaults to accent, accent-soft and ground. Only a
   surface that changes its own background needs to restate them - the accent-filled title bar
   is the one that does. */
.brand-glyph {
    width: 1.5rem;
    height: 1.5rem;
    flex: none;
}

/* =========================================================================================
   THE LEGAL PAGES
   =========================================================================================
   The privacy policy and the terms. Global rather than scoped to the component, because these
   are the only pages in the app that are mostly prose, and prose needs different rules from a
   dashboard: a measure narrow enough to read, and headings that separate parts rather than
   label tiles.

   A measure of 68 characters, not the full column width. A policy set across a wide screen is
   a policy nobody finishes, and these two pages exist to be read rather than skimmed. */
.legal {
    max-width: 68ch;
}

.legal__head {
    margin-bottom: var(--space-6);
}

.legal__draft {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin: var(--space-4) 0;
}

.legal__lede {
    font-size: var(--text-md);
    color: var(--muted);
    line-height: var(--leading-normal);
}

.legal__date {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--muted);
    margin: 0;
}

/* A card per part, so a reader can see where one ends. Sections in flat prose all run together. */
.legal__section {
    background: var(--surface);
    border: 0;
    box-shadow: var(--elev-card);
    border-radius: var(--radius-md);
    padding: var(--card-pad);
    margin-bottom: var(--space-4);

    /* The fragment links land here, and a heading flush against the top of the viewport reads as
       though the section above it is missing. */
    scroll-margin-top: var(--space-7);
}

.legal__section h2 {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    margin: 0 0 var(--space-3);
}

/* The number is the part's address, the thing somebody quotes on the phone. Quiet: it identifies
   the part without competing with what the part says. */
.legal__number {
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--accent);
    flex: none;
}

.legal__section p {
    margin: 0 0 var(--space-3);
}

.legal__section p:last-child,
.legal__section ul:last-child {
    margin-bottom: 0;
}

.legal__section ul {
    margin: 0 0 var(--space-3);
    padding-left: 1.25rem;
    display: grid;
    gap: var(--space-2);
}

.legal__toc {
    background: var(--accent-wash);
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-5);
}

.legal__toc ol {
    margin: 0;
    padding-left: 1.75rem;
    display: grid;
    gap: var(--space-1);
    font-size: var(--text-sm);
}

.legal__toc li::marker {
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.legal__foot {
    font-size: var(--text-sm);
    color: var(--muted);
    margin-top: var(--space-5);
}

/* A short document - the deletion page - gets a wider measure on a laptop. Its parts are lists of
   short lines, not paragraphs, so the 68-character measure only made it a long thin strip. The
   privacy policy and the terms keep the narrow one. */
.legal--wide {
    max-width: 52rem;
}

/* =========================================================================================
   PAGE HEADINGS - one style on every screen.

   The heading and its info icon on one line. No icon in front of a page heading: the tab or the
   sidebar row beside it already carries the screen's icon. The size and weight are the global h1
   rule above, so every page reads the same.
   ========================================================================================= */
.ctt-page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
    margin-bottom: var(--space-4);
}

.ctt-page-head h1 {
    margin: 0;
}

/* =========================================================================================
   THE "SAVED" TOAST. A short, quiet confirmation after a setting saves itself. Fixed above the
   phone tab bar (and its safe area), centred; polite, so a screen reader hears it once.
   ========================================================================================= */
.ctt-toast {
    position: fixed;
    left: 50%;
    bottom: calc(5rem + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%);
    z-index: 1080;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    max-width: calc(100vw - 2 * var(--space-4));
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill, 999px);
    background: var(--ink);
    color: var(--surface);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    pointer-events: none;
}

@media (min-width: 900px) {
    .ctt-toast {
        bottom: var(--space-6);
    }
}

/* =========================================================================================
   THE "NEW VERSION - RELOAD" BAR. In index.html, outside #app; js/pwa.js un-hides it when an
   updated service worker is waiting. Above the phone tab bar, so it never covers a tab.
   ========================================================================================= */
.ctt-update {
    position: fixed;
    left: 50%;
    bottom: calc(5rem + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%);
    z-index: 1090;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    background: var(--surface);
    color: var(--ink);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    box-shadow: var(--elev-card);
}

.ctt-update[hidden] {
    display: none;
}

@media (min-width: 900px) {
    .ctt-update {
        bottom: var(--space-6);
    }
}
