/* /Components/Batch/BatchComposer.razor.rz.scp.css */
/*
    The batch page (docs/08-UI-SPEC.md §4). Nothing here writes a colour, a space or
    a radius of its own — §9.2's rule.
*/

.batch[b-r9yrjpavel] {
    --stack-gap: var(--space-6);
    container-type: inline-size;
    container-name: batch;
}

.batch__paste[b-r9yrjpavel] {
    inline-size: 100%;
    font-size: var(--text-sm);

    /* A pasted table is read column by column, so it must not be re-wrapped. */
    white-space: pre;
    overflow-x: auto;
}

/*
    Type, mapping and the shared style side by side, and one column when the page is
    given less room. A container query rather than a viewport one (§9.4) — and on the
    FRAME rather than on the grid, because a container query styles a container's
    descendants and never the container itself.
*/
.batch__work[b-r9yrjpavel] {
    display: grid;
    grid-template-columns: 260px 1fr 380px;
    gap: var(--space-6);
    align-items: start;
}

@container batch (width < 60rem) {
    .batch__work[b-r9yrjpavel] {
        grid-template-columns: 1fr;
    }

    .batch__preview[b-r9yrjpavel] {
        order: -1;
    }
}

.batch__preview[b-r9yrjpavel] {
    display: grid;
    gap: var(--space-4);
    min-inline-size: 0;
}

.batch__map[b-r9yrjpavel],
.batch__rows[b-r9yrjpavel] {
    inline-size: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.batch__map th[b-r9yrjpavel],
.batch__map td[b-r9yrjpavel],
.batch__rows th[b-r9yrjpavel],
.batch__rows td[b-r9yrjpavel] {
    padding: var(--space-2) var(--space-3);
    text-align: start;
    vertical-align: top;
    border-block-end: 1px solid var(--border);
}

.batch__map thead th[b-r9yrjpavel],
.batch__rows thead th[b-r9yrjpavel] {
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--text-muted);
}

.batch__map select[b-r9yrjpavel],
.batch__rows select[b-r9yrjpavel] {
    inline-size: 100%;
}

/*
    The verdict table can be a few hundred rows: it scrolls in its own box rather
    than pushing the generate button off the bottom of the page. The virtualised
    table of task 6.10 is the log explorer's problem, not this one — a batch is
    bounded by MaxBatchSize.
*/
.batch__scroller[b-r9yrjpavel] {
    max-block-size: 24rem;
    overflow: auto;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
}

.batch__detail[b-r9yrjpavel] {
    /* Long encoded strings and long messages both live here. */
    overflow-wrap: anywhere;
    font-size: var(--text-xs);
}

.batch__row--bad td[b-r9yrjpavel] {
    background-color: color-mix(in oklch, var(--err) 8%, transparent);
}

.batch__progress[b-r9yrjpavel] {
    block-size: var(--space-2);
    border-radius: var(--r-full);
    background-color: var(--surface-2);
    overflow: hidden;
}

.batch__bar[b-r9yrjpavel] {
    block-size: 100%;
    inline-size: var(--batch-progress, 0%);
    background-color: var(--accent);
    transition: inline-size var(--dur-instant) var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {
    .batch__bar[b-r9yrjpavel] {
        transition: none;
    }
}
/* /Components/Charts/BarList.razor.rz.scp.css */
/*
    The ranked bar chart (docs/08-UI-SPEC.md §6). Tokens only.
*/

.chart[b-l1ij1gw4ef] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
}

.chart__title[b-l1ij1gw4ef] {
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
}

.bars[b-l1ij1gw4ef] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
}

.bars__row[b-l1ij1gw4ef] {
    display: grid;
    grid-template-columns: minmax(6rem, 10rem) 1fr;
    gap: var(--space-3);
    align-items: center;
}

.bars__label[b-l1ij1gw4ef] {
    overflow: hidden;
    font-size: var(--text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bars__value[b-l1ij1gw4ef] {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    margin: 0;
}

.bars__track[b-l1ij1gw4ef] {
    flex: 1;
    min-inline-size: 0;
    block-size: 0.75rem;
    border-radius: var(--r-sm);
    background: var(--surface-2);
}

.bars__fill[b-l1ij1gw4ef] {
    display: block;
    block-size: 100%;
    border-radius: var(--r-sm);
    background: var(--accent);
}

.bars__figure[b-l1ij1gw4ef] {
    min-inline-size: 3rem;
    font-size: var(--text-xs);
    text-align: end;
}
/* /Components/Charts/LineChart.razor.rz.scp.css */
/*
    The day-series chart (docs/08-UI-SPEC.md §6). One accent colour, a recessive
    tint under the line, and two axis labels. Tokens only.
*/

.chart[b-pnn0y1ucxu] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
}

.chart__title[b-pnn0y1ucxu] {
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
}

.chart__plot[b-pnn0y1ucxu] {
    inline-size: 100%;
    block-size: auto;
}

.chart__line[b-pnn0y1ucxu] {
    fill: none;
    stroke: var(--accent);
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.chart__area[b-pnn0y1ucxu] {
    fill: color-mix(in oklch, var(--accent) 10%, transparent);
    stroke: none;
}

/* A 2px ring in the surface colour, so a marker over the line reads as a marker
   rather than as a thickening of it. */
.chart__marker[b-pnn0y1ucxu] {
    fill: var(--accent);
    stroke: var(--surface);
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
}

.chart__axis[b-pnn0y1ucxu] {
    display: flex;
    justify-content: space-between;
    gap: var(--space-2);
    color: var(--text-muted);
    font-size: var(--text-xs);
}

.chart__peak[b-pnn0y1ucxu] {
    color: var(--text);
}
/* /Components/Charts/MiniBars.razor.rz.scp.css */
/*
    One panel of the errors-by-code small multiples (docs/08-UI-SPEC.md §6).
    Tokens only.
*/

.mini[b-8rxwx8h93v] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-3);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
}

.mini__title[b-8rxwx8h93v] {
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.mini__plot[b-8rxwx8h93v] {
    inline-size: 100%;
    block-size: 2.5rem;
}

.mini__bar[b-8rxwx8h93v] {
    fill: var(--accent);
}

.mini__total[b-8rxwx8h93v] {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}
/* /Components/Charts/Sparkline.razor.rz.scp.css */
/*
    The sparkline of docs/08-UI-SPEC.md §5. One accent colour, a recessive tint
    under it, and nothing else — no axis, no grid, no chartjunk.
*/

.sparkline[b-itpaunbj09] {
    inline-size: 100%;
    block-size: 2rem;
    overflow: visible;
}

.sparkline__line[b-itpaunbj09] {
    fill: none;
    stroke: var(--accent);

    /* Non-scaling so the 2px mark stays 2px after the viewBox is stretched to the
       column's width, which is the whole reason the stroke does not simply scale. */
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.sparkline__area[b-itpaunbj09] {
    fill: color-mix(in oklch, var(--accent) 12%, transparent);
    stroke: none;
}
/* /Components/Clients/KeyRevealDialog.razor.rz.scp.css */
/*
    The one-time key display (docs/08-UI-SPEC.md §5). The key itself is the largest
    thing in the dialog and is selectable, because somebody whose clipboard is
    refused has to be able to read it off the screen accurately.
*/

.key-reveal__value[b-i646hy9tj8] {
    display: block;
    padding: var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    background: var(--surface-2);

    font-size: var(--text-sm);
    word-break: break-all;
    user-select: all;
}

.key-reveal__facts[b-i646hy9tj8] {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-1) var(--space-4);
    margin: 0;
    font-size: var(--text-sm);
}

.key-reveal__facts dt[b-i646hy9tj8] {
    color: var(--text-muted);
}

.key-reveal__facts dd[b-i646hy9tj8] {
    margin: 0;
}

.key-reveal__ack[b-i646hy9tj8] {
    display: flex;
    gap: var(--space-2);
    align-items: flex-start;
    font-size: var(--text-sm);
}
/* /Components/Clients/PermissionsEditor.razor.rz.scp.css */
/*
    The permissions editor (docs/08-UI-SPEC.md §5). Tokens only.
*/

.permissions__group[b-mq3xp4p5dx] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);

    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
}

.permissions__group legend[b-mq3xp4p5dx] {
    padding-inline: var(--space-2);
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

/* Twenty-six types are a wall of checkboxes in one column and a readable grid in
   several. The column count follows the space, not the viewport. */
.permissions__options[b-mq3xp4p5dx] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
    gap: var(--space-1) var(--space-4);
}

.permissions__option[b-mq3xp4p5dx],
.permissions__all[b-mq3xp4p5dx] {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    min-block-size: var(--control-h);
    font-size: var(--text-sm);
    cursor: pointer;
}

.permissions__row[b-mq3xp4p5dx] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
    gap: var(--space-4);
}
/* /Components/Common/CopyButton.razor.rz.scp.css */
/*
    The copy button's check (docs/08-UI-SPEC.md §9.6). Tokens only.
*/

.copy__icon[b-si2qwoycxx] {
    transition: transform var(--dur-instant) var(--ease-out),
                color var(--dur-instant) var(--ease-out);
}

/* The check is the confirmation; the small overshoot is what makes it read as a
   change rather than as a different button. Removed entirely under
   prefers-reduced-motion by the primitive in base.css, where the icon simply
   swaps — which is the whole signal anyway. */
.copy__icon--done[b-si2qwoycxx] {
    color: var(--ok);
    transform: scale(1.15);
}
/* /Components/Generator/AnalysisPanel.razor.rz.scp.css */
.analysis__facts[b-vlqv8kns2e] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
    margin: 0;
}

.analysis__facts dt[b-vlqv8kns2e] {
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--text-muted);
}

.analysis__facts dd[b-vlqv8kns2e] {
    margin: 0;
    font-size: var(--text-md);
    font-variant-numeric: tabular-nums;
}

.analysis__raised[b-vlqv8kns2e] {
    margin-inline-start: var(--space-2);
    padding-inline: var(--space-2);
    border-radius: var(--r-full);
    background: color-mix(in oklch, var(--warn) 18%, transparent);
    color: var(--warn);
    font-size: var(--text-xs);
}

.analysis__capacity[b-vlqv8kns2e] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.analysis__capacityHead[b-vlqv8kns2e] {
    justify-content: space-between;
    font-size: var(--text-sm);
}

/*
    A real gauge (§9.6), not a decorated div: role="meter" with its value, so a
    screen reader reads the number rather than describing a rectangle. The colour
    is the second signal — the figure beside it is the first (§9.7).
*/
.gauge[b-vlqv8kns2e] {
    block-size: var(--space-2);
    border-radius: var(--r-full);
    background: var(--surface-2);
    overflow: hidden;
}

.gauge__fill[b-vlqv8kns2e] {
    block-size: 100%;
    border-radius: var(--r-full);
    background: var(--accent);
    transition: inline-size var(--dur-panel) var(--ease-panel),
                background-color var(--dur-instant) var(--ease-out);
}

.gauge[data-level='high'] .gauge__fill[b-vlqv8kns2e] {
    background: var(--warn);
}

.gauge[data-level='full'] .gauge__fill[b-vlqv8kns2e] {
    background: var(--err);
}

.analysis__warnings[b-vlqv8kns2e] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.analysis__warning[b-vlqv8kns2e] {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    color: var(--warn);
    font-size: var(--text-sm);
}
/* /Components/Generator/DeliveryPanel.razor.rz.scp.css */
/*
    The delivery controls (docs/08-UI-SPEC.md §3). Nothing here writes a colour, a
    space or a radius of its own — §9.2's rule — and the buttons themselves are the
    .button primitive.
*/

.delivery[b-viv1gr4uun] {
    gap: var(--space-2);
}

/*
    The format menu is a <details>, like the style editor's sections and for the
    same reason: the browser owns the open state, the keyboard and the
    announcement. A scripted menu would have to reimplement all three.
*/
.delivery__menu > summary[b-viv1gr4uun] {
    inline-size: 100%;
    justify-content: center;

    /* The disclosure triangle is replaced by the chevron inside the summary. */
    list-style: none;
}

.delivery__menu > summary[b-viv1gr4uun]::-webkit-details-marker {
    display: none;
}

.delivery__formats[b-viv1gr4uun] {
    display: grid;
    gap: var(--space-1);

    margin-block-start: var(--space-2);
    padding: var(--space-2);

    list-style: none;

    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background-color: var(--surface-2);
}

.delivery__format[b-viv1gr4uun] {
    inline-size: 100%;
    display: flex;
    justify-content: space-between;
    gap: var(--space-3);

    padding: var(--space-2) var(--space-3);

    font: inherit;
    text-align: start;
    color: var(--text);

    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: none;
    cursor: pointer;
}

.delivery__format:hover[b-viv1gr4uun] {
    background-color: var(--surface);
    border-color: var(--border);
}

.delivery__format:disabled[b-viv1gr4uun] {
    opacity: 0.6;
    cursor: progress;
}

.delivery__notice[b-viv1gr4uun] {
    font-size: var(--text-sm);
}
/* /Components/Generator/QrBuilder.razor.rz.scp.css */
/*
    The three-column builder of docs/08-UI-SPEC.md §3: the type, the parameters,
    the preview — 260px / 1fr / 380px, exactly as §9.4 specifies.

    The CONTAINER is the frame around the grid rather than the grid itself, and that
    is not a stylistic choice: a container query styles a container's DESCENDANTS
    and never the container element, so a `container-type` on `.builder` would leave
    every rule about `.builder`'s own columns silently inert — which is exactly what
    it did until this was found by looking at a narrow window.

    Everything below queries that frame rather than the viewport. §9.4's rule and its
    reason: the same builder is rendered in the admin shell's column and on the
    public page, where the space it is given is not the width of the window.
*/

.builder-frame[b-3j7hd99swn] {
    container-type: inline-size;
    container-name: builder;
}

.builder[b-3j7hd99swn] {
    display: grid;
    grid-template-columns: 260px 1fr 380px;
    gap: var(--space-6);
    align-items: start;
}

.builder__types[b-3j7hd99swn] {
    padding: var(--space-4);
}

.builder__form[b-3j7hd99swn] {
    --stack-gap: var(--space-6);
    min-inline-size: 0;
}

/*
    Sticky, so the code stays in view while the form scrolls (§9.4). The offset is
    the shell's own top bar plus a breath, and both are tokens.
*/
.builder__preview[b-3j7hd99swn] {
    position: sticky;
    inset-block-start: var(--space-4);
    min-inline-size: 0;

    display: grid;
    gap: var(--space-4);
}

/*
    One column when the builder is given less than three columns' worth of room.
    The preview goes FIRST at that width: on a narrow screen the code is what
    somebody is looking at, and putting it under thirty form fields would mean
    scrolling past the answer to reach it.
*/
@container builder (width < 60rem) {
    .builder[b-3j7hd99swn] {
        grid-template-columns: 1fr;
    }

    .builder__preview[b-3j7hd99swn] {
        position: static;
        order: -1;

        /* Capped and centred rather than stretched to the full width: a code that
           fills a phone screen top to bottom pushes the form somebody is trying to
           fill in off the bottom of it. */
        justify-self: center;
        inline-size: min(100%, 34rem);
    }
}
/* /Components/Generator/QrPreview.razor.rz.scp.css */
/*
    The preview frame. Its size is decided by the aspect ratio the analysis
    reported, not by the image — which is what makes the layout identical before
    and after the bytes arrive (docs/08-UI-SPEC.md §9.5, CLS 0).
*/

.preview[b-ea2wmd6fo4] {
    position: relative;
    display: grid;
    place-items: center;

    aspect-ratio: var(--preview-ratio, 1);
    inline-size: 100%;

    border: 1px solid var(--border);
    border-radius: var(--r-md);
    overflow: hidden;

    /*
        The transparency checkerboard of §9.6, so a transparent background reads
        as transparent rather than as white. Two gradients and no image file.
    */
    background-color: var(--surface);
    background-image:
        linear-gradient(45deg, var(--surface-2) 25%, transparent 25%),
        linear-gradient(-45deg, var(--surface-2) 25%, transparent 25%),
        linear-gradient(45deg, transparent 75%, var(--surface-2) 75%),
        linear-gradient(-45deg, transparent 75%, var(--surface-2) 75%);
    background-size: 16px 16px;
    background-position: 0 0, 0 8px, 8px -8px, -8px 0;
}

.preview__layer[b-ea2wmd6fo4] {
    grid-area: 1 / 1;
    inline-size: 100%;
    block-size: 100%;
    object-fit: contain;

    /* A QR code is a grid of hard edges. Smoothing it on the way down is what
       makes a preview look softer than the artefact it stands for. */
    image-rendering: pixelated;
}

/*
    The crossfade. @starting-style is the only way to transition an element that
    has just been created, and the incoming layer always has: it is @key'd on its
    own bytes, so a new image is a new element rather than a changed src.
*/
@starting-style {
    .preview__layer--current[b-ea2wmd6fo4] {
        opacity: 0;
    }
}

.preview__layer--current[b-ea2wmd6fo4] {
    opacity: 1;
    transition: opacity var(--dur-preview) var(--ease-crossfade);
}

/*
    While a newer render is on its way. Deliberately faint: the old code stays
    legible, because a preview that greys out on every keystroke is a preview
    that flickers.
*/
.preview--stale .preview__layer--current[b-ea2wmd6fo4] {
    opacity: 0.65;
}

.preview__empty[b-ea2wmd6fo4] {
    grid-area: 1 / 1;
    padding: var(--space-6);
    text-align: center;
    font-size: var(--text-sm);
}
/* /Components/Generator/RecentList.razor.rz.scp.css */
/*
    The recent-generations list (docs/08-UI-SPEC.md §3). One row per entry, each a
    button: restoring is the only thing that can be done to one.
*/

.recents__list[b-osmc3dja34] {
    display: grid;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.recents__entry[b-osmc3dja34] {
    inline-size: 100%;
    display: grid;
    gap: var(--space-1);

    padding: var(--space-2) var(--space-3);

    font: inherit;
    text-align: start;
    color: var(--text);

    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: none;
    cursor: pointer;
}

.recents__entry:hover[b-osmc3dja34] {
    border-color: var(--border);
    background-color: var(--surface-2);
}

.recents__label[b-osmc3dja34] {
    /* One line: a URL or a vCard note is longer than this column and wrapping it
       would make the list a wall of text instead of something to pick from. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.recents__meta[b-osmc3dja34] {
    font-size: var(--text-xs);
}

.recents__empty[b-osmc3dja34] {
    font-size: var(--text-sm);
}
/* /Components/Generator/StyleEditor.razor.rz.scp.css */
/*
    The style editor: collapsible sections, two-up rows where the controls are
    short enough for it, and a drop zone that is the file input's own label.
*/

.editor[b-ah1x2pq5ge] {
    --stack-gap: var(--space-3);
}

.editor__section[b-ah1x2pq5ge] {
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface);
}

.editor__summary[b-ah1x2pq5ge] {
    display: flex;
    align-items: center;

    min-block-size: var(--control-h);
    padding-inline: var(--space-4);

    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
    color: var(--text-muted);
}

.editor__section[open] > .editor__summary[b-ah1x2pq5ge] {
    color: var(--text);
    border-block-end: 1px solid var(--border);
}

.editor__body[b-ah1x2pq5ge] {
    padding: var(--space-4);
}

.editor__row[b-ah1x2pq5ge] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
}

.editor__note[b-ah1x2pq5ge] {
    font-size: var(--text-xs);
}

/*
    The drop zone IS the <label> of a hidden file input, so a click, a drop and a
    keyboard press are the same control — and the input keeps its own focus and
    its own announcement rather than needing a tabindex and a role.
*/
.dropzone[b-ah1x2pq5ge] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);

    padding: var(--space-6);

    border: 1px dashed var(--border-strong);
    border-radius: var(--r-md);
    background: var(--surface-2);
    color: var(--text-muted);
    cursor: pointer;
    text-align: center;
    font-size: var(--text-sm);

    transition: border-color var(--dur-instant) var(--ease-out),
                color var(--dur-instant) var(--ease-out);
}

.dropzone:hover[b-ah1x2pq5ge],
.dropzone:focus-within[b-ah1x2pq5ge] {
    border-color: var(--accent);
    color: var(--accent);
}

.dropzone--filled[b-ah1x2pq5ge] {
    border-style: solid;
    border-color: var(--accent);
    color: var(--text);
}
/* /Components/Generator/TypePalette.razor.rz.scp.css */
/*
    The ⌘K command palette (docs/08-UI-SPEC.md §9.6). Tokens only.
*/

/* A palette sits high on the screen rather than centred: it is a thing you type
   into and then look down through, and centring a list that changes height moves
   its first row under the cursor. */
.palette[b-aftu8qaxj2] {
    align-items: start;
    padding-block-start: 10vh;
}

.palette__panel[b-aftu8qaxj2] {
    inline-size: min(40rem, 100%);
    gap: var(--space-3);
}

.palette__search[b-aftu8qaxj2] {
    inline-size: 100%;
    font-size: var(--text-lg);
}

.palette__results[b-aftu8qaxj2] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    max-block-size: 50vh;
    overflow-y: auto;
}

.palette__category[b-aftu8qaxj2] {
    position: sticky;
    inset-block-start: 0;
    z-index: var(--z-sticky);
    padding-block: var(--space-1);
    background: var(--surface);
}

.palette__item[b-aftu8qaxj2] {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: var(--space-3);
    align-items: center;

    inline-size: 100%;
    min-block-size: var(--control-h);
    padding: var(--space-2) var(--space-3);

    border: 0;
    border-radius: var(--r-md);
    background: transparent;
    color: var(--text);
    cursor: pointer;
    text-align: start;
}

.palette__item:hover[b-aftu8qaxj2] {
    background: var(--surface-2);
}

/* The active row is what Enter would choose. Two signals — the tint and the rule
   down its edge — because colour is never the only one (§9.7). */
.palette__item--active[b-aftu8qaxj2] {
    background: var(--accent-tint);
    color: var(--accent);
    box-shadow: inset 2px 0 0 0 var(--accent);
}

.palette__text[b-aftu8qaxj2] {
    display: flex;
    flex-direction: column;
    min-inline-size: 0;
}

.palette__name[b-aftu8qaxj2] {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}

.palette__description[b-aftu8qaxj2],
.palette__id[b-aftu8qaxj2] {
    overflow: hidden;
    color: var(--text-muted);
    font-size: var(--text-xs);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.palette__item--active .palette__description[b-aftu8qaxj2],
.palette__item--active .palette__id[b-aftu8qaxj2] {
    color: inherit;
}

.palette__hint[b-aftu8qaxj2] {
    font-size: var(--text-xs);
}
/* /Components/Generator/TypePicker.razor.rz.scp.css */
.picker__groups[b-z2dy06usdb] {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);

    /* The column scrolls, the page does not: twenty-six types plus their
       categories is taller than a builder screen. */
    max-block-size: 60vh;
    overflow-y: auto;
}

.picker__category[b-z2dy06usdb] {
    position: sticky;
    inset-block-start: 0;
    z-index: var(--z-sticky);
    padding-block: var(--space-1);
    background: var(--surface);
}

.picker__list[b-z2dy06usdb] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.picker__item[b-z2dy06usdb] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;

    inline-size: 100%;
    min-block-size: var(--control-h);
    padding: var(--space-2) var(--space-3);

    border: 0;
    border-radius: var(--r-md);
    background: transparent;
    color: var(--text);
    cursor: pointer;
    text-align: start;

    transition: background-color var(--dur-instant) var(--ease-out);
}

.picker__item:hover[b-z2dy06usdb] {
    background: var(--surface-2);
}

/* The chosen one, marked with the attribute a screen reader reads rather than
   with a class kept in step with it — and with two signals, never colour alone. */
.picker__item[aria-current='true'][b-z2dy06usdb] {
    background: var(--accent-tint);
    color: var(--accent);
    box-shadow: inset 2px 0 0 0 var(--accent);
}

.picker__name[b-z2dy06usdb] {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
}

.picker__id[b-z2dy06usdb] {
    font-size: var(--text-xs);
    color: var(--text-muted);
}

.picker__item[aria-current='true'] .picker__id[b-z2dy06usdb] {
    color: inherit;
}

.picker__empty[b-z2dy06usdb] {
    font-size: var(--text-sm);
}
/* /Components/Layout/AccountMenu.razor.rz.scp.css */
.account[b-igenbl6smp] {
    position: relative;
}

.account__summary[b-igenbl6smp] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);

    min-block-size: var(--control-h);
    padding-inline: var(--space-3);
    border-radius: var(--r-full);

    color: var(--text);
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);

    /* The UA triangle would be a second disclosure arrow beside our own icon. */
    list-style: none;
}

.account__summary[b-igenbl6smp]::-webkit-details-marker {
    display: none;
}

.account__summary:hover[b-igenbl6smp] {
    background: var(--surface-2);
}

.account__panel[b-igenbl6smp] {
    position: absolute;
    inset-block-start: calc(100% + var(--space-2));
    inset-inline-end: 0;
    z-index: var(--z-overlay);

    min-inline-size: 15rem;
    box-shadow: var(--shadow-lg);
}

/* The icon sits on the label's line rather than above it. */
.account__panel a[b-igenbl6smp] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: var(--control-h);
    text-decoration: none;
}

.account__panel a:hover[b-igenbl6smp] {
    text-decoration: underline;
}

/*
    An enter animation the browser can run from nothing: @starting-style is the
    one way to transition from display:none without a script (§9.5). Reduced
    motion takes the duration to a hair through the token, so the panel still
    arrives — complete and usable — with no movement.
*/
@starting-style {
    .account[open] .account__panel[b-igenbl6smp] {
        opacity: 0;
        transform: translateY(calc(-1 * var(--space-2)));
    }
}

.account[open] .account__panel[b-igenbl6smp] {
    opacity: 1;
    transform: translateY(0);
    transition: opacity var(--dur-panel) var(--ease-panel),
                transform var(--dur-panel) var(--ease-panel);
}
/* /Components/Layout/AdminLayout.razor.rz.scp.css */
/*
    The admin shell: a bar across the top, a section list down the side, the page
    in the rest (docs/08-UI-SPEC.md §2).

    The sidebar collapses to a scrolling row above the content on a narrow screen
    rather than hiding behind a hamburger: there are six sections, they fit, and a
    menu nobody can see is a menu nobody uses.
*/

.admin[b-owq9cmzhdq] {
    display: grid;
    grid-template-columns: 15rem 1fr;
    grid-template-rows: auto 1fr;
    grid-template-areas:
        'top top'
        'nav main';
    min-block-size: 100dvh;
}

.admin__top[b-owq9cmzhdq] {
    grid-area: top;

    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);

    padding: var(--space-3) var(--space-6);
    border-block-end: 1px solid var(--border);
    background: var(--surface);
}

.admin__controls[b-owq9cmzhdq] {
    --cluster-gap: var(--space-2);
}

.admin__badge[b-owq9cmzhdq] {
    padding-inline: var(--space-2);
    padding-block: var(--space-1);
    border-radius: var(--r-full);
    background: var(--accent-tint);
    color: var(--accent);

    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-eyebrow);
    text-transform: uppercase;
}

.admin__nav[b-owq9cmzhdq] {
    grid-area: nav;

    padding: var(--space-4) var(--space-3);
    border-inline-end: 1px solid var(--border);
    background: var(--surface);
}

.admin__sections[b-owq9cmzhdq] {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

.admin__link[b-owq9cmzhdq] {
    display: flex;
    align-items: center;
    gap: var(--space-3);

    min-block-size: var(--control-h);
    padding-inline: var(--space-3);
    border-radius: var(--r-md);

    color: var(--text-muted);
    text-decoration: none;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);

    transition: background-color var(--dur-instant) var(--ease-out),
                color var(--dur-instant) var(--ease-out);
}

.admin__link:hover[b-owq9cmzhdq] {
    background: var(--surface-2);
    color: var(--text);
}

/*
    The current section, marked by the attribute a screen reader reads rather than
    by a class kept in step with it. Two signals, never colour alone (§9.7): the
    tint, and the bar down the leading edge.
*/
.admin__link[aria-current='page'][b-owq9cmzhdq] {
    background: var(--accent-tint);
    color: var(--accent);
    box-shadow: inset 2px 0 0 0 var(--accent);
}

.admin__main[b-owq9cmzhdq] {
    grid-area: main;

    inline-size: min(100%, 82rem);
    padding: var(--space-8) var(--space-6);
}

/*
    A viewport query, and the one place §9.4's "container queries, not viewport
    media queries" does not apply — recorded there as such. That rule exists so a
    COMPONENT lays itself out against the space it is given and therefore works in
    the admin column and on the public page alike. A shell is the outermost thing
    on the page: it has no container but the viewport, and making one for it would
    mean putting `container-type` on <body>, which turns it into the containing
    block for the fixed-position error strip and drops that strip to the bottom of
    the document instead of the window.
*/
@media (width < 60rem) {
    .admin[b-owq9cmzhdq] {
        grid-template-columns: 1fr;
        grid-template-areas:
            'top'
            'nav'
            'main';
    }

    .admin__nav[b-owq9cmzhdq] {
        border-inline-end: 0;
        border-block-end: 1px solid var(--border);
        overflow-x: auto;
    }

    .admin__sections[b-owq9cmzhdq] {
        flex-direction: row;
    }

    .admin__link[aria-current='page'][b-owq9cmzhdq] {
        box-shadow: inset 0 -2px 0 0 var(--accent);
    }
}
/* /Components/Layout/LanguagePicker.razor.rz.scp.css */
/*
    The same segmented shape as the theme toggle: two controls in one header that
    look like two different ideas is exactly the kind of drift §9.2 exists to stop.
*/

.language-picker__options[b-uvxiphhbt5] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1);
    border: 1px solid var(--border);
    border-radius: var(--r-full);
    background: var(--surface-2);
}

.language-picker__option[b-uvxiphhbt5] {
    display: inline-flex;
    align-items: center;
    justify-content: center;

    min-inline-size: var(--control-h);
    block-size: var(--control-h);
    padding-inline: var(--space-2);

    border: 0;
    border-radius: var(--r-full);
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;

    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-eyebrow);

    transition: background-color var(--dur-instant) var(--ease-out),
                color var(--dur-instant) var(--ease-out);
}

.language-picker__option:hover[b-uvxiphhbt5] {
    color: var(--text);
}

.language-picker__option[aria-pressed='true'][b-uvxiphhbt5] {
    background: var(--accent);
    color: var(--accent-contrast);
}
/* /Components/Layout/MainLayout.razor.rz.scp.css */
/*
    The bare page frame. The skip link and the error strip moved into
    primitives.css with task 6.1, because all three shells carry exactly one of
    each and none of them may look different.
*/

.page[b-8nudrnivcl] {
    display: grid;
    min-block-size: 100dvh;
    grid-template-rows: 1fr;
}

.page__content[b-8nudrnivcl] {
    inline-size: min(100% - var(--space-8), 72rem);
    margin-inline: auto;
    padding-block: var(--space-12);
}
/* /Components/Layout/PublicLayout.razor.rz.scp.css */
/*
    The public shell: a header, the page, a footer, and nothing else. The page is
    the hero (docs/08-UI-SPEC.md §0), so the chrome around it stays quiet — a
    hairline under the header rather than a filled bar, and a footer that reads as
    a note rather than as a second navigation.
*/

.shell[b-ulopn1s0jy] {
    display: grid;
    grid-template-rows: auto 1fr auto;
    min-block-size: 100dvh;
}

.shell__header[b-ulopn1s0jy],
.shell__main[b-ulopn1s0jy],
.shell__footer[b-ulopn1s0jy] {
    inline-size: min(100% - var(--space-8), 82rem);
    margin-inline: auto;
}

/*
    Wrapping, because at 320 px it has to: the brand and the two control groups
    beside it come to more than the viewport, and without this the whole page
    scrolls sideways — which the design QA of task 6.13 measured as 349 px of
    document inside a 320 px window (§9.8).
*/
.shell__header[b-ulopn1s0jy] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding-block: var(--space-4);
}

.shell__controls[b-ulopn1s0jy] {
    --cluster-gap: var(--space-2);
}

.shell__main[b-ulopn1s0jy] {
    padding-block: var(--space-6) var(--space-12);
}

.shell__footer[b-ulopn1s0jy] {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);

    padding-block: var(--space-6);
    border-block-start: 1px solid var(--border);
    font-size: var(--text-sm);
}

.shell__notice[b-ulopn1s0jy] {
    max-inline-size: var(--measure);
    margin: 0;
}

/*
    Discreet, as §0 asks: the same size as the notice beside it, the muted colour
    until it is hovered, and no wording anywhere near it about who may use it.
*/
.shell__signin[b-ulopn1s0jy] {
    color: var(--text-muted);
}

.shell__signin:hover[b-ulopn1s0jy] {
    color: var(--accent);
}
/* /Components/Layout/ReconnectModal.razor.rz.scp.css */
/*
    The reconnect dialog. The framework's own markup and class contract — its JS
    module toggles these state classes — restyled onto the design system, because
    it is the one piece of interface a user sees at the worst possible moment
    (docs/08-UI-SPEC.md §9.2, §9.5).

    The ripple is the only animation in the product that loops, and it earns it:
    it is the signal that the service is still trying. It is removed entirely
    under prefers-reduced-motion, where the wording alone carries the state.
*/

.components-reconnect-first-attempt-visible[b-gxs6c9giqy],
.components-reconnect-repeated-attempt-visible[b-gxs6c9giqy],
.components-reconnect-failed-visible[b-gxs6c9giqy],
.components-pause-visible[b-gxs6c9giqy],
.components-resume-failed-visible[b-gxs6c9giqy],
.components-rejoining-animation[b-gxs6c9giqy] {
    display: none;
}

#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-show .components-rejoining-animation[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-paused .components-pause-visible[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-retrying[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-failed[b-gxs6c9giqy],
#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible[b-gxs6c9giqy] {
    display: block;
}

#components-reconnect-modal[b-gxs6c9giqy] {
    inline-size: 20rem;
    margin: 20vh auto;
    padding: var(--space-8);

    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-lg);

    opacity: 0;
    transition: display var(--dur-panel) allow-discrete, overlay var(--dur-panel) allow-discrete;
    animation: components-reconnect-modal-fade-out-b-gxs6c9giqy var(--dur-panel) both;
}

#components-reconnect-modal[open][b-gxs6c9giqy] {
    animation: components-reconnect-modal-fade-in-b-gxs6c9giqy var(--dur-panel) var(--ease-panel) both;
}

#components-reconnect-modal[b-gxs6c9giqy]::backdrop {
    background: color-mix(in oklch, var(--shadow-color) 45%, transparent);
    backdrop-filter: blur(2px);
}

@keyframes components-reconnect-modal-fade-in-b-gxs6c9giqy {
    from { opacity: 0; transform: translateY(var(--space-2)); }
    to   { opacity: 1; transform: translateY(0); }
}

@keyframes components-reconnect-modal-fade-out-b-gxs6c9giqy {
    from { opacity: 1; }
    to   { opacity: 0; }
}

.components-reconnect-container[b-gxs6c9giqy] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
}

#components-reconnect-modal p[b-gxs6c9giqy] {
    margin: 0;
    text-align: center;
    text-wrap: pretty;
}

#components-reconnect-modal button[b-gxs6c9giqy] {
    min-block-size: var(--control-h);
    padding-inline: var(--space-6);

    border: 0;
    border-radius: var(--r-md);
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: var(--weight-medium);
    cursor: pointer;

    transition: background-color var(--dur-instant) var(--ease-out);
}

#components-reconnect-modal button:hover[b-gxs6c9giqy] {
    background: color-mix(in oklch, var(--accent) 88%, var(--text));
}

.components-rejoining-animation[b-gxs6c9giqy] {
    position: relative;
    inline-size: 5rem;
    block-size: 5rem;
}

.components-rejoining-animation div[b-gxs6c9giqy] {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    inline-size: 0;
    block-size: 0;
    border: 2px solid var(--accent);
    border-radius: var(--r-full);
    animation: components-rejoining-ripple-b-gxs6c9giqy 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}

.components-rejoining-animation div:nth-child(2)[b-gxs6c9giqy] {
    animation-delay: -0.5s;
}

@keyframes components-rejoining-ripple-b-gxs6c9giqy {
    from {
        inset-block-start: 50%;
        inset-inline-start: 50%;
        inline-size: 0;
        block-size: 0;
        opacity: 1;
    }

    to {
        inset-block-start: 0;
        inset-inline-start: 0;
        inline-size: 100%;
        block-size: 100%;
        opacity: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .components-rejoining-animation[b-gxs6c9giqy] {
        display: none !important;
    }
}
/* /Components/Layout/SessionTimeout.razor.rz.scp.css */
/*
    A real <dialog>, so the browser owns the focus trap, the Escape key and the
    backdrop. Everything here is appearance.
*/

.session[b-4f0dukd8i1] {
    max-inline-size: min(100% - var(--space-8), 32rem);
    padding: var(--space-6);

    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--shadow-lg);
}

.session[b-4f0dukd8i1]::backdrop {
    background: color-mix(in oklch, var(--shadow-color) 45%, transparent);
}
/* /Components/Layout/ThemeToggle.razor.rz.scp.css */
/*
    A segmented control: three states side by side, the chosen one filled. Showing
    all three rather than cycling through them means a visitor can see which one
    they are on without pressing anything.
*/

.theme-toggle[b-y5sa183zg8] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1);
    border: 1px solid var(--border);
    border-radius: var(--r-full);
    background: var(--surface-2);
}

.theme-toggle__option[b-y5sa183zg8] {
    display: inline-flex;
    align-items: center;
    justify-content: center;

    /* The 40 px minimum touch target of §9.4, which is what --control-h is. */
    inline-size: var(--control-h);
    block-size: var(--control-h);
    padding: 0;

    border: 0;
    border-radius: var(--r-full);
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;

    transition: background-color var(--dur-instant) var(--ease-out),
                color var(--dur-instant) var(--ease-out);
}

.theme-toggle__option:hover[b-y5sa183zg8] {
    color: var(--text);
}

/*
    aria-pressed is the state, so it is also the selector: a class kept in step with
    it by hand is a class that eventually disagrees with what a screen reader says.
*/
.theme-toggle__option[aria-pressed='true'][b-y5sa183zg8] {
    background: var(--accent);
    color: var(--accent-contrast);
}
/* /Components/Pages/Admin/ClientDetail.razor.rz.scp.css */
/*
    One client's page (docs/08-UI-SPEC.md §5). Tokens only.
*/

.client__header[b-ioeieuc8p7],
.client__section[b-ioeieuc8p7] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: flex-end;
    justify-content: space-between;
}

.client__section[b-ioeieuc8p7] {
    align-items: center;
}

.client__row[b-ioeieuc8p7] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-4);
}

.client__figures[b-ioeieuc8p7] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--space-4);
}

.client__scroll[b-ioeieuc8p7] {
    overflow-x: auto;
}

/* The save button sits at the end of the page and stays reachable while the
   permissions list is long. */
.client__actions[b-ioeieuc8p7] {
    position: sticky;
    inset-block-end: var(--space-4);
    justify-content: flex-end;
}
/* /Components/Pages/Admin/Clients.razor.rz.scp.css */
/*
    The client list (docs/08-UI-SPEC.md §5). Tokens only.
*/

.clients__header[b-frup1xcckt] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: flex-end;
    justify-content: space-between;
}

.clients__search[b-frup1xcckt] {
    max-inline-size: 24rem;
}

/* A table of nine columns does not fit a narrow window, and a page that scrolls
   sideways as a whole loses its navigation. The table scrolls inside its own box. */
.clients__scroll[b-frup1xcckt] {
    overflow-x: auto;
}

.clients__trend[b-frup1xcckt] {
    inline-size: 8rem;
}
/* /Components/Pages/Admin/Diagnostics.razor.rz.scp.css */
/*
    The diagnostics page (docs/08-UI-SPEC.md §8). Tokens only.
*/

.diagnostics__header[b-08tphrz22j] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: flex-end;
    justify-content: space-between;
}

.diagnostics__grid[b-08tphrz22j] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-4);
}

/* A definition list read as two columns: the label recedes, the value carries the
   figure. Every value that is a number gets tabular figures from `.numeric`. */
.facts[b-08tphrz22j] {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: var(--space-2) var(--space-4);
    margin: 0;
    font-size: var(--text-sm);
}

.facts dt[b-08tphrz22j] {
    color: var(--text-muted);
}

.facts dd[b-08tphrz22j] {
    margin: 0;
    min-inline-size: 0;
}

/* Paths, host lists and the twenty-six type ids wrap rather than widening the grid
   past the panel they are in. */
.diagnostics__list[b-08tphrz22j] {
    overflow-wrap: anywhere;
}

.diagnostics__scroll[b-08tphrz22j] {
    overflow-x: auto;
}
/* /Components/Pages/Admin/Logs.razor.rz.scp.css */
/*
    The log explorer (docs/08-UI-SPEC.md §7). Tokens only.
*/

.logs__filters[b-gartfql2aq] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--space-3) var(--space-4);
}

.logs__actions[b-gartfql2aq] {
    align-items: center;
}

.logs__follow[b-gartfql2aq] {
    display: flex;
    gap: var(--space-2);
    align-items: center;
    min-block-size: var(--control-h);
    font-size: var(--text-sm);
    cursor: pointer;
}

.logs__count[b-gartfql2aq] {
    margin-inline-start: auto;
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

/* The scroll container the virtualiser measures against. A fixed height is what
   makes it ask for a screenful rather than a day: without one the table would
   render every row it was told exists. */
.logs__table[b-gartfql2aq] {
    block-size: min(60vh, 40rem);
    overflow: auto;
}

.logs__grid[b-gartfql2aq] {
    table-layout: fixed;
    font-size: var(--text-xs);
}

/* Every cell clips. A fixed layout gives each column its share and a catalogued
   event id is longer than its share — without this the id runs under the client
   beside it, which the design QA of task 6.13 caught in a screenshot. */
.logs__grid th[b-gartfql2aq],
.logs__grid td[b-gartfql2aq] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The columns that carry a known shape get the room they need; the message takes
   whatever is left, because it is the one that can always be read in the row
   expansion below. */
.logs__grid th:nth-child(1)[b-gartfql2aq] { inline-size: 7rem; }
.logs__grid th:nth-child(2)[b-gartfql2aq] { inline-size: 6rem; }
.logs__grid th:nth-child(3)[b-gartfql2aq] { inline-size: 14rem; }
.logs__grid th:nth-child(4)[b-gartfql2aq] { inline-size: 8rem; }
.logs__grid th:nth-child(5)[b-gartfql2aq] { inline-size: 6rem; }
.logs__grid th:nth-child(6)[b-gartfql2aq] { inline-size: 5rem; }
.logs__grid th:nth-child(7)[b-gartfql2aq] { inline-size: 8rem; }
.logs__grid th:nth-child(8)[b-gartfql2aq] { inline-size: 5rem; }

.logs__grid thead th[b-gartfql2aq] {
    position: sticky;
    inset-block-start: 0;
    z-index: 1;
    background: var(--surface);
}

.logs__row[b-gartfql2aq] {
    cursor: pointer;
}

.logs__row--open[b-gartfql2aq] {
    background: color-mix(in oklch, var(--accent) 10%, transparent);
}

.logs__row--placeholder td[b-gartfql2aq] {
    color: var(--text-muted);
}

.logs__message[b-gartfql2aq] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.logs__detailHeader[b-gartfql2aq] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
}

.logs__json[b-gartfql2aq] {
    overflow: auto;
    max-block-size: 24rem;
    margin: 0;
    padding: var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background: var(--surface-2);
    font-size: var(--text-xs);
    white-space: pre;
}
/* /Components/Pages/Admin/Usage.razor.rz.scp.css */
/*
    The usage dashboard (docs/08-UI-SPEC.md §6). Tokens only.
*/

.usage__row[b-ixoso22pd0] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--space-4);
    align-items: end;
}

.usage__export[b-ixoso22pd0] {
    justify-content: flex-end;
}

.usage__kpis[b-ixoso22pd0] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));
    gap: var(--space-4);
}

/* Two charts abreast where there is room for them, one where there is not. The
   breakpoint is the container's, not the viewport's (§9.4). */
.usage__grid[b-ixoso22pd0] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: var(--space-4);
}

.usage__multiples[b-ixoso22pd0] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: var(--space-3);
}

.chart__title[b-ixoso22pd0] {
    color: var(--text-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
}
/* /Components/Pages/DesignSystem.razor.rz.scp.css */
/*
    The design reference page's own layout. Tokens only — it would be a poor
    advertisement for the system if the page that documents it cheated.
*/

.design[b-5a9vz0z0s3] {
    --stack-gap: var(--space-10);
}

.design section[b-5a9vz0z0s3] {
    --stack-gap: var(--space-4);
}

.design header[b-5a9vz0z0s3] {
    --stack-gap: var(--space-2);
}

/* ── Colour ──────────────────────────────────────────────────────────────── */

.themes[b-5a9vz0z0s3] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
    gap: var(--space-4);
}

/*
    A container that pins a theme has to repaint BOTH — `light-dark()` resolves
    where it is written, so text that only inherits its colour keeps the value
    the body computed under the other scheme. The same rule applies to the
    theme toggle of task 6.1: set the attribute on <html>, not on a subtree,
    unless the subtree restates these two.
*/
.theme-panel[b-5a9vz0z0s3] {
    background: var(--bg);
    color: var(--text);
}

.swatches[b-5a9vz0z0s3] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(13rem, 100%), 1fr));
    gap: var(--space-2);
}

.swatch[b-5a9vz0z0s3] {
    display: grid;
    grid-template-columns: var(--space-6) 1fr;
    grid-template-rows: auto auto;
    column-gap: var(--space-2);
    align-items: center;
    font-size: var(--text-xs);
}

.swatch__chip[b-5a9vz0z0s3] {
    grid-row: span 2;
    block-size: var(--space-6);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-sm);
}

.swatch__name[b-5a9vz0z0s3] {
    color: var(--text);
}

.swatch__note[b-5a9vz0z0s3] {
    font-size: var(--text-xs);
}

.pill[b-5a9vz0z0s3] {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--r-full);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
}

.pill--ok[b-5a9vz0z0s3]   { color: var(--ok);   background: color-mix(in oklch, var(--ok) 14%, transparent); }
.pill--warn[b-5a9vz0z0s3] { color: var(--warn); background: color-mix(in oklch, var(--warn) 16%, transparent); }
.pill--err[b-5a9vz0z0s3]  { color: var(--err);  background: color-mix(in oklch, var(--err) 14%, transparent); }

/* ── Controls, for the focus demonstration ───────────────────────────────── */

.demo-button[b-5a9vz0z0s3] {
    min-block-size: var(--control-h);
    padding-inline: var(--space-5);
    border: 0;
    border-radius: var(--r-md);
    background: var(--accent);
    color: var(--accent-contrast);
    font-weight: var(--weight-medium);
    cursor: pointer;
    transition: background-color var(--dur-instant) var(--ease-out);
}

.demo-button:hover[b-5a9vz0z0s3] {
    background: color-mix(in oklch, var(--accent) 88%, var(--text));
}

.demo-input[b-5a9vz0z0s3] {
    min-block-size: var(--control-h);
    padding-inline: var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-md);
    background: var(--surface);
    color: var(--text);
}

/* ── Typography ──────────────────────────────────────────────────────────── */

.type-row[b-5a9vz0z0s3] {
    display: grid;
    /* `min(…, 30%)` rather than a flat column: at 320 px a fixed label column
       leaves the sample nothing to sit in, and the sample is the point. */
    grid-template-columns: min(8rem, 30%) 1fr;
    gap: var(--space-4);
    align-items: baseline;
    padding-block-end: var(--space-2);
    border-block-end: 1px solid var(--border);
}

.type-row__token[b-5a9vz0z0s3] {
    font-size: var(--text-xs);
}

.numbers[b-5a9vz0z0s3] {
    inline-size: 100%;
    font-size: var(--text-sm);
}

.numbers :is(th, td)[b-5a9vz0z0s3] {
    padding: var(--space-2) var(--space-3);
    border-block-end: 1px solid var(--border);
}

.numbers td.numeric[b-5a9vz0z0s3],
.numbers th.numeric[b-5a9vz0z0s3] {
    text-align: end;
}

/* ── Scales ──────────────────────────────────────────────────────────────── */

.scale[b-5a9vz0z0s3] {
    display: grid;
    gap: var(--space-2);
    font-size: var(--text-xs);
}

.scale__row[b-5a9vz0z0s3] {
    display: grid;
    grid-template-columns: min(8rem, 40%) 1fr;
    gap: var(--space-4);
    align-items: center;
}

.scale__bar[b-5a9vz0z0s3] {
    display: block;
    block-size: var(--space-4);
    border-radius: var(--r-sm);
    background: var(--accent);
}

.radius-demo[b-5a9vz0z0s3],
.elevation-demo[b-5a9vz0z0s3],
.motion-demo[b-5a9vz0z0s3] {
    display: grid;
    place-items: center;
    gap: var(--space-1);
    min-inline-size: 8rem;
    min-block-size: 5rem;
    padding: var(--space-3);
    font-size: var(--text-xs);
    background: var(--surface-2);
}

.radius-demo[b-5a9vz0z0s3] {
    border: 1px solid var(--border-strong);
}

.elevation-demo[b-5a9vz0z0s3],
.motion-demo[b-5a9vz0z0s3] {
    background: var(--surface);
    border-radius: var(--r-lg);
}

.motion-demo[b-5a9vz0z0s3] {
    border: 1px solid var(--border);
    transition-property: transform, box-shadow, background-color;
    transition-timing-function: var(--ease-panel);
}

.motion-demo:hover[b-5a9vz0z0s3] {
    transform: translateY(calc(-1 * var(--space-1)));
    box-shadow: var(--shadow-md);
    background: var(--surface-2);
}

/* ── Icons ───────────────────────────────────────────────────────────────── */

.icons[b-5a9vz0z0s3] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(8rem, 100%), 1fr));
    gap: var(--space-3);
}

.icons__item[b-5a9vz0z0s3] {
    display: grid;
    justify-items: center;
    gap: var(--space-1);
    padding: var(--space-2);
    border-radius: var(--r-md);
    font-size: var(--text-xs);
    background: var(--surface-2);
}

/* ── Density ─────────────────────────────────────────────────────────────── */

.densities[b-5a9vz0z0s3] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr));
    gap: var(--space-4);
}

.density-row[b-5a9vz0z0s3] {
    display: flex;
    align-items: center;
    min-block-size: var(--density-row);
    padding-inline: var(--space-3);
    border-radius: var(--r-sm);
    background: var(--surface-2);
    font-size: var(--text-sm);
}
/* /Components/Pages/FormReference.razor.rz.scp.css */
/*
    The form reference: the form on the left, what it would produce on the right.
    Two columns where there is room, stacked where there is not.
*/

.forms[b-9cqxkoi1uy] {
    --stack-gap: var(--space-6);
}

.forms__picker[b-9cqxkoi1uy] {
    max-inline-size: 24rem;
}

.forms__layout[b-9cqxkoi1uy] {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-6);
    align-items: start;
}

@media (width >= 64rem) {
    .forms__layout[b-9cqxkoi1uy] {
        grid-template-columns: 1fr 24rem;
    }

    /* The result follows the form down a long type — a Swiss bill is taller than
       the window, and a verdict scrolled off the screen is a verdict nobody reads. */
    .forms__result[b-9cqxkoi1uy] {
        position: sticky;
        inset-block-start: var(--space-4);
    }
}

.forms__encoded[b-9cqxkoi1uy] {
    margin: 0;
    padding: var(--space-3);

    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface-2);

    font-size: var(--text-xs);
    white-space: pre-wrap;

    /* An encoded payload has no spaces to break at — a Swiss bill is one long
       run of LF-separated lines and a vCard URL can be hundreds of characters. */
    overflow-wrap: anywhere;
    max-block-size: 20rem;
    overflow-y: auto;
}
/* /Components/Pages/Home.razor.rz.scp.css */
/*
    The landing page is the generator (docs/08-UI-SPEC.md §0), so the only thing
    above the builder is one line of type and the quota indicator. Everything below
    the bar is the shared builder of §3, which brings its own layout.
*/

.home[b-cd0s4osgkw] {
    --stack-gap: var(--space-8);
}

.home__tagline[b-cd0s4osgkw] {
    font-size: var(--text-md);
    color: var(--text-muted);
}

/*
    The strip the quota indicator sits in. It belongs to the PAGE rather than to the
    shell's own header: the shell is static SSR, and the number changes over the
    circuit — rendered up there it could not update without a reload. It is aligned
    with the header above it so it still reads as part of the top of the page.
*/
.home__bar[b-cd0s4osgkw] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);

    margin-block-end: var(--space-6);
}

.home__bar .home__tagline[b-cd0s4osgkw] {
    margin: 0;
}
/* /Components/Public/QuotaExhausted.razor.rz.scp.css */
/*
    The exhausted state (docs/08-UI-SPEC.md §0). It reads as information rather than
    as an error: nothing has gone wrong, a free allowance has been used up.
*/

.exhausted[b-tpjotq4zdg] {
    padding: var(--space-4);

    border: 1px solid var(--border);
    border-radius: var(--r-md);
    background-color: var(--surface-2);

    font-size: var(--text-sm);
}

.exhausted__title[b-tpjotq4zdg] {
    display: flex;
    align-items: center;
    gap: var(--space-2);

    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
}

.exhausted time[b-tpjotq4zdg] {
    font-variant-numeric: tabular-nums;
}
/* /Components/Public/QuotaIndicator.razor.rz.scp.css */
/*
    The quota indicator (docs/08-UI-SPEC.md §0). Subdued by default: a visitor who
    has spent one of ten should not be reading a warning.
*/

.quota[b-hkr6mcoskh] {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);

    padding: var(--space-1) var(--space-3);

    border: 1px solid var(--border);
    border-radius: var(--r-full);

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

.quota__count[b-hkr6mcoskh] {
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-semibold);
    color: var(--text);
}

/*
    From 80 %, the same threshold as the capacity bar of §3. Colour is not the only
    signal — the number itself is the message — so this satisfies §9.8's rule that
    nothing is conveyed by colour alone.
*/
.quota--low[b-hkr6mcoskh] {
    border-color: var(--warn);
    color: var(--warn);
}

.quota--low .quota__count[b-hkr6mcoskh] {
    color: var(--warn);
}
