/*
 * ESL Staff - the employee web clock and the admin console.
 *
 * One stylesheet for both surfaces, dressed in the league's own colours so
 * the console reads as the same organisation as the uniform store. The
 * palette, the type scale and the depth below are adapted from that store's
 * storefront.css; a brand change there should be copied here.
 *
 * Everything is mobile first. The clock is one thumb on a phone held in one
 * hand, outdoors, often in sun. The console is a dashboard that also has to
 * work on the president's phone: tables scroll sideways inside their card,
 * never the page.
 *
 * Colour never carries meaning alone. Every pill, banner and state says what
 * it means in words; the colour is a second, faster signal for those who can
 * see it.
 *
 * Light only. The previous dark palette was dropped rather than half-kept:
 * every pair below has been measured on light paper, and a second palette
 * nobody measures is how contrast regressions ship.
 */

@font-face {
    font-family: "Montserrat";
    src: url("/static/fonts/Montserrat-Regular.woff2") format("woff2"),
         url("/static/fonts/Montserrat-Regular.ttf") format("truetype");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Montserrat";
    src: url("/static/fonts/Montserrat-SemiBold.woff2") format("woff2"),
         url("/static/fonts/Montserrat-SemiBold.ttf") format("truetype");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

/* ------------------------------------------------------------------ tokens */

/* One palette, one place.
 *
 * --brand is the league's light blue. White text on it fails contrast, so it
 * is only ever a fill under --ink (8.36:1; 6.77:1 on the hover blue).
 * --brand-deep is the darkened blue for link text and focus rings: 5.89:1 on
 * paper, 5.44:1 on the page tone, 5.36:1 on --brand-wash.
 *
 * --accent is the league's yellow. On white it measures about 1.7:1, so it
 * is never text: it appears as a fill or a rule, with --accent-ink on top
 * (7.54:1 on the yellow, 10.84:1 on --accent-wash).
 *
 * The status washes each pair an ink with the fill it is read on, and every
 * pair clears AA for body text: --good on --good-wash 6.58:1, --bad on
 * --bad-wash 6.06:1, --bad-deep on --bad-wash 9.29:1. --ink-soft is 7.45:1
 * on paper and 6.88:1 on the page tone, so secondary text is still text.
 */
:root {
    --brand: #4dbbff;
    --brand-hover: #23a7fb;
    --brand-deep: #0a6a9e;
    --brand-wash: #eaf6ff;
    --accent: #facc15;
    --accent-ink: #4a3600;
    --accent-wash: #fff8dd;
    --accent-rule: #f1dc8a;

    --ink: #14181f;
    --ink-raised: #2b3442;
    --ink-soft: #4c5663;
    --paper: #ffffff;
    --page: #f4f6f9;
    --zebra: #f8fafc;
    --rule: #e6eaef;
    --rule-strong: #c2cbd6;

    --good: #10633c;
    --good-wash: #e9f6ef;
    --good-rule: #bfe3d0;
    --bad: #b02420;
    --bad-deep: #7d1a17;
    --bad-wash: #fdf0ef;
    --bad-rule: #f0c2bd;

    /* The type scale. Montserrat 400 for prose, 600 for anything that names
     * something. Nothing in this sheet uses a size that is not one of these,
     * except the punch button and the two display numbers, which are sized
     * for a thumb and a glance rather than for reading. */
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-lg: 1.125rem;
    --text-xl: 1.5rem;
    --text-2xl: 1.875rem;

    /* Spacing steps. Gutters, card padding and stack gaps all come from
     * here so the rhythm is the same on every page. */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;

    --radius: 10px;
    --radius-sm: 6px;
    --radius-pill: 999px;

    /* Depth is three layers, not one blur: a hairline contact shadow, a
     * short ambient one and a long soft one that does the lifting. */
    --shadow: 0 1px 1px rgba(20, 24, 31, 0.05),
              0 2px 4px rgba(20, 24, 31, 0.04),
              0 8px 20px rgba(20, 24, 31, 0.05);
    --shadow-lift: 0 1px 2px rgba(20, 24, 31, 0.06),
                   0 6px 12px rgba(20, 24, 31, 0.06),
                   0 18px 38px rgba(20, 24, 31, 0.10);
    --shadow-button: 0 1px 2px rgba(20, 24, 31, 0.14),
                     0 1px 0 rgba(255, 255, 255, 0.45) inset;

    --font: "Montserrat", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
    --mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;

    --content-max: 72rem;
}

/* ------------------------------------------------------------------ reset */

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: var(--font);
    font-size: var(--text-base);
    line-height: 1.5;
    color: var(--ink);
    /* The page is a step darker than the cards, and cooler under the header:
     * that one step of tone is what makes a card read as an object. */
    background-color: var(--page);
    background-image: linear-gradient(180deg, #e9eef5 0, var(--page) 26rem);
    background-repeat: no-repeat;
    min-height: 100vh;
}

h1,
h2,
h3 {
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.25;
}

img {
    max-width: 100%;
}

/* Links take the deep blue and the semibold weight, with the underline on
 * hover and focus, offset clear of the descenders. */
a {
    color: var(--brand-deep);
    font-weight: 600;
    text-decoration: none;
}

a:hover,
a:focus-visible {
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

/* Keyboard focus is visible everywhere, on a ring dark enough to read on
 * paper, on the page tone and on the light-blue fills. */
:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 2px;
    border-radius: 4px;
}

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

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.num,
.tabular {
    font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------------------------ buttons */

/* The primary button. Dark ink on the league's light blue, never white:
 * white on this blue does not measure. A one-stop gradient and a contact
 * shadow so it sits on the card, and it presses in by a pixel. */
.button,
.button-primary,
.button-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: 0.6rem 1.1rem;
    border-radius: var(--radius);
    font-size: var(--text-sm);
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background 120ms ease, box-shadow 120ms ease,
                border-color 120ms ease, transform 80ms ease;
}

.button-primary {
    background: linear-gradient(180deg, #62c3ff 0%, var(--brand) 100%);
    color: var(--ink);
    border: 0;
    box-shadow: var(--shadow-button);
}

.button-primary:hover {
    background: linear-gradient(180deg, var(--brand) 0%, var(--brand-hover) 100%);
    box-shadow: 0 3px 8px rgba(20, 24, 31, 0.18),
                0 1px 0 rgba(255, 255, 255, 0.45) inset;
}

/* The quiet button: paper with a strong rule, ink text. Used for every
 * per-row action so a table of twenty rows is not twenty blue blocks. */
.button,
.button-secondary {
    background: var(--paper);
    color: var(--ink);
    border: 1px solid var(--rule-strong);
    box-shadow: 0 1px 1px rgba(20, 24, 31, 0.05);
}

.button:hover,
.button-secondary:hover {
    border-color: var(--ink-soft);
    background: var(--zebra);
    text-decoration: none;
}

.button-primary:active,
.button-secondary:active,
.button:active {
    transform: translateY(1px);
}

.button-primary:disabled,
.button-secondary:disabled,
.button:disabled,
.button[aria-disabled="true"],
.button-primary[aria-disabled="true"] {
    opacity: 0.55;
    cursor: default;
    transform: none;
}

.button-sm {
    min-height: 36px;
    padding: 0.35rem 0.75rem;
    font-size: var(--text-sm);
    border-radius: var(--radius-sm);
}

/* A button that reads as a link: the header's sign-out. */
.link-button {
    background: none;
    border: 0;
    cursor: pointer;
    color: var(--ink-soft);
    font-size: var(--text-sm);
    font-weight: 600;
    min-height: 44px;
    padding: 0.4rem 0.6rem;
    border-radius: var(--radius-sm);
    white-space: nowrap;
}

.link-button:hover {
    color: var(--ink);
    background: var(--brand-wash);
}

/* ------------------------------------------------------------------ pills */

/* A status in a word. The word is the signal; the wash is a faster second
 * one. Every ink/wash pair here is measured in the token block above. */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.15rem 0.6rem;
    border-radius: var(--radius-pill);
    border: 1px solid var(--rule-strong);
    background: var(--page);
    color: var(--ink-soft);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}

/* The dot is decoration: aria-hidden in the markup, and the pill's word is
 * what is read. Its shape repeats the tone for a glance across a table. */
.pill::before {
    content: "";
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.pill--plain::before {
    display: none;
}

.pill--good {
    background: var(--good-wash);
    border-color: var(--good-rule);
    color: var(--good);
}

.pill--bad {
    background: var(--bad-wash);
    border-color: var(--bad-rule);
    color: var(--bad-deep);
}

.pill--warn {
    background: var(--accent-wash);
    border-color: var(--accent-rule);
    color: var(--accent-ink);
}

.pill--info {
    background: var(--brand-wash);
    border-color: #b9def6;
    color: var(--brand-deep);
}

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

/* ------------------------------------------------------------------ notices */

.notice,
.form-error {
    border-radius: var(--radius-sm);
    border: 1px solid var(--rule);
    border-left-width: 4px;
    padding: 0.65rem 0.85rem;
    font-size: var(--text-sm);
}

.notice--error,
.notice--bad,
.form-error {
    background: var(--bad-wash);
    border-color: var(--bad-rule);
    border-left-color: var(--bad);
    color: var(--bad-deep);
    font-weight: 600;
}

.notice--good {
    background: var(--good-wash);
    border-color: var(--good-rule);
    border-left-color: var(--good);
    color: var(--good);
    font-weight: 600;
}

.notice--warn {
    background: var(--accent-wash);
    border-color: var(--accent-rule);
    border-left-color: var(--accent);
    color: var(--accent-ink);
}

.form-note {
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

/* ------------------------------------------------------------------ cards */

.card {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--space-5);
    margin-bottom: var(--space-4);
    min-width: 0;
}

.card-title {
    font-size: var(--text-lg);
    margin-bottom: var(--space-3);
}

.card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    margin-bottom: var(--space-3);
}

.card-head .card-title {
    margin-bottom: 0;
}

.card-sub {
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.card h3 {
    font-size: var(--text-base);
    margin: var(--space-6) 0 var(--space-1);
}

.card p + p {
    margin-top: var(--space-3);
}

/* A table card has no padding of its own: the table runs edge to edge and
 * its cells carry the gutter, which keeps the header band flush. */
.card--flush {
    padding: 0;
    overflow: hidden;
}

.card--flush > .card-head {
    padding: var(--space-4) var(--space-5) 0;
}

.card--flush > .card-foot {
    padding: var(--space-3) var(--space-5) var(--space-4);
    border-top: 1px solid var(--rule);
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.prose {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    max-width: 44rem;
}

/* ================================================================== */
/* The employee web clock                                              */
/* ================================================================== */

.topbar {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-2) var(--space-4);
    padding-top: calc(var(--space-2) + 4px + env(safe-area-inset-top));
    background: linear-gradient(180deg, #ffffff 0%, #f7fafd 100%);
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 2px 8px rgba(20, 24, 31, 0.06);
}

/* The league's two colours as a stripe across the top of every header. */
.topbar::before,
.app-header::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 4px;
    background: linear-gradient(90deg, var(--brand) 0 78%, var(--accent) 78% 100%);
}

.topbar-brand,
.app-brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-width: 0;
    min-height: 44px;
    color: var(--ink);
    text-decoration: none;
}

.app-brand:hover {
    text-decoration: none;
}

.brand-logo {
    height: 34px;
    width: 34px;
    flex: none;
}

.brand-wordmark {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.15;
}

.brand-name {
    font-size: var(--text-base);
    font-weight: 600;
    white-space: nowrap;
}

.brand-tag {
    font-size: var(--text-xs);
    color: var(--ink-soft);
    white-space: nowrap;
}

.topbar-link {
    font-size: var(--text-sm);
}

.topbar-user,
.app-user {
    margin-left: auto;
    min-width: 0;
    font-size: var(--text-sm);
    color: var(--ink-soft);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.topbar-signout,
.app-signout {
    flex: none;
}

.topbar-signout button {
    background: none;
    border: 0;
    cursor: pointer;
    color: var(--ink-soft);
    font-size: var(--text-sm);
    font-weight: 600;
    min-height: 44px;
    padding: 0.4rem 0.6rem;
    border-radius: var(--radius-sm);
}

.topbar-signout button:hover {
    color: var(--ink);
    background: var(--brand-wash);
}

.screen {
    width: 100%;
    max-width: 30rem;
    margin: 0 auto;
    padding: var(--space-5) var(--space-4);
    padding-bottom: calc(2rem + env(safe-area-inset-bottom));
}

/* The status card: one word for where the shift stands, big enough to read
 * at arm's length, with a coloured rule along the top as a second signal. */
.status-card {
    position: relative;
    overflow: hidden;
    text-align: center;
    padding: var(--space-8) var(--space-5) var(--space-6);
}

.status-card::before {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 6px;
    background: var(--rule-strong);
}

.status-card[data-state="clocked_in"]::before {
    background: var(--good);
}

.status-card[data-state="on_break"]::before {
    background: var(--accent);
}

.status-state {
    font-size: var(--text-2xl);
    font-weight: 600;
    letter-spacing: -0.01em;
}

.status-card[data-state="clocked_in"] .status-state {
    color: var(--good);
}

.status-card[data-state="on_break"] .status-state {
    color: var(--accent-ink);
}

.status-worked {
    display: inline-block;
    margin-top: var(--space-3);
    padding: 0.3rem 0.85rem;
    border-radius: var(--radius-pill);
    background: var(--page);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
}

/* The one button the whole clock exists for. Deliberately enormous: it is
 * pressed with a thumb, sometimes with gloves on, sometimes in a hurry. Dark
 * ink on the light blue (8.36:1), never white. */
.punch-button {
    display: block;
    width: 100%;
    font-size: 1.375rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--ink);
    background: linear-gradient(180deg, #62c3ff 0%, var(--brand) 100%);
    border: 0;
    border-radius: var(--radius);
    padding: 1.5rem 1rem;
    min-height: 5.5rem;
    margin-bottom: var(--space-4);
    cursor: pointer;
    box-shadow: var(--shadow-lift), 0 1px 0 rgba(255, 255, 255, 0.45) inset;
    transition: background 120ms ease, transform 80ms ease;
}

.punch-button:hover {
    background: linear-gradient(180deg, var(--brand) 0%, var(--brand-hover) 100%);
}

.punch-button:active {
    transform: translateY(1px);
}

.punch-button:disabled {
    opacity: 0.55;
    cursor: default;
}

.punch-button:focus-visible {
    outline-offset: 3px;
}

/* Clocking out ends the day, so it does not look like the three buttons
 * that carry on with it. White on --ink is 17.79:1. */
.punch-button[data-kind="clock_out"] {
    background: linear-gradient(180deg, var(--ink-raised) 0%, var(--ink) 100%);
    color: #ffffff;
}

.punch-button[data-kind="clock_out"]:hover {
    background: linear-gradient(180deg, #3a4454 0%, #262f3d 100%);
}

/* The break banner. Each severity is an ink on its own wash, plus a rule. */
.banner {
    display: none;
    border-radius: var(--radius);
    border: 1px solid var(--rule);
    border-left-width: 5px;
    padding: 0.9rem 1rem;
    margin-bottom: var(--space-4);
    font-weight: 600;
    box-shadow: var(--shadow);
}

.banner[data-visible="true"] {
    display: block;
}

.banner[data-severity="rest"] {
    background: var(--good-wash);
    border-color: var(--good-rule);
    border-left-color: var(--good);
    color: var(--good);
}

.banner[data-severity="soon"] {
    background: var(--accent-wash);
    border-color: var(--accent-rule);
    border-left-color: var(--accent);
    color: var(--accent-ink);
}

.banner[data-severity="overdue"] {
    background: var(--bad-wash);
    border-color: var(--bad-rule);
    border-left-color: var(--bad);
    color: var(--bad-deep);
}

.banner-countdown {
    display: block;
    font-variant-numeric: tabular-nums;
    font-size: var(--text-lg);
    margin-top: 0.2rem;
}

.banner-countdown:empty {
    display: none;
}

.punch-list {
    list-style: none;
}

.punch-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--rule);
}

.punch-row:last-child {
    border-bottom: none;
}

.punch-time {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    min-width: 5.5rem;
}

.punch-kind {
    flex: 1;
    color: var(--ink-soft);
}

/* The location word beside a punch. Inside the fence says nothing worth
 * reading, so it is hidden; anything else is shown as its own word. */
.punch-flag {
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: capitalize;
    padding: 0.1rem 0.55rem;
    border-radius: var(--radius-pill);
    border: 1px solid var(--accent-rule);
    background: var(--accent-wash);
    color: var(--accent-ink);
}

.punch-flag[data-status="inside"] {
    display: none;
}

.punch-empty {
    color: var(--ink-soft);
    font-size: var(--text-sm);
    padding: var(--space-2) 0;
}

.waiver-box {
    display: none;
    border-left: 4px solid var(--accent);
}

.waiver-box[data-visible="true"] {
    display: block;
}

.waiver-text {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    margin-bottom: var(--space-3);
}

.waiver-box .button-secondary {
    width: 100%;
}

.punch-error {
    display: none;
    margin-bottom: var(--space-4);
}

.punch-error[data-visible="true"] {
    display: block;
}

.noscript-note {
    margin-bottom: var(--space-4);
}

/* ================================================================== */
/* Sign in, forgot, reset and password: one branded card               */
/* ================================================================== */

body.centered {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--space-6) var(--space-4);
}

body.centered .screen {
    max-width: 26rem;
    padding: 0;
}

.auth-brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
    text-align: center;
}

.auth-logo {
    width: 64px;
    height: 64px;
}

.auth-brand .brand-name {
    font-size: var(--text-lg);
}

.card-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: 100%;
    margin-bottom: 0;
    padding: var(--space-6) var(--space-5);
    border-top: 4px solid var(--brand);
}

.card-form .card-title {
    font-size: var(--text-xl);
    margin-bottom: var(--space-1);
    text-align: center;
}

.card-form > .form-note:first-of-type {
    text-align: center;
}

.card-form label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    margin-top: var(--space-2);
}

.card-form .button-primary {
    margin-top: var(--space-4);
    min-height: 48px;
    font-size: var(--text-base);
}

.card-form .form-note:last-child {
    margin-top: var(--space-2);
    text-align: center;
}

.auth-footer {
    margin-top: var(--space-5);
    font-size: var(--text-xs);
    color: var(--ink-soft);
    text-align: center;
}

/* Privacy and Support: a row of real tap targets above the line of small
 * print, so a thumb can hit them on the sign-in card. */
.auth-footer-links {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-1);
    margin-bottom: var(--space-1);
    font-size: var(--text-sm);
}

.auth-footer-links a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--space-2);
}

/* "English | Español" above the Privacy and Support cards: tap targets as
 * tall as the footer's. The page's own language reads as ink, underlined
 * with the brand's yellow rule; the other is an ordinary link. */
.lang-switch {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: var(--space-1);
    margin: calc(-1 * var(--space-3)) 0 var(--space-3);
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.lang-switch a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--space-2);
}

.lang-switch a[aria-current="page"] {
    color: var(--ink);
    text-decoration: underline;
    text-decoration-color: var(--accent);
    text-decoration-thickness: 3px;
    text-underline-offset: 6px;
}

/* ================================================================== */
/* Privacy and Support: the branded card, widened for reading          */
/* ================================================================== */

/* Long pages start at the top rather than centring: a policy centred in
 * the viewport would open scrolled to its middle on a short screen. */
body.public-page {
    justify-content: flex-start;
    padding-top: var(--space-8);
}

body.public-page .screen {
    max-width: 44rem;
    width: 100%;
}

.legal-card {
    margin-bottom: 0;
    padding: var(--space-6) var(--space-5);
    border-top: 4px solid var(--brand);
    line-height: 1.6;
}

.legal-head {
    text-align: center;
    padding-bottom: var(--space-5);
    border-bottom: 1px solid var(--rule);
}

.legal-title {
    font-size: var(--text-2xl);
}

.legal-lede {
    margin: var(--space-3) auto 0;
    max-width: 34rem;
    color: var(--ink-soft);
}

.legal-meta {
    margin-top: var(--space-3);
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

/* The short version: the four facts most people came for, on the league's
 * blue wash with the yellow rule the brand uses for emphasis. */
.legal-summary,
.legal-toc {
    text-align: left;
}

.legal-summary {
    margin-top: var(--space-5);
    padding: var(--space-4) var(--space-5);
    background: var(--brand-wash);
    border: 1px solid var(--rule);
    border-left: 4px solid var(--accent);
    border-radius: var(--radius-sm);
}

.legal-summary h2 {
    font-size: var(--text-base);
    margin-bottom: var(--space-2);
}

.legal-toc {
    margin-top: var(--space-5);
}

.legal-toc h2 {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: var(--space-2);
}

.legal-toc ol {
    list-style: none;
    display: grid;
    gap: 0 var(--space-4);
    counter-reset: toc;
}

.legal-toc li {
    counter-increment: toc;
    border-bottom: 1px solid var(--rule);
}

.legal-toc a {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    min-height: 44px;
    font-size: var(--text-sm);
}

.legal-toc a::before {
    content: counter(toc);
    flex: none;
    display: inline-grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    border-radius: var(--radius-pill);
    background: var(--brand-wash);
    color: var(--brand-deep);
    font-size: var(--text-xs);
}

.legal-section {
    margin-top: var(--space-6);
    padding-top: var(--space-6);
    border-top: 1px solid var(--rule);
    scroll-margin-top: var(--space-4);
}

.legal-head + .legal-section {
    border-top: 0;
    padding-top: 0;
}

.legal-section h2 {
    font-size: var(--text-lg);
    margin-bottom: var(--space-3);
}

.legal-card h3 {
    font-size: var(--text-base);
    margin: var(--space-5) 0 var(--space-1);
}

.legal-section h2 + h3 {
    margin-top: 0;
}

.legal-card p + p,
.legal-card p + ul,
.legal-card ul + p,
.legal-card ol + p {
    margin-top: var(--space-3);
}

.legal-list {
    padding-left: 1.25rem;
}

.legal-list li + li {
    margin-top: var(--space-2);
}

.legal-list li::marker {
    color: var(--brand-deep);
}

ol.legal-list li::marker {
    font-weight: 600;
}

.legal-note {
    margin-top: var(--space-4);
    padding: var(--space-3) var(--space-4);
    background: var(--zebra);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.legal-note strong {
    color: var(--ink);
}

/* The way to reach a person: one obvious button, the address in words
 * beside it for anyone who would rather copy it. */
.legal-contact {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    margin-top: var(--space-4);
    padding: var(--space-4) var(--space-5);
    background: var(--brand-wash);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
}

.legal-contact .button-primary {
    min-height: 48px;
    font-size: var(--text-base);
}

.legal-card a.button-primary:hover,
.legal-card a.button-primary:focus-visible {
    text-decoration: none;
}

.legal-address {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    overflow-wrap: anywhere;
}

.legal-postal {
    font-style: normal;
    line-height: 1.5;
    color: var(--ink);
}

@media (min-width: 40rem) {
    .legal-card {
        padding: var(--space-8);
    }

    .legal-toc ol {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* ------------------------------------------------------------------ inputs */

/* 16px minimum on a phone: iOS Safari zooms the whole page when a focused
 * input is smaller than that, and never zooms back out. */
input[type="text"],
input[type="email"],
input[type="password"],
input[type="date"],
input[type="datetime-local"],
input[type="number"],
input[type="search"],
select {
    font-size: max(16px, var(--text-base));
    min-height: 44px;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-sm);
    background: var(--paper);
    color: var(--ink);
    width: 100%;
    min-width: 0;
}

input:hover,
select:hover {
    border-color: var(--ink-soft);
}

input:focus-visible,
select:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
    border-color: var(--brand-deep);
}

input[type="checkbox"] {
    width: 1.15rem;
    height: 1.15rem;
    accent-color: var(--brand-deep);
    flex: none;
}

/* ================================================================== */
/* The admin console                                                   */
/* ================================================================== */

.app-header {
    position: sticky;
    top: 0;
    z-index: 10;
    padding-top: calc(4px + env(safe-area-inset-top));
    background: linear-gradient(180deg, #ffffff 0%, #f7fafd 100%);
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.9) inset,
                0 2px 8px rgba(20, 24, 31, 0.06);
}

.topbar {
    position: sticky;
}

.topbar::before {
    top: 0;
}

.app-header-inner,
.app-nav-list,
.app-main {
    max-width: var(--content-max);
    margin: 0 auto;
}

/* On a phone the header is two rows: the brand and the account on top, the
 * navigation as a full-width band beneath, so three targets never share a
 * 44px row with the logo. From 79rem, where all nine links fit, it is one row. */
.app-header-inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: var(--space-2);
    padding: var(--space-1) var(--space-4) 0;
}

.app-brand {
    order: 1;
    flex: 0 1 auto;
}

.app-account {
    order: 2;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-left: auto;
    min-width: 0;
}

.app-user {
    margin-left: 0;
    max-width: 9rem;
}

/* The navigation band. Equal thirds on a phone so nothing wraps and nothing
 * scrolls; from 79rem the links shrink to their own width beside the brand.
 *
 * The current page is marked three ways and only one is colour: the weight
 * steps up, a 3px rule appears under the label, and the link carries
 * aria-current="page". */
.app-nav {
    order: 3;
    flex: 1 0 100%;
    margin: var(--space-1) calc(-1 * var(--space-4)) 0;
    border-top: 1px solid var(--rule);
}

.app-nav-list {
    display: flex;
    list-style: none;
    padding: 0 0.65rem;
}

.app-nav-item {
    flex: 1 1 0;
    min-width: 0;
    /* Keeps the pending-count's hidden text inside the scrolling nav, not the page. */
    position: relative;
}

.app-nav-link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0.5rem 0.35rem;
    font-size: var(--text-sm);
    font-weight: 400;
    color: var(--ink-soft);
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    border-bottom: 3px solid transparent;
    transition: color 120ms ease, background 120ms ease;
}

.app-nav-link:hover {
    color: var(--ink);
    background: var(--brand-wash);
    text-decoration: none;
}

.app-nav-link:focus-visible {
    outline-offset: -3px;
    border-radius: var(--radius-sm);
}

.app-nav-link.is-current {
    color: var(--ink);
    font-weight: 600;
    border-bottom-color: var(--brand-deep);
}

.app-main {
    padding: var(--space-5) var(--space-4) var(--space-8);
}

/* The page title row: an eyebrow, the title, a line of context, and the
 * page's own actions to the right on a wide screen, beneath on a phone. */
.page-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-3) var(--space-4);
    margin-bottom: var(--space-5);
}

.page-head > div {
    min-width: 0;
}

/* The title block takes the room and the actions keep theirs, so a long
 * subtitle wraps under the title instead of pushing the buttons onto a line
 * of their own. */
.page-head > div:first-child {
    flex: 1 1 26rem;
}

.page-head > .page-actions {
    flex: none;
}

.eyebrow {
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: var(--space-1);
}

.eyebrow a {
    letter-spacing: 0.02em;
}

.page-title {
    font-size: var(--text-xl);
    overflow-wrap: anywhere;
}

.page-sub {
    margin-top: var(--space-1);
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

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

.page-actions form {
    display: contents;
}

.page-notice {
    margin-bottom: var(--space-4);
}

/* Stat tiles: one number per tile, the label above it. */
.stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

.stat {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: var(--space-3) var(--space-4);
    min-width: 0;
}

.stat-label {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-soft);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.stat-value {
    margin-top: 0.15rem;
    font-size: var(--text-xl);
    font-weight: 600;
    letter-spacing: -0.01em;
    font-variant-numeric: tabular-nums;
    line-height: 1.2;
}

.stat-unit {
    font-size: var(--text-sm);
    font-weight: 400;
    color: var(--ink-soft);
    margin-left: 0.15rem;
}

.stat-note {
    margin-top: 0.15rem;
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

/* The accent rule on the first tile: the league's yellow, as a rule only. */
.stat--key {
    border-top: 3px solid var(--accent);
}

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

/* A table scrolls sideways inside its card, never the page. The wrapper is
 * focusable so a keyboard user can scroll it too. */
.table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    max-width: 100%;
}

.card .table-wrap + p,
.card h3 + .prose {
    margin-top: var(--space-3);
}

.table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--text-sm);
}

.table th,
.table td {
    padding: 0.7rem var(--space-4);
    text-align: left;
    vertical-align: middle;
    border-bottom: 1px solid var(--rule);
}

.table th:first-child,
.table td:first-child {
    padding-left: var(--space-5);
}

.table th:last-child,
.table td:last-child {
    padding-right: var(--space-5);
}

/* The header band stays in view while a long table scrolls under the
 * sticky app header. Sticky inside the scroll wrapper only works vertically
 * when the wrapper is not the scroller, which on a desktop it is not. */
.table thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--zebra);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-soft);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    white-space: nowrap;
    border-bottom: 1px solid var(--rule-strong);
}

.table tbody tr:nth-child(even) td {
    background: var(--zebra);
}

.table tbody tr:last-child td {
    border-bottom: 0;
}

.table tbody tr:hover td {
    background: var(--brand-wash);
}

.table .num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.table td.num {
    font-weight: 600;
}

.table .nowrap {
    white-space: nowrap;
}

.table .cell-name {
    font-weight: 600;
    white-space: nowrap;
}

.table .cell-soft {
    color: var(--ink-soft);
}

/* A small table inside a padded card (a card's small table) sits
 * in its own hairline box rather than bleeding to the card's edge. */
.card:not(.card--flush) .table-wrap {
    margin-top: var(--space-3);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
}

.card:not(.card--flush) .table th,
.card:not(.card--flush) .table td,
.card:not(.card--flush) .table th:first-child,
.card:not(.card--flush) .table td:first-child,
.card:not(.card--flush) .table th:last-child,
.card:not(.card--flush) .table td:last-child {
    padding: 0.5rem var(--space-3);
}

/* The row that says the table is empty, and what to do about it. */
.table td.empty,
.empty {
    padding: var(--space-8) var(--space-5);
    text-align: center;
    color: var(--ink-soft);
    font-size: var(--text-sm);
}

.empty strong {
    display: block;
    color: var(--ink);
    font-size: var(--text-base);
    margin-bottom: var(--space-1);
}

.table tbody tr:hover td.empty {
    background: transparent;
}

.card .empty {
    padding: var(--space-4) 0;
    text-align: left;
}

.table td.empty {
    text-align: center;
}

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

/* The audit trail under an edited punch. */
.audit {
    list-style: none;
    margin-top: var(--space-2);
    padding-left: var(--space-3);
    border-left: 2px solid var(--accent);
    font-family: var(--font);
    font-size: var(--text-xs);
    color: var(--ink-soft);
    white-space: normal;
    min-width: 16rem;
}

.audit li + li {
    margin-top: var(--space-1);
}

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

.field {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    min-width: 0;
}

.field > span,
.field > label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
}

.check {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
    min-height: 36px;
    white-space: nowrap;
}

.form-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-3);
}

/* A long tick-box sentence wraps inside the grid instead of widening it. */
.form-grid > .check {
    white-space: normal;
    align-items: flex-start;
}

.form-grid input,
.form-grid select,
.form-grid textarea {
    max-width: 100%;
    min-width: 0;
}

.form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-2);
}

.inline-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
}

/* The correction form inside a punch row. Compact controls, and wide enough
 * that the three inputs sit on one line on a desktop and wrap on a phone
 * rather than squeeze to nothing. */
.correct-form {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-width: 20rem;
}

/* On a desktop the whole correction is one line: time, kind, reason, Save. */
@media (min-width: 64rem) {
    .correct-form {
        flex-wrap: nowrap;
        min-width: 34rem;
    }

    .correct-form input[type="datetime-local"] {
        flex: 0 0 12.5rem;
    }

    .correct-form select {
        flex: 0 0 8.5rem;
    }

    .correct-form input[type="text"] {
        flex: 1 1 8rem;
        min-width: 0;
    }

    .correct-form button {
        flex: none;
    }
}

.correct-form input,
.correct-form select {
    min-height: 38px;
    padding: 0.35rem 0.55rem;
    font-size: var(--text-sm);
    width: auto;
}

.correct-form input[type="datetime-local"] {
    flex: 1 1 12rem;
}

.correct-form select {
    flex: 0 1 9rem;
}

.correct-form input[type="text"] {
    flex: 1 1 10rem;
}

/* The shifts a person may start: two boxes and their Save on one line. */
.roles-form {
    flex-wrap: nowrap;
    gap: var(--space-3);
}

/* A punch's time in the site's zone, with its date under it only when the
 * punch fell on another calendar day than the work date (past midnight). */
.punch-when {
    display: block;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.punch-when-date {
    display: block;
    font-size: var(--text-xs);
    font-weight: 400;
    color: var(--ink-soft);
}

/* Under a clock-out set to the time the person left the office. */
.punch-note {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

/* The layout of Shift details: one column on a phone, two cards side by side
 * from 64rem. */
.split {
    display: grid;
    gap: var(--space-4);
    align-items: start;
}

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

.split + .card,
.card + .split {
    margin-top: var(--space-4);
}

/* ------------------------------------------------------------------ findings */

/* Activity colours. Walking and driving match the route map (the old flyer
 * dashboard's blue and grey); every band also says what it is in the list
 * beneath the track, so the colour is never the only signal. */
:root {
    --walking: #1e6fd9;
    --driving: #8a8f98;
    --still: #c2cbd6;
    /* White on it is 5.75:1. */
    --finding: #a84a12;
}

/* The day drawn to scale: lane names on the left, a plot on the right with
 * an hour axis, faint hour rules and one row per lane. Decorative for a
 * screen reader (aria-hidden): the list below it says the same in words. */
.track {
    display: grid;
    grid-template-columns: 4.75rem minmax(0, 1fr);
    gap: 0 var(--space-3);
    margin: var(--space-2) 0 var(--space-3);
}

.track-names {
    display: flex;
    flex-direction: column;
}

.track-name {
    height: 30px;
    display: flex;
    align-items: center;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-soft);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.track-name--axis {
    height: 22px;
}

.track-plot {
    position: relative;
    min-width: 0;
}

.track-grid {
    position: absolute;
    top: 22px;
    bottom: 0;
    width: 1px;
    background: var(--rule);
}

.track-axis {
    position: relative;
    height: 22px;
}

.track-tick {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--ink-soft);
    white-space: nowrap;
}

.track-tick.is-first {
    transform: none;
}

.track-tick.is-last {
    transform: translateX(-100%);
}

.track-row {
    position: relative;
    height: 30px;
    border-top: 1px dashed var(--rule);
}

.track-row:last-child {
    border-bottom: 1px dashed var(--rule);
}

.track-mark {
    position: absolute;
    top: 7px;
    height: 16px;
    min-width: 3px;
    border-radius: 4px;
    background: var(--rule-strong);
}

/* A moment rather than a span: a tick the height of the row. */
.track-mark.is-moment {
    top: 4px;
    height: 22px;
    width: 3px !important;
    margin-left: -1px;
    border-radius: 2px;
}

.track-mark--office,
.track-mark--signs,
.track-mark--flyer,
.track-mark--fields {
    background: var(--brand-wash);
    border: 1px solid #9fd3f7;
}

.track-mark--signs,
.track-mark--flyer,
.track-mark--fields {
    background: #e7efff;
    border-color: #b7cdf5;
}

.track-mark--meal {
    background: repeating-linear-gradient(
        135deg,
        #ffffff 0 4px,
        var(--rule-strong) 4px 6px
    );
    border: 1px solid var(--rule-strong);
}

.track-mark--away {
    background: var(--accent);
    border: 1px solid #d4ad0c;
}

.track-mark--away_short {
    background: var(--accent-wash);
    border: 1px dashed #d4ad0c;
}

.track-mark--walking {
    background: var(--walking);
    border-radius: 0;
}

.track-mark--driving {
    background: var(--driving);
    border-radius: 0;
}

.track-mark--still {
    background: var(--still);
    border-radius: 0;
}

.track-mark--travel {
    background: repeating-linear-gradient(
        135deg,
        var(--driving) 0 5px,
        #b7bbc2 5px 9px
    );
    border-radius: 0;
}

.track-mark--tracking_off {
    background: repeating-linear-gradient(
        135deg,
        var(--bad-wash) 0 4px,
        var(--bad-rule) 4px 7px
    );
    border: 1px solid var(--bad-rule);
    border-radius: 0;
}

.track-mark--punch {
    background: var(--ink);
}

.track-mark--rest_tap {
    background: var(--good);
}

.track-mark--attestation {
    background: var(--brand-deep);
}

.track-row[data-lane="findings"] .track-mark {
    background: var(--finding);
    opacity: 0.85;
    height: 10px;
    top: 10px;
}

.track-row[data-lane="findings"] .track-mark.is-moment {
    width: 10px !important;
    height: 10px;
    top: 10px;
    margin-left: -5px;
    border-radius: 50%;
}

.track-legend {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
    margin-bottom: var(--space-4);
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

.track-legend li {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.swatch {
    display: inline-block;
    width: 0.9rem;
    height: 0.6rem;
    border-radius: 2px;
    background: var(--rule-strong);
}

.swatch--office { background: var(--brand-wash); border: 1px solid #9fd3f7; }
.swatch--meal {
    background: repeating-linear-gradient(135deg, #ffffff 0 2px, var(--rule-strong) 2px 3px);
    border: 1px solid var(--rule-strong);
}
.swatch--away { background: var(--accent); }
.swatch--walking { background: var(--walking); }
.swatch--driving { background: var(--driving); }
.swatch--still { background: var(--still); }
.swatch--travel {
    background: repeating-linear-gradient(135deg, var(--driving) 0 3px, #b7bbc2 3px 5px);
}
.swatch--tracking_off {
    background: repeating-linear-gradient(135deg, var(--bad-wash) 0 2px, var(--bad-rule) 2px 4px);
    border: 1px solid var(--bad-rule);
}
.swatch--finding { background: var(--finding); border-radius: 50%; width: 0.6rem; }
/* The written timeline: a time column, a rail of markers, and the entry. */
.tl-list {
    list-style: none;
    border-top: 1px solid var(--rule);
}

.tl-item {
    position: relative;
    display: grid;
    grid-template-columns: 4.75rem 1.25rem minmax(0, 1fr);
    grid-template-areas: "time marker body" ". . actions";
    column-gap: var(--space-3);
    align-items: start;
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--rule);
}

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

/* The rail: a hairline through every marker. */
.tl-item::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(4.75rem + var(--space-3) + 0.625rem - 1px);
    width: 2px;
    background: var(--rule);
}

.tl-time {
    grid-area: time;
    font-size: var(--text-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    padding-top: 0.1rem;
}

.tl-marker {
    grid-area: marker;
    position: relative;
    z-index: 1;
    justify-self: center;
    width: 0.8rem;
    height: 0.8rem;
    margin-top: 0.3rem;
    border-radius: 50%;
    background: var(--paper);
    border: 2px solid var(--ink-soft);
}

.tl-body {
    grid-area: body;
    min-width: 0;
}

.tl-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: 600;
}

.tl-detail {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.2rem var(--space-2);
    margin-top: 0.2rem;
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

.card .tl-detail {
    margin-top: 0.2rem;
}

.tl-quote {
    display: block;
    flex-basis: 100%;
    color: var(--ink);
    font-style: italic;
}

.tl-actions {
    grid-area: actions;
    margin-top: var(--space-2);
}

.tl-item--punch .tl-marker {
    background: var(--ink);
    border-color: var(--ink);
    border-radius: 3px;
}

.tl-item--away .tl-marker {
    background: var(--accent);
    border-color: #d4ad0c;
}

.tl-item--away.is-minor .tl-marker {
    background: var(--accent-wash);
}

.tl-item--away.is-minor .tl-title {
    color: var(--ink-soft);
}

.tl-item--band .tl-marker {
    border-radius: 3px;
    border: 0;
    height: 0.6rem;
    margin-top: 0.4rem;
}

.tl-marker--walking { background: var(--walking); }
.tl-marker--driving { background: var(--driving); }
.tl-marker--still { background: var(--still); }
.tl-marker--travel {
    background: repeating-linear-gradient(135deg, var(--driving) 0 3px, #b7bbc2 3px 5px);
}
.tl-marker--tracking_off {
    background: var(--bad-rule);
}

.tl-item--rest .tl-marker {
    border-color: var(--good);
    background: var(--good-wash);
}

.tl-item--attestation .tl-marker {
    border-color: var(--brand-deep);
    background: var(--brand-wash);
    transform: rotate(45deg);
    border-radius: 2px;
}

.tl-item--attestation.is-no .tl-marker {
    border-color: var(--bad);
    background: var(--bad-wash);
}

/* A finding is the line an administrator acts on: tinted, with its button. */
.tl-item--finding {
    background: linear-gradient(90deg, #fff6ee 0, var(--paper) 70%);
}

.tl-item--finding .tl-marker {
    border-color: var(--finding);
    background: var(--finding);
}

.tl-item--finding.is-reviewed {
    background: none;
}

.tl-item--finding.is-reviewed .tl-marker {
    background: var(--paper);
    border-color: var(--rule-strong);
}

.tl-item--finding.is-reviewed .tl-title {
    color: var(--ink-soft);
}

.tl-empty {
    list-style: none;
}

.review-form {
    display: inline-flex;
}

.route-map,
.route-map-empty {
    height: 320px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--rule);
    background: var(--page);
    min-width: 0;
}

/* Leaflet's own stacking stays inside the map, under the sticky header. */
.route-map {
    isolation: isolate;
}

.route-map-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    padding: var(--space-4);
    text-align: center;
    font-size: var(--text-sm);
    color: var(--ink-soft);
    height: 180px;
}

.route-map-empty strong {
    color: var(--ink);
}

.facts {
    display: grid;
    gap: 0;
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
}

.facts > div {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
    padding: 0.55rem var(--space-3);
    border-bottom: 1px solid var(--rule);
}

.facts > div:last-child {
    border-bottom: 0;
}

.facts dt {
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--ink-soft);
}

.facts dd {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.move-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--accent-wash);
    border: 1px solid var(--accent-rule);
}

.move-form .button-primary {
    align-self: flex-start;
}

.pill--link {
    text-decoration: none;
    color: var(--brand-deep);
    border-color: #b9def6;
    background: var(--paper);
}

.pill--link:hover {
    background: var(--brand-wash);
    text-decoration: none;
}

/* The Shifts list's filter bar. */
.filter-card {
    padding: var(--space-4);
}

.filter-form {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    align-items: end;
}

.filter-form .field:nth-child(3),
.filter-form .segmented,
.filter-actions {
    grid-column: 1 / -1;
}

.filter-actions {
    display: flex;
    gap: var(--space-2);
}

/* Two radio buttons drawn as one control; the inputs stay focusable. */
.segmented {
    border: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    min-width: 0;
}

.segmented legend {
    width: 100%;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    margin-bottom: 0.2rem;
}

.segmented label {
    display: inline-flex;
    position: relative;
}

.segmented input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.segmented label span {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--space-4);
    border: 1px solid var(--rule-strong);
    background: var(--paper);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    cursor: pointer;
}

.segmented label:first-of-type span {
    border-radius: var(--radius-sm) 0 0 var(--radius-sm);
}

.segmented label:last-of-type span {
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    margin-left: -1px;
}

.segmented input:checked + span {
    background: var(--brand-wash);
    border-color: var(--brand-deep);
    color: var(--brand-deep);
    position: relative;
    z-index: 1;
}

.segmented input:focus-visible + span {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
}

/* ------------------------------------------------------------------ corrections */

/* The pending count beside "Corrections" in the header. A count, not a
 * colour: the number is the signal, and the visually hidden word after it
 * says what it counts. */
.nav-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.35rem;
    height: 1.35rem;
    margin-left: 0.35rem;
    padding: 0 0.35rem;
    border-radius: var(--radius-pill);
    background: var(--accent);
    color: var(--accent-ink);
    font-size: var(--text-xs);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    flex: none;
}

.corrections-table .cell-name {
    min-width: 9rem;
}

.correction-day-link {
    display: block;
    margin-top: 0.15rem;
    font-size: var(--text-xs);
    font-weight: 600;
}

.correction-kind {
    font-weight: 600;
}

.correction-change,
.correction-resolver {
    display: block;
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

.correction-resolver {
    white-space: nowrap;
}

.corrections-table .cell-punch {
    min-width: 10rem;
}

.corrections-table .cell-reason {
    min-width: 11rem;
    max-width: 26rem;
    overflow-wrap: anywhere;
}

.corrections-table tr.is-resolved td {
    color: var(--ink-soft);
}

.corrections-table tr.is-resolved .correction-kind {
    color: var(--ink);
}

/* The row the message above is about: a brand rule down its left edge. */
.corrections-table tr.is-highlighted td {
    background: var(--brand-wash);
}

.corrections-table tr.is-highlighted td:first-child {
    box-shadow: inset 3px 0 0 var(--brand-deep);
}

/* Approve above Reject, one width, so the column stays narrow and the two
 * buttons line up row after row. A phone lays them side by side instead. */
.decision {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
    width: 6.75rem;
}

.decision-form {
    flex-direction: column;
    align-items: stretch;
}

.cell-stack {
    display: block;
    min-width: 0;
}

.cell-stack > .nowrap {
    display: block;
}

.decision-form {
    display: flex;
    gap: var(--space-2);
}

.decision-select {
    min-height: 34px;
    padding: 0.3rem 0.5rem;
    font-size: var(--text-sm);
    width: auto;
}

@media (min-width: 48rem) {
    .route-map {
        height: 360px;
    }

    .tl-item {
        grid-template-columns: 5.25rem 1.25rem minmax(0, 1fr) auto;
        grid-template-areas: "time marker body actions";
    }

    .tl-item::before {
        left: calc(5.25rem + var(--space-3) + 0.625rem - 1px);
    }

    .tl-actions {
        margin-top: 0;
    }

    .track {
        grid-template-columns: 5.25rem minmax(0, 1fr);
    }

    .filter-form {
        grid-template-columns: repeat(3, minmax(0, 1fr)) auto auto;
    }

    .filter-form .field:nth-child(3),
    .filter-form .segmented,
    .filter-actions {
        grid-column: auto;
    }
}

/* On a phone the track's hour labels would touch: label every fourth tick,
 * and give the lane names less room. The rules still mark every tick. */
@media (max-width: 39.99rem) {
    .track {
        grid-template-columns: 3.9rem minmax(0, 1fr);
        gap: 0 var(--space-2);
    }

    .track-tick {
        visibility: hidden;
    }

    .track-tick:nth-child(4n + 1) {
        visibility: visible;
    }

    .track-tick.is-last {
        transform: translateX(-50%);
    }

    .track-name {
        font-size: 0.625rem;
    }
}

/* On a phone a wide table becomes a stack of row cards: each cell carries
 * its column name (data-label) on the left and its value on the right, so a
 * row reads top to bottom without any sideways scrolling. The header row is
 * still in the markup for screen readers and wider screens. */
@media (max-width: 39.99rem) {
    .table--stack thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
    }

    .table--stack,
    .table--stack tbody,
    .table--stack tr,
    .table--stack td {
        display: block;
        width: 100%;
    }

    .table--stack tbody tr {
        padding: var(--space-3) var(--space-5);
        border-bottom: 1px solid var(--rule);
    }

    .table--stack tbody tr:last-child {
        border-bottom: 0;
    }

    .table--stack tbody tr:nth-child(even) {
        background: var(--zebra);
    }

    .table.table--stack tbody tr td,
    .table.table--stack tbody tr:hover td {
        background: transparent;
    }

    .table.table--stack td,
    .table.table--stack td:first-child,
    .table.table--stack td:last-child {
        display: flex;
        align-items: flex-start;
        justify-content: space-between;
        gap: var(--space-3);
        padding: 0.3rem 0;
        border: 0;
        text-align: right;
    }

    .table--stack td[data-label]::before {
        content: attr(data-label);
        flex: none;
        padding-top: 0.1rem;
        font-size: var(--text-xs);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        color: var(--ink-soft);
        text-align: left;
    }

    /* The first cell names the row, so it is the row's title, not a pair. */
    .table.table--stack td:first-child {
        font-size: var(--text-base);
        text-align: left;
        padding-bottom: var(--space-2);
    }

    .table--stack td:first-child::before {
        display: none;
    }

    .table--stack .pill-row,
    .table--stack .inline-form {
        justify-content: flex-end;
        min-width: 0;
    }

    .table--stack .correct-form {
        min-width: 0;
        flex: 1;
    }

    .table--stack .decision,
    .table--stack .decision-form {
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: flex-end;
        align-items: center;
        width: auto;
    }

    .table--stack .cell-stack > .nowrap {
        display: inline;
    }

    .table--stack .punch-when {
        text-align: right;
    }

    .corrections-table .cell-reason {
        max-width: none;
        min-width: 0;
    }

    .corrections-table tr.is-highlighted {
        box-shadow: inset 3px 0 0 var(--brand-deep);
        background: var(--brand-wash);
    }

    .corrections-table tr.is-highlighted td:first-child {
        box-shadow: none;
    }

    .table--stack .correct-form input,
    .table--stack .correct-form select,
    .table--stack .correct-form button {
        flex: 1 1 100%;
        width: 100%;
    }

    .table--stack td:has(.correct-form) {
        flex-direction: column;
        text-align: left;
    }

    .table--stack .audit {
        min-width: 0;
        text-align: left;
    }

    .table--stack td:has(.audit) {
        flex-wrap: wrap;
    }

    .table.table--stack td.empty {
        display: block;
        text-align: center;
        padding: var(--space-6) 0;
    }
}

/* ------------------------------------------------------------------ wider */

@media (min-width: 40rem) {
    .app-main {
        padding: var(--space-6) var(--space-6) var(--space-8);
    }

    .app-header-inner {
        padding-inline: var(--space-6);
    }

    .app-nav {
        margin-inline: calc(-1 * var(--space-6));
    }

    .app-user {
        max-width: 16rem;
    }

    .page-title {
        font-size: var(--text-2xl);
    }

    .stats {
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: var(--space-4);
    }

    .form-grid--3 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .form-grid--2 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .form-grid--site {
        grid-template-columns: minmax(0, 2fr) repeat(3, minmax(0, 1fr));
    }

    .form-grid--thresholds {
        grid-template-columns: repeat(2, minmax(0, 20rem));
    }

    .card-form {
        padding: var(--space-8);
    }
}

/* ------------------------------------------------------------ gas price card */

.gas-line {
    font-size: var(--text-base);
    font-weight: 600;
    color: var(--ink);
}

.gas-meta {
    margin-top: var(--space-1);
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.card .gas-line--override {
    margin-top: var(--space-3);
}

.gas-form {
    margin-top: var(--space-4);
}

.gas-history-title {
    margin-top: var(--space-6);
}

/* The history is a short two-column table: boxed and capped so it does not
 * stretch across a wide card. */
.gas-history {
    max-width: 28rem;
    margin-top: var(--space-2);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

/* The navigation band, below the brand row, until the header is wide enough
 * to hold every link beside the brand and the account (79rem). */
@media (max-width: 78.99rem) {
    /* Eight links share the width: each keeps its whole word, and when they
     * do not all fit (a phone, a tablet, a pending count) the band scrolls
     * sideways on its own rather than widening the page. */
    .app-nav {
        min-width: 0;
        overflow-x: auto;
        scrollbar-width: none;
    }

    .app-nav::-webkit-scrollbar {
        display: none;
    }

    .app-nav-item {
        flex: 0 0 auto;
    }

    .app-nav-list {
        padding-inline: var(--space-2);
        gap: var(--space-1);
    }

    .app-nav-link {
        padding-inline: var(--space-3);
        overflow: visible;
    }

    /* A soft edge at the right says the band goes on past the screen. */
    .app-nav {
        -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent);
        mask-image: linear-gradient(90deg, #000 calc(100% - 2rem), transparent);
    }

    .app-nav.is-scrolled-end {
        -webkit-mask-image: none;
        mask-image: none;
    }
}

/* On a phone the header script folds the navigation behind a Menu button
 * (it marks the header .has-menu and shows the button). The brand and the
 * button share one row; the tabs and the account open beneath as a list of
 * full-width rows. Without the script the band above still scrolls, and from
 * 40rem the button keeps its hidden attribute, so nothing here reaches a
 * wider screen. */
@media (max-width: 39.99rem) {
    .app-header.has-menu .app-header-inner {
        padding-bottom: var(--space-1);
    }

    .app-header.has-menu .app-menu-button {
        order: 2;
        display: inline-flex;
        align-items: center;
        gap: var(--space-2);
        min-height: 44px;
        margin-left: auto;
        padding: 0.4rem 0.8rem;
        border: 1px solid var(--rule-strong);
        border-radius: var(--radius-sm);
        background: var(--paper);
        box-shadow: var(--shadow-button);
        color: var(--ink);
        font: inherit;
        font-size: var(--text-sm);
        font-weight: 600;
        cursor: pointer;
    }

    .app-header.has-menu .app-menu-button:focus-visible {
        outline: 3px solid var(--brand-deep);
        outline-offset: 1px;
    }

    .app-header.has-menu .app-menu-button .nav-count {
        margin-left: 0;
    }

    /* Three bars that turn into a cross while the menu is open. */
    .app-menu-icon,
    .app-menu-icon::before,
    .app-menu-icon::after {
        display: block;
        width: 1.05rem;
        height: 2px;
        border-radius: 1px;
        background: currentColor;
        transition: transform 150ms ease, background-color 150ms ease;
    }

    .app-menu-icon {
        position: relative;
    }

    .app-menu-icon::before,
    .app-menu-icon::after {
        content: "";
        position: absolute;
        left: 0;
    }

    .app-menu-icon::before {
        top: -6px;
    }

    .app-menu-icon::after {
        top: 6px;
    }

    .app-header.is-menu-open .app-menu-icon {
        background: transparent;
    }

    .app-header.is-menu-open .app-menu-icon::before {
        transform: translateY(6px) rotate(45deg);
    }

    .app-header.is-menu-open .app-menu-icon::after {
        transform: translateY(-6px) rotate(-45deg);
    }

    .app-header.has-menu .app-nav,
    .app-header.has-menu .app-account {
        display: none;
    }

    .app-header.is-menu-open .app-nav {
        display: block;
        order: 3;
        overflow: visible;
        -webkit-mask-image: none;
        mask-image: none;
    }

    .app-header.is-menu-open .app-nav-list {
        flex-direction: column;
        gap: 0;
        padding: var(--space-1) 0;
    }

    .app-header.is-menu-open .app-nav-link {
        justify-content: flex-start;
        min-height: 48px;
        padding: 0 var(--space-4);
        font-size: var(--text-base);
        border-bottom: 0;
        border-left: 3px solid transparent;
    }

    .app-header.is-menu-open .app-nav-link.is-current {
        border-left-color: var(--brand-deep);
        background: var(--brand-wash);
    }

    .app-header.is-menu-open .app-account {
        display: flex;
        order: 4;
        flex: 1 0 100%;
        justify-content: space-between;
        margin: 0 calc(-1 * var(--space-4));
        padding: var(--space-1) var(--space-4) 0;
        border-top: 1px solid var(--rule);
    }

    .app-header.is-menu-open .app-user {
        max-width: none;
    }
}

/* From 79rem the navigation sits in the header row beside the brand, and the
 * links shrink to their own width: a wide row of equal thirds reads as a
 * form rather than as navigation. */
@media (min-width: 79rem) {
    .app-header-inner {
        flex-wrap: nowrap;
        padding: 0 var(--space-6);
        column-gap: var(--space-4);
    }

    .app-nav {
        order: 2;
        flex: 0 1 auto;
        margin: 0;
        border-top: 0;
        align-self: stretch;
        display: flex;
    }

    .app-account {
        order: 3;
    }

    .app-nav-list {
        padding: 0;
        gap: 0;
    }

    .app-nav-item {
        flex: 0 0 auto;
        display: flex;
    }

    .app-nav-link {
        padding: 0 var(--space-3);
        min-height: 60px;
    }
}

@media (min-width: 48rem) {
    .split {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

@media (min-width: 64rem) {
    .split {
        grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    }
}

/* ------------------------------------------------------------------ print */

/* Shift details and the pay confirm are the two pages that end up on paper:
 * drop the chrome and the forms, keep the tables and their words. */
/* ------------------------------------------------------------- campaigns */

/* A route name that links to its editor. */
.campaign-link {
    font-weight: 600;
    color: var(--ink);
    text-decoration-color: var(--rule-strong);
    text-underline-offset: 3px;
}

.campaign-link:hover {
    color: var(--brand-deep);
    text-decoration-color: currentColor;
}

.field-optional,
.field > .field-optional {
    font-weight: 400;
    color: var(--ink-soft);
}

/* Help under a field: quieter and smaller than its label. */
.field > .field-optional {
    font-size: var(--text-xs);
    line-height: 1.45;
    margin-top: 0.1rem;
}

.title-pill {
    vertical-align: middle;
    margin-left: var(--space-2);
    position: relative;
    top: -0.15em;
}

.page-sub .campaign-notes {
    display: block;
    margin-top: var(--space-1);
    color: var(--ink);
}

/* The map's filter bar: walker and dates, then layers and colour. */
.map-filters {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    align-items: end;
}

.map-filters .field:first-child,
.map-filters .layer-toggles,
.map-filters .segmented,
.map-filters .filter-actions {
    grid-column: 1 / -1;
}

.layer-toggles {
    border: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    min-width: 0;
}

.layer-toggles legend {
    width: 100%;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    margin-bottom: 0.2rem;
}

.layer-toggle {
    display: inline-flex;
    position: relative;
}

.layer-toggle input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
}

.layer-toggle span {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    min-height: 44px;
    padding: 0 var(--space-4);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-pill);
    background: var(--paper);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    cursor: pointer;
}

.layer-toggle input:checked + span {
    background: var(--brand-wash);
    border-color: var(--brand-deep);
    color: var(--brand-deep);
}

.layer-toggle input:focus-visible + span {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
}

/* A short sample of the line each layer draws, in the single-colour styles. */
.layer-key {
    display: inline-block;
    width: 1.1rem;
    height: 0;
    border-top: 3px solid #1e6fd9;
}

.layer-toggle--driving .layer-key { border-top: 3px dashed #6b7280; }
.layer-toggle--still .layer-key { border-top: 3px dotted #b45309; }
.layer-toggle--unverified .layer-key { border-top: 3px dashed #7c3aed; }

.layer-toggle input:not(:checked) + span .layer-key {
    opacity: 0.45;
}

/* The map itself, with its status line laid over the top edge. Its own
 * stacking context, so Leaflet's panes, controls and popups (z-index up to
 * 1000) stay under the sticky header (z-index 10). */
.coverage-frame {
    position: relative;
    isolation: isolate;
}

.coverage-map {
    height: 420px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--rule);
    background: var(--page);
    min-width: 0;
}

.map-status {
    position: absolute;
    top: var(--space-3);
    left: 50%;
    transform: translateX(-50%);
    z-index: 500;
    max-width: calc(100% - 6rem);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--paper);
    box-shadow: var(--shadow-lift);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink);
    text-align: center;
    pointer-events: none;
}

.map-status[hidden] {
    display: none;
}

.map-status[data-tone="error"] {
    background: var(--bad-wash);
    color: var(--bad-deep);
    border: 1px solid var(--bad-rule);
}

.coverage-legend {
    margin: var(--space-3) 0 var(--space-2);
}

.coverage-legend:empty {
    display: none;
}

/* Leaflet marks every interactive path with a pointer; a legacy line has a
 * tooltip but nowhere to go. */
.leaflet-container path.coverage-line--legacy {
    cursor: default;
}

.coverage-tooltip {
    font-family: var(--font);
    border-radius: var(--radius-sm);
    border-color: var(--rule);
    box-shadow: var(--shadow);
    padding: var(--space-2) var(--space-3);
}

.coverage-tip strong {
    display: block;
    font-size: var(--text-sm);
    color: var(--ink);
}

.coverage-tip span {
    display: block;
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

@media (min-width: 48rem) {
    .map-filters {
        grid-template-columns: minmax(12rem, 1.4fr) repeat(2, minmax(9rem, 1fr));
    }

    .map-filters .field:first-child {
        grid-column: auto;
    }

    .map-filters .layer-toggles {
        grid-column: 1 / 3;
    }

    .map-filters .segmented {
        grid-column: 3 / 4;
    }

    .coverage-map {
        height: 560px;
    }
}

@media (max-width: 39.99rem) {
    .map-status {
        max-width: calc(100% - 2rem);
    }
}

@media print {
    body {
        background: #ffffff;
    }

    .app-header,
    .topbar,
    .page-actions,
    .no-print,
    form {
        display: none !important;
    }

    .app-main {
        max-width: none;
        padding: 0;
    }

    .card,
    .stat {
        box-shadow: none;
        border-color: #999999;
        break-inside: avoid;
    }

    .table-wrap {
        overflow: visible;
    }

    .table thead th {
        position: static;
    }

    .pill {
        border-color: #666666;
    }

    a {
        color: var(--ink);
    }
}

@media (prefers-reduced-motion: reduce) {
    * {
        transition: none !important;
        animation: none !important;
    }
}

/* The one destructive action in the console: an archived campaign's raw
 * points. White on --bad is 6.4:1; the card repeats the red rule. */
.button-danger {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    padding: 0.6rem 1.1rem;
    border-radius: var(--radius);
    border: 0;
    background: var(--bad);
    color: #ffffff;
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 1px 2px rgba(20, 24, 31, 0.2);
    transition: background 120ms ease, transform 80ms ease;
}

.button-danger.button-sm {
    min-height: 36px;
    padding: 0.35rem 0.75rem;
}

.button-danger:hover {
    background: var(--bad-deep);
}

.button-danger:active {
    transform: translateY(1px);
}

.danger-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    align-items: flex-start;
}

.confirm-check {
    display: flex;
    gap: var(--space-2);
    align-items: flex-start;
    font-size: var(--text-sm);
    font-weight: 600;
}

.confirm-check input {
    margin-top: 0.2rem;
}

/* ------------------------------------------------------- signs and banners */

/* The map's shapes, shared by its legend, the table and the popup: a sign is a
 * circle and a banner a square, filled with the person's colour (--person)
 * under a white ring; a dashed person gets a dark ring instead. A taken-down
 * placement is grey. Shape and word always travel together. */
.sign-key {
    display: inline-block;
    flex: none;
    width: 0.85rem;
    height: 0.85rem;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px rgba(20, 24, 31, 0.45);
    background: var(--person, var(--ink-soft));
}

.sign-key--banner {
    border-radius: 2px;
}

.sign-key--ring {
    border: 2px dashed var(--ink);
    box-shadow: none;
}

/* The legend's own shapes carry no person. */
.sign-key--plain {
    background: var(--ink-soft);
}

.sign-key--down {
    border-color: #6b7280;
    box-shadow: none;
    background: #b8bfc8;
}

/* The banner on the map: a divIcon holding this square. */
.sign-square {
    display: block;
    width: 14px;
    height: 14px;
    border-radius: 2px;
    border: 2px solid #fff;
    box-shadow: 0 0 0 1px rgba(20, 24, 31, 0.45);
    background: var(--person, var(--ink-soft));
    box-sizing: border-box;
}

.sign-square--ring {
    border: 2px dashed var(--ink);
    box-shadow: none;
}

.sign-square--down {
    border-color: #6b7280;
    box-shadow: none;
    background: #b8bfc8;
}

.leaflet-marker-icon.sign-square-icon {
    background: transparent;
    border: 0;
}

.sign-type {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 600;
    color: var(--ink);
    white-space: nowrap;
}

.signs-legend {
    margin: var(--space-3) 0 0;
}

.sign-popup-shift {
    display: inline-block;
    margin-bottom: var(--space-2);
    font-weight: 600;
}

.signs-legend li {
    font-size: var(--text-sm);
}

/* A dot gets a soft lift so it reads on busy tiles. */
.leaflet-container path.sign-dot {
    filter: drop-shadow(0 1px 1.5px rgba(20, 24, 31, 0.35));
}

/* A click focuses the dot; only keyboard focus draws the ring. */
.leaflet-container path.sign-dot:focus:not(:focus-visible) {
    outline: none;
}

/* The table. */
.signs-table td {
    vertical-align: middle;
}

.signs-table .cell-note {
    min-width: 8rem;
    max-width: 16rem;
    white-space: normal;
    overflow-wrap: anywhere;
}

.signs-table .cell-campaign {
    min-width: 7rem;
    max-width: 9rem;
}

.signs-table tr.is-down .sign-type,
.signs-table tr.is-down .cell-name a {
    color: var(--ink-soft);
}

.signs-table .cell-stack .pill {
    align-self: flex-start;
}

.sign-thumb {
    display: inline-block;
    line-height: 0;
    border-radius: var(--radius-sm);
    box-shadow: 0 0 0 1px var(--rule);
    overflow: hidden;
    cursor: zoom-in;
}

/* A photo that has not loaded keeps its box and says "Photo" instead of
 * showing a broken image: the label shows only when the picture fails. */
.sign-thumb--photo {
    position: relative;
    width: 56px;
    height: 42px;
    background: var(--page);
}

.sign-thumb--photo img::after {
    content: "Photo";
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    background: var(--page);
    font-size: var(--text-xs, 0.75rem);
    font-weight: 600;
    line-height: 1;
    color: var(--ink-soft);
}

.sign-thumb--photo img {
    position: relative;
    display: block;
}

.sign-thumb img {
    width: 56px;
    height: 42px;
    object-fit: cover;
    background: var(--page);
}

.sign-thumb:hover,
.sign-thumb:focus-visible {
    box-shadow: 0 0 0 2px var(--brand-deep);
}

/* The popup: Leaflet's bubble, dressed as a console card. */
.sign-popup-wrap .leaflet-popup-content-wrapper {
    border-radius: var(--radius);
    box-shadow: var(--shadow-lift);
    padding: 0;
}

.sign-popup-wrap .leaflet-popup-content {
    margin: var(--space-3);
    font-family: var(--font);
    font-size: var(--text-sm);
    line-height: 1.45;
    color: var(--ink);
}

.sign-popup-wrap a.leaflet-popup-close-button {
    top: 6px;
    right: 6px;
    width: 28px;
    height: 28px;
    font-size: 20px;
    line-height: 26px;
    color: var(--ink-soft);
}

.sign-popup-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin: 0 var(--space-6) var(--space-2) 0;
}

.sign-popup-photo {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: var(--page);
    cursor: zoom-in;
}

.sign-popup-photo img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.sign-popup-photo:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 2px;
}

.sign-popup-zoom {
    position: absolute;
    right: var(--space-2);
    bottom: var(--space-2);
    padding: 0.15rem 0.55rem;
    border-radius: var(--radius-pill);
    background: rgba(20, 24, 31, 0.78);
    color: #ffffff;
    font-size: var(--text-xs);
    font-weight: 600;
}

.sign-popup-photo.is-pending,
.sign-popup-photo.is-missing {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.2rem;
    border: 1px dashed var(--rule-strong);
    text-align: center;
    cursor: default;
    color: var(--ink-soft);
    font-size: var(--text-xs);
}

.sign-popup-photo.is-pending strong,
.sign-popup-photo.is-missing strong {
    color: var(--ink);
    font-size: var(--text-sm);
}

.sign-popup-details {
    display: grid;
    grid-template-columns: 5.25rem minmax(0, 1fr);
    gap: 0.3rem var(--space-3);
    margin-top: var(--space-3);
}

.sign-popup-details dt {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-soft);
    padding-top: 0.1rem;
}

.sign-popup-details dd {
    min-width: 0;
    overflow-wrap: anywhere;
}

.sign-popup-details .pill {
    font-size: var(--text-xs);
}

.sign-popup-line {
    display: block;
}

.sign-popup-note {
    font-style: italic;
}

.sign-popup-flag {
    margin-top: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    background: var(--accent-wash);
    color: var(--accent-ink);
    font-size: var(--text-xs);
    font-weight: 600;
}

.sign-popup-form {
    margin-top: var(--space-3);
}

.sign-popup-form .button-secondary {
    width: 100%;
}

/* The full-size photo, over a dimmed page. */
.photo-lightbox {
    width: min(92vw, 60rem);
    max-height: 92vh;
    margin: auto;
    padding: 0;
    border: 0;
    border-radius: var(--radius);
    background: var(--paper);
    box-shadow: var(--shadow-lift);
    overflow: hidden;
}

.photo-lightbox::backdrop {
    background: rgba(20, 24, 31, 0.72);
}

.photo-lightbox-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--rule);
}

.photo-lightbox-bar .link-button {
    margin: 0;
}

.photo-lightbox-caption {
    flex: 1 1 12rem;
    font-size: var(--text-sm);
    font-weight: 600;
}

.photo-lightbox-img {
    display: block;
    width: 100%;
    max-height: calc(92vh - 4.5rem);
    object-fit: contain;
    background: var(--ink);
}

/* Shift details: each placement is a dot in its own lane and a line in the list. */
.track-row[data-lane="placements"] .track-mark.is-moment {
    width: 12px !important;
    height: 12px;
    top: 9px;
    margin-left: -6px;
    border-radius: 50%;
}

.track-mark--placement_sign {
    background: var(--brand);
    border: 2px solid var(--brand-deep);
}

.track-mark--placement_banner {
    background: var(--accent);
    border: 2px solid var(--ink);
}

.swatch--placement_sign,
.swatch--placement_banner {
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    background: var(--brand);
    border: 2px solid var(--brand-deep);
}

.swatch--placement_banner {
    background: var(--accent);
    border-color: var(--ink);
}

.tl-item--placement .tl-marker--placement_sign {
    background: var(--brand);
    border-color: var(--brand-deep);
}

.tl-item--placement .tl-marker--placement_banner {
    background: var(--accent);
    border-color: var(--ink);
}

.tl-item--placement.is-down .tl-marker {
    background: #b8bfc8;
    border-color: #6b7280;
}

/* A control the script switched off stays off, whatever its display. */
.signs-table [hidden] {
    display: none !important;
}

/* Eight columns in the console's width: a little less air between them. */
.signs-table th,
.signs-table td {
    padding-left: 0.6rem;
    padding-right: 0.6rem;
}

.sign-cell .sign-thumb,
.sign-cell .sign-type {
    flex: none;
}

.sign-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    min-width: max-content;
}

.sign-thumb.is-pending,
.sign-thumb.is-missing {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 42px;
    padding: 0 0.2rem;
    border: 1px dashed var(--rule-strong);
    box-shadow: none;
    background: var(--brand-wash);
    color: var(--brand-deep);
    font-size: 0.625rem;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
    cursor: default;
}

/* A photo missing for over a day is not on its way: a neutral, solid box,
 * not the brand-blue dashed "uploading" one. */
.sign-popup-photo.is-missing,
.sign-thumb.is-missing {
    border-style: solid;
    border-color: var(--rule);
    background: var(--zebra);
    color: var(--ink-soft);
}

.sign-popup-photo.is-missing strong {
    color: var(--ink-soft);
}

/* The outside-shift flag: the popup's yellow-ruled block, so a long phrase
 * wraps cleanly instead of bending a pill. */
.sign-flag {
    display: block;
    max-width: 9rem;
    padding: 0.2rem var(--space-2);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    background: var(--accent-wash);
    color: var(--accent-ink);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1.35;
    white-space: normal;
}

.signs-actions {
    width: 9rem;
}

.signs-actions .decision-form,
.signs-actions .button-sm {
    width: 100%;
}

/* Between a phone and a wide laptop the eight columns do not fit, so each
 * placement becomes a card: the photo and type across the top, then its
 * details as label and value pairs in two columns, then its actions. The
 * header row stays in the markup for screen readers. */
@media (min-width: 40rem) and (max-width: 74.99rem) {
    .signs-table thead {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        clip-path: inset(50%);
    }

    .signs-table,
    .signs-table tbody {
        display: block;
        width: 100%;
    }

    .signs-table tbody tr {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: var(--space-6);
        padding: var(--space-4) var(--space-5);
        border-bottom: 1px solid var(--rule);
    }

    .signs-table tbody tr:last-child {
        border-bottom: 0;
    }

    .signs-table tbody tr:nth-child(even) {
        background: var(--zebra);
    }

    .table.signs-table tbody tr td,
    .table.signs-table tbody tr:hover td,
    .table.signs-table tbody tr td:first-child,
    .table.signs-table tbody tr td:last-child {
        display: grid;
        grid-template-columns: 6.5rem minmax(0, 1fr);
        align-items: start;
        gap: var(--space-3);
        min-width: 0;
        max-width: none;
        padding: 0.3rem 0;
        border: 0;
        background: transparent;
        text-align: left;
    }

    .signs-table td[data-label]::before {
        content: attr(data-label);
        padding-top: 0.1rem;
        font-size: var(--text-xs);
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.05em;
        color: var(--ink-soft);
    }

    /* The photo and type title the card; the actions close it. */
    .table.signs-table tbody tr td:first-child,
    .table.signs-table tbody tr td:last-child {
        display: block;
        grid-column: 1 / -1;
    }

    .table.signs-table tbody tr td:first-child {
        padding-bottom: var(--space-2);
    }

    .table.signs-table tbody tr td:last-child {
        padding-top: var(--space-3);
    }

    .signs-table td:first-child::before,
    .signs-table td:last-child::before {
        display: none;
    }

    .signs-table .signs-actions {
        flex-direction: row;
        flex-wrap: wrap;
        width: auto;
    }

    .signs-actions .decision-form,
    .signs-actions .button-sm {
        width: auto;
    }

    .signs-table td.empty {
        display: block;
        grid-column: 1 / -1;
        text-align: center;
    }
}

@media (max-width: 39.99rem) {
    .signs-table .cell-note,
    .signs-table .cell-campaign {
        min-width: 0;
        max-width: none;
    }

    .sign-popup-photo {
        aspect-ratio: 16 / 9;
    }

    /* Tall enough for a whole popup below the zoom buttons. */
    .signs-map {
        height: 540px;
    }

}

/* ------------------------------------------------------------ field routes */

/* Shift details: each visit to a field is a bar in its own lane and a line in
 * the list, in the league blue under its deep ring, like a sign's dot. */
.track-row[data-lane="fields"] .track-mark {
    background: var(--brand);
    border: 1px solid var(--brand-deep);
    border-radius: 3px;
}

.swatch--field_stop {
    background: var(--brand);
    border: 1px solid var(--brand-deep);
}

.tl-item--field_stop .tl-marker--field_stop {
    background: var(--brand);
    border-color: var(--brand-deep);
    border-radius: 3px;
}

.tl-item--field_stop .tl-title .pill {
    margin-left: var(--space-1);
}

/* The status dots: visited blue, still there yellow, skipped grey, and not
 * visited a white dot under the alert red. Always beside the word. */
.status-dot {
    display: inline-block;
    flex: none;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 50%;
    border: 2px solid var(--brand-deep);
    background: var(--brand);
}

.status-dot--still_there {
    border-color: var(--ink);
    background: var(--accent);
}

.status-dot--skipped {
    border-color: #6b7280;
    background: #b8bfc8;
}

.status-dot--not_visited {
    border-color: var(--bad);
    background: var(--paper);
}

.status-dot--not_yet {
    border-color: #6b7280;
    background: var(--paper);
}

.pill .status-dot {
    width: 0.6rem;
    height: 0.6rem;
    border-width: 1.5px;
}

/* A stop's number: the same badge in the list, the tables and the popups. */
.stop-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    min-width: 1.9rem;
    height: 1.9rem;
    padding: 0 0.35rem;
    border-radius: var(--radius-pill);
    background: var(--brand);
    color: var(--ink);
    font-size: var(--text-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    box-shadow: inset 0 0 0 2px var(--brand-deep);
}

/* The editor: the map and its add form, and beside it the stops in order
 * and the route's own settings. One column on a phone, map first. */
.route-editor {
    display: grid;
    gap: 0 var(--space-4);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

.route-side {
    min-width: 0;
}

.route-map {
    height: 380px;
}

/* The search bar above the map. */
.stop-search {
    display: flex;
    gap: var(--space-2);
    align-items: flex-end;
    margin-bottom: var(--space-3);
}

.stop-search[hidden] {
    display: none;
}

.stop-search-field {
    flex: 1 1 auto;
}

.stop-search .button-primary {
    flex: none;
}

.stop-search[aria-busy="true"] .button-primary {
    cursor: progress;
}

.search-status {
    margin-bottom: var(--space-3);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--brand-wash);
    color: var(--ink);
    font-size: var(--text-sm);
    font-weight: 600;
}

.search-status[hidden] {
    display: none;
}

.search-status[data-tone="error"] {
    background: var(--bad-wash);
    color: var(--bad-deep);
    border: 1px solid var(--bad-rule);
}

.search-results {
    list-style: none;
    display: grid;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
}

.search-results[hidden] {
    display: none;
}

.search-result {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    width: 100%;
    min-height: 44px;
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
    background: var(--paper);
    color: var(--ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
    transition: border-color 0.12s ease, background-color 0.12s ease, box-shadow 0.12s ease;
}

.search-result:hover {
    border-color: var(--brand);
    background: var(--brand-wash);
}

.search-result:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
}

.search-result.is-picked {
    border-color: var(--brand-deep);
    background: var(--brand-wash);
    box-shadow: inset 3px 0 0 var(--accent);
}

.search-result-name {
    font-size: var(--text-sm);
    font-weight: 600;
}

.search-result-address {
    font-size: var(--text-xs);
    color: var(--ink-soft);
    overflow-wrap: anywhere;
}

.route-legend li {
    font-size: var(--text-xs);
}

.pin-key {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.15rem;
    height: 1.15rem;
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    background: var(--brand);
    box-shadow: inset 0 0 0 1.5px var(--brand-deep);
    color: transparent;
    font-size: 0;
}

.pin-key--new {
    background: var(--accent);
    box-shadow: inset 0 0 0 1.5px var(--ink);
}

/* The pins themselves: a teardrop with the stop's number, the point at the
 * spot. A new field's pin is the yellow under ink, so it stands apart. */
.field-pin-wrap {
    background: none;
    border: 0;
}

.field-pin {
    position: relative;
    width: 30px;
    height: 30px;
    margin-top: 1px;
    border-radius: 50% 50% 50% 0;
    transform: rotate(-45deg);
    background: var(--brand);
    border: 2.5px solid var(--brand-deep);
    box-shadow: 0 2px 4px rgba(20, 24, 31, 0.35);
    cursor: grab;
}

.field-pin-label {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transform: rotate(45deg);
    color: var(--ink);
    font-family: var(--font);
    font-size: 0.8125rem;
    font-weight: 600;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.field-pin--new {
    background: var(--accent);
    border-color: var(--ink);
}

.field-pin--new .field-pin-label {
    font-size: 1.05rem;
}

.leaflet-dragging .field-pin {
    cursor: grabbing;
}

.leaflet-marker-icon:focus-visible .field-pin {
    outline: 3px solid var(--brand-deep);
    outline-offset: 3px;
}

.field-tip {
    display: grid;
    gap: 0.1rem;
    width: max-content;
    max-width: min(16rem, 60vw);
    white-space: normal;
}

.field-tip strong {
    font-size: var(--text-sm);
    color: var(--ink);
}

.field-tip span {
    font-size: var(--text-xs);
    color: var(--ink-soft);
}

.field-tip .field-tip-hint {
    color: var(--brand-deep);
    font-weight: 600;
}

/* The add form, under the map. */
.add-stop-form {
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--rule);
}

.add-stop-pin {
    margin-bottom: var(--space-3);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
}

.add-stop-pin [data-pin-placed] {
    color: var(--good);
}

.add-stop-pin [hidden] {
    display: none;
}

.form-grid--coords {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.coords {
    margin-top: var(--space-3);
    font-size: var(--text-sm);
}

.coords > summary {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    color: var(--brand-deep);
    font-weight: 600;
    cursor: pointer;
}

.coords > .form-grid {
    margin-top: var(--space-2);
}

.add-stop-form .form-actions,
.route-settings .form-actions {
    margin-top: var(--space-4);
}

[data-add-clear][hidden] {
    display: none;
}

/* The stops, in route order. */
.stop-list {
    list-style: none;
    border-top: 1px solid var(--rule);
}

.stop-row {
    padding: var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--rule);
    transition: background-color 0.3s ease;
    scroll-margin-top: 8rem;
}

.stop-row:last-child {
    border-bottom: 0;
}

.stop-row:target,
.stop-row.is-focused {
    background: var(--brand-wash);
}

.stop-row.is-moved {
    box-shadow: inset 3px 0 0 var(--accent);
}

.stop-row-main {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.stop-text {
    flex: 1 1 auto;
    min-width: 0;
    border-radius: var(--radius-sm);
}

.stop-text.is-linked {
    cursor: pointer;
}

.stop-text.is-linked:hover .stop-name {
    color: var(--brand-deep);
}

.stop-text:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 2px;
}

.stop-name {
    font-weight: 600;
    color: var(--ink);
    overflow-wrap: anywhere;
}

.stop-address {
    font-size: var(--text-xs);
    color: var(--ink-soft);
    overflow-wrap: anywhere;
}

.stop-order {
    display: flex;
    flex: none;
    gap: var(--space-1);
}

.icon-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-sm);
    background: var(--paper);
    color: var(--ink);
    cursor: pointer;
    box-shadow: var(--shadow-button);
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

.icon-button:hover:not(:disabled) {
    border-color: var(--brand-deep);
    background: var(--brand-wash);
}

.icon-button:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
}

.icon-button:disabled {
    color: var(--rule-strong);
    border-color: var(--rule);
    box-shadow: none;
    cursor: default;
}

.stop-edit {
    margin-left: calc(1.9rem + var(--space-3));
}

.stop-edit > summary {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    list-style: none;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--brand-deep);
    cursor: pointer;
}

.stop-edit > summary::-webkit-details-marker {
    display: none;
}

.stop-edit > summary::after {
    content: "";
    width: 0.45rem;
    height: 0.45rem;
    margin-left: 0.4rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translateY(-2px);
}

.stop-edit[open] > summary::after {
    transform: rotate(-135deg) translateY(-1px);
}

.stop-edit-close,
.stop-edit[open] .stop-edit-open {
    display: none;
}

.stop-edit[open] .stop-edit-close {
    display: inline;
}

.stop-edit-form {
    display: grid;
    gap: var(--space-3);
    margin-top: var(--space-2);
    padding: var(--space-4);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
    background: var(--zebra);
}

.stop-edit-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
}

.stop-moved {
    background: var(--accent-wash);
    border-color: var(--accent-rule);
    border-left-color: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

.stop-moved[hidden] {
    display: none;
}

.stop-save-state {
    margin: var(--space-1) 0 0;
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--good);
}

.stop-save-state[hidden] {
    display: none;
}

.stop-save-state[data-tone="bad"] {
    color: var(--bad);
}

.stop-save-state[data-tone="info"] {
    color: var(--ink-soft);
}

.stop-delete-form {
    margin-top: var(--space-1);
}

.link-button--danger {
    color: var(--bad);
    padding-left: 0;
}

.link-button--danger:hover {
    color: var(--bad-deep);
    background: var(--bad-wash);
    padding-left: 0.6rem;
}

.stop-empty {
    padding: var(--space-6) var(--space-5);
    border-top: 1px solid var(--rule);
}

/* The route card: rename and archive. */
.route-rename {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
}

.route-rename .field {
    flex: 1 1 auto;
}

.route-status {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-3);
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--rule);
}

.route-status .form-note {
    flex: 1 1 14rem;
}

@media (min-width: 40rem) {
    .field-routes-table tr.is-active td:first-child {
        box-shadow: inset 3px 0 0 var(--accent);
    }

    .form-grid--stop {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
    }

    .form-grid--route {
        grid-template-columns: minmax(0, 32rem);
    }
}

@media (min-width: 48rem) {
    .route-map {
        height: 480px;
    }
}

/* Side by side from 64rem: the map takes the larger share, the list stays
 * in reach beside it. */
@media (min-width: 64rem) {
    .route-editor {
        grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
    }

    .route-map {
        height: 520px;
    }
}

@media (max-width: 39.99rem) {
    .stop-row {
        padding: var(--space-3) var(--space-4);
    }

    .stop-edit {
        margin-left: 0;
    }

    .stop-search .button-primary {
        padding-inline: var(--space-4);
    }

    .route-map {
        height: 340px;
    }
}

/* The runs report. */
.summary-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0 var(--space-4);
}

.field-summary-table td,
.field-runs-table td {
    vertical-align: middle;
}

.completion {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-2);
}

.completion-bar {
    display: inline-block;
    width: 4.5rem;
    height: 0.5rem;
    border-radius: var(--radius-pill);
    background: var(--rule);
    overflow: hidden;
}

.completion-bar > span {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--brand-deep);
}

.completion-count {
    display: block;
    font-size: var(--text-xs);
}

.field-summary-table--fields tr.is-unvisited .cell-name {
    color: var(--ink-soft);
}

@media (max-width: 39.99rem) {
    .completion {
        justify-content: flex-end;
    }
}

/* A status pill carries its own coloured dot instead of the plain one. */
.pill--status::before {
    display: none;
}

.stop-text p + p {
    margin-top: 0.1rem;
}

/* Edit and Archive side by side: the list has room for both. */
.field-routes-table .decision {
    flex-direction: row;
    width: auto;
}

.field-routes-table .decision .button-sm {
    min-width: 5.5rem;
}

@media (max-width: 39.99rem) {
    .field-routes-table .decision {
        justify-content: flex-end;
    }
}

/* Long numeric headers wrap rather than pushing the last column off the card. */
.field-summary-table thead th.num,
.field-runs-table thead th.num {
    white-space: normal;
    max-width: 9.5rem;
    line-height: 1.3;
}

/* ------------------------------------------------- Settings: office site map */
.site-map {
    height: 320px;
}

.site-map-frame {
    margin-bottom: var(--space-2);
}

.site-map-hint {
    margin-bottom: var(--space-4);
}

@media (min-width: 768px) {
    .site-map {
        height: 400px;
    }
}

/* People and the person page: a faded Owed now row or undone payment, and a flush card's
 * empty line. */
.is-faded {
    color: var(--ink-soft);
}

.is-faded .shift-link {
    font-weight: 400;
}

/* A "nothing here" line inside a flush card keeps the card's side padding. */
.card--flush > .empty {
    padding: var(--space-4) var(--space-5);
}

@media (max-width: 39.99rem) {
    .roles-form {
        flex-wrap: wrap;
    }
}

/* Owed now: one person a row. The dot, the name and role, how they are paid
 * and the amount sit on one line; the sums, chips and actions sit under it. */
.owed-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.owed-person {
    display: grid;
    grid-template-columns: auto minmax(0, 1.4fr) minmax(0, 1.6fr) auto;
    align-items: baseline;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--rule);
}

.owed-person .person-dot,
.owed-person .person-photo {
    margin-right: 0;
}

.owed-person > .person-photo {
    align-self: center;
}

.owed-list:has(.person-photo) .owed-person > .person-dot {
    margin: 0 6px;
}

.owed-who {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--space-2);
    min-width: 0;
}

.owed-role {
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.owed-method,
.owed-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-1) var(--space-2);
    min-width: 0;
}

.owed-head {
    justify-content: space-between;
}

.owed-method-label {
    font-weight: 600;
}

.owed-handle {
    overflow-wrap: anywhere;
    user-select: all;
}

.owed-amount {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: 600;
    text-align: right;
    white-space: nowrap;
}

.owed-body {
    grid-column: 2 / -1;
    min-width: 0;
}

.owed-empty {
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--rule);
}

.owed-total {
    margin: 0;
    font-weight: 600;
    color: var(--ink);
}

.owed-calc {
    list-style: none;
    margin: 0;
    padding: 0;
    max-width: 36rem;
    font-size: var(--text-sm);
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
}

.owed-calc > li {
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);
    padding: 0.15rem 0;
}

.owed-calc-amount {
    flex: none;
    text-align: right;
}

.owed-chips {
    margin: var(--space-2) 0 0;
}

.owed-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    margin-top: var(--space-2);
}

.owed-actions:empty {
    display: none;
}

.owed-note,
.owed-paid {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.owed-paid {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0 var(--space-1);
}

.owed-undo {
    display: inline;
}

.owed-undo-button {
    min-height: 0;
    padding: 0.1rem 0.25rem;
    color: var(--brand-deep);
    text-decoration: underline;
}

.mark-paid > summary {
    display: inline-flex;
    list-style: none;
}

.mark-paid > summary::-webkit-details-marker {
    display: none;
}

.mark-paid[open] > summary {
    display: none;
}

.mark-paid-form {
    display: grid;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-sm);
    background: var(--zebra);
}

.mark-paid-question {
    margin: 0;
    font-weight: 600;
}

.mark-paid-form .form-actions {
    margin-top: 0;
}

.person-owed .owed-head {
    margin-bottom: var(--space-2);
}

/* Owed now on a phone: everything in one column, every control a thumb's width. */
@media (max-width: 39.99rem) {
    /* The dot stays beside the name, out of the single column's flow. */
    .owed-person {
        position: relative;
        grid-template-columns: 1fr;
        gap: var(--space-2);
        padding: var(--space-3) var(--space-4) var(--space-4) calc(var(--space-4) + 1.25rem);
    }

    .owed-person > .person-dot {
        position: absolute;
        top: calc(var(--space-3) + 16px);
        left: var(--space-4);
    }

    /* A photo is wider than the dot: with one in the list, every row's column
     * moves over to clear it, and a dot sits in the middle of a photo's width. */
    .owed-list:has(.person-photo) .owed-person {
        padding-left: calc(var(--space-4) + 24px + var(--space-2));
    }

    .owed-list:has(.person-photo) .owed-person > .person-dot {
        left: calc(var(--space-4) + 6px);
        margin: 0;
    }

    .owed-person > .person-photo {
        position: absolute;
        top: calc(var(--space-3) + 10px);
        left: var(--space-4);
        margin: 0;
    }

    .owed-who .shift-link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    .owed-amount {
        text-align: left;
    }

    .owed-body {
        grid-column: 1;
    }

    .mark-paid > summary {
        min-height: 44px;
        align-items: center;
    }

    .mark-paid select {
        min-height: 44px;
        font-size: 1rem;
    }

    /* A payment has nothing to download; its stacked row leaves the line out. */
    .table--stack td.pay-history-blank {
        display: none;
    }

    .owed-undo-button {
        min-height: 44px;
        padding: 0 var(--space-2);
        margin-block: -12px;
    }
}

.decided > summary,
.add-person > summary {
    cursor: pointer;
    padding: var(--space-3) var(--space-4);
    min-height: 44px;
    font-weight: 600;
}

.decided[open] > summary {
    border-bottom: 1px solid var(--rule);
}

/* Add a person is a padded card, so its summary lines up with the form below
 * it rather than adding the flush cards' inset on top of the card's own. */
.add-person > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    list-style: none;
    padding: 0;
}

/* display: flex drops the native triangle, so a chevron says it expands. */
.add-person > summary::-webkit-details-marker {
    display: none;
}

.add-person > summary::after {
    content: "";
    width: 0.5rem;
    height: 0.5rem;
    margin-right: 0.25rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translateY(-2px);
}

.add-person[open] > summary::after {
    transform: rotate(-135deg) translateY(-1px);
}

.add-person-sub {
    margin: var(--space-2) 0 var(--space-4);
}

.add-person-error {
    margin: 0 0 var(--space-4);
}

/* The spreadsheet upload: the file picker in the same frame as a text field. */
input[type="file"] {
    font-size: max(16px, var(--text-base));
    min-height: 44px;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-sm);
    background: var(--paper);
    color: var(--ink);
    width: 100%;
}

/* The spreadsheet preview: a row's problems, one per line, in the error colour. */
.import-problems {
    margin: 0;
    padding-left: 1.1rem;
    color: var(--bad-deep);
    font-size: var(--text-sm);
    font-weight: 600;
}

.import-confirm .form-actions {
    margin-top: 0;
}

/* "Row 2" heads a person's card on a phone; the column says Row on a desktop. */
.import-row-word {
    display: none;
}

@media (max-width: 39.99rem) {
    .import-row-word {
        display: inline;
    }
}

/* The person page's end date: the date and its Save on one line, like Status. */
.ends-on-form {
    flex-wrap: nowrap;
}

.ends-on-form input[type="date"] {
    width: auto;
    min-height: 36px;
    padding: 0.3rem 0.5rem;
}

/* Shift details: the header's facts sit side by side on a wide screen, and
 * each flag is a row with its review on the right. */
.shift-head .page-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

.shift-head .card {
    display: grid;
    gap: var(--space-3);
}

@media (min-width: 40rem) {
    .shift-head .facts {
        grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    }

    .shift-head .facts > div {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-1);
        border-bottom: 0;
        border-right: 1px solid var(--rule);
    }

    .shift-head .facts > div:last-child {
        border-right: 0;
    }

    .shift-head .facts dd {
        text-align: left;
    }
}

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

.shift-flag {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-3) 0;
    border-top: 1px solid var(--rule);
}

.shift-flag:first-child {
    border-top: 0;
}

.shift-flag {
    scroll-margin-top: calc(var(--space-6) * 3);
}

.shift-flag:target {
    outline: 2px solid var(--brand);
    outline-offset: -2px;
    background: var(--brand-wash);
}

.shift-flag-body {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
    font-size: var(--text-sm);
}

.shift-flag-when {
    font-variant-numeric: tabular-nums;
    color: var(--ink-soft);
}

.shift-flag.is-reviewed .shift-flag-body {
    color: var(--ink-soft);
}

/* Shift details: the office section's lists and sub-headings. */
.shift-subtitle {
    margin: var(--space-4, 1rem) 0 var(--space-2, 0.5rem);
    font-size: 0.95rem;
    font-weight: 600;
}

.shift-list {
    margin: 0;
    padding-left: 1.25rem;
}

/* The Shifts list. */
.filter-form--shifts {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.filter-form--shifts .field:nth-child(3) {
    grid-column: auto;
}

@media (min-width: 40rem) {
    .filter-form--shifts {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 64rem) {
    .filter-form--shifts {
        grid-template-columns:
            minmax(0, 1.2fr) minmax(0, 1.2fr) minmax(0, 1.1fr) minmax(0, 1fr)
            minmax(0, 1.4fr) minmax(0, 1fr) auto;
    }

    .filter-form--shifts .filter-actions {
        grid-column: auto;
    }
}

.shifts-totals {
    margin: var(--space-4) 0 var(--space-1);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.shift-row {
    position: relative;
    /* A row is only a containing block for the stretched link in some browsers
       unless it makes its own: a transform does, in all of them. */
    transform: translate(0);
}

.shift-row:hover {
    background: var(--brand-wash);
}

/* The person's name link covers its whole row, so the row is the target. Only
   inside a row: elsewhere the stretch would cover the page and swallow clicks. */
.shift-link {
    color: inherit;
    text-decoration: none;
}

.shift-row .shift-link::after {
    content: "";
    position: absolute;
    inset: 0;
}

.shift-link:focus-visible {
    outline: 2px solid var(--brand-deep);
    outline-offset: -2px;
}

.person-dot {
    display: inline-block;
    width: 0.75rem;
    height: 0.75rem;
    margin-right: var(--space-2);
    border-radius: 50%;
    background: var(--person, var(--ink-soft));
    vertical-align: baseline;
}

/* The palette's second lap is drawn as a ring, so repeated colours stay apart. */
.person-dot--ring {
    background: transparent;
    border: 2px dashed var(--person, var(--ink-soft));
}

/* A person with a photo shows it where their dot would be, ringed in their colour.
 * It sits on the middle of the line, not the baseline: a 24px picture on the
 * baseline pushes the name below the rest of its row. */
.person-photo {
    display: inline-block;
    margin-right: var(--space-2);
    border-radius: 50%;
    object-fit: cover;
    box-shadow: 0 0 0 2px var(--person, var(--ink-soft));
    background: var(--zebra);
    vertical-align: middle;
}

.person-photo--sm {
    width: 24px;
    height: 24px;
    margin-block: -4px;
}

/* In a list where someone has a photo, a dot takes a photo's width, so every
 * name starts in the same place. A list with no photo is drawn as before. */
.table:has(.person-photo--sm) .person-dot {
    margin-left: 6px;
    margin-right: calc(var(--space-2) + 6px);
}

.person-photo--md {
    width: 40px;
    height: 40px;
}

.person-photo--lg {
    width: 96px;
    height: 96px;
    margin-right: 0;
}

.pager {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
}

/* Home: a pill that is also a link keeps its tone, and sits above the row's
 * stretched link so it takes its own click. */
.pill--link.pill--bad {
    background: var(--bad-wash);
    border-color: var(--bad-rule);
    color: var(--bad-deep);
}

.pill--link.pill--warn {
    background: var(--accent-wash);
    border-color: var(--accent-rule);
    color: var(--accent-ink);
}

.pill--link.pill--info {
    background: var(--brand-wash);
    border-color: #b9def6;
    color: var(--brand-deep);
}

.pill--link.pill--muted {
    background: var(--page);
    border-color: var(--rule-strong);
    color: var(--ink-soft);
}

.pill--link.pill--bad:hover,
.pill--link.pill--warn:hover,
.pill--link.pill--info:hover,
.pill--link.pill--muted:hover {
    filter: brightness(0.96);
}

/* Chips, the move-walk form and buttons sit above the row's stretched link. */
.shift-row .pill--link,
.shift-row .move-form,
.shift-row .button-secondary,
.shift-row .button-primary {
    position: relative;
    z-index: 1;
}

.home-detail {
    display: block;
}

.home-finished > summary {
    padding: var(--space-4);
    font-weight: 600;
    cursor: pointer;
}

.home-finished > .empty {
    margin: 0;
    padding: 0 var(--space-4) var(--space-4);
}

/* Home's live map: a coloured pin per person and a neutral square for the office. */
.home-pin-wrap {
    background: transparent;
    border: 0;
}

.home-pin {
    display: block;
    width: 20px;
    height: 20px;
    box-sizing: border-box;
    border-radius: 50%;
    background: var(--person, var(--ink-soft));
    border: 2px solid var(--paper);
    box-shadow: 0 1px 4px rgba(20, 24, 31, 0.45);
}

.home-pin--ring {
    background: var(--paper);
    border: 3px dashed var(--person, var(--ink-soft));
}

.home-pin--faded {
    opacity: 0.45;
}

.home-office {
    display: block;
    width: 14px;
    height: 14px;
    box-sizing: border-box;
    background: var(--ink-soft);
    border: 2px solid var(--paper);
    box-shadow: 0 1px 3px rgba(20, 24, 31, 0.4);
}

@media (max-width: 39.99rem) {
    .home-map {
        height: 320px;
    }

    /* A stacked row's title is the dot and the name together, at the left. */
    .table.table--stack.home-table td.cell-name {
        justify-content: flex-start;
        align-items: center;
    }

    .table.table--stack.home-table td.cell-name .person-dot,
    .table.table--stack.home-table td.cell-name .person-photo {
        flex: none;
        margin-right: 0;
    }
}

/* Neighborhoods: the map beside its key, stacked on narrow screens. */
.map-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-4);
    margin-bottom: var(--space-4);
}

.map-layout .coverage-card,
.map-layout .map-key {
    margin: 0;
}

.map-layout .coverage-card {
    align-self: start;
}

.map-layout .map-layers {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    margin-top: var(--space-3);
}

.map-key {
    padding: var(--space-4);
    align-self: start;
}

.key-list {
    list-style: none;
    margin: var(--space-3) 0 0;
    padding: 0;
    display: grid;
    gap: var(--space-1);
}

.key-person {
    display: flex;
    align-items: center;
    width: 100%;
    min-height: 44px;
    padding: 0 var(--space-3);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: transparent;
    font: inherit;
    font-size: var(--text-sm);
    color: var(--ink);
    text-align: left;
    cursor: pointer;
}

.key-person:hover {
    background: var(--brand-wash);
}

.key-person[aria-pressed="true"] {
    background: var(--brand-wash);
    border-color: var(--brand-deep);
    color: var(--brand-deep);
    font-weight: 600;
}

.key-person:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
}

.key-swatch {
    flex: none;
}

.key-swatch--dashed {
    background: transparent;
    border: 2px dashed var(--person, var(--ink-soft));
}

.quick-ranges {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.chip {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--space-4);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-pill);
    background: var(--paper);
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    text-decoration: none;
}

.chip:hover {
    background: var(--brand-wash);
}

.chip[aria-current] {
    background: var(--brand-wash);
    border-color: var(--brand-deep);
    color: var(--brand-deep);
}

.chip:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: 1px;
}

tr.is-highlighted {
    background: var(--brand-wash);
    box-shadow: inset 3px 0 0 var(--brand-deep);
}

@media (min-width: 56.25rem) {
    .map-layout {
        grid-template-columns: minmax(0, 2fr) minmax(16rem, 1fr);
        grid-template-rows: auto auto;
        align-content: start;
    }

    .map-layout .coverage-card {
        grid-column: 1;
        grid-row: 1 / span 2;
    }

    .map-layout .map-key {
        grid-column: 2;
        grid-row: 1;
    }

    .map-layout .campaigns-card {
        grid-column: 2;
        grid-row: 2;
    }
}

/* The campaigns card beside the map: the running campaign, the start form and
 * the archived ones. */
.map-layout .campaigns-card {
    margin: 0;
    padding: var(--space-4);
    align-self: start;
}

.campaigns-card .campaign-active,
.campaigns-card .start-campaign-form,
.campaigns-card .campaign-archive > li {
    display: grid;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.campaigns-card .campaign-active > *,
.campaigns-card .campaign-archive > li > *,
.campaigns-card p {
    margin: 0;
}

.campaigns-card .campaign-archive {
    list-style: none;
    margin: 0;
    padding: 0;
}

.campaigns-card .campaign-archive > li {
    padding-top: var(--space-3);
    border-top: 1px solid var(--rule);
}

.campaigns-card .start-campaign-form {
    padding-top: var(--space-3);
    border-top: 1px solid var(--rule);
}

.campaigns-subhead {
    margin: 0;
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--ink);
}

.campaigns-card > .campaigns-subhead {
    margin-top: var(--space-4);
}

/* Moving a walk sits above the row's link, so it takes its own click. */
.move-form {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    position: relative;
    z-index: 1;
}

/* Fields: the routes and runs sections sit apart, and the averages fold away. */
#runs {
    margin-top: var(--space-6);
}

.card[data-averages] > summary {
    cursor: pointer;
    font-weight: 700;
    min-height: 44px;
    display: flex;
    align-items: center;
}

.card[data-averages][open] > summary {
    margin-bottom: var(--space-4);
}

/* Signs: the row's buttons are as wide as their words, not the narrow decision column. */
.signs-actions {
    width: auto;
    min-width: 7.5rem;
}

.signs-actions .button-sm {
    white-space: nowrap;
}

/* The person page: stacked cards with a little air between them, the Pay
 * section's Owed now block, and each week's header over its shift rows. */
.person-card {
    margin-bottom: var(--space-4);
}

.person-card .inline-form {
    display: inline-flex;
}

.person-owed {
    margin-top: var(--space-4);
}

.person-sub {
    margin: 0 0 var(--space-2);
    font-size: var(--text-sm);
    font-weight: 600;
}

.person-chips {
    margin-top: var(--space-3);
}

.week-head {
    margin: 0;
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-sm);
    font-weight: 600;
    border-top: 1px solid var(--rule);
    border-bottom: 1px solid var(--rule);
}

/* The Everyone card on People: one compact link per person. */
.everyone-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin: 0;
    padding: var(--space-3) var(--space-4);
    list-style: none;
}

/* Shift types in the Add a person form. */
.field-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin: var(--space-3) 0 0;
    padding: 0;
    border: 0;
}

.field-group > legend {
    padding: 0;
    margin-bottom: var(--space-1);
    font-size: var(--text-sm);
    font-weight: 600;
}

/* The person page's Profile card: the photo (or its empty circle) beside the
 * upload and remove buttons, then how the person is paid. */
.profile-error {
    margin: 0 0 var(--space-3);
}

.profile-photo {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--rule);
}

.profile-photo .person-photo--lg {
    flex: none;
}

.profile-no-photo {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 96px;
    height: 96px;
    margin: 0;
    padding: 0 var(--space-3);
    border: 2px dashed var(--rule-strong);
    border-radius: 50%;
    line-height: 1.25;
    text-align: center;
}

.profile-photo-actions {
    display: flex;
    flex: 1 1 16rem;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    min-width: 0;
}

.profile-upload {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    min-width: 0;
}

.profile-upload input[type="file"] {
    width: auto;
    max-width: 100%;
    min-height: 36px;
    padding: 0.3rem 0.5rem;
    font-size: var(--text-sm);
}

@media (max-width: 39.99rem) {
    .profile-form input,
    .profile-form select,
    .profile-upload input[type="file"] {
        min-height: 44px;
        font-size: 1rem;
    }

    .profile-form button {
        min-height: 44px;
    }
}

/* The home code on the person page: large, spaced digits that are easy to read aloud. */
.home-code-line {
    margin: 0 0 var(--space-1, 4px);
    font-size: 2rem;
    letter-spacing: 0.08em;
    font-variant-numeric: tabular-nums;
}

/* The home code help text on the person page. */
.home-code-help {
    margin-top: var(--space-3);
}

/* The Works remotely switch under the home code: label and Save, wrapping on a phone. */
.person-card .works-remotely-form {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--rule);
}

/* Shift details, fields: the day's laps in one line over their table, then each
 * lap's fields behind a summary that opens like the other expanding cards. */
.lap-day {
    padding: 0 var(--space-5) var(--space-3);
    font-size: var(--text-sm);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}

/* Each figure of the line keeps its words together when the line wraps. */
.lap-day > span {
    white-space: nowrap;
}

.table a[data-lap-link] {
    font-weight: 600;
    color: var(--brand-deep);
    text-underline-offset: 3px;
}

.lap {
    border-top: 1px solid var(--rule);
    scroll-margin-top: var(--space-4);
}

.lap > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    min-height: 44px;
    padding: var(--space-3) var(--space-5);
    list-style: none;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--brand-deep);
    cursor: pointer;
}

.lap > summary:hover {
    background: var(--brand-wash);
}

.lap > summary:focus-visible {
    outline: 3px solid var(--brand-deep);
    outline-offset: -3px;
}

/* display: flex drops the native triangle, so a chevron says it expands. */
.lap > summary::-webkit-details-marker {
    display: none;
}

.lap > summary::after {
    content: "";
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    margin-right: 0.25rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg) translateY(-2px);
}

.lap[open] > summary::after {
    transform: rotate(-135deg) translateY(-1px);
}

.lap-sub {
    padding: 0 var(--space-5) var(--space-2);
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

/* ================================================================== */
/* The console on a phone                                              */
/* ================================================================== */

/* Below 40rem every control is a 44px target and every text box is 16px,
 * so a thumb can hit it and iOS Safari never zooms the page when it takes
 * focus. Nothing here reaches a wider screen. */
@media (max-width: 39.99rem) {
    .button-sm,
    .button-primary,
    .button-secondary,
    .button {
        min-height: 44px;
    }

    .icon-button {
        width: 44px;
        height: 44px;
    }

    .check {
        min-height: 44px;
        white-space: normal;
    }

    .stop-edit > summary,
    .coords > summary {
        min-height: 44px;
    }

    .correct-form input,
    .correct-form select,
    .ends-on-form input[type="date"] {
        min-height: 44px;
        font-size: 1rem;
    }

    /* A stacked row's title is the dot and the name together, at the left. */
    .table.table--stack td.cell-name:first-child {
        justify-content: flex-start;
        align-items: center;
        gap: var(--space-2);
        min-height: 44px;
        white-space: normal;
    }

    .table.table--stack td.cell-name:first-child .person-dot,
    .table.table--stack td.cell-name:first-child .person-photo {
        flex: none;
        margin-right: 0;
    }

    .table.table--stack:has(.person-photo--sm) td.cell-name:first-child .person-dot {
        margin: 0 6px;
    }

    /* A long column name (Avg minutes walking at the field) wraps beside its
     * value instead of pushing the value out of the card, between words only
     * (a long value's cell breaks anywhere, and the name would inherit that). */
    .table--stack td[data-label]::before {
        flex: 0 1 auto;
        min-width: 0;
        overflow-wrap: normal;
        text-align: left;
    }

    /* Links that title a stacked row, or open a day, get a whole-line target. */
    .table--stack td.cell-name:first-child > a,
    .correction-day-link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    .correction-day-link {
        margin-top: 0;
        font-size: var(--text-sm);
    }

    /* The eyebrow's way back (People, Fields, Shifts that day) is a 44px
     * target; the negative margin keeps the title where it was. */
    .eyebrow a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        margin-block: -14px;
    }

    /* A timeline line's links (View photo, On the map): a 44px target that
     * does not open up the line spacing. */
    .tl-detail a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
        margin-block: -12px;
    }

    /* Everyone on People: one person a row, each a full-height target. */
    .everyone-list {
        flex-direction: column;
        gap: 0;
        padding-block: var(--space-1);
    }

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

    .everyone-list .shift-link {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /* A card's own link beside its title (All their shifts). */
    .card-head > a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /* The page's own buttons share the width and wrap, never push it wider. */
    .page-head > .page-actions {
        flex: 1 1 100%;
    }

    .page-actions > a,
    .page-actions > button,
    .page-actions > form > button {
        flex: 1 1 auto;
        justify-content: center;
        text-align: center;
    }

    /* A fact whose value is a form (status, end date) wraps under its name. */
    .facts > div {
        flex-wrap: wrap;
        min-height: 44px;
    }

    .facts dd {
        flex-wrap: wrap;
        justify-content: flex-end;
        min-width: 0;
        overflow-wrap: anywhere;
    }

    .ends-on-form {
        flex-wrap: wrap;
        justify-content: flex-end;
    }

    .ends-on-form input[type="date"] {
        flex: 1 1 10rem;
    }

    /* A flag chip keeps its pill size but takes a 44px-tall hit area; with
     * the row's gap the areas of two stacked chips barely overlap. */
    .pill--link {
        position: relative;
    }

    .pill--link::before {
        content: "";
        position: absolute;
        inset: -10px -2px;
    }

    /* A placement's photo opens the lightbox: a thumb-sized target. */
    .sign-thumb,
    .sign-thumb img {
        width: 60px;
        height: 45px;
    }

    /* A fact's own link (Paid in file #1) is a whole-line target. */
    .facts dd > a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    /* The map's status pill keeps to one line over a narrow map. */
    .map-status {
        white-space: nowrap;
        padding: var(--space-1) var(--space-3);
    }
}

/* ================================================================== */
/* Sessions (Plan 18)                                                  */
/* ================================================================== */

/* The Live pill sits beside the session's name in the list and the page head. */
.sessions-table .title-pill {
    margin-left: var(--space-2);
}

.session-form {
    max-width: 40rem;
}

/* New session's Kind: three segments. The middle one shares both borders, and the
 * segments are desktop height here; phones stack them full width at 44 px below. */
.session-kinds {
    margin-bottom: var(--space-3);
}

.session-kinds label span {
    min-height: 36px;
}

.session-kinds label + label span {
    margin-left: -1px;
}

.session-kinds label:not(:first-of-type):not(:last-of-type) span {
    border-radius: 0;
}

@media (max-width: 39.99rem) {
    .session-form {
        max-width: none;
    }

    .session-kinds {
        flex-direction: column;
        align-items: stretch;
    }

    .session-kinds label {
        width: 100%;
    }

    .session-kinds label span {
        min-height: 44px;
        width: 100%;
        justify-content: center;
        padding: 0 var(--space-2);
        text-align: center;
    }

    .session-kinds label + label span {
        margin-left: 0;
        margin-top: -1px;
    }

    .session-kinds label:first-of-type span {
        border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    }

    .session-kinds label:last-of-type span {
        border-radius: 0 0 var(--radius-sm) var(--radius-sm);
        margin-left: 0;
    }

    .session-form input {
        min-height: 44px;
    }

    .session-row-title,
    .route-sessions-note a {
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }

    .sessions-table .cell-name {
        flex-wrap: wrap;
        align-items: center;
    }

    #delete-session .button-danger,
    .session-form + .form-actions .button-primary,
    .session-form + .form-actions .button-secondary {
        width: 100%;
    }
}

/* ---------------------------------------------------- session page (Task 6) */

/* sessions.js hides and shows these with the hidden attribute; a class's own
 * display must never win over it. */
.problems-box [hidden],
.session-card [hidden],
.session-card[hidden] {
    display: none;
}

/* Problems: red while there is anything to fix, one green line when there is not. */
.problems-box {
    background: var(--bad-wash);
    border-color: var(--bad-rule);
    border-left: 4px solid var(--bad);
    color: var(--bad-deep);
}

.problems-box .card-title {
    color: var(--bad-deep);
    margin-bottom: var(--space-2);
}

.problems-list {
    margin: 0;
    padding-left: 1.15rem;
    display: grid;
    gap: var(--space-1);
    font-size: var(--text-sm);
    font-weight: 600;
}

.problems-box[data-state="clear"] {
    background: var(--good-wash);
    border-color: var(--good-rule);
    border-left-color: var(--good);
    color: var(--good);
    padding-top: var(--space-3);
    padding-bottom: var(--space-3);
}

.problems-none,
.save-state[data-tone="good"] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-weight: 600;
    color: var(--good);
}

.problems-none {
    margin: 0;
}

/* The tick: a check drawn in the text's own colour. */
.problems-none::before,
.save-state[data-tone="good"]::before {
    content: "";
    flex: none;
    width: 1em;
    height: 1em;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.2 11.6 2.6 8l1.2-1.2 2.4 2.4 6-6L13.4 4.4z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.2 11.6 2.6 8l1.2-1.2 2.4 2.4 6-6L13.4 4.4z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Routes and people: one row each, between the card's head and its add form. */
.session-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.session-route,
.session-person {
    display: flex;
    align-items: center;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-3) var(--space-5);
    border-top: 1px solid var(--rule);
}

.session-route-name {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 1 1 auto;
    min-width: 0;
}

.route-swatch {
    flex: none;
    width: 0.875rem;
    height: 0.875rem;
    border-radius: 3px;
    background: var(--route, var(--ink-soft));
}

.session-row-text {
    flex: 1 1 auto;
    min-width: 0;
}

.session-person .session-row-text {
    margin-right: auto;
}

.session-row-title {
    font-weight: 600;
    color: var(--ink);
    overflow-wrap: anywhere;
}

.session-row-meta {
    margin: 0;
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.session-row-meta.is-missing {
    color: var(--bad);
    font-weight: 600;
}

.session-runner-form {
    display: flex;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    flex: 0 1 26rem;
    min-width: 0;
}

.session-runner-form select {
    flex: 1 1 14rem;
}

.save-state {
    flex: none;
    min-width: 5.5rem;
    font-size: var(--text-sm);
    color: var(--ink-soft);
}

.save-state[data-tone="bad"] {
    color: var(--bad);
    font-weight: 600;
}

.session-remove-form {
    flex: none;
}

/* A person's routes, each a chip with a × back to Nobody, then "Give a route". */
.session-chips {
    list-style: none;
    margin: var(--space-1) 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-2);
}

.session-chips:empty,
.session-person [hidden] {
    display: none;
}

.session-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 0 var(--space-1) 0 var(--space-2);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-pill);
    background: var(--zebra);
    font-size: var(--text-sm);
    max-width: 100%;
}

.session-chip-name {
    overflow-wrap: anywhere;
}

.session-chip-form {
    display: flex;
}

.session-chip-remove {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--ink-soft);
    font-size: var(--text-base);
    line-height: 1;
    cursor: pointer;
}

.session-chip-remove:hover,
.session-chip-remove:focus-visible {
    background: var(--bad-wash);
    color: var(--bad);
}

.session-chip-remove:disabled {
    opacity: 0.5;
    cursor: default;
}

.session-give {
    display: flex;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    flex: 0 1 20rem;
    min-width: 0;
}

.session-give select {
    flex: 1 1 12rem;
    min-width: 0;
}

.session-runner-hint {
    flex: 0 1 26rem;
    margin: 0;
}

.session-empty {
    border-top: 1px solid var(--rule);
}

.session-card > .card-foot {
    margin: 0;
}

/* "Add routes" / "Add people": a <details> whose summary looks like a button. */
.session-add-toggle {
    display: inline-block;
    cursor: pointer;
    list-style: none;
}

.session-add-toggle::-webkit-details-marker {
    display: none;
}

.session-add-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 26rem;
    margin-top: var(--space-3);
}

.session-checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 18rem;
    overflow-y: auto;
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
}

.session-check + .session-check {
    border-top: 1px solid var(--rule);
}

.session-check label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    cursor: pointer;
}

.session-check-name {
    flex: 1 1 auto;
    min-width: 0;
}

.session-check-meta {
    color: var(--ink-soft);
    font-size: var(--text-sm);
}

.session-filter-empty {
    margin: 0;
}

.session-add-actions {
    display: flex;
}

.session-card .card-foot .form-note {
    margin: 0;
}

@media (max-width: 39.99rem) {
    .session-route,
    .session-person {
        flex-wrap: wrap;
    }

    /* Name and Remove on top, then the picker at full width, then "Saved". */
    .session-route-name {
        flex: 1 1 0;
        order: 0;
    }

    .session-route .session-remove-form {
        order: 1;
    }

    .session-runner-form {
        order: 2;
        flex: 1 1 100%;
        flex-wrap: wrap;
    }

    .session-runner-form select {
        flex: 1 1 100%;
        min-height: 44px;
        font-size: 16px;
    }

    .session-runner-hint {
        order: 2;
        flex: 1 1 100%;
    }

    /* A person: photo, name and Remove on top, then "Give a route" at full width. */
    .session-person .session-person-remove {
        order: 1;
    }

    .session-give {
        order: 2;
        flex: 1 1 100%;
        flex-wrap: wrap;
    }

    .session-give select {
        flex: 1 1 100%;
        min-height: 44px;
        font-size: 16px;
    }

    .session-chip {
        min-height: 44px;
        padding-left: var(--space-3);
    }

    .session-chip-remove {
        width: 44px;
        height: 44px;
        font-size: var(--text-lg);
    }

    .save-state:empty {
        display: none;
    }

    .session-add-form {
        max-width: none;
    }

    .session-add-toggle,
    .session-add-actions .button-primary,
    .session-filter {
        min-height: 44px;
        font-size: 16px;
    }

    .session-add-actions .button-primary {
        flex: 1 1 100%;
    }

    .session-check label {
        min-height: 44px;
    }

    .session-check input[type="checkbox"] {
        width: 24px;
        height: 24px;
    }
}

/* The session map: routes in their colours, numbered stops, a red ring on shared fields. */
.session-map-card .card-head {
    margin-bottom: var(--space-3);
}

.session-map {
    height: 28rem;
    isolation: isolate;
    border-radius: var(--radius-sm);
    border: 1px solid var(--rule);
    background: var(--page);
    min-width: 0;
}

/* The marker's tap area is 44 x 44; the 28 px pin sits centred inside it. */
.session-stop-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: 0;
}

.session-stop {
    flex: none;
    box-sizing: border-box;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2px solid #fff;
    background: var(--route, var(--ink-soft));
    color: #fff;
    font-weight: 700;
    font-size: 0.8125rem;
    line-height: 24px;
    text-align: center;
    box-shadow: 0 1px 3px rgba(20, 24, 31, 0.4);
}

/* Two routes at one field: the red ring sits outside the white edge. */
.session-stop--overlap {
    box-shadow: 0 0 0 3px var(--bad), 0 1px 3px rgba(20, 24, 31, 0.4);
}

.session-popup {
    font-family: inherit;
    line-height: 1.4;
}

.session-popup-title {
    display: block;
}

.session-popup-shared-title {
    margin-top: 0.5rem;
    font-weight: 600;
    font-size: 0.8125rem;
}

.session-popup-shared {
    font-size: 0.8125rem;
}

.session-legend {
    list-style: none;
    margin: var(--space-3) 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-5);
}

.session-legend-item {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    min-width: 0;
}

.session-legend-item .route-swatch {
    margin-top: 0.2rem;
}

.session-legend-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow-wrap: anywhere;
}

.session-legend-name {
    font-weight: 600;
}

.session-legend-runner {
    color: var(--ink-soft);
    font-size: 0.875rem;
}

@media (max-width: 39.99rem) {
    .session-map {
        height: 60vh;
    }

    .session-legend {
        flex-direction: column;
    }
}

/* Plan 20 Task 4: Delete beside Edit/Archive, and Delete on session rows; 44 px on phones. */
.field-routes-table .decision {
    flex-wrap: wrap;
}

.fields-sessions-table .cell-name,
.sessions-table .decision-form {
    white-space: normal;
}

#sessions .card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.route-delete {
    margin-top: var(--space-4);
}

@media (max-width: 39.99rem) {
    .field-routes-table .decision .button-sm,
    .sessions-table .decision-form .button-sm,
    #sessions .page-actions .button-sm,
    .fields-sessions-table .campaign-link,
    .route-delete .button-danger {
        min-height: 44px;
    }

    .fields-sessions-table .campaign-link {
        display: inline-flex;
        align-items: center;
    }

    .route-delete .button-danger {
        width: 100%;
    }
}
