/*
 * AIMcom console.
 *
 * A working tool used under time pressure in a busy venue, not a marketing
 * surface. Three rules follow from that and every choice below is one of them:
 *
 *   1. Clarity and density over decoration. One accent colour for anything
 *      interactive. The other colours in here are state, not styling: a
 *      provisional booking, a breached latency ceiling and a disabled channel
 *      each have to be identifiable without reading the label.
 *   2. Generous type. The board is read across a room and the bookings list is
 *      read on a tablet on a host stand, so the base size is 16px and the
 *      board's own figures are much larger.
 *   3. No dependency. System fonts, no icon font, no web font. The console has
 *      to work with no network beyond the application itself.
 */

:root {
    /*
     * Aimatric brand palette, taken from aimatric.com. Teal is the brand and
     * carries structure: navigation, links, focus. Orange is the action colour
     * and is reserved for the one primary control on a screen. That division
     * is how the marketing site uses them, and it happens to be right for a
     * dense operational tool as well: if every link were orange, nothing would
     * read as the thing to press.
     */
    --accent: #0E6E7C;          /* teal-700 */
    --accent-strong: #0A4A54;   /* teal-900 */
    --accent-wash: #EDFBFD;     /* teal-50  */

    --action: #F97316;          /* orange-500, primary control only */
    --action-strong: #EA580C;   /* orange-600 */

    --ink: #111827;             /* gray-900 */
    --ink-soft: #4B5563;        /* gray-600 */
    --ink-faint: #6B7280;       /* gray-500 */
    --page: #F9FAFB;            /* gray-50  */
    --card: #FFFFFF;
    --line: #E5E7EB;            /* gray-200 */
    --line-soft: #F3F4F6;       /* gray-100 */

    /*
     * State. Not decoration: each of these carries a fact, and each is darker
     * than the brand equivalent would be because it sits behind text on white
     * and has to stay legible on a tablet at arm's length. The brand's
     * --success #10B981 is used for the indicator dot, not for the label.
     */
    --state-good: #047857;
    --state-good-wash: #ECFDF5;
    --state-good-dot: #10B981;
    --state-warn: #9A3412;
    --state-warn-wash: #FFF7ED;
    --state-bad: #B91C1C;
    --state-bad-wash: #FEF2F2;
    --state-idle: #4B5563;
    --state-idle-wash: #F3F4F6;

    --radius: 8px;
    --radius-sm: 5px;
    --radius-pill: 999px;
    --gap: 1rem;
    --shadow: 0 1px 2px rgba(10, 74, 84, .08), 0 1px 8px rgba(10, 74, 84, .05);
    --shadow-lift: 0 1px 2px rgba(10, 74, 84, .10), 0 6px 20px rgba(10, 74, 84, .10);

    /*
     * The type scale, named by role rather than by size, because the sizes are
     * the part that gets argued about and the roles are the part that has to
     * stay consistent.
     *
     * --fs-board is the only figure meant to be read across a room, and it
     * belongs to the service board and to nothing else. Four dashboard
     * counters wearing it made a page where the largest thing on screen was a
     * count of yesterday's calls and the smallest was the exceptions banner
     * that needed acting on, which is the hierarchy exactly inverted.
     */
    --fs-board: clamp(3.2rem, 6vw, 5.5rem);
    --fs-tile: clamp(1.5rem, 2.4vw, 1.9rem);
    --fs-30: clamp(1.5rem, 2.2vw, 1.85rem);
    --fs-body: 1rem;
    --fs-small: .9rem;
    --fs-fine: .8rem;

    /* Spacing, so that a card, a table cell and a form row are not each
       spaced by a different developer's taste. */
    --space-1: .25rem;
    --space-2: .5rem;
    --space-3: .75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;

    /*
     * The system stack, which is what has always actually rendered. Inter was
     * declared at the top of this file and its woff2 has never been in
     * static/console/fonts/, so every console has drawn in the fallback since
     * the first commit. The @font-face is gone rather than the file added,
     * because rule 3 in the header above is no dependency and no web font: a
     * console that has to work on a venue network with no route to anything
     * should not be waiting on a font, and one that renders identically
     * without it does not need one.
     */
    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
            "Helvetica Neue", Arial, "Noto Sans", sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
            "Liberation Mono", monospace;
}

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

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

body {
    margin: 0;
    font-family: var(--font);
    font-size: 16px;
    line-height: 1.5;
    color: var(--ink);
    background: var(--page);
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent-strong); }

h1, h2, h3, h4 { margin: 0 0 .5rem; line-height: 1.25; font-weight: 650; }
h1 { font-size: 1.5rem; }
h2 { font-size: 1.2rem; }
h3 { font-size: 1.02rem; }
p { margin: 0 0 .75rem; }

/* Focus is never removed. A console driven from a keyboard on a busy pass is
   unusable without it. */
:focus-visible {
    outline: 3px solid var(--accent);
    outline-offset: 2px;
    border-radius: 3px;
}

/* ------------------------------------------------------------------ layout */

.shell { display: flex; min-height: 100vh; align-items: stretch; }

.sidebar {
    flex: 0 0 240px;
    background: var(--card);
    border-right: 1px solid var(--line);
    padding: 1rem .75rem 2rem;
}

.brand small {
    display: block;
    font-weight: 400;
    color: var(--ink-faint);
    font-size: var(--fs-fine);
}

.nav-group { margin-bottom: 1.1rem; }
.nav-group > h4 {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--ink-faint);
    margin: 0 .5rem .3rem;
}
.nav-link {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: .5rem;
    padding: .45rem .5rem;
    border-radius: var(--radius);
    color: var(--ink);
    text-decoration: none;
}
.nav-link:hover { background: var(--line-soft); }
.nav-link[aria-current="page"] {
    background: var(--accent-wash);
    color: var(--accent-strong);
    font-weight: 600;
}

.main { flex: 1 1 auto; min-width: 0; padding: 1.25rem 1.5rem 4rem; }

.page-head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap);
    align-items: flex-end;
    justify-content: space-between;
    margin-bottom: 1rem;
}
.page-head p { margin: 0; color: var(--ink-soft); max-width: 62ch; }

.scope-bar {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
    padding: .5rem .75rem;
    margin-bottom: 1rem;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    font-size: .9rem;
}
.scope-bar form { display: flex; gap: .4rem; align-items: center; margin: 0; }

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

.card {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1rem;
    margin-bottom: 1rem;
    box-shadow: var(--shadow);
}
.card > h2:first-child, .card > h3:first-child { margin-top: 0; }
.card-note { color: var(--ink-soft); font-size: .9rem; }

.grid { display: grid; gap: var(--gap); }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }

/* ------------------------------------------------------ the service board */

/* Read across a room. The figure is the screen; everything else supports it. */
.occupancy {
    display: flex;
    flex-wrap: wrap;
    gap: 1.5rem;
    align-items: flex-end;
}
.occupancy-figure {
    font-size: clamp(3rem, 10vw, 5.5rem);
    line-height: .95;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}
.occupancy-unit { font-size: .32em; font-weight: 600; color: var(--ink-soft); }
.occupancy-label {
    text-transform: uppercase;
    letter-spacing: .07em;
    font-size: .75rem;
    color: var(--ink-faint);
    margin-bottom: .35rem;
}
.occupancy-meter {
    height: 14px;
    background: var(--line-soft);
    border-radius: 7px;
    overflow: hidden;
    margin-top: .6rem;
}
.occupancy-meter > span { display: block; height: 100%; background: var(--accent); }
.occupancy-meter.is-full > span { background: var(--state-warn); }

.board-list { list-style: none; margin: 0; padding: 0; }
.board-list > li {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    align-items: baseline;
    padding: .6rem .25rem;
    border-bottom: 1px solid var(--line-soft);
    font-size: 1.05rem;
}
.board-list > li:last-child { border-bottom: 0; }
.board-time {
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 1.3rem;
    min-width: 4.2rem;
}
.board-party { font-weight: 600; }
.board-name { flex: 1 1 12rem; min-width: 0; }
.board-actions { display: flex; gap: .35rem; margin-left: auto; }

/* -------------------------------------------------------------- states */

.tag {
    display: inline-block;
    padding: .1rem .45rem;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 650;
    letter-spacing: .01em;
    white-space: nowrap;
    background: var(--state-idle-wash);
    color: var(--state-idle);
}
.tag-good { background: var(--state-good-wash); color: var(--state-good); }
.tag-warn { background: var(--state-warn-wash); color: var(--state-warn); }
.tag-bad  { background: var(--state-bad-wash);  color: var(--state-bad); }
.tag-accent { background: var(--accent-wash); color: var(--accent-strong); }

/* A provisional booking is the one state that must never read as confirmed
   (FR-09-006). It is given a stripe as well as a colour so it survives a
   monochrome screen and colour vision deficiency. */
.is-provisional {
    background-image: repeating-linear-gradient(
        135deg, transparent 0 8px, rgba(138, 88, 0, .09) 8px 16px);
}

.banner {
    border: 1px solid var(--line);
    border-left: 4px solid var(--state-idle);
    background: var(--card);
    border-radius: var(--radius);
    padding: .75rem 1rem;
    margin-bottom: 1rem;
}
.banner-good { border-left-color: var(--state-good); background: var(--state-good-wash); }
.banner-warn { border-left-color: var(--state-warn); background: var(--state-warn-wash); }
.banner-bad  { border-left-color: var(--state-bad);  background: var(--state-bad-wash); }
.banner p:last-child { margin-bottom: 0; }

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

.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: .94rem;
}
table.data th, table.data td {
    text-align: left;
    padding: .5rem .6rem;
    border-bottom: 1px solid var(--line-soft);
    vertical-align: top;
}
table.data th {
    font-size: .76rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-faint);
    border-bottom: 1px solid var(--line);
    white-space: nowrap;
}
table.data tbody tr:hover { background: var(--line-soft); }
table.data td.num { font-variant-numeric: tabular-nums; text-align: right; }
.mono { font-family: var(--mono); font-size: .88em; }

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

label { display: block; font-weight: 600; font-size: .9rem; margin-bottom: .2rem; }
.field { margin-bottom: .85rem; }
.field .helptext { display: block; font-size: .83rem; color: var(--ink-soft); margin-top: .2rem; }
.field .errorlist { margin: .2rem 0 0; padding-left: 1.1rem; color: var(--state-bad); font-size: .85rem; }

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=date], input[type=time], input[type=datetime-local], input[type=search],
select, textarea {
    width: 100%;
    max-width: 34rem;
    padding: .45rem .55rem;
    font: inherit;
    color: var(--ink);
    background: #fff;
    border: 1px solid var(--line);
    border-radius: var(--radius);
}
textarea { max-width: 52rem; }
input[type=checkbox], input[type=radio] { width: auto; margin-right: .35rem; }

ul.checkbox-list { list-style: none; margin: 0; padding: 0; columns: 2; }
ul.checkbox-list label { font-weight: 400; }

.form-row { display: flex; flex-wrap: wrap; gap: var(--gap); align-items: flex-end; }
.form-row > .field { flex: 1 1 12rem; margin-bottom: 0; }

.button, button {
    display: inline-block;
    font: inherit;
    font-weight: 600;
    padding: .45rem .9rem;
    border-radius: var(--radius);
    border: 1px solid var(--line);
    background: #fff;
    color: var(--ink);
    cursor: pointer;
    text-decoration: none;
}
.button:hover, button:hover { background: var(--line-soft); }
/* One obvious primary action per screen. */
/*
 * The one primary control on a screen, in the Aimatric action colour. Every
 * other control is a link or an outlined button, which is what makes this one
 * read as the thing to press rather than as decoration.
 */
.button-primary, button.primary {
    background: var(--action);
    border-color: var(--action);
    color: #fff;
}
.button-primary:hover, button.primary:hover,
.button-primary:focus, button.primary:focus {
    background: var(--action-strong);
    border-color: var(--action-strong);
    color: #fff;
}
.button-danger, button.danger { color: var(--state-bad); border-color: var(--state-bad); }
.button-danger:hover, button.danger:hover { background: var(--state-bad-wash); }
.button-small, button.small { padding: .25rem .55rem; font-size: .85rem; }
button[disabled], .button[disabled] { opacity: .5; cursor: not-allowed; }

/*
 * Controls in a row sit on their bottom edge, not their centre. A submit
 * button next to a labelled input is taller than the input alone, and centring
 * the two leaves the button floating above the line the eye follows.
 */
.actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .5rem;
    margin-top: 1rem;
}
.actions .form-row { align-items: flex-end; }
.actions .button, .actions button { align-self: flex-end; }

/* --------------------------------------------------------- sign in page */

.centred {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}
.centred .card { width: 100%; max-width: 25rem; }

/* ------------------------------------------------------------ transcript */

.turn {
    border-left: 3px solid var(--line);
    padding: .4rem 0 .4rem .75rem;
    margin-bottom: .5rem;
}
.turn-inbound { border-left-color: var(--accent); }
.turn-meta { font-size: .8rem; color: var(--ink-faint); display: flex; gap: .75rem; flex-wrap: wrap; }

.stage-bar { height: 8px; background: var(--line-soft); border-radius: 4px; overflow: hidden; }
.stage-bar > span { display: block; height: 100%; background: var(--accent); }
.stage-bar.breached > span { background: var(--state-bad); }

/* --------------------------------------------------------------- misc */

.muted { color: var(--ink-soft); }
.small { font-size: .87rem; }
.stack > * + * { margin-top: .75rem; }
.legend { display: grid; gap: .35rem; font-size: .87rem; }
.legend dt { font-weight: 700; }
.legend > div { display: flex; gap: .6rem; align-items: baseline; }

.empty {
    padding: 1.5rem;
    text-align: center;
    color: var(--ink-soft);
    border: 1px dashed var(--line);
    border-radius: var(--radius);
}

/* HTMX marks the element it is loading. A board that polls silently should not
   flicker, so only a deliberate request shows the indicator. */
.htmx-request .poll-dot { opacity: 1; }
.poll-dot {
    display: inline-block; width: .5rem; height: .5rem; border-radius: 50%;
    background: var(--accent); opacity: .25; transition: opacity .2s;
}

/* -------------------------------------------------------- narrow widths */

/* A tablet on a host stand. The board and the bookings list have to be usable
   here, which means the navigation stops taking a column and the board's cards
   stack rather than shrink. */
@media (max-width: 900px) {
    .shell { flex-direction: column; }
    .sidebar {
        flex: 0 0 auto;
        border-right: 0;
        border-bottom: 1px solid var(--line);
        padding: .5rem;
        display: flex;
        flex-wrap: wrap;
        gap: .25rem;
        align-items: center;
    }
    .brand { padding: .25rem .5rem; }
    .brand small { display: none; }
    .nav-group { margin: 0; display: flex; flex-wrap: wrap; gap: .25rem; }
    .nav-group > h4 { display: none; }
    .nav-link { padding: .35rem .55rem; font-size: .92rem; }
    .main { padding: 1rem .75rem 3rem; }
    .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
    ul.checkbox-list { columns: 1; }

    /* The bookings list becomes one card per booking rather than a table that
       scrolls sideways: a host looking for one party should not have to swipe
       to find out whose it is. */
    table.responsive-cards thead { display: none; }
    table.responsive-cards, table.responsive-cards tbody,
    table.responsive-cards tr, table.responsive-cards td { display: block; width: 100%; }
    table.responsive-cards tr {
        border: 1px solid var(--line);
        border-radius: var(--radius);
        margin-bottom: .6rem;
        padding: .5rem .6rem;
        background: var(--card);
    }
    table.responsive-cards td { border: 0; padding: .15rem 0; }
    table.responsive-cards td::before {
        content: attr(data-label) " ";
        font-size: .74rem;
        text-transform: uppercase;
        letter-spacing: .05em;
        color: var(--ink-faint);
        margin-right: .4rem;
    }
    table.responsive-cards td.num { text-align: left; }
}

@media print {
    .sidebar, .actions, .scope-bar { display: none; }
    .card { box-shadow: none; break-inside: avoid; }
}


/* --------------------------------------------------------------------------
 * Aimatric brand chrome.
 * -------------------------------------------------------------------------- */

/*
 * The brand lockup: the AIMatric mark, then the product name.
 *
 * The mark alone rather than the full logo, because the wordmark beside
 * "AIMcom" read as "AIMatric AIMcom", one garbled phrase. The icon says whose
 * product this is without competing with the name that says which. It is the
 * company's mark, so it is used as supplied and never recoloured or stretched.
 */
.brand {
    display: flex;
    align-items: center;
    gap: .6rem;
    text-decoration: none;
    color: var(--ink);
    letter-spacing: .01em;
    padding: var(--space-1) var(--space-2) var(--space-4);
    flex-wrap: wrap;
}

.brand img {
    height: 26px;
    width: 26px;
    display: block;
    flex: none;
    border-radius: 5px;
}

.brand .brand-name {
    display: flex;
    align-items: center;
    font-size: 1.02rem;
    font-weight: 600;
    letter-spacing: -.01em;
}

/* The hairline. Disappears when the mark is missing, so the name stands alone
   rather than sitting behind a rule with nothing before it. */
.brand .brand-mark { color: var(--accent); }
.brand .brand-rest { color: var(--ink); }

/*
 * The one primary control per screen: take a booking, save, submit. Everything
 * else is a link or a secondary button, and the distinction is the point.
 */
.state-dot {
    display: inline-block;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--state-good-dot);
    margin-right: .35rem;
}

/* ====================================================================
   Components added for the operational screens
   ==================================================================== */

/*
 * The warning sentence.
 *
 * ``providers.html`` has used ``class="small warn"`` since it was written and
 * nothing defined ``.warn``, so the sentence that says caller utterances and
 * customer detail leave the market rendered as grey small print
 * indistinguishable from a caption. That sentence is the NFR-25 disclosure and
 * it is the most consequential thing in the console: an operator who misses it
 * does not know their tenant's data is crossing a border.
 */
.warn {
    color: var(--state-warn);
    font-weight: 600;
}
.warn strong { color: var(--state-warn); }

/*
 * The stat tile.
 *
 * One number and what it is a number of. The dashboard counters wore
 * --fs-board, the size built for the service board to be read across a room,
 * so a count of yesterday's calls was the largest thing on the screen and the
 * exceptions banner that needed acting on was among the smallest. A tile is
 * --fs-tile, and it carries a denominator note because a number with nothing
 * to compare it against cannot be acted on.
 */
.tile {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: var(--space-4);
    box-shadow: var(--shadow);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}
.tile-label {
    font-size: var(--fs-fine);
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--ink-faint);
}
.tile-figure {
    font-size: var(--fs-tile);
    font-weight: 680;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}
.tile-note { font-size: var(--fs-small); color: var(--ink-soft); }
.tile-figure .tile-unit {
    font-size: .5em;
    font-weight: 600;
    color: var(--ink-soft);
    margin-left: .15em;
}
/* A tile that carries a state carries it on its left edge as well as in the
   label, because status must never be conveyed by colour alone. */
.tile-good { border-left: 3px solid var(--state-good); }
.tile-warn { border-left: 3px solid var(--state-warn); }
.tile-bad  { border-left: 3px solid var(--state-bad); }

/*
 * The proportion bar.
 *
 * Drawn in CSS from a percentage the template computes, because a chart
 * library is a dependency and this is one rectangle. Every caller must guard
 * its own denominator: a widthratio with a zero total renders as a full bar,
 * which reads as "everything" when it means "nothing yet".
 */
.meter {
    display: flex;
    height: .6rem;
    border-radius: var(--radius-pill);
    overflow: hidden;
    background: var(--line-soft);
    margin: var(--space-2) 0;
}
.meter > span { display: block; height: 100%; }
.meter-good > span, .meter > .seg-good { background: var(--state-good); }
.meter > .seg-warn { background: var(--state-warn); }
.meter > .seg-bad  { background: var(--state-bad); }
.meter > .seg-idle { background: var(--state-idle); }

/* The legend, which is what makes the bar readable rather than decorative. A
   proportion nobody can put a number to is a picture. */
.meter-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--fs-small);
}
.meter-legend > li { display: flex; align-items: baseline; gap: .4rem; }
.meter-legend .swatch {
    width: .6rem;
    height: .6rem;
    border-radius: 2px;
    flex: none;
    transform: translateY(1px);
}
.meter-legend .swatch-good { background: var(--state-good); }
.meter-legend .swatch-warn { background: var(--state-warn); }
.meter-legend .swatch-bad  { background: var(--state-bad); }
.meter-legend .swatch-idle { background: var(--state-idle); }
.meter-legend b { font-variant-numeric: tabular-nums; }

/* A labelled bar list: one row per category, for the counts that used to be a
   paragraph of "x taken, y changed, z cancelled". */
.bar-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.bar-list > li { display: grid; grid-template-columns: 9rem 1fr auto; gap: var(--space-3); align-items: center; }
.bar-list .bar-label { font-size: var(--fs-small); color: var(--ink-soft); }
.bar-list .bar-track { background: var(--line-soft); border-radius: var(--radius-pill); height: .5rem; }
.bar-list .bar-fill { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--accent); }
.bar-list .bar-value { font-variant-numeric: tabular-nums; font-weight: 600; }
@media (max-width: 640px) {
    .bar-list > li { grid-template-columns: 1fr auto; }
    .bar-list .bar-track { grid-column: 1 / -1; }
}

/*
 * The empty state.
 *
 * A table with no rows used to render as a header and nothing, which reads as
 * a page that failed rather than a day with no bookings. It says what would be
 * here and, where there is one, offers the action that would put something
 * here.
 */
.empty {
    border: 1px dashed var(--line);
    border-radius: var(--radius);
    padding: var(--space-6) var(--space-4);
    text-align: center;
    color: var(--ink-soft);
    background: var(--card);
}
.empty h3 { color: var(--ink); margin-bottom: var(--space-2); }
.empty p { max-width: 46ch; margin: 0 auto var(--space-3); }

/*
 * The drawer: a write, opened in place.
 *
 * A native <details>, so it needs no JavaScript to open and keeps its state
 * without one. Amending and cancelling from the register used to be two
 * screens away, and an operator moving four tables before a service does not
 * want eight page loads.
 */
.drawer { margin-top: var(--space-2); }
.drawer > summary {
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--accent);
    padding: .2rem .45rem;
    border-radius: var(--radius-sm);
    list-style: none;
}
.drawer > summary::-webkit-details-marker { display: none; }
.drawer > summary::after { content: "▾"; font-size: .8em; }
.drawer[open] > summary::after { content: "▴"; }
.drawer > summary:hover { background: var(--accent-wash); }
.drawer-body {
    border: 1px solid var(--line);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-sm);
    background: var(--card);
    padding: var(--space-3);
    margin-top: var(--space-2);
    display: grid;
    gap: var(--space-3);
    max-width: 34rem;
}
.drawer-body .field-row { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.drawer-body label { display: block; font-size: var(--fs-small); font-weight: 600; }
/* The consequence sentence, stated where the operator is about to act. An
   admin who changes somebody's table without telling them should read that
   rather than infer it from a checkbox they left alone. */
.drawer-consequence {
    font-size: var(--fs-small);
    color: var(--ink-soft);
    background: var(--line-soft);
    border-radius: var(--radius-sm);
    padding: var(--space-2) var(--space-3);
    margin: 0;
}

/* Pagination. The register said "showing the first 50" and gave no way to see
   the fifty-first. */
.pager {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
    font-size: var(--fs-small);
}
.pager-count { color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* The dense table variant, for the register and the message log. Opt in with
   data-dense so the ordinary two column key/value tables keep their air. */
table[data-dense] { font-size: var(--fs-small); }
table[data-dense] th, table[data-dense] td { padding: .35rem .5rem; }
table[data-dense] .col-key { font-weight: 650; font-variant-numeric: tabular-nums; white-space: nowrap; }
table[data-dense] .col-low { color: var(--ink-soft); }

/* The page header block, so every screen names itself the same way and the
   one primary action sits in the same place on all of them. */
.page-head .page-actions { margin-left: auto; display: flex; gap: var(--space-2); align-items: center; }
.page-head { flex-wrap: wrap; gap: var(--space-3); }

/* Skip link. Present and invisible until focused, which is the only way a
   keyboard user gets past a sidebar of thirty links. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--card);
    color: var(--accent-strong);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    z-index: 10;
}
.skip-link:focus { left: var(--space-3); top: var(--space-3); }

/* The HTMX in flight state. Without it a filter change looks like nothing
   happened until the rows swap. */
[aria-busy="true"] { opacity: .55; transition: opacity .12s ease-in; }

/* An inline checkbox with its label beside it, for the one consequential
   choice on a write form. Distinct from ul.checkbox-list, which is a column of
   them on a configuration screen. */
.check {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--fs-small);
    font-weight: 600;
}
.check input { flex: none; margin: 0; }

/* .button-small exists on links as well as buttons, so the size lives here
   rather than only on the element. */
.button-small { font-size: var(--fs-fine); padding: .25rem .5rem; }

/* The board figure, where the board summary is embedded in a page that is not
   the board. Full size belongs to the board itself, read across a room; on the
   dashboard it made the visiting panel the loudest thing on the screen. */
.occupancy-figure.occupancy-compact { font-size: var(--fs-tile); }

/*
 * The manage cell on a schedule row.
 *
 * The two controls sit side by side so a row stays one line high, and the
 * drawer is anchored to the cell rather than laid out inside it. A 30rem form
 * in a table cell widens the whole table and pushes the columns a host reads
 * off the screen; overlaying it leaves the table alone and puts the form where
 * the operator's eye already is.
 */
.row-manage { position: relative; white-space: nowrap; }
.row-manage .drawer { display: inline-block; margin: 0 var(--space-2) 0 0; }
.row-manage .drawer-body {
    position: absolute;
    right: 0;
    top: 100%;
    z-index: 5;
    width: min(28rem, calc(100vw - 3rem));
    white-space: normal;
    box-shadow: var(--shadow-lift);
}
/* The row that has a drawer open sits above its neighbours, so the overlay is
   not clipped by the next row's background. */
tr:has(.drawer[open]) { position: relative; z-index: 6; }

/* The horizontal scroller clips vertically too, because overflow-x: auto
   implies overflow-y: auto, so an open drawer lost its submit button off the
   bottom of the card. Released only while one is open, so the table still
   scrolls sideways the rest of the time. */
.table-scroll:has(.drawer[open]) { overflow: visible; }

/* ------------------------------------------------------------- signing in */

/*
 * The one page with no navigation and one job.
 *
 * The wordmark used to wear .brand, whose 1.02rem is right in a sidebar beside
 * thirty links and wrong at first contact: the product name rendered smaller
 * than the "Email" label beneath it.
 */
.sign-in-card { max-width: 27rem; padding: var(--space-6) var(--space-5); }

.sign-in-mark {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--fs-30);
    font-weight: 700;
    letter-spacing: -.015em;
    margin-bottom: var(--space-3);
}
.sign-in-mark img { height: 34px; width: 34px; border-radius: 7px; flex: none; }
.sign-in-mark .brand-mark { color: var(--accent); }
.sign-in-mark .brand-rest { color: var(--ink); }

.sign-in-lead {
    color: var(--ink-soft);
    max-width: 34ch;
    margin-bottom: var(--space-2);
}

/* A quiet ground behind the card, so the card is the thing on the page rather
   than a white rectangle on a white page. */
.sign-in-page { background:
    radial-gradient(1200px 600px at 15% -10%, var(--accent-wash), transparent 70%),
    var(--page);
}

.sign-in-eyebrow {
    font-size: var(--fs-fine);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: var(--space-2);
}

/* A select and its save button on one line, for the per language voice choice.
   The shared .form-row stacks its children at this width, which put a button
   under every dropdown and made three languages look like nine controls. */
.voice-choice { display: flex; gap: var(--space-2); align-items: center; margin: 0; }
.voice-choice select { flex: 1 1 12rem; min-width: 0; }
