/* Copyright 2026 InferLink Corporation. All rights reserved. */

/* The design tokens from docs/ui-proposal.html, wired up for real.
 *
 * A palette is CHROME ONLY: ground, surface, ink, lines, and the accent that
 * carries the interactive language. The categorical series (--s-*) are
 * deliberately excluded from every palette block. They encode identity --- which
 * project a line belongs to --- and they are validated for lightness band,
 * chroma floor, colour-vision separation and contrast, not chosen to match a
 * mood. A project changing colour because somebody picked a different palette
 * would make two screenshots of the same data disagree.
 *
 * Each palette defines the FULL token set in both light and dark rather than a
 * delta, because the cascade otherwise resolves wrongly: `:root[data-palette]`
 * outranks a bare `:root`, so a dark block written as a delta on `:root` would
 * lose to a light palette block. Every dark block is therefore complete and
 * comes last.
 */

/* ---- desert: the default, on bare :root ---------------------------------
 * Southwestern desert. Turquoise carries the interactive language, because it
 * is the region's actual signature --- silverwork, painted doors --- and
 * because it leaves iron-oxide red free to mean "destructive", which is a
 * better job for it than decoration.
 */
:root {
    color-scheme: light dark;

    --ground:      #F4EDE1;   /* caliche */
    --surface:     #FFFCF6;   /* sun-bleached plaster */
    --surface-2:   #EDE3D3;   /* sand */
    --ink:         #2A211C;   /* dark umber */
    --ink-2:       #5C4E45;
    --ink-3:       #7D6C5E;
    --line:        #DCCFBB;
    --line-strong: #C2AF97;
    --grid:        #E7DDCC;   /* fainter than --line: a gridline is scaffolding */
    /* Turquoise, darkened from #0E7A85 --- which was 4.36:1 on the ground and
     * therefore under AA for the ordinary link, on every page. This clears it
     * everywhere it lands: 5.00 on ground, 5.68 on surface, 4.66 on
     * accent-soft, and 5.82 for white on it. */
    --accent:      #0C7079;   /* turquoise */
    --accent-ink:  #FFFFFF;
    --accent-soft: #D5EAEC;
    --warn:        #9A5B15;   /* desert ochre */
    --warn-soft:   #F7E6CB;
    --danger:      #A33A22;   /* mesa red, iron oxide */
    --danger-ink:  #FFFFFF;
    --danger-soft: #F8DED4;
    --ok:          #4F6B3A;   /* sage */
    --ok-soft:     #E2EAD6;
    --shadow: 0 1px 2px rgba(42, 33, 28, .07), 0 8px 24px -12px rgba(42, 33, 28, .22);

/* Series colours: four bold hues, cycled, with the dash pattern separating
     * each repeat. Chosen for COLOUR-VISION SEPARATION first --- red, light
     * green, blue and yellow keep their distance under the common forms of
     * colour blindness because their lightness differs as much as their hue
     * does, which is the part red-versus-green normally gets wrong. Never
     * per-palette: a colour here says which project a line is.
     *
     * Yellow is darkened well past a pure one so it clears 3:1 against the
     * light surface; a true yellow line is invisible on paper-coloured ground.
     */
    --s-1: #D32F2F;   /* red */
    --s-2: #4CAF50;   /* light green */
    --s-3: #1565C0;   /* blue */
    --s-4: #B58900;   /* yellow, darkened to stay visible */

    --font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
                 "Liberation Mono", monospace;
    --r: 5px;
    --ctl-h: 34px;
    /* One size and one face for every control in the filter row. A row where
     * the dropdown, the fields, the segments and the buttons each pick their
     * own reads as four rows that happen to be adjacent. */
    --ctl-font: 13px;
}

/* ---- ice ----------------------------------------------------------------
 * Cold neutrals with a blue bias, a glacial accent, and a warm amber kept for
 * warnings precisely because it is the only warm thing on the page.
 */
:root[data-palette="ice"] {
    --ground:      #EFF4F8;   /* frost */
    --surface:     #FBFDFE;
    --surface-2:   #E3ECF3;   /* packed snow */
    --ink:         #142430;   /* deep slate, not black */
    --ink-2:       #42596B;
    --ink-3:       #6D8496;
    --line:        #CDDCE7;
    --line-strong: #ABC1D2;
    --grid:        #DCE7EF;
    --accent:      #0E6C99;   /* glacial blue */
    --accent-ink:  #FFFFFF;
    --accent-soft: #D5E8F4;
    --warn:        #8F5A05;
    --warn-soft:   #F6E8CF;
    --danger:      #AF2A38;   /* crevasse */
    --danger-ink:  #FFFFFF;
    --danger-soft: #F9DEE1;
    --ok:          #106B67;
    --ok-soft:     #D8ECEA;
    --shadow: 0 1px 2px rgba(20, 36, 48, .07), 0 8px 24px -12px rgba(20, 36, 48, .25);
}

/* ---- dark -----------------------------------------------------------------
 * Only when the system asks for it. There is no light/dark toggle; the palette
 * button switches chrome, and dark is the viewer's own setting.
 */
@media (prefers-color-scheme: dark) {
    /* desert, after sundown */
    :root {
        --ground:      #17120F;   /* canyon shadow */
        --surface:     #201A15;
        --surface-2:   #2A221B;
        --ink:         #F0E6DA;
        --ink-2:       #BCA997;
        --ink-3:       #8B7A6B;
        --line:        #362C24;
        --line-strong: #4A3D33;
        --grid:        #2E251E;
        --accent:      #4FC0C7;
        --accent-ink:  #0C1516;
        --accent-soft: #17383A;
        --warn:        #DFA24A;
        --warn-soft:   #382713;
        --danger:      #E8735A;
        --danger-ink:  #2A1410;
        --danger-soft: #3A201A;
        --ok:          #9DBB6E;
        --ok-soft:     #232E19;
        --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -12px rgba(0, 0, 0, .7);

        /* Series, lifted for a dark ground. Still not per-palette. */
        --s-1: #FF6B6B;   /* red */
        --s-2: #81E06B;   /* light green */
        --s-3: #64B5F6;   /* blue */
        --s-4: #FFD54F;   /* yellow, which needs no darkening here */
    }

    /* ice: polar night rather than an inversion of the light set */
    :root[data-palette="ice"] {
        --ground:      #0A1319;
        --surface:     #101C24;
        --surface-2:   #17252E;
        --ink:         #E2ECF2;
        --ink-2:       #A2B6C3;
        --ink-3:       #718594;
        --line:        #23323C;
        --line-strong: #344753;
        --grid:        #1E2C35;
        --accent:      #4FB8E8;
        --accent-ink:  #05121A;
        --accent-soft: #112E3E;
        --warn:        #E0A94A;
        --warn-soft:   #33280F;
        --danger:      #E8737F;
        --danger-ink:  #2A1014;
        --danger-soft: #351A1E;
        --ok:          #57BDB4;
        --ok-soft:     #122E2C;
        --shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 8px 24px -12px rgba(0, 0, 0, .7);
    }


}

/* ---- page ---------------------------------------------------------------- */

body {
    font: 15px/1.5 var(--font-ui);
    margin: 0;
    padding: 2rem clamp(1rem, 5vw, 4rem);
    max-width: 70rem;
    background: var(--ground);
    color: var(--ink);
}

/* Any rule that sets `display` outranks the user agent's `[hidden]`, so a
 * `.btn` with `display: inline-flex` stayed on screen while marked hidden ---
 * which is how the PNG export appeared before the script that makes it work.
 * One `!important` for the one attribute whose whole job is to win. */
[hidden] { display: none !important; }

h2 { font-size: 1.35rem; letter-spacing: -.01em; margin: 0 0 1.25rem; }
h3 { font-size: 1rem; margin: 1.75rem 0 .5rem; }

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

/* ---- header -------------------------------------------------------------
 * Two lines. The first is identity --- the product's, then yours. The second
 * is where you can go. Spend pages add a third for the vendor.
 */

header { margin: 0 0 1.5rem; }

.brandbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    padding-bottom: .5rem;
}

.brand {
    font-size: 1.05rem;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--ink);
    text-decoration: none;
}
.brand:hover { color: var(--accent); }

.who { margin-left: auto; display: flex; align-items: center; gap: .35rem; }
.whoami { display: flex; align-items: center; gap: .4rem; font-size: .85rem; color: var(--ink-2);
          margin-right: .4rem; }
/* The role sits under the name rather than beside it: it is the answer to a
 * different question, and reads as part of the name on one line. */
.whoami .role { display: block; font-size: .7rem; text-transform: uppercase;
                letter-spacing: .06em; }

.icon { width: 18px; height: 18px; flex: none; fill: none; stroke: currentColor;
        stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }

/* Square, quiet, and the same size whether it is a link or a submit button ---
 * signing out has to be a POST, so one of the three cannot be a link. */
.iconbtn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--ctl-h);
    height: var(--ctl-h);
    padding: 0;
    border: 1px solid transparent;
    border-radius: var(--r);
    background: none;
    color: var(--ink-2);
    cursor: pointer;
}
.iconbtn:hover { background: var(--surface-2); color: var(--ink); border-color: var(--line); }
.iconbtn[aria-current="page"] { background: var(--accent); color: var(--accent-ink);
                                border-color: var(--accent); }

/* ---- tabs ---------------------------------------------------------------- */

.tabs { display: flex; gap: .25rem; flex-wrap: wrap; border-bottom: 1px solid var(--line); }
.tabs a {
    padding: .5rem .9rem;
    font-size: .9rem;
    color: var(--ink-2);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}
.tabs a:hover { color: var(--ink); background: var(--surface-2); }
.tabs a[aria-current="page"] { color: var(--accent); border-bottom-color: var(--accent);
                               font-weight: 600; }

/* The sign-in page has a brand bar and nothing to navigate. */
header.signin .brandbar { justify-content: center; }

/* ---- palette switch ------------------------------------------------------ */

/* One segmented control, used by the palette switch (buttons, since switching
 * is a script) and by the spend page's Columns choice (links, since each is a
 * page of its own). */
.palette { display: inline-flex; align-items: center; margin: .25rem 0 1rem; }
.seg { display: flex; }
.seg button, .seg a {
    appearance: none;
    display: inline-flex;
    align-items: center;
    cursor: pointer;
    font: inherit;
    font-size: var(--ctl-font);
    padding: 0 12px;
    height: var(--ctl-h);
    box-sizing: border-box;
    background: var(--surface);
    color: var(--ink-2);
    text-decoration: none;
    border: 1px solid var(--line-strong);
    border-right-width: 0;
    border-radius: 0;
}
.seg :first-child { border-radius: var(--r) 0 0 var(--r); }
.seg :last-child { border-radius: 0 var(--r) var(--r) 0; border-right-width: 1px; }
.seg button:hover, .seg a:hover { background: var(--surface-2); color: var(--ink); }
/* Filled rather than tinted. Accent-on-accent-soft is only about 4:1 in
 * several of these palettes, which fails AA at this size; accent-ink on accent
 * is the pair that is designed to carry text and clears it everywhere. */
.seg button[aria-pressed="true"], .seg a[aria-current="true"] {
    background: var(--accent);
    color: var(--accent-ink);
    border-color: var(--accent);
    font-weight: 600;
}
.seg button[aria-pressed="true"] + button,
.seg a[aria-current="true"] + a { border-left-color: var(--accent); }

/* No script, no switching --- so do not offer a control that cannot work. */
.no-js .palette { display: none; }

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

table {
    border-collapse: collapse;
    width: 100%;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow);
    overflow: hidden;
}
th, td { text-align: left; padding: .45rem .7rem; border-bottom: 1px solid var(--line); }
thead th {
    background: var(--surface-2);
    font-size: .78rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--ink-2);
}
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td { background: var(--surface-2); }
tfoot th { background: var(--surface-2); border-bottom: 0; border-top: 1px solid var(--line-strong); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono);
                 font-size: .92em; }

/* ---- notes and warnings -------------------------------------------------- */

/* --ink-2, not --ink-3: the muted ink falls under 4.5:1 against ground in
 * several palettes, and a note is prose somebody has to read. */
.note { font-size: .85rem; color: var(--ink-2); }
/* A note about the table it follows --- the same remark `.panel-note` makes on
 * the spend pages, so it is written in the same voice: one size down from the
 * table, italic, and running the table's full width rather than sitting in a
 * column of its own. */
table + .note {
    margin-top: .6rem;
    font-size: .72rem;
    font-style: italic;
}

/* Confirmation that something happened, which §4.4 asks for and nothing
 * provided: a password change --- which signs out every other session ---
 * redirected to a page that looked identical to the one before it.
 *
 * A banner rather than the mockup's fading toast: a toast needs script to
 * fade, and one that disappears on a timer is unreadable to anyone who looks
 * away. This goes when the next page does. */
.done {
    margin: 1rem 0 0;
    padding: .55rem .8rem;
    border-left: 3px solid var(--ok);
    border-radius: 0 var(--r) var(--r) 0;
    background: var(--ok-soft);
    color: var(--ink);
    font-size: .85rem;
}

.warn {
    border-left: 3px solid var(--warn);
    background: var(--warn-soft);
    color: var(--ink);
    padding: .55rem .8rem;
    margin: 1rem 0;
    border-radius: 0 var(--r) var(--r) 0;
    font-size: .85rem;
}

/* ---- controls ------------------------------------------------------------ */

form.inline { display: inline; }
button, input, select { font: inherit; }

input, select {
    padding: .4rem .5rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    background: var(--surface);
    color: var(--ink);
    width: 100%;
    box-sizing: border-box;
}

/* One control height, so a row of mixed controls lines up. Native date inputs
 * and selects each have their own intrinsic height, and a button was already
 * pinned to --ctl-h, which is exactly why the filter row did not line up. */
input:not([type="checkbox"]):not([type="radio"]), select { height: var(--ctl-h); }
input:focus-visible, select:focus-visible, button:focus-visible, summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

button {
    padding: .4rem .9rem;
    cursor: pointer;
    background: var(--accent);
    color: var(--accent-ink);
    border: 1px solid var(--accent);
    border-radius: var(--r);
    height: var(--ctl-h);
}
button:hover { filter: brightness(1.08); }

label { display: block; margin: .75rem 0 .25rem; font-size: .85rem; color: var(--ink-2); }

/* ---- panels --------------------------------------------------------------
 * One card holding the controls, the chart and the table, in that order. They
 * are one thing --- the controls say what the chart and table show --- and a
 * page of separately floating blocks does not say so.
 */

.panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow);
}

/* ---- filter row ----------------------------------------------------------
 * Every control is exactly one line tall, so a plain flex row bottom-aligns
 * them and each label sits directly above its own control. That is a property
 * of the controls, not of the layout: the tall multi-select this row used to
 * carry is now a disclosure, which is what made the row line up.
 */
.controls {
    display: flex;
    gap: .6rem;
    align-items: flex-end;
    flex-wrap: wrap;
    padding: .9rem 1.1rem;
    border-bottom: 1px solid var(--line);
}
.ctl-group { display: flex; flex-direction: column; gap: .3rem; }
.ctl-group.push { margin-left: auto; }
.controls input, .controls select, .controls button, .controls .btn,
.controls summary, .seg a, .seg button {
    font-family: var(--font-ui);
    font-size: var(--ctl-font);
}
.controls input, .controls select { width: auto; }
.controls .isodate {
    /* Wide enough for YYYY-MM-DD and then some: a field that clips 2026-07-31
     * is worse than one that is a little wide. `ch` follows the control font,
     * so this stays right if that changes.
     *
     * Nothing else is set here. The date is the same face and the same size as
     * every other control on the row --- not the table's mono, and not even
     * `tabular-nums`, which lines the digits up by widening them and reads as a
     * different font sitting in the middle of the row. */
    width: calc(11ch + 1.2rem);
}
.controls .isodate:invalid { border-color: var(--danger); }

/* A disabled Apply is the resting state: it says the page already shows what
 * the controls describe, so it has to read as inert rather than as broken. */
button[disabled] { opacity: .45; cursor: default; }
button[disabled]:hover { filter: none; }

.lbl {
    display: block;
    margin: 0;
    font-size: .7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--ink-2);
}

/* The project filter: a disclosure, so it needs no script to open and no
 * second page to choose on. */
.projpick { position: relative; min-width: 12rem; }
.projpick > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    height: var(--ctl-h);
    padding: 0 .6rem;
    box-sizing: border-box;
    font-size: var(--ctl-font);
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    cursor: pointer;
    list-style: none;
}
.projpick > summary::-webkit-details-marker { display: none; }
.projpick > summary::after { content: "▾"; color: var(--ink-2); }
.projpick[open] > summary { border-color: var(--accent); }
.projmenu {
    position: absolute;
    z-index: 30;
    top: calc(100% + 4px);
    left: 0;
    min-width: 100%;
    max-height: 16rem;
    overflow-y: auto;
    padding: .35rem;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
    box-shadow: var(--shadow);
}
.projmenu label {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0;
    padding: .3rem .45rem;
    border-radius: 3px;
    font-size: var(--ctl-font);
    color: var(--ink);
    white-space: nowrap;
    cursor: pointer;
}
.projmenu label:hover { background: var(--surface-2); }
.projmenu input { width: auto; }
/* Not a member of the dimension but an answer to the same question, so it sits
 * with them --- above the rule, where a summary line belongs. */
.projmenu .pick-top { font-weight: 600; }
.projmenu hr { margin: .3rem .45rem; border: 0; border-top: 1px solid var(--line); }


/* The exports. One is an anchor and one is a button --- the CSV is a link to a
 * file, the PNG is drawn here --- so they have to look alike despite being
 * different elements, and both have to be quieter than Apply. */
/* Smaller than the controls they sit beside: exporting is something you do
 * after reading the page, not part of describing what to read. */
.btn {
    display: inline-flex;
    align-items: center;
    height: 26px;
    padding: 0 .6rem;
    box-sizing: border-box;
    font-size: 12px;
    color: var(--ink);
    text-decoration: none;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--r);
}
.btn:hover { background: var(--surface-2); filter: none; }

/* ---- chart ---------------------------------------------------------------
 * The series colours are set inline by the renderer, because they say which
 * project a line is; everything here is the frame around them.
 */

.chartwrap { padding: 1rem 1.1rem .25rem; }
/* The height is set inline by the renderer, which drew the SVG at that size.
 * The fallback matches its default, so a chart still has a height if the
 * property is ever missing. */
.chart { display: block; width: 100%; height: var(--chart-h, 210px); overflow: visible; }
.chart .gridline { stroke: var(--grid); stroke-width: 1; }
.chart .axis { stroke: var(--line-strong); stroke-width: 1; }
.chart text { fill: var(--ink-2); font-size: 10px; font-family: var(--font-mono); }
/* Half again as wide as a hairline: these have to be followable by someone
 * separating them by lightness rather than by hue. */
.chart .series { fill: none; stroke-width: 3; stroke-linejoin: round; stroke-linecap: butt; }
.chart .bar { stroke: var(--surface); stroke-width: .5; }
/* Days still settling, shaded rather than annotated: the reader can see which
 * part of the shape is expected to move. */
.chart .est-band { fill: var(--warn); opacity: .09; }

.legend {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem 1rem;
    padding: .25rem 1.1rem .9rem;
    font-size: .78rem;
    color: var(--ink-2);
}
.legend span { display: inline-flex; align-items: center; gap: .4rem; }
/* 14px, not 10: a dashed series needs room for more than one dash, or
 * it reads as the solid swatch of the same hue two entries up. */
.legend i { display: inline-block; width: 14px; height: 3px; border-radius: 2px; }
.legend i.est-key-line { background: var(--warn); opacity: .35; }

/* Drag to make the chart taller. A full-width bar rather than the corner grip
 * `resize: vertical` would give: the gesture is vertical and the target should
 * be too, and the grip cannot be reached from a keyboard.
 *
 * A plain band, edge to edge, with no handle drawn on it: the pointer turns
 * into a resize cursor over it, which says what it does without putting an
 * ornament in the middle of the page. Thin, but a band rather than the
 * two-pixel hairline this was first --- that was discoverable by accident. */
.chartsize {
    height: 8px;
    margin: .15rem 0 .4rem;
    cursor: ns-resize;
    border-top: 1px solid var(--line);
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
    touch-action: none;
    user-select: none;
}
.chartsize:hover, .chartsize.dragging, .chartsize:focus-visible {
    background: var(--accent-soft);
    border-color: var(--accent);
}

.exports { display: flex; gap: .5rem; }

/* ---- spend table --------------------------------------------------------- */

.tblwrap { overflow-x: auto; }
.spendtbl { font-size: .82rem; border: 0; border-radius: 0; box-shadow: none; }
.spendtbl td, .spendtbl th { padding: .35rem .7rem; white-space: nowrap; }
.spendtbl .day { font-family: var(--font-mono); }
/* A day still settling is one thing, not a row of separately suspicious
 * numbers, so the whole row carries the warning colour. */
.spendtbl td.est { color: var(--warn); }
.spendtbl .unknown { color: var(--ink-2); font-style: italic; }
.spendtbl tfoot td {
    background: var(--surface-2);
    font-weight: 600;
    border-top: 1px solid var(--line-strong);
    border-bottom: 0;
}
.est-key {
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-left: .35rem;
    border-radius: 50%;
    background: var(--warn);
    vertical-align: middle;
}

/* Full width rather than a measure: it sits under a table that already sets
 * the width, and a short column of text beneath a wide table reads as a
 * different element rather than as a note about it. */
.panel-note {
    margin: 0;
    padding: .8rem 1.1rem;
    border-top: 1px dashed var(--line);
    font-size: .72rem;
    font-style: italic;
    color: var(--ink-2);
}

.empty-state { padding: 2.5rem 1.1rem; text-align: center; color: var(--ink-2); font-size: .85rem; }
.empty-state b { display: block; color: var(--ink); font-size: .95rem; margin-bottom: .35rem; }

/* ---- the register: a list beside the one entry it has open ---------------
 * Two panes rather than an index page and a form page. The list is what tells
 * you which project you want next, and a form that hides it makes you go back
 * for it.
 */

.cols { display: grid; grid-template-columns: 310px minmax(0, 1fr); gap: 1rem; align-items: start; }
@media (max-width: 860px) { .cols { grid-template-columns: 1fr; } }

.list-head {
    display: flex;
    flex-direction: column;
    gap: .55rem;
    padding: .75rem;
    border-bottom: 1px solid var(--line);
}
.list-sub { display: flex; align-items: center; gap: .5rem; font-size: .72rem; color: var(--ink-2); }
.list-sub .count { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.search { background: var(--surface-2); border-color: var(--line); }
.search::placeholder { color: var(--ink-3); }

/* A quiet control: it changes what the page shows without being the thing the
 * page is for. Shared with the people register's status filter. */
.toggle-inactive {
    margin-left: auto;
    font-size: .72rem;
    padding: .18rem .5rem;
    border-radius: 3px;
    color: var(--ink-2);
    text-decoration: none;
    background: var(--surface-2);
    border: 1px solid var(--line);
}
.toggle-inactive:hover { color: var(--accent); border-color: var(--accent); }
.toggle-inactive[aria-pressed="true"] {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: var(--accent);
}

.plist { list-style: none; margin: 0; padding: .35rem; max-height: 68vh; overflow-y: auto; }
.prow {
    display: flex;
    flex-direction: column;
    gap: .12rem;
    padding: .5rem .6rem;
    border-radius: var(--r);
    color: var(--ink);
    text-decoration: none;
}
.prow:hover { background: var(--surface-2); }
.prow[aria-current="true"] { background: var(--accent-soft); }
.prow[aria-current="true"] .pname { color: var(--accent); }
.pname { font-weight: 600; font-size: .88rem; }
.pname .sub { color: var(--ink-2); font-weight: 400; }
.pmeta {
    font-family: var(--font-mono);
    font-size: .68rem;
    color: var(--ink-2);
    font-variant-numeric: tabular-nums;
}
.empty-hint { padding: .9rem .75rem; font-size: .78rem; color: var(--ink-2); line-height: 1.45; }

/* ---- the entry itself ---------------------------------------------------- */

.dhead {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    padding: .9rem 1.1rem;
    border-bottom: 1px solid var(--line);
}
.dhead h2 { margin: 0 0 .1rem; font-size: 1rem; }
.dhead .id { font-family: var(--font-mono); font-size: .7rem; color: var(--ink-2); }
.dhead-acts { display: flex; gap: .35rem; margin-left: auto; }

/* The form is one fieldset so a reader can be handed the whole thing inert. */
.panel fieldset { margin: 0; padding: 0; border: 0; min-width: 0; }
.panel fieldset[disabled] { opacity: .8; }

section.blk { padding: .9rem 1.1rem; border-bottom: 1px solid var(--line); }
.fgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: .8rem; }
.cell { display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
.cell > .lbl { margin: 0; }
.hint { margin: .7rem 0 0; font-size: .72rem; font-style: italic; color: var(--ink-2); }

/* One period per row, the three fields lined up down the column. */
.periods { display: flex; flex-direction: column; gap: .5rem; }
.prd {
    display: grid;
    grid-template-columns: 1fr 1fr 150px;
    gap: .5rem .6rem;
    padding: .6rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 3px solid var(--line);
    border-radius: var(--r);
}
.prd.bad { border-left-color: var(--danger); background: var(--danger-soft); }
@media (max-width: 640px) { .prd { grid-template-columns: 1fr 1fr; } }
.err-msg { grid-column: 1 / -1; margin: 0; font-size: .75rem; color: var(--danger); }

/* A bound with no date says so here. Ticked, it reads as the state it is
 * rather than as an unfilled field --- which is the whole distinction: "not
 * filled in yet" and "runs forever" are different answers. */
.openbox {
    display: flex;
    align-items: center;
    gap: .35rem;
    margin: .3rem 0 0;
    font-size: .7rem;
    color: var(--ink-2);
    cursor: pointer;
}
.openbox input { width: auto; margin: 0; }
.openbox input:checked ~ span {
    padding: 1px 6px;
    border-radius: 100px;
    font-weight: 600;
    color: var(--accent);
    background: var(--accent-soft);
    border: 1px dashed var(--accent);
}
.banner/* Confirmation that something happened, which §4.4 asks for and nothing
 * provided: a password change --- which signs out every other session ---
 * redirected to a page that looked identical to the one before it.
 *
 * A banner rather than the mockup's fading toast: a toast needs script to
 * fade, and one that disappears on a timer is unreadable to anyone who looks
 * away. This goes when the next page does. */
.done {
    margin: 1rem 0 0;
    padding: .55rem .8rem;
    border-left: 3px solid var(--ok);
    border-radius: 0 var(--r) var(--r) 0;
    background: var(--ok-soft);
    color: var(--ink);
    font-size: .85rem;
}

.warn { background: var(--warn-soft); color: var(--warn); }

/* A banner belongs to the panel it sits in, unlike `.warn`, which floats in
 * the page with a margin around it. */
.banner {
    margin: 0;
    padding: .6rem 1.1rem;
    font-size: .82rem;
    border-bottom: 1px solid var(--line);
}
.banner.bad { background: var(--danger-soft); color: var(--danger); }

.actions {
    display: flex;
    gap: .6rem;
    align-items: center;
    padding: .8rem 1.1rem;
    background: var(--surface-2);
    border-radius: 0 0 var(--r) var(--r);
}

/* ---- coverage timeline ---------------------------------------------------
 * The gap between two bars is what this is for: a stretch of days that will
 * price under no cost code at all, which is nearly invisible in a list of
 * dates.
 */

.tl-wrap { margin: .5rem 0 .9rem; }
.tl {
    position: relative;
    height: 40px;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r);
    overflow: hidden;
}
.tl-year {
    position: absolute;
    top: 0;
    bottom: 0;
    border-left: 1px solid var(--line);
    padding: 2px 0 0 4px;
    font-family: var(--font-mono);
    font-size: 10px;
    color: var(--ink-2);
}
.bar {
    position: absolute;
    top: 13px;
    height: 14px;
    border-radius: 3px;
    background: var(--accent);
    opacity: .85;
}
/* An open bound runs off the edge rather than stopping short of it, which is
 * the difference between "ends here" and "does not end". */
.bar.open-l { border-radius: 0 3px 3px 0; mask-image: linear-gradient(to right, transparent 0, #000 26px); }
.bar.open-r { border-radius: 3px 0 0 3px; mask-image: linear-gradient(to left, transparent 0, #000 26px); }
.bar.open-l.open-r {
    mask-image: linear-gradient(to right, transparent 0, #000 26px,
                                #000 calc(100% - 26px), transparent 100%);
}
.tl-legend { margin: .35rem 0 0; font-size: .7rem; font-style: italic; color: var(--ink-2); }

/* ---- people register ----------------------------------------------------- */

/* Handles, ids and dates: nowrap, or a date breaks across two lines
 * mid-token. The table already scrolls inside `.tblwrap`. */
td.mono { font-family: var(--font-mono); font-size: .8rem; white-space: nowrap; }
td.rowacts { text-align: right; white-space: nowrap; }
tr.inactive td { color: var(--ink-2); }
tr.inactive td:nth-child(2) { text-decoration: line-through; text-decoration-color: var(--line-strong); }
.chip {
    display: inline-block;
    margin-left: .35rem;
    padding: 1px 5px;
    border-radius: 3px;
    font-family: var(--font-mono);
    font-size: .62rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    background: var(--warn-soft);
    color: var(--warn);
}
/* The default is the warn palette; a severity says which it is. */
.chip.error { background: var(--danger-soft); color: var(--danger); }
.chip.warning { background: var(--warn-soft); color: var(--warn); }
.chip.ok { background: var(--ok-soft); color: var(--ok); }
.arw { font-size: .55rem; color: var(--accent); }
.mono-in { font-family: var(--font-mono); font-size: .82rem; }
.list-add { padding: 0 .75rem .6rem; border-bottom: 1px solid var(--line); }
.btn.wide { width: 100%; justify-content: center; border-style: dashed; }
/* The row being edited, and the block under it holding what does not fit in a
 * row: the hint, and the delete. */
tr.editing td { background: var(--accent-soft); }
tr.editing td.rowform { padding-top: 0; }
tr.editing input { height: 28px; }
.rowacts .btn, .rowacts button { margin-left: .3rem; }
.rowacts button { height: 26px; padding: 0 .6rem; font-size: 12px; }

/* ---- the destructive half -----------------------------------------------
 * Quiet, separated, and never the obvious path. Both of these are disclosures
 * rather than dialogs: a dialog needs script to open, and one that opens by
 * itself is one you can dismiss into by reflex. Opening this is a deliberate
 * act, and what is behind it still has to be typed out.
 */

.danger-zone { padding: .8rem 1.1rem; border-top: 1px dashed var(--line); }
.danger-zone > details > summary,
.linkdanger {
    display: inline-block;
    font-size: .75rem;
    color: var(--danger);
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
    list-style: none;
}
.linkdanger::-webkit-details-marker { display: none; }

/* What a disclosure opens onto, here and on the retire control. */
.popbox {
    margin-top: .6rem;
    padding: .7rem .8rem;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--r);
    max-width: 46rem;
}
.popbox p { margin: 0 0 .6rem; font-size: .78rem; color: var(--ink-2); line-height: 1.5; }
/* A label naming a string that has to be typed back exactly cannot be
 * uppercased: it would ask for ARBITAL and refuse Arbital. */
.lbl.exact { text-transform: none; letter-spacing: 0; font-size: .75rem; }
.popbox b { color: var(--ink); }
.popact { display: flex; gap: .5rem; align-items: center; margin-top: .35rem; }
.popact input { max-width: 22rem; }

/* Confirmation on the actions that cannot be walked back: deactivating an
 * account (which drops its sessions and tokens), revoking a token, retiring
 * somebody. A disclosure rather than a dialog --- it needs no script, and it
 * cannot be dismissed by the reflex that dismisses a modal. The reversible
 * ones deliberately do not ask. */
.confirm { position: relative; display: inline-block; }
.confirm > summary { list-style: none; }
.confirm > summary::-webkit-details-marker { display: none; }
.confirm > .popbox {
    position: absolute;
    z-index: 30;
    right: 0;
    width: 22rem;
    text-align: left;
    white-space: normal;
    background: var(--surface);
    box-shadow: var(--shadow);
}

/* Retire sits in the actions bar, so its box has to escape the bar rather than
 * stretch it. */
.retire { position: relative; margin-left: auto; }
.retire > summary { list-style: none; }
.retire > summary::-webkit-details-marker { display: none; }
.retire > .popbox {
    position: absolute;
    z-index: 30;
    right: 0;
    bottom: calc(100% + 6px);
    width: 22rem;
    background: var(--surface);
    box-shadow: var(--shadow);
}

button.danger {
    background: var(--danger);
    border-color: var(--danger);
    color: var(--danger-ink);
    height: 26px;
    padding: 0 .6rem;
    font-size: 12px;
}

/* ---- vendor nav ---------------------------------------------------------- */

.subnav { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; margin: 0 0 1.5rem; }
.subnav a {
    font-size: .8rem;
    color: var(--ink-2);
    text-decoration: none;
    padding: .3rem .75rem;
    border: 1px solid transparent;
    border-radius: 100px;
}
.subnav a:hover { background: var(--surface-2); color: var(--ink); }
.subnav a[aria-current="page"] {
    background: var(--accent);
    color: var(--accent-ink);
    border-color: var(--accent);
    font-weight: 600;
}
/* The nav itself encodes that AWS is not one of the LLMs. */

code { font-family: var(--font-mono); font-size: .92em; }
