/* ============================================================================
   BUGgy — page styles
   ----------------------------------------------------------------------------
   Layout only. Every colour, size, radius, and space is a token from
   public/styles/app/palette.css, and every control is a bbs-* component from
   base.css. There are NO hex values in this file, deliberately: the house rule
   is that palette.css is the only place a colour is written down, and a public
   form is exactly the wrong place to start a second palette.

   Load order on the pages: palette.css -> base.css -> this -> theme.css.

   theme.css is LAST on purpose, so anything it says about a selector this file
   also styles wins. Values here are the unthemed baseline: correct on their
   own, and expected to be overridden by the theme.
   Prefix: bg- (BUGgy), to stay clear of the bbs- component namespace.
   ============================================================================ */

/* ---------------------------------------------------------------- header --- */
.bg-head {
    background: var(--header-bg);
    border-bottom: 1px solid var(--header-border);
}
.bg-head__inner {
    max-width: 760px;
    margin: 0 auto;
    padding: var(--sp-4) var(--sp-5);
    display: flex;
    align-items: center;
    gap: var(--sp-3);
}
.bg-logo { height: 26px; width: auto; display: block; }
/* display:block above is an author rule, so it beats the UA's [hidden] and the
   image was showing whether or not it had loaded - meaning a 204 from
   /buggy/logo showed the browser's broken-image placeholder instead of nothing.
   The attribute has to win. */
.bg-logo[hidden] { display: none; }
/* Ink, not brand green. The logo is a red bug, and a red mark beside green
   lettering reads as two brands. Red itself stays out of the chrome because
   --danger-color is red everywhere else in this app, and a red header on a form
   people reach when something is already broken is the wrong signal. */
.bg-wordmark {
    font-size: var(--fs-h4);
    font-weight: 700;
    letter-spacing: var(--ls-tight);
    color: var(--font-color);
}
.bg-head__sub {
    font-size: var(--fs-caption);
    color: var(--text-muted);
    /* Pushed to its own line on a phone rather than crowding the wordmark. */
    margin-left: auto;
}

/* ------------------------------------------------------------------ page --- */
.bg-wrap {
    max-width: 760px;
    margin: 0 auto;
    padding: var(--sp-7) var(--sp-5) var(--sp-8);
}
.bg-title {
    font-size: var(--fs-h1);
    font-weight: 300;
    letter-spacing: var(--ls-tight);
    margin: 0 0 var(--sp-3);
}
.bg-lede {
    font-size: var(--fs-body);
    line-height: 1.6;
    margin: 0 0 var(--sp-6);
    max-width: 60ch;
}
.bg-hint {
    font-size: var(--fs-small);
    line-height: 1.5;
    margin: var(--sp-2) 0 0;
}
.bg-foot {
    max-width: 760px;
    margin: 0 auto;
    padding: 0 var(--sp-5) var(--sp-7);
    font-size: var(--fs-caption);
}

/* ---------------------------------------------------------------- blocks ---
   A fieldset per question group, separated by whitespace and a hairline rather
   than by a card or a border. Same reasoning as the rest of the house style:
   sections, not boxes. */
.bg-block {
    border: 0;
    border-top: 1px solid var(--hairline);
    margin: 0 0 var(--sp-6);
    padding: var(--sp-6) 0 0;
}
.bg-block:first-of-type { border-top: 0; padding-top: 0; }

.bg-block__title {
    padding: 0;
    margin: 0 0 var(--sp-2);
    font-size: var(--fs-h4);
    font-weight: 600;
}

/* A <legend> does not participate in normal flow inside a <fieldset>, so it is
   floated to make it behave like a heading. The float is scoped to legends
   ONLY, and everything after it clears.

   Both of those matter. A grid or flex sibling does not overlap a float - it
   SHRINKS to avoid one - and beside a 100%-wide float the space left is zero,
   so the sibling collapses to width 0 and its children spill off the right of
   the page. That is exactly what broke the admin form grid (inputs ending at
   x=1639 in a 1280px viewport) and the console history rows. The same class is
   used on plain <h2> elements in the console and admin pages, where the float
   was never needed in the first place. */
legend.bg-block__title { float: left; width: 100%; }
.bg-block > *:not(legend) { clear: both; }
.bg-block__hint {
    font-size: var(--fs-small);
    line-height: 1.55;
    margin: 0 0 var(--sp-4);
    max-width: 62ch;
}
.bg-field--tight { margin-top: var(--sp-4); margin-bottom: 0; }

.bg-req, .bg-opt {
    font-size: var(--fs-caption);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
    margin-left: var(--sp-2);
    vertical-align: middle;
}
.bg-req { color: var(--danger-color); }
.bg-opt { color: var(--text-muted); }

.bg-two {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-4);
}

/* --------------------------------------------------------------- groups ---
   Selectable tiles rather than a multi-select, because the guidance text is the
   whole reason a group exists and a <select> has nowhere to put it. */
.bg-groups { display: grid; gap: var(--sp-3); }

.bg-group {
    display: block;
    position: relative;
    padding: var(--sp-4) var(--sp-4) var(--sp-4) var(--sp-7);
    background: var(--surface);
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius-md);
    cursor: pointer;
}
.bg-group:hover { background: var(--surface-2); }
.bg-group input { position: absolute; left: var(--sp-4); top: var(--sp-4); margin: 0; }
.bg-group:has(input:checked) {
    border-color: var(--primary-color);
    background: var(--brand-wash);
}
/* :has() is well supported now, but a browser without it must still show the
   selection, so the script mirrors the state onto a class. */
.bg-group.is-on {
    border-color: var(--primary-color);
    background: var(--brand-wash);
}
.bg-group:has(input:focus-visible), .bg-group.has-focus {
    outline: 2px solid var(--focus-ring-color);
    outline-offset: 2px;
}
.bg-group__name { display: block; font-weight: 600; }
.bg-group__guide {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-small);
    line-height: 1.5;
    color: var(--text-muted);
}
/* A child group - a new member of the Caddy series - is indented under its
   parent so the family is visible at a glance. */
.bg-group--child { margin-left: var(--sp-5); }

/* ------------------------------------------------------- an inline warning ---
   A consequence worth noticing in a table cell, without the weight of an alert
   box: a muted lead whose applications now reach nobody by mail. Louder than
   .bbs-faint, quieter than .bbs-alert--danger, because it is a configuration to
   review rather than something that is broken. */
.bbs-warn {
    /* --warning-fg comes from the shared palette, which .bbs-badge--warning
       already uses - no new token, and it moves with the theme. */
    color: var(--warning-fg, var(--text-muted));
    font-size: var(--fs-small);
    line-height: 1.4;
}

/* ---------------------------------------------------------------- dictate ---
   The microphone beside the description. Inserted by dictateJS only where the
   browser supports it, so none of this renders otherwise. */
.bg-dictate {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-2) var(--sp-3);
    margin-top: var(--sp-2);
}
.bg-dictate__btn { display: inline-flex; align-items: center; gap: var(--sp-2); }
.bg-dictate__icon { font-size: 1.05em; line-height: 1; }

/* Live. The dot is a second, non-colour signal - "recording" carried by hue
   alone is invisible to a good number of people, and this is the one state
   where not knowing means talking to a microphone that is not listening. */
.bg-dictate__btn.is-live {
    border-color: var(--danger-color, var(--primary-color));
    color: var(--danger-color, var(--primary-color));
}
.bg-dictate__btn.is-live .bg-dictate__icon::after {
    content: '';
    display: inline-block;
    width: 0.5em;
    height: 0.5em;
    margin-left: var(--sp-2);
    border-radius: 50%;
    background: currentColor;
    animation: bg-dictate-pulse 1.4s ease-in-out infinite;
}
@keyframes bg-dictate-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.25; }
}
/* Somebody who has asked for less motion still needs to see that it is live,
   so the dot stays and only the pulse goes. */
@media (prefers-reduced-motion: reduce) {
    .bg-dictate__btn.is-live .bg-dictate__icon::after { animation: none; }
}

.bg-dictate__status { margin: 0; font-size: var(--fs-small); }
/* The notice about where the audio goes. Small, but on its own line and always
   present rather than a tooltip - it is the only thing telling somebody not to
   read a payroll complaint into a third-party speech service. */
.bg-dictate__notice {
    flex-basis: 100%;
    margin: 0;
    font-size: var(--fs-small);
    line-height: 1.45;
}

/* --------------------------------------------------------- who can read ---
   The division checkboxes in the People panel. Stacked rather than inline: two
   or three of these read as a list of grants, and a row of them reads as a
   toolbar - which is not what "this person can read payroll tickets" should
   look like. */
.bg-access { line-height: 1.5; }

/* GRID, not flex, and that is the fix rather than a preference.
 *
 * As a flex row the select had `flex: none; min-width: 8.5rem` and the name
 * took what was left - which on the row with the widest options
 * ("Engineer admin", "Finance admin") was almost nothing, so "Software" came
 * out as "S of t w ar e" stacked down the cell. Two fixed tracks cannot
 * negotiate: the name gets its column, the control gets the rest. */
.bg-access__item {
    display: grid;
    grid-template-columns: 5.5rem minmax(0, 1fr);
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-small);
    cursor: pointer;
}
.bg-access__item + .bg-access__item { margin-top: var(--sp-2); }

/* "Software" and "Finance" are single words and must never break. Ellipsis
   rather than overflow for a longer division name added later - a clipped word
   with a visible cue beats one that pushes the column. */
.bg-access__name {
    color: var(--text-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* minmax(0, 1fr) above plus this: a <select> has an intrinsic minimum width
   from its longest option, and without both it refuses to shrink and pushes
   the track wider than the column. */
.bg-access__item select { width: 100%; min-width: 0; }

.bg-access__item.is-on .bg-access__name { color: var(--font-color); font-weight: 600; }
.bg-access__item:has(select:disabled) { cursor: default; opacity: 0.6; }

/* ------------------------------------------------------ division switcher ---
   Sits above the tab strip on the admin page. Deliberately heavier than a tab:
   the tabs move you between panels within one division, this moves you between
   divisions and re-fetches everything below it. Two controls that look alike
   but do different-sized things is how somebody changes division by accident. */
.bg-divswitch {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    margin-bottom: var(--sp-4);
}
.bg-divswitch__item {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-4);
    font: inherit;
    font-weight: 600;
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius-pill, var(--radius-md));
    cursor: pointer;
}
.bg-divswitch__item:hover { background: var(--surface-2); color: var(--text-strong); }
.bg-divswitch__item.is-on {
    color: var(--on-primary, var(--text-strong));
    background: var(--primary-color);
    border-color: var(--primary-color);
}
.bg-divswitch__item:focus-visible {
    outline: 2px solid var(--focus-ring-color);
    outline-offset: 2px;
}
/* A division that is populated but not yet switched on. Still administerable -
   that is how one gets set up - so it is offered, and labelled. */
.bg-divswitch__off { font-size: var(--fs-small); font-weight: 400; }
.bg-divswitch__item.is-on .bg-divswitch__off { color: inherit; opacity: 0.8; }

/* -------------------------------------------------------- admin divisions ---
   A heading row inside a table, used by the severity matrix to say which
   division the five rows under it belong to. */
.bg-row--group th {
    padding-top: var(--sp-4);
    font-size: var(--fs-small);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    border-bottom: 1px solid var(--hairline-strong);
    text-align: left;
}
/* The ?bug= link on the Divisions panel. Shown, not edited. */
.bg-slug {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--fs-small);
    padding: 2px var(--sp-2);
    background: var(--surface-2);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-sm);
    white-space: nowrap;
}
/* Two controls that answer one question - a weekday and an hour. */
.bg-cell--pair { white-space: nowrap; }
.bg-cell--pair > * { display: inline-block; vertical-align: middle; }
.bg-cell--pair > * + * { margin-left: var(--sp-2); }

/* ------------------------------------------------------- division chooser ---
   Which side of the house a report belongs to, shown only when nothing in the
   URL or the embed origin decided it.

   Deliberately the same tile as an application: it is the same kind of question
   asked one level up, and a second visual language for it would just be a
   second thing to learn. The difference is that these are anchors with an href
   rather than labels wrapping a checkbox, so the left gutter that holds the
   checkbox is given back. */
.bg-divisionpick {
    display: grid;
    gap: var(--sp-3);
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    margin-top: var(--sp-5);
}
.bg-group--choice {
    padding-left: var(--sp-4);
    text-decoration: none;
    color: inherit;
}
.bg-group--choice:focus-visible {
    outline: 2px solid var(--focus-ring-color);
    outline-offset: 2px;
}

/* ----------------------------------------------------------------- pills --- */
.bg-pills { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.bg-pill {
    /* The input inside is absolutely positioned; without this it escapes to
       whatever ancestor happens to be positioned. */
    position: relative;
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: 0 var(--sp-4);
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius-pill);
    cursor: pointer;
}
.bg-pill:hover { background: var(--surface-3); }
.bg-pill input { position: absolute; opacity: 0; pointer-events: none; }
.bg-pill.is-on {
    color: var(--on-accent);
    background: var(--accent-press);
    border-color: var(--accent-press);
}
.bg-pill.has-focus { outline: 2px solid var(--focus-ring-color); outline-offset: 2px; }

/* ------------------------------------------------------------------ tags --- */
.bg-tags { display: grid; gap: var(--sp-4); }

.bg-tagcat__name {
    font-size: var(--fs-caption);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
    color: var(--text-muted);
    margin: 0 0 var(--sp-2);
}
.bg-tagcat__list { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

/* ----------------------------------------------------------------- links --- */
.bg-links { display: grid; gap: var(--sp-2); }
.bg-link-row { display: flex; gap: var(--sp-2); align-items: flex-start; }
.bg-link-row .bbs-input { flex: 1; }
.bg-addlink { margin-top: var(--sp-2); padding-left: 0; padding-right: 0; }

/* ---------------------------------------------------------------- submit --- */
.bg-submit { margin-top: var(--sp-6); }
.bg-submit__note {
    font-size: var(--fs-small);
    margin: var(--sp-3) 0 0;
    text-align: center;
}
.bg-actions {
    display: flex;
    justify-content: center;
    gap: var(--sp-2);
    margin-top: var(--sp-5);
    flex-wrap: wrap;
}

.bg-textarea { min-height: 180px; resize: vertical; line-height: 1.6; }
.bg-counter { font-size: var(--fs-caption); margin: var(--sp-1) 0 0; text-align: right; }

/* Wide, spaced, tabular. A six-digit code typed into a normal input is easy to
   mis-key; this makes each character its own target. */
.bg-code {
    font-size: var(--fs-h2);
    font-weight: 600;
    letter-spacing: 0.32em;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* -------------------------------------------------------------- the ref --- */
.bg-ref {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    padding: var(--sp-5);
    margin: 0 0 var(--sp-5);
    background: var(--accent-wash);
    border-radius: var(--radius-lg);
}
.bg-ref__label {
    font-size: var(--fs-caption);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
    color: var(--info-fg);
}
.bg-ref__value {
    font-size: var(--fs-h2);
    font-weight: 700;
    letter-spacing: var(--ls-tight);
    color: var(--info-fg);
}
.bg-again { margin-top: var(--sp-7); }
.bg-loading { margin: 0; font-size: var(--fs-small); }

/* --------------------------------------------------------------- honeypot ---
   Off-screen, not display:none. Some form-fillers skip hidden inputs but fill
   positioned ones, which is the whole point of the field. */
.bg-hp {
    position: absolute;
    left: -9999px;
    top: auto;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* ============================================================ status page === */
.bg-status {
    display: inline-flex;
    align-items: center;
    padding: var(--sp-1) var(--sp-3);
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    font-weight: 600;
    background: var(--info-bg);
    color: var(--info-fg);
}
.bg-status--open { background: var(--warning-bg); color: var(--warning-fg); }
.bg-status--done { background: var(--success-bg); color: var(--success-fg); }
.bg-status--closed { background: var(--surface-3); color: var(--text-muted); }

.bg-meta {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--sp-2) var(--sp-5);
    margin: 0 0 var(--sp-6);
    padding: var(--sp-5) 0;
    border-top: 1px solid var(--hairline);
    border-bottom: 1px solid var(--hairline);
    font-size: var(--fs-small);
}
.bg-meta dt {
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
    font-size: var(--fs-caption);
    color: var(--text-muted);
    margin: 0;
}
.bg-meta dd { margin: 0; }

.bg-body {
    white-space: pre-wrap;
    line-height: 1.65;
    margin: 0 0 var(--sp-6);
}

/* A timeline, marked with a hairline rail rather than dots and cards. */
.bg-timeline { list-style: none; margin: 0; padding: 0 0 0 var(--sp-5); border-left: 1px solid var(--hairline); }
.bg-timeline li { position: relative; padding: 0 0 var(--sp-5); }
.bg-timeline li::before {
    content: '';
    position: absolute;
    left: calc(-1 * var(--sp-5) - 4px);
    top: 6px;
    width: 7px;
    height: 7px;
    border-radius: var(--radius-pill);
    background: var(--accent-color);
}
.bg-update__who {
    font-size: var(--fs-small);
    font-weight: 600;
    margin: 0 0 var(--sp-1);
}
.bg-update__when { font-size: var(--fs-caption); color: var(--text-muted); font-weight: 400; }
.bg-update__body { white-space: pre-wrap; line-height: 1.6; margin: 0; font-size: var(--fs-small); }

/* ------------------------------------------------------------- similar ---
   Duplicate deflection, shown mid-form. Quiet on purpose: it is a suggestion,
   not a warning, and styling it as an alert would read as "you have done
   something wrong" to someone who has not. */
.bg-similar {
    margin: 0 0 var(--sp-4);
    padding: var(--sp-4);
    background: var(--surface-2);
    border-left: 3px solid var(--accent-color);
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
.bg-similar__head {
    margin: 0 0 var(--sp-1);
    font-size: var(--fs-small);
    font-weight: 600;
}
.bg-similar__hint { margin: 0 0 var(--sp-3); font-size: var(--fs-caption); line-height: 1.5; }
.bg-similar__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
/* The row is the flex line, not the li: since the li also carries the "me too"
   form, making the li itself a flex row would lay that form out as a fourth
   column beside the title. */
.bg-similar__row {
    display: flex;
    gap: var(--sp-3);
    align-items: baseline;
    font-size: var(--fs-small);
    flex-wrap: wrap;
}
.bg-similar__ref { font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.bg-similar__title { flex: 1; min-width: 200px; }
.bg-similar__status { font-size: var(--fs-caption); white-space: nowrap; }
.bg-similar__join { flex: 0 0 auto; min-height: 32px; padding: 0 var(--sp-3); font-size: var(--fs-caption); }

/* ------------------------------------------------------- me too ---
   The form that opens under a similar ticket. Indented and ruled so it reads as
   belonging to the row above it rather than to the panel. */
.bg-metoo {
    margin: var(--sp-3) 0 var(--sp-1);
    padding: var(--sp-3) 0 0 var(--sp-4);
    border-left: 2px solid var(--hairline-strong);
    display: grid;
    gap: var(--sp-3);
    max-width: 460px;
}
.bg-metoo__field { margin: 0; }
.bg-metoo__note { min-height: 56px; }
.bg-metoo__go { justify-self: start; }

/* -------------------------------------------------------------- uploads --- */
.bg-drop {
    padding: var(--sp-5);
    text-align: center;
    background: var(--surface-2);
    border: 1px dashed var(--hairline-strong);
    border-radius: var(--radius-md);
    cursor: pointer;
}
.bg-drop:hover, .bg-drop.is-over {
    background: var(--accent-wash);
    border-color: var(--accent-color);
}
.bg-drop:focus-visible { outline: 2px solid var(--focus-ring-color); outline-offset: 2px; }
.bg-drop__main { margin: 0; font-size: var(--fs-small); }
.bg-drop__link { color: var(--link-color); font-weight: 600; text-decoration: underline; }
.bg-drop__hint { margin: var(--sp-2) 0 0; font-size: var(--fs-caption); }

/* The real input is hidden but must stay focusable-by-proxy, so it is moved
   off-screen rather than display:none - a display:none input cannot be clicked
   programmatically in every browser. */
.bg-hidden-input { position: absolute; left: -9999px; width: 1px; height: 1px; }

.bg-files { list-style: none; margin: var(--sp-3) 0 0; padding: 0; display: grid; gap: var(--sp-1); }
.bg-file {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    background: var(--surface-2);
    border-radius: var(--radius-sm);
    font-size: var(--fs-small);
}
.bg-file__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bg-file__meta { font-size: var(--fs-caption); white-space: nowrap; }
.bg-file__x { min-height: 28px; padding: 0 var(--sp-2); font-size: var(--fs-caption); }
.bg-file--done { background: var(--success-bg); }
.bg-file--done .bg-file__meta { color: var(--success-fg); }
.bg-file--failed { background: var(--danger-bg); }
.bg-file--failed .bg-file__meta { color: var(--danger-fg); }
.bg-file--uploading .bg-file__meta { color: var(--info-fg); font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- embedded ---
   Inside somebody else's iframe there is no browser chrome to give the page
   breathing room, and the host has already drawn a dialog around it - so the
   page loses its own header and its outer padding shrinks. */
.bg-embedded .bg-head { display: none; }
.bg-embedded .bg-wrap { padding: var(--sp-5) var(--sp-5) var(--sp-6); }
.bg-embedded .bg-foot { display: none; }

/* --------------------------------------------------- status page: replying ---
   The reply box is quiet by default and loud when the ticket is actually waiting
   on the reporter - that is the one state where the page is asking them for
   something, and it should look like it. */
.bg-reply--wanted {
    padding: var(--sp-5);
    background: var(--warning-bg);
    border-radius: var(--radius-lg);
}
.bg-reply--wanted .bg-block__title { color: var(--warning-fg); }

.bg-notify {
    margin-top: var(--sp-7);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--hairline);
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    flex-wrap: wrap;
}
.bg-notify .bg-hint { margin: 0; flex: 1; min-width: 240px; }

/* ============================================================ console ======
   Engineer side. Wider than the public pages, three columns on a desktop:
   filters, the list, and on a ticket the triage rail. Still sections and
   hairlines rather than cards and shadows.
   ========================================================================== */

.bg-wrap--narrow { max-width: 460px; }

.bg-head--wide .bg-head__inner--wide { max-width: 1480px; }
.bg-head__spacer { flex: 1; }
.bg-brand { display: flex; align-items: center; gap: var(--sp-3); text-decoration: none; }
.bg-brand:hover { text-decoration: none; }
.bg-who { font-size: var(--fs-small); }

.bg-console {
    max-width: 1480px;
    margin: 0 auto;
    padding: var(--sp-5);
    display: grid;
    grid-template-columns: 240px minmax(0, 1fr);
    gap: var(--sp-6);
    align-items: start;
}
/* On a ticket the rail moves to the right and carries the controls. */
.bg-console--detail { grid-template-columns: minmax(0, 1fr) 300px; }

/* --------------------------------------------------------------- the rail --- */
.bg-rail { position: sticky; top: var(--sp-4); }
.bg-rail__head {
    font-size: var(--fs-caption);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
    color: var(--text-muted);
    margin: var(--sp-5) 0 var(--sp-2);
}
.bg-rail__head:first-child { margin-top: 0; }
.bg-rail__nav, .bg-rail__list { display: grid; gap: 2px; }
/* The problem-tag list is long by design; give it its own scroll rather than
   pushing the queue off the screen. */
.bg-rail__list--scroll { max-height: 240px; overflow-y: auto; }

.bg-rail__item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    font: inherit;
    font-size: var(--fs-small);
    text-align: left;
    color: var(--text-muted);
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.bg-rail__item:hover { background: var(--surface-3); color: var(--font-color); }
.bg-rail__item.is-on {
    background: var(--accent-wash);
    color: var(--info-fg);
    font-weight: 600;
}
.bg-rail__item--child { padding-left: var(--sp-5); }
.bg-rail__n { margin-left: auto; font-size: var(--fs-caption); }
.bg-rail__tag { margin-left: auto; font-size: var(--fs-caption); }
.bg-rail__clear { margin-top: var(--sp-4); padding-left: var(--sp-3); }
/* A saved view is a row with two controls, so the label takes the click and the
   remove button sits beside it rather than inside it. */
.bg-rail__label {
    flex: 1;
    min-width: 0;
    padding: 0;
    font: inherit;
    text-align: left;
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.bg-rail__empty {
    margin: 0;
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-caption);
    line-height: 1.5;
}

/* ------------------------------------------------------------- the queue --- */
.bg-toolbar { display: flex; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.bg-search { flex: 1; }
.bg-sort { width: 220px; flex: none; }
.bg-count { font-size: var(--fs-caption); margin: 0 0 var(--sp-3); }
/* Says which list-only filters are on, because the rail's badges ignore them. */
.bg-filternotice {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--sp-2) var(--sp-3); margin: 0 0 var(--sp-3);
}
.bg-filternotice__clear { flex: none; }

.bg-list { list-style: none; margin: 0; padding: 0; }
.bg-row { border-top: 1px solid var(--hairline); }
.bg-row:last-child { border-bottom: 1px solid var(--hairline); }

.bg-row__link {
    display: block;
    padding: var(--sp-4) var(--sp-3);
    text-decoration: none;
    color: inherit;
}
.bg-row__link:hover { background: var(--row-hover-bg); text-decoration: none; }
.bg-row__top { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.bg-row__ref { font-size: var(--fs-caption); font-weight: 600; color: var(--text-muted); }
.bg-row__title { margin: var(--sp-2) 0 var(--sp-1); font-weight: 600; }
.bg-row__meta { font-size: var(--fs-caption); }

/* The severity chip sits immediately beside the status pill, in the queue row
   and again on the detail page, and the two were different shapes: 1px of
   padding against sp-1/sp-3, a small radius against a full pill, caption type
   against small. Side by side that read as one deliberate control and one
   leftover label rather than as two facts about the same ticket.

   Same geometry as .bg-status now. Only the colours differ, which is the one
   thing that SHOULD differ - severity and status are different questions. */
.bg-sev {
    display: inline-flex;
    align-items: center;
    padding: var(--sp-1) var(--sp-3);
    border-radius: var(--radius-pill);
    font-size: var(--fs-small);
    font-weight: 600;
    background: var(--surface-3);
    color: var(--text-muted);
    /* Matches the tinted pill's 1px edge so the pair sit on the same baseline
       and measure the same height. Transparent where the chip has no colour of
       its own, so a Minor chip does not gain a box the others lack. */
    border: 1px solid transparent;
}
.bg-sev--blocking { background: var(--danger-bg); color: var(--danger-fg); }
.bg-sev--major { background: var(--warning-bg); color: var(--warning-fg); }
/* .bg-sev--unverified is gone with the chip it styled. The comment here used to
   say an unconfirmed address was worth flagging, which was true while a code
   was confirmed before filing; since spec §18 removed that step, no address is
   confirmed and the flag was on every ticket. See console.js renderRow. */

.bg-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-3);
    margin-top: var(--sp-5);
}
.bg-pager__at { font-size: var(--fs-small); }

/* ------------------------------------------------------------- the ticket --- */
.bg-crumb { margin: 0 0 var(--sp-4); font-size: var(--fs-small); }
.bg-chips { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin: 0 0 var(--sp-5); }
.bg-chip {
    display: inline-flex;
    padding: 1px var(--sp-2);
    border-radius: var(--radius-sm);
    font-size: var(--fs-caption);
    background: var(--surface-3);
    color: var(--text-muted);
}

/* --------------------------------------------------------- the conversation ---
   The public/internal distinction is the most consequential thing on this page,
   so it is carried by a left rule and a label, not by colour alone - colour
   alone fails for anyone who cannot distinguish these two hues, and the cost of
   misreading it is an internal note sent to a customer. */
.bg-thread { list-style: none; margin: 0 0 var(--sp-5); padding: 0; display: grid; gap: var(--sp-3); }
.bg-note { padding: var(--sp-3) var(--sp-4); border-left: 3px solid var(--hairline-strong); }
.bg-note--public { border-left-color: var(--accent-color); background: var(--accent-wash); }
.bg-note--internal { border-left-color: var(--hairline-strong); background: var(--surface-2); }

.bg-note__head { display: flex; align-items: center; gap: var(--sp-2); margin: 0 0 var(--sp-2); flex-wrap: wrap; }
.bg-note__who { font-size: var(--fs-small); font-weight: 600; }
.bg-note__vis {
    font-size: var(--fs-caption);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--ls-wide);
    padding: 1px var(--sp-2);
    border-radius: var(--radius-sm);
}
.bg-note__vis--public { background: var(--info-bg); color: var(--info-fg); }
.bg-note__vis--internal { background: var(--surface-3); color: var(--text-muted); }
.bg-note__when { font-size: var(--fs-caption); margin-left: auto; }
.bg-note__body { margin: 0; white-space: pre-wrap; line-height: 1.6; font-size: var(--fs-small); }

.bg-reply { margin: 0 0 var(--sp-6); }
.bg-reply__actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); flex-wrap: wrap; }

/* ----------------------------------------------------------- the history --- */
.bg-history { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-1); }
.bg-event {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1) var(--sp-3);
    justify-content: space-between;
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--hairline);
    font-size: var(--fs-caption);
}
/* min-width:0 is the load-bearing part. A flex item defaults to min-width:auto,
   so it refuses to shrink below its content - and an audit Detail string like
   "Major - 2 group(s) ... via embed on https://..." pushed the whole document
   to 1589px wide in a 1280px viewport. */
.bg-event__what {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
    color: var(--text-muted);
}
.bg-event__who { color: var(--text-muted); }

/* ------------------------------------------------------------- triage rail --- */
.bg-side { position: sticky; top: var(--sp-4); display: grid; gap: var(--sp-5); }
.bg-side__block { display: grid; gap: var(--sp-2); }
.bg-side__row { display: flex; gap: var(--sp-2); }
.bg-side__row .bbs-input { flex: 1; min-width: 0; }
.bg-side__row .bbs-select { width: 130px; flex: none; }
.bg-side__go { justify-self: start; }
.bg-side__note { font-size: var(--fs-caption); }

/* ------------------------------------------- console: attachments & similar --- */
.bg-attachments { list-style: none; margin: 0 0 var(--sp-5); padding: 0; display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.bg-att {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    width: 180px;
    padding: var(--sp-2);
    background: var(--surface-2);
    border-radius: var(--radius-md);
    font-size: var(--fs-caption);
}
.bg-att__thumb {
    display: block;
    width: 100%;
    height: 110px;
    object-fit: cover;
    background: var(--surface-3);
    border-radius: var(--radius-sm);
}
/* Video has no thumbnail - no media pipeline here - so it gets a labelled block
   rather than a broken image. */
.bg-att__placeholder {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 110px;
    background: var(--surface-3);
    border-radius: var(--radius-sm);
    color: var(--text-muted);
    font-weight: 600;
}
.bg-att__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
/* A MIME type is one long token with nowhere to break, so without this the
   meta line is silently clipped rather than wrapped -
   "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet". */
.bg-att > span { overflow-wrap: anywhere; }

.bg-relatedbox { margin: 0 0 var(--sp-5); }
.bg-suggest { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.bg-suggest li { display: flex; gap: var(--sp-3); align-items: baseline; font-size: var(--fs-small); flex-wrap: wrap; }
.bg-suggest__score { font-size: var(--fs-caption); color: var(--text-muted); white-space: nowrap; }

.bg-viewsave { display: flex; gap: var(--sp-2); margin-top: var(--sp-3); }
.bg-viewsave .bbs-input { flex: 1; min-width: 0; }
.bg-rail__x {
    margin-left: var(--sp-1);
    padding: 0 var(--sp-1);
    min-height: 20px;
    font-size: var(--fs-caption);
}

/* ================================================================ admin === */
.bg-admin { max-width: 1480px; margin: 0 auto; padding: var(--sp-5); }

.bg-tabs {
    display: flex;
    gap: var(--sp-1);
    flex-wrap: wrap;
    margin: 0 0 var(--sp-6);
    border-bottom: 1px solid var(--hairline);
}
.bg-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    font-size: var(--fs-small);
    font-weight: 600;
    color: var(--text-muted);
    text-decoration: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
}
.bg-tab:hover { color: var(--font-color); text-decoration: none; }
/* The active tab is marked by its underline and not by colour alone - which is
   both the accessible choice and the reason these can be full-strength ink.
   --text-muted measures 4.36:1 against the page canvas rather than against
   white, which misses AA by a hair, and --nav-fg-active is the bright brand
   green at roughly 2.3:1. */
.bg-tab { color: var(--font-color); }
.bg-tab.is-on { color: var(--info-fg); border-bottom-color: var(--nav-underline); }
.bg-tab__n {
    padding: 0 var(--sp-2);
    border-radius: var(--radius-pill);
    background: var(--warning-bg);
    color: var(--warning-fg);
    font-size: var(--fs-caption);
}

.bg-panel .bg-title { font-size: var(--fs-h2); }
.bg-table { width: 100%; margin: 0 0 var(--sp-6); font-size: var(--fs-small); }
.bg-table td, .bg-table th { vertical-align: middle; }
/* A retired row stays legible but stops competing for attention. */
.bg-row--off { opacity: 0.55; }
.bg-inline-input, .bg-inline-select { min-height: 32px; padding: var(--sp-1) var(--sp-2); font-size: var(--fs-small); }
.bg-cell-actions { display: flex; gap: var(--sp-2); }

.bg-form-grid {
    display: grid;
    grid-template-columns: max-content minmax(240px, 520px);
    gap: var(--sp-3) var(--sp-4);
    align-items: center;
    margin: 0 0 var(--sp-6);
}
.bg-form-grid__go { justify-self: start; }
.bg-textarea--short { min-height: 60px; }

/* Renamed from .bg-code, which the login page already used for something else
   entirely: the six-digit sign-in input, at 260px wide with 0.32em letter
   spacing and centred text. Two unrelated things under one class, and the
   later rule only overrode the properties it happened to declare - so the
   embed snippet inherited max-width: 260px and the wide letter spacing, and
   rendered as a narrow box of spaced-out characters with a scrollbar. The OTP
   input, in the other direction, was picking up this rule's padding,
   background and white-space: pre. Both were wrong; neither looked broken
   enough to question. */
.bg-snippet {
    margin: 0;
    padding: var(--sp-4);
    background: var(--surface-3);
    border-radius: var(--radius-md);
    font-size: var(--fs-caption);
    line-height: 1.6;
    overflow-x: auto;
    white-space: pre;
}


/* The contrast overrides that used to live here moved into theme.css, which
   now owns every colour decision for these pages. The measured problems they
   fixed are gone by construction: the theme's own tokens are the accessible
   ones, and the ramp step used for small text (--bg-accent-ink) measures 6.4:1
   rather than the base accent's 3.78:1.
   ========================================================================== */

/* ============================================ unbreakable tokens ==========
   Emails, URLs and MIME types are single tokens with no break opportunity, and
   this app deals in customer domains long enough to matter: one 54-character
   address in the console history forced a 375px viewport out to 461px, so the
   whole page scrolled sideways on a phone.

   overflow-wrap: anywhere only takes effect when there is genuinely nowhere
   else to break, so it costs nothing on ordinary prose and prevents this class
   of bug wherever one of those values can land.
   ========================================================================== */
.bg-meta dd,
.bg-body,
.bg-event__what, .bg-event__who,
.bg-note__who, .bg-note__body,
.bg-update__who, .bg-update__body,
.bg-row__meta, .bg-row__title,
.bg-similar__title, .bg-suggest li,
.bg-table td, .bg-table th,
.bg-file__meta,
.bg-reply .bg-hint { overflow-wrap: anywhere; }

/* ------------------------------------------------------------ responsive --- */
@media (max-width: 1080px) {
    /* The rails stop being rails and become blocks above the content. Sticky is
       dropped with them - a sticky element in a single column eats the screen. */
    .bg-console, .bg-console--detail { grid-template-columns: minmax(0, 1fr); }
    .bg-rail, .bg-side { position: static; }
    .bg-rail__list--scroll { max-height: 180px; }
    .bg-toolbar { flex-wrap: wrap; }
    .bg-sort { width: 100%; }
}

@media (max-width: 640px) {
    .bg-wrap { padding: var(--sp-5) var(--sp-4) var(--sp-7); }
    .bg-two { grid-template-columns: 1fr; gap: 0; }
    .bg-group--child { margin-left: var(--sp-3); }
    .bg-head__sub { display: none; }
    .bg-meta { grid-template-columns: 1fr; gap: var(--sp-1) 0; }
    .bg-meta dd { margin-bottom: var(--sp-3); }
    .bg-link-row { flex-direction: column; }
    /* The label column stops being a column - two words and a 240px input do not
       fit side by side on a phone. */
    .bg-form-grid { grid-template-columns: 1fr; gap: var(--sp-1); }
    .bg-att { width: 100%; }
    .bg-side__row { flex-direction: column; }
    .bg-side__row .bbs-select { width: 100%; }
}

/* ============================================================ loader ========
   The overlay from the supplied design. Colours come from theme.css, so it
   follows light and dark without a second definition.
   ========================================================================== */

.bg-loading-now { overflow: hidden; }

.bg-loader {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: var(--bg-ground);
    display: grid;
    grid-template-rows: 1fr auto;
    /* The wipe from the design: a long ease that starts slow and finishes fast. */
    transition: transform 900ms cubic-bezier(.76, 0, .24, 1), opacity 700ms ease;
    transform: translateY(0);
    opacity: 1;
}
.bg-loader.is-gone { transform: translateY(-100%); opacity: 0; pointer-events: none; }

.bg-loader__centre {
    display: grid;
    place-items: center;
    align-content: center;
    gap: var(--sp-2);
}

.bg-loader__stage {
    position: relative;
    width: 240px;
    height: 220px;
    display: grid;
    place-items: center;
}
.bg-loader__bug {
    width: 150px;
    height: auto;
    animation: bgBugBob 900ms ease-in-out infinite;
}
.bg-loader__shadow {
    position: absolute;
    bottom: 16px;
    width: 110px;
    height: 10px;
    background: var(--bg-ink);
    filter: blur(6px);
    animation: bgBugShadow 900ms ease-in-out infinite;
}

.bg-loader__headline {
    margin: 0;
    font-size: 34px;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--bg-ink);
}

.bg-loader__dots { display: flex; gap: 6px; margin-top: var(--sp-1); }
.bg-loader__dots span {
    width: 6px;
    height: 6px;
    background: var(--bg-accent);
    animation: bgDotFade 1.05s ease-in-out infinite;
}
.bg-loader__dots span:nth-child(2) { animation-delay: .18s; }
.bg-loader__dots span:nth-child(3) { animation-delay: .36s; }

.bg-loader__bottom { padding: 0 var(--bg-gutter) var(--bg-gutter); }
.bg-loader__meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-bottom: var(--sp-3);
    font-size: 12px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--bg-muted);
}
.bg-loader__pct {
    font-size: 22px;
    font-weight: 800;
    letter-spacing: 0;
    color: var(--bg-ink);
    font-variant-numeric: tabular-nums;
}

.bg-loader__track {
    position: relative;
    height: 6px;
    background: var(--bg-line-solid);
    overflow: hidden;
}
.bg-loader__fill {
    height: 100%;
    width: 0;
    background: var(--bg-accent);
    transition: width 160ms linear;
}
.bg-loader__sweep {
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .55), transparent);
    animation: bgTrackSweep 1.4s linear infinite;
}

.bg-loader__foot {
    display: flex;
    justify-content: space-between;
    margin-top: var(--sp-5);
    padding-top: var(--sp-3);
    border-top: var(--bg-rule) solid var(--bg-line);
    font-size: 11px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--bg-faint);
}

@keyframes bgBugBob {
    0%, 100% { transform: translateY(0) rotate(-1.5deg); }
    50%      { transform: translateY(-14px) rotate(1.5deg); }
}
@keyframes bgBugShadow {
    0%, 100% { transform: scaleX(1); opacity: .22; }
    50%      { transform: scaleX(.72); opacity: .1; }
}
@keyframes bgDotFade {
    0%, 100% { opacity: .2; }
    50%      { opacity: 1; }
}
@keyframes bgTrackSweep {
    0%   { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

/* Somebody who has asked for less motion gets the loader without the bobbing,
   the sweep or the wipe - it still reports progress, it just holds still. */
@media (prefers-reduced-motion: reduce) {
    .bg-loader, .bg-loader__bug, .bg-loader__shadow,
    .bg-loader__dots span, .bg-loader__sweep { animation: none; transition: none; }
    .bg-loader__sweep { display: none; }
    .bg-loader.is-gone { display: none; }
}

/* The light/dark switch in the header. */
.bg-themebtn {
    margin-left: auto;
    min-height: 34px;
    padding: 0 16px;
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    border: var(--bg-rule) solid var(--bg-line);
    color: var(--bg-ink);
}
.bg-head__sub + .bg-themebtn { margin-left: var(--sp-4); }

/* --------------------------------------------------------- also affected ---
   Everybody who said "this is happening to me too" (migration 016). On the
   detail page a list; on a queue row a single chip.

   The chip is not another entry in the meta line on purpose. That line is where
   provenance goes - groups, tags, who filed it, how old it is - and it is read
   at a glance or not at all. "Eleven other people have this" is the one number
   on the row that should change which ticket gets picked up next, and it would
   not do that sitting between "unassigned" and "1 link(s)". */
.bg-instances { list-style: none; margin: 0 0 var(--sp-5); padding: 0; display: grid; gap: var(--sp-3); }
.bg-instance {
    padding: var(--sp-3) 0 0 var(--sp-4);
    border-left: 2px solid var(--hairline-strong);
}
.bg-instance__who { margin: 0; font-size: var(--fs-small); }
.bg-instance__note {
    margin: var(--sp-1) 0 0;
    font-size: var(--fs-small);
    line-height: 1.5;
    white-space: pre-wrap;
}
.bg-instance__meta { margin: var(--sp-1) 0 0; font-size: var(--fs-caption); }
/* Emails and machine names are single tokens with no break opportunity - the
   same fix already applied to every other holder of one. */
.bg-instance__who, .bg-instance__note, .bg-instance__meta { overflow-wrap: anywhere; }

.bg-alsochip {
    display: inline-block;
    margin-top: var(--sp-2);
    padding: 2px var(--sp-3);
    font-size: var(--fs-caption);
    font-weight: 600;
    color: var(--warning-fg);
    background: var(--warning-bg);
    border-radius: var(--radius-pill);
}

/* ------------------------------------------------------ the embed snippet ---
   Full width with a copy button pinned to its corner. It was 260px and letter-
   spaced, because .bg-code was styling both this and the login page's six-digit
   input; see the note beside .bg-snippet above. */
.bg-snipwrap { position: relative; margin: 0 0 var(--sp-6); }
.bg-snipwrap__copy {
    position: absolute;
    top: var(--sp-3);
    right: var(--sp-3);
    min-height: 30px;
    padding: 0 var(--sp-3);
    font-size: var(--fs-caption);
}
/* Room for the button, so a long attribute never runs under it. */
.bg-snippet { padding-right: 96px; }

/* ------------------------------------------------------- the embed guide ---
   EMBEDDING.md, rendered onto the page that describes the same thing. Measured
   rather than full-bleed: this is the one part of a working screen that is
   genuinely read as prose, and 78ch is where a line stops being comfortable. */
.bg-doc__head {
    display: flex;
    align-items: baseline;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-top: var(--sp-6);
}
.bg-doc__head .bg-block__title { margin: 0; }
.bg-doc__meta { font-size: var(--fs-caption); }
.bg-doc__dl { margin-left: auto; }

.bg-doc {
    max-width: 78ch;
    margin: var(--sp-4) 0 0;
    font-size: var(--fs-small);
    line-height: 1.65;
}
.bg-doc p { margin: 0 0 var(--sp-4); }
.bg-doc__h { margin: var(--sp-6) 0 var(--sp-3); line-height: 1.25; }
.bg-doc__h:first-child { margin-top: 0; }
.bg-doc h2 { font-size: var(--fs-h3); }
.bg-doc h3, .bg-doc h4 { font-size: var(--fs-h4); }
.bg-doc__rule { border: 0; border-top: 1px solid var(--hairline); margin: var(--sp-6) 0; }
.bg-doc__list { margin: 0 0 var(--sp-4); padding-left: var(--sp-5); }
.bg-doc__list li { margin-bottom: var(--sp-2); }

.bg-doc__code {
    margin: 0 0 var(--sp-4);
    padding: var(--sp-4);
    background: var(--surface-3);
    border-radius: var(--radius-md);
    font-size: var(--fs-caption);
    line-height: 1.6;
    overflow-x: auto;
    white-space: pre;
}
.bg-doc code {
    padding: 1px 4px;
    background: var(--surface-3);
    border-radius: var(--radius-sm);
    font-size: 0.94em;
}
/* Inside a block the background is already there; a second one on every span
   turns a code sample into a patchwork. */
.bg-doc__code code { padding: 0; background: none; font-size: inherit; }

/* The guide's tables carry origins and CSP directives - single tokens with no
   break opportunity, so the wrapper scrolls rather than the page. */
.bg-doc__tablewrap { width: 100%; overflow-x: auto; margin: 0 0 var(--sp-4); }
.bg-doc__table { width: 100%; border-collapse: collapse; font-size: var(--fs-caption); }
.bg-doc__table th, .bg-doc__table td {
    text-align: left;
    vertical-align: top;
    padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
    border-bottom: 1px solid var(--hairline);
    overflow-wrap: anywhere;
}
.bg-doc__table th { font-weight: 600; }

/* ------------------------------------------------------------- the nudge ---
   "Email them again", under the assignee picker. Full width so it lines up
   with the control it belongs to rather than floating beside it, and quiet:
   it is a thing you occasionally need, not one of the three controls this
   panel is actually for. */
.bg-renotify {
    width: 100%;
    margin-top: var(--sp-2);
}

/* ========================================================== the filter bar ===
   One labelled row per dimension, above the queue. The label column is fixed
   so the chip rows line up down the left edge and read as a table of
   questions rather than four ragged lines. */
.bg-filters {
    display: grid;
    gap: var(--sp-2);
    margin: 0 0 var(--sp-4);
}
.bg-filters__row {
    display: grid;
    grid-template-columns: 7.5rem minmax(0, 1fr);
    align-items: start;
    gap: var(--sp-3);
}
.bg-filters__label {
    padding-top: 0.35rem;
    font-size: var(--fs-caption);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--text-faint);
}
/* Wraps rather than scrolls. A row of twenty people that scrolls sideways
   hides the twentieth behind a gesture nobody makes on a desktop. */
.bg-filters__chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
}
.bg-filters__search { max-width: 26rem; }
.bg-filters__clear { padding-inline: var(--sp-3); }

@media (max-width: 720px) {
    /* The label above its chips, because a 7.5rem column and a wrapped row of
       chips in the remaining space is mostly gutter on a phone. */
    .bg-filters__row { grid-template-columns: 1fr; gap: var(--sp-1); }
    .bg-filters__label { padding-top: 0; }
}

/* ---- the chip itself ----
   --fchip is the status colour when there is one, set per element by the
   client. Everything here degrades to the neutral when it is absent, which is
   what the person and division rows use. */
.bg-fchip {
    --fchip: var(--text-muted);
    display: inline-flex;
    align-items: center;
    padding: 0.3rem var(--sp-3);
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--font-color);
    font: inherit;
    font-size: var(--fs-caption);
    line-height: 1.6;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.bg-fchip:hover { background: var(--surface-3); }

/* On. Solid, because a selected filter has to survive being glanced at - a
   tint that only differs in saturation reads as "hovered" rather than "on". */
.bg-fchip.is-on {
    background: var(--fchip);
    border-color: var(--fchip);
    color: #fff;
}
.bg-fchip:focus-visible {
    outline: 2px solid var(--focus-ring, var(--primary-color));
    outline-offset: 2px;
}

/* =========================================================== status pills ===
   One colour per status, from Buggy.StatusStyle (024), set as --status on the
   element by the client.

   The admin picks ONE colour - the ink - and the background is mixed from it
   here. Asking for a pair is asking for a contrast failure, and this way the
   two cannot disagree. The fallback keeps the old neutral for a status that
   somehow has no row: unstyled is better than a broken rule. */
.bg-status--tinted {
    --status: var(--info-fg);
    background: color-mix(in srgb, var(--status) 14%, var(--surface));
    color: var(--status);
    border: 1px solid color-mix(in srgb, var(--status) 28%, transparent);
}

/* Dark mode needs the mix the other way round - 14% of the ink over a dark
   surface is nearly black - and the ink lifted so it still reads. */
:root[data-theme="dark"] .bg-status--tinted {
    background: color-mix(in srgb, var(--status) 22%, var(--surface));
    color: color-mix(in srgb, var(--status) 55%, #fff);
}

/* color-mix has been in every current browser since 2023, but a browser
   without it would paint the pill with no background at all, so the old
   variables stay as the declared fallback ahead of it. */
@supports not (background: color-mix(in srgb, red 10%, white)) {
    .bg-status--tinted { background: var(--info-bg); color: var(--info-fg); border-color: transparent; }
}

/* ================================================================= the fold ===
   "Tag they suggested", collapsed and remembered. */
.bg-fold__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
    text-align: left;
}
.bg-fold__caret {
    width: 0;
    height: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transform: rotate(90deg);
    transition: transform 0.12s ease;
}
.bg-fold.is-folded .bg-fold__caret { transform: rotate(0deg); }
.bg-fold__n {
    padding: 0 var(--sp-2);
    border-radius: var(--radius-pill);
    background: var(--surface-3);
    color: var(--text-muted);
    font-size: var(--fs-caption);
}
.bg-fold__toggle:focus-visible {
    outline: 2px solid var(--focus-ring, var(--primary-color));
    outline-offset: 3px;
}

/* ============================================== copy the ticket reference ===
   A button that looks like the text it replaced until you go near it. The
   reference is the string people quote, and selecting it out of a line of
   small grey type is fiddly. */
.bg-refcopy {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: 0.1rem var(--sp-1);
    margin-inline-start: calc(var(--sp-1) * -1);
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}
.bg-refcopy:hover { background: var(--surface-3); border-color: var(--hairline-strong); }
.bg-refcopy__icon { opacity: 0.55; }
.bg-refcopy:hover .bg-refcopy__icon { opacity: 1; }

/* Bigger than the line it sits on.
 *
 * The reference is the most-quoted string on the page and it was set at the
 * eyebrow's 12px, the same size as the "· 5 days old" beside it - so the one
 * thing people came to copy was tied for smallest. It is also longer now:
 * FIN-26-0001 against the old BUG-1024.

   Scoped to this button rather than to .bbs-eyebrow, which is shared with the
   intake form, the status page and the admin panels - none of which asked to
   get louder. The age text keeps the eyebrow size, so the two read as a
   heading and its footnote rather than as one wide label. */
.bg-refcopy {
    font-size: 17px;
    letter-spacing: 0.06em;
    /* Tabular figures, so a column of references lines up and a digit that
       changes does not shift the ones after it. */
    font-variant-numeric: tabular-nums;
}
.bg-refcopy__icon {
    /* Back down to the surrounding size; the glyph is an affordance, not part
       of the reference, and scaling with the text makes it shout. */
    font-size: 13px;
}

/* Confirmation in the control itself. A copy that silently did nothing looks
   exactly like one that worked, until somebody pastes the wrong thing. */
.bg-refcopy.is-copied {
    background: var(--success-bg);
    border-color: var(--success-fg);
    color: var(--success-fg);
}
.bg-refcopy.is-failed {
    background: var(--warning-bg);
    border-color: var(--warning-fg);
    color: var(--warning-fg);
}
.bg-refcopy:focus-visible {
    outline: 2px solid var(--focus-ring, var(--primary-color));
    outline-offset: 2px;
}

/* ---------------------------------------------------- the colour pickers ---
   A native <input type="color"> next to the hex. The picker is how people
   choose; the hex is how they paste a brand colour somebody sent them, which
   is the only way to enter one exactly. */
.bg-swatch {
    width: 2.6rem;
    height: 2rem;
    padding: 0;
    border: 1px solid var(--hairline-strong);
    border-radius: var(--radius-sm);
    background: none;
    cursor: pointer;
    flex: none;
}
/* Chrome and Safari draw their own inset swatch with padding around it, which
   leaves a white frame inside the border. */
.bg-swatch::-webkit-color-swatch-wrapper { padding: 2px; }
.bg-swatch::-webkit-color-swatch { border: 0; border-radius: 3px; }
.bg-swatch::-moz-color-swatch { border: 0; border-radius: 3px; }

.bg-hex {
    width: 6.5rem;
    font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    text-transform: lowercase;
}

/* ---------------------------------------------------------- My tickets ---
   BUG-26-0037. Cards use the house tokens so theme.css re-skins them. */
.bg-mine__form { margin: 1rem 0 1.25rem; }
.bg-mine__row { display: flex; gap: .5rem; flex-wrap: wrap; }
.bg-mine__row input { flex: 1 1 16rem; min-width: 0; }
.bg-mine__list { list-style: none; margin: 0 0 1.5rem; padding: 0; display: grid; gap: .75rem; }
.bg-mine__card {
    display: block; padding: .9rem 1rem; border: 1px solid var(--hairline-strong, #d0d4db);
    border-radius: 10px; color: inherit; text-decoration: none;
    background: var(--surface, transparent);
}
a.bg-mine__card:hover, a.bg-mine__card:focus-visible { border-color: var(--accent-color, #1f5fbf); }
.bg-mine__card--nolink { opacity: .85; }
.bg-mine__top { display: flex; justify-content: space-between; align-items: center; gap: .5rem; margin: 0 0 .35rem; flex-wrap: wrap; }
.bg-mine__title { margin: 0 0 .25rem; font-weight: 600; overflow-wrap: anywhere; }
.bg-mine__meta, .bg-mine__count, .bg-mine__nolink { margin: .15rem 0 0; font-size: .875rem; }
.bg-mine__latest { margin: .5rem 0 0; overflow-wrap: anywhere; }
.bg-mine__report { margin-top: 1.5rem; }

/* Form: link to My tickets (BUG-26-0037). */
.bg-mine-link { margin: 0 0 .75rem; text-align: right; font-size: .9rem; }

/* ------------------------------------------- Status page: files, back ---
   BUG-26-0037. */
.bg-back { margin: 0 0 1rem; }
.bg-files { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; gap: .35rem; }
.bg-files a { overflow-wrap: anywhere; }
.bg-files--pending li { display: flex; align-items: center; gap: .5rem; justify-content: space-between; overflow-wrap: anywhere; }
.bg-reply__files { margin: .75rem 0; }
