/* Document -- the meeting workflow's "what do the minutes say?" step.
 *
 * The third and last of these files. discussions.css and votes.css were extractions: their
 * rules had been JS string arrays injected into a <style> tag at runtime, unreachable by
 * /design/ and by the design-system checks, which is how 77 and 142 hardcoded colours
 * survived in them (issue #364). This one is not an extraction -- the Document tab's rules
 * were in styles.css all along, reachable and checked. It is a MOVE, made for a different
 * reason: the tab is being rebuilt onto the two-column stage Discussions uses (#448), and
 * a surface with its own layout is easier to read as its own file than as 150 lines in the
 * middle of the app's general stylesheet.
 *
 * Two groups of rules live here:
 *
 *   .mbdoc-*  new, for the stage, the video column and the side panel. Modelled on
 *             discussions.css so the two screens resolve identically -- a clerk moving
 *             between them should not be able to tell they were built separately.
 *   .mb-*     moved from styles.css: the item body box, the action line, the format bar.
 *             Same markup, same behaviour; the values are on tokens now, which is what
 *             moving them was worth doing for.
 *
 * The literals that came with the .mb-* rules resolve to SEMANTIC roles, not to whichever
 * ramp step happens to match. Following the mapping discussions.css already documents:
 *
 *   #374151  body text        -> --gv-ink-700
 *   #e5e7eb  hairline         -> --gv-border
 *   #94a3b8  gutter label     -> --gv-text-subtle   (NOT --gv-ink-500: 4.39:1, fails AA)
 *   #cbd5e1  placeholder      -> --gv-text-disabled
 *   #6366f1 / #c7d2fe / #eef2ff  indigo focus and "on" states -> the brand ramp. There is
 *            no indigo in this product; it was the same borrowed hue the AI buttons wore
 *            before DESIGN-SYSTEM.md 4 settled that question, and it sat directly beside a
 *            brand-green-and-gold nib on this very screen.
 *
 * .pm-md-body p.mb-action stays in styles.css. It draws the same ACTION label on published
 * minutes -- a page a resident reaches without signing in -- and belongs with the portal's
 * rules, not with the editor's.
 */

/* Palette scope, the shape discussions.css and votes.css use, so a rule can be read here
 * without holding the whole token list in your head. */
.mbdoc {
    --line: var(--gv-border);
    --ink: var(--gv-text);
    --ink2: var(--gv-ink-700);
    --mut: var(--gv-text-subtle);
    --mut2: var(--gv-text-disabled);
    --g: var(--gv-brand-700);
    --gd: var(--gv-brand-900);
    --gl: var(--gv-brand-50);
    --gb: var(--gv-brand-200);
    /* How wide "Writing this item from the transcript…" renders in the body box, and so
       where the typewriter reveal stops and the caret comes to rest. MEASURED, not chosen:
       215.79px at the box's 13px, which is 16.6em. Short and the ellipsis is clipped; long
       and the caret floats in space past the last character.
       RE-MEASURE IF THE STRING OR THE TYPE CHANGES, and move the steps(38) in
       mbDocTyping with it — that is the character count of the same sentence. */
    --mbdoc-typing-w: 16.6em;
    color: var(--gv-text);
}
.mbdoc .tnum { font-variant-numeric: tabular-nums; }

/* ── the stage ──────────────────────────────────────────────────────────────
 * Gone to components.css as .gv-stage (#466), and it went as THE reference: this tab's
 * numbers are the ones all five steps now share -- clamp(260px, 36%, 420px), an 18px
 * gutter, calc(100vh - 164px), 520px min-height. .mbdoc-left and .mbdoc-right went with
 * it as .gv-stage__main and .gv-stage__side, containment wall included.
 *
 * That this tab won was not a coin toss. It was already where the other three had been
 * converging -- it borrows Discussions' player and the Motions tab's transport and panel,
 * and the notes further down this file record two earlier attempts to build its own
 * instead, both reverted. The only thing it lacked was somewhere to live that was not one
 * screen's stylesheet.
 *
 * ONE RULE OF THIS TAB'S DID NOT SURVIVE. The comment here used to argue that the
 * recording must never drop below the document, which is why the column shrank to 260px
 * rather than collapsing, and why this was the one stage with no breakpoint. The shared
 * stage stacks at 1180px like the other four. What changed the answer is that the rule
 * was only ever weighed against THIS tab: on Motions, holding two columns down to 1180
 * leaves the filter bar 722px of the 760px it needs on a busy meeting, so it wraps to
 * three lines (votes.css measures this). A recording the clerk has to scroll to costs
 * less than a control bar that has come apart, and five steps behaving alike costs less
 * again than either. The reasoning is kept here rather than deleted because it is still
 * the right reasoning about this tab in isolation -- it just is not about this tab any
 * more. */

/* The bar and the format rail sit INSIDE the left column, so they are exactly as wide as
 * the document under them rather than running on past it beneath the video. The 2px is the
 * gutter .mbdoc-doc keeps clear for its scrollbar -- without it the bar and the document
 * miss their right edges by exactly that much. */
#mbdoc-bar { flex: 0 0 auto; padding-right: 2px; }

/* Every card the format bar can park in gets this while it holds the bar, and gives it
 * back on blur. It is what `#mb-format-bar { position: absolute }` resolves against, so
 * the bar is placed by the card rather than by the viewport -- see the note on the bar.
 *
 * Safe on all three card shapes: every absolutely-positioned thing inside one already has
 * a nearer positioned ancestor of its own (.mb-story-head for the delete, .mb-body-wrap
 * for the writing indicator), so nothing is re-anchored by this appearing above them. */
.mb-fmt-anchor { position: relative; }

/* The document itself, and the only thing in this column that scrolls. */
.mbdoc-doc {
    overflow-y: auto;
    min-height: 0;
    flex: 1 1 auto;
    padding-right: 2px;
}
/* The gap under the filter row (the replace row lives inside it, #602), moved inside the
   scroller so it scrolls away with the minutes instead of standing as a strip of page
   below the filters (#603); see .gv-filterbar in components.css. Only when there IS a
   filter row: the empty and not-yet-started states draw the bar without one, and
   they had no gap to move. */
#mbdoc-bar:has(.gv-filterbar) + .mbdoc-doc { padding-top: var(--gv-space-2); }

/* Stable wrappers so switching what the panel shows can swap innerHTML without the video
 * between them ever being re-parented -- moving an <iframe> in the DOM reloads it, and the
 * clerk loses their place in the recording. discussions.css says the same thing about
 * #mbtv-above / #mbtv-below; this is the third surface to need it. */
/* The order down this column is fixed: recording, then its transport, then the
 * Agenda/Transcript panel -- the Motions tab's arrangement.
 *
 * Every one of these carries an explicit `flex`, and the two above the panel are
 * `0 0 auto` (never shrink). The default is `0 1 auto`, which means a short viewport
 * squeezes them: #mbdoc-tr-w had no rule at all, so the transport could be compressed
 * toward zero height and -- because .mbdoc-right clips -- disappear silently instead of
 * overflowing where it could be seen. The panel is the only thing here that may give up
 * space, because it is the only thing that can scroll its own content. */
#mbdoc-above { display: flex; flex-direction: column; min-height: 0; flex: 0 0 auto; }
#mbdoc-tr-w  { flex: 0 0 auto; }
#mbdoc-below { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
/* The two rules that made the borrowed panel scroll internally rather than push the column
 * past its bottom were here, scoped to #mbdoc-below. They are in votes.css now, keyed on
 * .gv-stage__side (#466) -- because the Recording and Speakers steps borrow the same panel
 * and are not inside #mbdoc-below, and because the panel is votes.css's to size. */

/* ── the video ──────────────────────────────────────────────────────────────
 * .gv-stage__vid and .gv-stage__noplay in components.css now (#466), taken from here
 * whole -- the 16:9 frame, the 32vh cap, and the long argument for laying the player out
 * in normal flow instead of position:absolute;inset:0. That argument is why THIS box was
 * the one promoted: .mbtv-vid and .mbmv-vid both used the absolute recipe, which is the
 * one way a child of this column can escape it and lay itself out against the viewport. */

/* The transport's slot, under the video and above the panel. */
#mbdoc-tr-w { flex: 0 0 auto; }

/* ── the right column's panel and transport ───────────────────────────────
 * Neither is here. Both are the Motions tab's, from votes.css: .mbmv-tr for the transport
 * and .mbmv-pan / .mbmv-ph / .mbmv-flip / .mbmv-pb for the Agenda ⟷ Transcript panel.
 *
 * This file briefly carried its own — .mbdoc-tp with a Jump / AI / Verbatim head, a jump
 * list and a per-item transcript slice. Three views nobody else in the workflow offered,
 * beside a screen that already had the two that matter. Deleting them left #mbdoc-below
 * holding the borrowed panel directly, which is all it ever needed to do.
 */

/* The legacy-minutes note on the empty state. A statement, not a warning: nothing is at
 * risk, and the old data is not deleted by building new minutes. */
.mbdoc-legacy {
    display: flex;
    align-items: flex-start;
    gap: var(--gv-space-2);
    margin: 0 0 var(--gv-space-3);
    padding: var(--gv-space-3);
    border-radius: var(--gv-radius-md);
    background: var(--gv-warning-bg);
    border: var(--gv-border-width) solid var(--gv-warning-border);
    color: var(--gv-warning-fg);
    font-size: var(--gv-text-sm);
    line-height: var(--gv-leading-snug);
}

/* ── an item that was never discussed ─────────────────────────────────────── */
/* The clerk answered this on the Discussions step, and the generator has always honoured
 * it. What the document did with that answer was render an empty box captioned "Write what
 * happened on this item...", which is the one thing that did not happen.
 *
 * One dotted row: the item, and the one thing you can do with it. The same shape the
 * Motions tab gives an agenda item with no motions on it (.mbmv-i.none and its + Motion) --
 * a row on the agenda with nothing recorded against it and a single standing action.
 *
 * Outlined rather than tinted: the row is not an alert and nothing is wrong with it, it
 * simply holds nothing yet. It stays full height and full width, so a clerk scanning the
 * document still sees the item in its place on the agenda.
 *
 * DOTTED, not dashed. Motions draws its equivalent row dashed and this followed it, which
 * is why it shipped that way -- but the three states on this tab each want their own mark
 * and dotted is the quietest of the three outlines. Asked for directly; the two screens
 * differ here on purpose.
 *
 * The standing action is plainly visible, never revealed on :hover -- a tablet has no hover
 * to give, and a tablet is what a clerk runs a meeting on (DESIGN-SYSTEM.md). */
.mbdoc-nd {
    display: flex;
    align-items: center;
    gap: var(--gv-space-3);
    padding: var(--gv-space-2) var(--gv-space-3);
    /* An ordinary card with a DOTTED LEFT EDGE — the same 3px rail the other two states
     * wear, so all three are read in one place down the left margin rather than one of
     * them being a differently-shaped box:
     *
     *   written        3px solid green
     *   still owed     3px solid amber
     *   needs none     3px dotted, this
     *
     * It was a dotted outline all the way round, which said "this row is different" but
     * not "this row is the third of three". Motions draws its equivalent the same way
     * (.mbmv-i.none: a normal border with border-left:3px dashed) — dotted rather than
     * dashed here, which is the one difference between the two screens and was asked for. */
    border: var(--gv-border-width) solid var(--gv-border);
    border-left: 3px dotted var(--gv-border-strong);
    border-radius: var(--gv-radius-md);
    background: var(--gv-surface-sunk);
    min-height: var(--gv-control-lg);
    /* Pulled left by the same amount .mbdoc-item is, so the three rails sit in ONE column
     * down the margin. Without it this row's rail was 8px inboard of the other two —
     * measured — and three marks that do not line up read as three unrelated decorations
     * rather than one scale. */
    margin-left: calc(var(--gv-space-2) * -1);
}
/* Only the rows that actually GAINED a grip (#533) — a read-only or approved document
 * renders none, and those rows stay pixel-identical to what they were.
 *
 * Adding a flex child at the head of a 12px-gapped, 12px-padded row pushes everything after
 * it right, and this row's 12px did not match the 8px the written row's head uses. Measured
 * in a browser against the written rows above and below, left edges:
 *
 *              rail   grip   number   title TEXT
 *   dashed, before     16      —        31       63.0
 *   written            16     27      45.2       73.2
 *   dashed, after      16     27      45.2       73.2
 *
 * So this is not a concession, it is an alignment: grip, number and title all land in the
 * written row's columns. Compare the title on the BOX and it looks 6px out — that is the
 * -6px margin / 6px padding pair the discussed row's editable title carries so its text,
 * not its box, sits on the column. The dashed row's .t is a bare span, so box and text are
 * the same edge. Measuring the wrong one is what makes this rule look unnecessary.
 *
 * Worth noting the dashed row was 10.2px off the written rows to begin with, which nobody
 * had recorded.
 *
 * These are the row's box metrics, which #533 otherwise leaves alone. They are here because
 * the grip cannot be added without them. */
.mbdoc-nd:has(.mb-story-group-handle) {
    padding-left: var(--gv-space-2);
    gap: var(--gv-space-2);
}
.mbdoc-nd .n {
    flex: 0 0 auto;
    min-width: 1.25rem;
    font-family: var(--gv-font-mono);
    font-size: var(--gv-text-sm);
    font-weight: var(--gv-weight-bold);
    color: var(--gv-text-disabled);
}
/* Muted, because nothing has been written about it -- but not struck through and not
 * hidden. The item is on the agenda and has to stay readable. */
.mbdoc-nd .t {
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--gv-text-sm);
    line-height: var(--gv-leading-snug);
    color: var(--gv-text-subtle);
}
.mbdoc-nd .gv-btn { flex: 0 0 auto; margin-left: auto; }

/* ── moved from styles.css: the item body ─────────────────────────────────── */
/* One text box per item. It reads as a surface you can write on -- a whole clickable
 * rectangle with room to breathe -- rather than as the row of one-line fields it used
 * to be. */
.mb-body-wrap { position: relative; }
.mb-body-box {
    display: block;
    cursor: text;
    min-height: 3.25rem;
    padding: 9px 12px;
    border: var(--gv-border-width) solid transparent;
    border-radius: var(--gv-radius-md);
    font-size: var(--gv-text-sm);
    line-height: var(--gv-leading-relaxed);
    color: var(--gv-ink-700);
    background: var(--gv-surface-sunk);
    transition: background-color var(--gv-dur-fast), border-color var(--gv-dur-fast), box-shadow var(--gv-dur-fast);
}
.mb-body-box:hover { border-color: var(--gv-border); background: var(--gv-surface); }
.mb-body-box:focus {
    outline: none;
    background: var(--gv-surface);
    border-color: var(--gv-brand-300);
    box-shadow: 0 0 0 3px var(--gv-brand-100);
}
/* Even spacing between lines, whichever tag the browser reached for. */
.mb-body-box > p,
.mb-body-box > div { margin: 0 0 0.5em 0; }
.mb-body-box > p:last-child,
.mb-body-box > div:last-child { margin-bottom: 0; }
.mb-body-box > ul,
.mb-body-box > ol { margin: 0.35em 0 0.5em 0; }
/* The action taken. Ruled off from the narrative above it, labelled in the gutter, and
 * bold -- the same three things the PDF does, so the editor and the printed page say the
 * same thing. The label is DRAWN, not typed, so it cannot be half-deleted and it never
 * lands in the saved text. Each action gets its own rule, so two or three of them read as
 * separate actions. */
.mb-body-box > p.mb-action,
.mb-body-box > div.mb-action {
    position: relative;
    margin-top: 0.6em;
    padding-top: 0.55em;
    padding-left: 3.4rem;
    border-top: var(--gv-border-width) solid var(--gv-border);
    font-weight: var(--gv-weight-semibold);
    color: var(--gv-ink-800);
}
.mb-body-box > p.mb-action::before,
.mb-body-box > div.mb-action::before {
    content: 'Action';
    position: absolute;
    left: 0;
    top: 0.55em;
    font-size: var(--gv-text-3xs);
    font-weight: var(--gv-weight-bold);
    letter-spacing: var(--gv-tracking-wide);
    text-transform: uppercase;
    color: var(--gv-text-subtle);
    /* Sit the small label on the same baseline as the bigger text beside it — which the
     * three tuned numbers that used to be here did not: measured, the label's baseline sat
     * 4.65px ABOVE the paragraph's.
     *
     * The label is absolutely positioned, so it can never share the paragraph's line box,
     * and that is where the error came from. A baseline sits at half-leading + ascent from
     * the top of its line box, so two boxes of DIFFERENT heights put their baselines in
     * different places even when the boxes start at the same y — 10px of leading against
     * 22.1px is a 6px discrepancy before anything else is counted.
     *
     * Giving the label the paragraph's line box removes it: line-height in absolute px,
     * built from the same two tokens the paragraph uses, so the two stay in step if either
     * token moves. `em` cannot do this — inside the pseudo-element it means the LABEL's
     * 10px, not the paragraph's 13px, which is precisely the trap the old 0.42em fell into.
     *
     * What is left after that is the ascent: a baseline also sits lower for a bigger font,
     * and the two sizes differ by 3px. ONE nudge closes it, and the number is measured
     * rather than derived — a calculated 0.3 ascent ratio left it 2.35px out, because the
     * ratio is the typeface's and not a constant worth pretending to know. Measured in the
     * browser against this stylesheet: 0.25 of the paragraph's own size lands it on the
     * baseline. Scaled to --gv-text-sm so it follows if that token moves; if the type
     * changes, re-measure rather than trusting the 0.25. */
    line-height: calc(var(--gv-text-sm) * var(--gv-leading-relaxed));
    padding-top: calc(0.25 * var(--gv-text-sm));
    pointer-events: none;
}
/* "+ Add action". It used to be revealed on :hover, which is a control that does not
 * exist on the device a clerk runs a meeting on -- a tablet has no hover to give. Visible
 * at rest and quiet, the way DESIGN-SYSTEM.md settled it for the Votes tab's Add motion. */
/* The controls under the body box. Usually one button; two when the item has an action and
 * no narrative yet, where "+ Add text" is the named way to start some (#470) -- the line
 * mbBodyBoxHtml leaves above the action is the one you get by just clicking. The ROW
 * carries the margin the button used to, so a lone button sits exactly where it always
 * has. */
.mb-add-row {
    display: flex;
    align-items: center;
    gap: var(--gv-space-4);
    margin: 5px 0 0 1px;
}
.mb-add-action {
    display: flex;
    align-items: center;
    gap: var(--gv-space-1);
    margin: 0;
    padding: 2px;
    border: none;
    background: none;
    cursor: pointer;
    font-size: var(--gv-text-3xs);
    font-weight: var(--gv-weight-semibold);
    letter-spacing: var(--gv-tracking-wide);
    text-transform: uppercase;
    color: var(--gv-text-subtle);
    transition: color var(--gv-dur-fast);
}
.mb-add-action:hover { color: var(--gv-brand-700); }

.mb-body-box.is-empty::before {
    content: attr(data-placeholder);
    color: var(--gv-text-disabled);
    pointer-events: none;
    position: absolute;
    padding: 1px 0;
}
.mb-story-item-group { padding: 0; }

/* ── the box while the AI is writing into it ───────────────────────────────
 * A sentence typing itself out, character by character, behind a caret. "Someone is
 * typing" is unambiguous in a way a bare blinking cursor is not — a cursor sitting still
 * is what an IDLE field looks like — and it is the only treatment that can also say WHY
 * the field went blank and stopped taking input.
 *
 * DRAWN AS ::after, and that is a correctness point rather than a styling one. The text
 * must never be able to reach Firestore, and a pseudo-element is not part of innerHTML or
 * innerText: mbSaveBodyBox cannot see it even if a save that was already debounced fires
 * while the box is in this state. A <span> in the box would have been one badly-timed
 * 800ms timer away from writing "Writing this item from the transcript…" into the minutes.
 *
 * steps(38) is the character count of the string. Re-word it and that number moves with
 * it, or the reveal stops part-way and restarts.
 *
 * The box also holds the height its prose had (set inline by mbDocWritingStart) so the
 * document does not jump while it is empty. */
.mb-body-box.is-writing {
    caret-color: transparent;   /* it is not editable; a blinking system caret would lie */
}
/* THE PLACEHOLDER GETS OUT OF THE WAY.
 *
 * .is-empty::before draws "Write what happened on this item…" absolutely positioned at the
 * box's origin; the typing sentence below is in normal flow at the same origin. Both are
 * on whenever a rewrite starts on a box that had no prose yet -- which is most of the time,
 * since an item with nothing written is the one a clerk reaches for Rewrite on -- and the
 * two sentences render straight through each other, letter over letter.
 *
 * `content: none` rather than a colour or a z-index: the placeholder is not meant to be
 * behind the animation, it is not meant to be there. Same technique .mbdoc--locked uses to
 * drop it from a frozen document. */
.mb-body-box.is-writing.is-empty::before { content: none; }

.mb-body-box.is-writing::after {
    content: 'Writing this item from the transcript…';
    display: inline-block;
    overflow: hidden;
    white-space: nowrap;
    vertical-align: bottom;
    width: 0;
    color: var(--gv-text-disabled);
    border-right: 2px solid var(--gv-ai-fg);
    animation: mbDocTyping 3.4s steps(38) infinite,
               mbDocTypingCaret 1.06s steps(1) infinite;
}
/* Types for 70% of the loop, then rests on the finished sentence before starting again —
 * a reveal that restarts the instant it lands reads as a glitch rather than a rhythm. */
@keyframes mbDocTyping {
    0%        { width: 0; }
    70%, 100% { width: var(--mbdoc-typing-w); }
}
/* The caret keeps blinking while the words appear, which is what a cursor does. */
@keyframes mbDocTypingCaret {
    0%, 66%   { border-right-color: var(--gv-ai-fg); }
    67%, 100% { border-right-color: transparent; }
}
/* Reduced motion: the sentence is simply there, whole and still. It still says what is
 * happening and why the field is blank — it just does not perform it. */
@media (prefers-reduced-motion: reduce) {
    .mb-body-box.is-writing::after {
        animation: none;
        width: var(--mbdoc-typing-w);
        border-right-color: transparent;
    }
}

/* ── the item's state, down its left edge ───────────────────────────────────
 * Three states, three treatments, and between them they cover every item on the agenda:
 *
 *   needs no minutes   .mbdoc-nd  — the dotted row with + Minutes, above
 *   written            green
 *   still owed         amber, the same --gv-warning the Incomplete chip wears
 *
 * The edge is always drawn and only its colour changes, so nothing moves when an item goes
 * from owed to written — a border appearing on save would shift the paragraph the clerk
 * has just finished typing into.
 *
 * A NOTE ON THE GREEN, because the file should carry the disagreement rather than pretend
 * there wasn't one. Motions marks only the exception: .mbmv-i.incomplete is the only
 * coloured edge in votes.css and a finished motion gets nothing, and the August step-status
 * work rejected per-row marks as "busy", putting green at the step level instead. The
 * argument against green here is that a finished thirty-item document becomes a wall of it,
 * making DONE the loudest thing on a page whose done items are the ones you never need to
 * look at. That was raised and overruled: the product owner wants written items marked, and
 * the amber is easier to find with something to contrast against. --gv-brand-300 rather
 * than a full-strength green keeps it quiet enough to scan past. */
.mbdoc-item {
    border-left: 3px solid transparent;   /* .mbmv-i.incomplete draws 3px too */
    padding-left: var(--gv-space-2);
    margin-left: calc(var(--gv-space-2) * -1);
    border-radius: var(--gv-radius-sm) 0 0 var(--gv-radius-sm);
}
.mbdoc-item.is-written { border-left-color: var(--gv-brand-300); }
.mbdoc-item.is-owed    { border-left-color: var(--gv-warning); }

/* The item's title line, and the meeting header and free-text blocks, which are the same
 * kind of field since Quill left this tab (#448). */
/* The item's title row.
 *
 * .mb-story-del is gone with the arrangement it existed for. The item's one control used
 * to be lifted out of the flow with `position: absolute; right: 0` so that the title field
 * and the prose box under it shared a right edge — while the control sat IN the row, the
 * title stopped a button's width short and the item's two fields did not line up.
 *
 * The row carries three controls now (see .mbdoc-item-acts), and a fixed offset cannot
 * reserve room for a group whose width depends on whether the item has an id to key on.
 * So they are back in the flow, the title gives up the width, and that alignment is spent.
 * It bought less than a Rewrite a clerk could see without clicking into a box first.
 *
 * `position: relative` stays: .mb-fill-anchor inside is positioned in its own right, but
 * this is the containing block a stray absolute in this row would otherwise resolve
 * against, and it once resolved 18px outside the document column. */
.mb-story-head { position: relative; }

/* The item's three actions, as one group at the end of the title row.
 *
 * Sized DOWN from .gv-btn--sm, which is the shared small button and still a size up from
 * what a title row can carry forty-seven times. The type stays at --gv-text-xs so the
 * labels read; what comes off is the padding, which is what makes a button look like a
 * form control rather than a row action.
 *
 * Quiet at rest and lit on hover, the treatment .mbdoc-item-menu beside them already
 * wears: three loud buttons per item, down a 47-item document, is the objection that
 * moved Rewrite off this row once already. */
.mbdoc-item-acts {
    display: flex;
    align-items: center;
    gap: var(--gv-space-1);
    flex: 0 0 auto;
    line-height: var(--gv-leading-none);
}
/* CHILD COMBINATORS, ALL THE WAY DOWN, and that is not fussiness.
 *
 * The Rewrite panel is appended INSIDE .mb-fill-anchor (mbOpenFillOptions does
 * `wrap.appendChild(pop)`, because the popover is positioned against the button's own
 * wrapper). So every .gv-btn--sm in that panel — Cancel, Rewrite, and the "change where
 * in the transcript" row — is a descendant of this group. Written with spaces, these rules
 * repainted all three: transparent background, transparent border, 600 weight, brand
 * green. The panel's Cancel button lost its outline and its own Rewrite lost its fill,
 * and the where-row rendered as green bold text floating with no edge at all.
 *
 * `>` stops at the row. Anything the panel puts on screen keeps the appearance
 * components.css gave it. */
.mbdoc-item-acts > .gv-btn--sm,
.mbdoc-item-acts > .mb-fill-anchor > .gv-btn--sm {
    padding: var(--gv-space-1) var(--gv-space-2);
    gap: var(--gv-space-1);
    font-weight: 600;
    color: var(--gv-text-subtle);
    border-color: transparent;
    background: transparent;
    white-space: nowrap;
}
.mbdoc-item-acts > .gv-btn--sm:hover,
.mbdoc-item-acts > .gv-btn--sm:focus-visible,
.mbdoc-item-acts > .mb-fill-anchor > .gv-btn--sm:hover,
.mbdoc-item-acts > .mb-fill-anchor > .gv-btn--sm:focus-visible {
    color: var(--gv-ink-700);
    border-color: var(--gv-border);
    background: var(--gv-surface);
}
/* Rewrite carries the brand: it is the control that writes, and the only labelled thing
   on the row. */
.mbdoc-item-acts > .mb-fill-anchor > .gv-btn--sm { color: var(--gv-brand-700); }
.mbdoc-item-acts > .mb-fill-anchor > .gv-btn--sm:hover,
.mbdoc-item-acts > .mb-fill-anchor > .gv-btn--sm:focus-visible {
    color: var(--gv-brand-900);
    background: var(--gv-brand-50);
    border-color: var(--gv-brand-300);
}

/* The item's actions trigger — on the title row and on the dotted not-discussed row.
 *
 * VISIBLE AT REST. What it replaces was a bare × carrying Tailwind's
 * `opacity-0 group-hover:opacity-100`, so on a tablet — the device a clerk runs a meeting
 * on — the only way to delete an item was a control that never appeared. Quiet until
 * hovered, present always: the same treatment .gv-filterbar__more gets, and the rule
 * DESIGN-SYSTEM.md settled for the Votes tab's Add motion.
 *
 * Styled here rather than in Tailwind classes on the button because it is drawn from two
 * different renderers and they must not drift. */
.mbdoc-item-menu {
    flex: 0 0 auto;
    border: var(--gv-border-width) solid transparent;
    background: transparent;
    color: var(--gv-text-disabled);
    border-radius: var(--gv-radius-sm);
    padding: var(--gv-space-1);
    cursor: pointer;
    line-height: var(--gv-leading-none);
    transition: color var(--gv-dur-fast) var(--gv-ease),
                border-color var(--gv-dur-fast) var(--gv-ease);
}
.mbdoc-item-menu:hover {
    color: var(--gv-ink-700);
    border-color: var(--gv-border);
    background: var(--gv-surface);
}
.mbdoc-item-menu:focus-visible {
    outline: var(--gv-focus-width) solid var(--gv-focus-color);
    outline-offset: 1px;
}
/* On the dotted row it sits in the flex flow after + Minutes, so it needs no lifting —
 * only a little air, since that row's gap is sized for text and a button. */
.mbdoc-nd-menu { margin-left: var(--gv-space-1); }

.mb-story-header-edit {
    display: block;
    min-height: 1.25rem;
    cursor: text;
    /* Right padding clears the absolutely-placed Delete: the BOX runs the full width, so it
     * shares a right edge with the prose box below it, while the text stops before the
     * button rather than running under it. */
    padding: 2px 26px 2px 6px;
    margin-left: -6px;
    border-radius: var(--gv-radius-sm);
    transition: background-color var(--gv-dur-fast), box-shadow var(--gv-dur-fast);
}
.mb-story-header-edit:hover { background: var(--gv-surface-sunk); }
.mb-story-header-edit:focus {
    outline: none;
    background: var(--gv-surface);
    box-shadow: 0 0 0 2px var(--gv-brand-200);
}

/* ── moved from styles.css: the formatting toolbar ────────────────────────── */
/* ONE BAR PER CARD, parked on that card's top-right corner. An item's heading and its
 * prose are two fields of one card, and they share it: move the caret between them and the
 * bar does not move at all. It moves only when the clerk leaves the card. mbPositionFormatBar
 * computes the corner; mbFmtCard decides which card. A docked rail in the flow was built and
 * rejected (it reserved 38px whether or not anything was being edited), as were placements
 * that followed the focused BOX -- controls that move depending on which paragraph you are
 * in are controls you have to go and find.
 *
 * It overlays what is beneath it, including the section's delete on the top row.
 * Deliberate. The shadow is what separates it from the document under it, so this is one
 * of the few things on the tab that earns an elevation.
 *
 * IT IS POSITIONED IN THE DOCUMENT, not against the viewport, and that is not a detail.
 * position:fixed put it in viewport coordinates, which meant a scroll handler had to
 * re-place it on every scroll event -- and a handler always runs after the scroll it is
 * reacting to, so the bar arrived a frame late and visibly swam against the card it was
 * pinned to. Absolutely positioned inside that card, the scroll that carries the card
 * carries the bar, and there is nothing left to keep in step. The scroll handler is gone
 * with it.
 *
 * Hidden with [hidden], never opacity. An opacity:0 toolbar still takes clicks, and this
 * one would park seven invisible buttons directly over the line being written. */
#mb-format-bar {
    /* ── the two knobs ──────────────────────────────────────────────────────
     * Where the bar sits relative to the card it belongs to. Both offsets are CSS
     * and nothing else: JS writes no coordinates at all now, because the bar lives
     * INSIDE the card and `left: 100%; top: 0` is already the card's top-right
     * corner. These two are the whole adjustment surface and they take any unit.
     *
     *   --mb-fmt-gap       clear air between the bar's bottom and the card's top.
     *                      Raise it to lift the bar further off the card.
     *   --mb-fmt-nudge-x   pull the bar left of the card's right edge. 0 aligns the
     *                      two right edges exactly.
     */
    --mb-fmt-gap: 6px;
    --mb-fmt-nudge-x: 0px;

    /* absolute, against .mb-fmt-anchor -- the card mbShowFormatBar parked it in. */
    position: absolute;
    left: 100%;
    top: 0;
    z-index: var(--gv-z-dropdown);
    display: flex;
    align-items: center;
    gap: 1px;
    padding: var(--gv-space-1);
    border-radius: var(--gv-radius-md);
    background: var(--gv-surface);
    border: var(--gv-border-width) solid var(--gv-border);
    box-shadow: var(--gv-shadow-md);
    animation: mbFmtIn var(--gv-dur-fast) var(--gv-ease);

    /* The alignment itself, and the reason nothing measures the bar: `left: 100%`
     * puts its left edge on the card's RIGHT edge and `top: 0` its top edge on the
     * card's top, then -100% -- the bar's OWN width and height -- pulls it back left
     * by its width and up by its height. Bar-right lands on card-right, bar-bottom on
     * card-top. It stays true if the bar's contents ever change size, because every
     * term is resolved at paint rather than by a number JS wrote earlier. */
    transform: translate(calc(-100% - var(--mb-fmt-nudge-x)),
                         calc(-100% - var(--mb-fmt-gap)));
    transform-origin: 100% 100%;
}
/* No room above the card -- the first one in the document, which has nothing above it to
 * hang in. Flip to just inside its top edge rather than being clipped away by the column's
 * own top. Same right alignment either way.
 *
 * Now decided in DOCUMENT space rather than against the window, which is what makes it a
 * property of the card rather than of how far the clerk has scrolled: it is settled once,
 * when the bar appears, and scrolling can never change the answer. */
#mb-format-bar.is-below {
    transform: translate(calc(-100% - var(--mb-fmt-nudge-x)), var(--mb-fmt-gap));
}
#mb-format-bar[hidden] { display: none; }
/* Opacity only. This used to rise 2px on entry, which meant the keyframes set `transform`
 * -- and `transform` is now what ALIGNS the bar, so the animation overrode the alignment
 * for its whole duration and the bar snapped into place when it ended. Measured: during
 * the animation it sat a full bar-width right and a bar-height low. A fade needs no
 * transform, so the two no longer compete for the one property. */
@keyframes mbFmtIn { from { opacity: 0; } to { opacity: 1; } }
.mb-fmt-btn {
    width: var(--gv-control-sm);
    height: var(--gv-control-sm);
    border: none;
    border-radius: var(--gv-radius-sm);
    cursor: pointer;
    background: transparent;
    color: var(--gv-text-subtle);
    font-size: var(--gv-text-xs);
    line-height: var(--gv-leading-none);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color var(--gv-dur-fast), color var(--gv-dur-fast);
}
.mb-fmt-btn:hover:not(:disabled) { background: var(--gv-surface-sunk); color: var(--gv-text); }
/* A control that does not apply to the field in hand -- lists and alignment on a one-line
 * title. It stays in place rather than disappearing: the bar sits in one fixed spot per
 * card, and a bar that changes width under the caret is not in one fixed spot. Doubly so
 * now that moving from an item's heading to its prose does not move the bar: a width that
 * changed on that move would be the only thing that did. */
.mb-fmt-btn:disabled { color: var(--gv-text-disabled); cursor: default; }
/* And it does not take the pointer, which is not a nicety -- it is what keeps the caret in
 * the box. A disabled <button> fires no mouse events at all and does not let them through
 * to its parent, so the bar's own mousedown handler never ran for these, and the
 * preventDefault() in it is the ONE thing stopping a press on the toolbar from moving
 * focus out of the field. Pressing a greyed control therefore dropped the clerk out of the
 * box entirely: caret gone, selection gone, and the bar with it. pointer-events:none lets
 * the press land on the bar instead, where preventDefault holds the caret exactly where it
 * was and the handler finds no button to run. The cost is the tooltip on a control that is
 * doing nothing, which is the cheaper of the two. */
.mb-fmt-btn:disabled { pointer-events: none; }
.mb-fmt-btn.is-on { background: var(--gv-brand-50); color: var(--gv-brand-900); }
.mb-fmt-sep { width: 1px; height: 12px; background: var(--gv-border); margin: 0 2px; }

/* ── the bar's item actions ───────────────────────────────────────────────── */
/* Right of the bar's one separator. Everything left of it changes how the text LOOKS;
 * these two do something to the ITEM -- rewrite its prose, file an exhibit against it.
 * Clear formatting moved back in with the formatting when this arrived: it had a divider
 * to itself, which left it sitting beside these, and "clear the formatting" next to
 * "replace the paragraph" is two consequential controls with nothing between them.
 *
 * Disabled, never hidden, when the focused field is not part of an item -- the meeting
 * header and the free-text blocks are blocks, not agenda items. Same reason the list and
 * alignment controls are disabled rather than hidden: the bar must not change width as the
 * caret moves, because it is right-aligned to the box and every button would shift. */
/* position:relative so the Rewrite popover, which appends itself to this element, hangs
 * off the actions group rather than off whatever happens to be positioned above it. */
.mb-fmt-actions { display: inline-flex; align-items: center; gap: var(--gv-space-1); position: relative; }
/* `hidden` is an attribute the UA stylesheet turns into display:none — and ANY display
   declaration of our own outbids it, because a class beats a UA rule. Both of these set
   display, so both need saying explicitly or mbShowFormatBar's act.hidden = true does
   nothing at all and the buttons stay on screen looking enabled.
   #mb-format-bar[hidden] above needs the same line for the same reason. */
.mb-fmt-actions[hidden],
.mb-fmt-sep[hidden] { display: none; }
.mb-fmt-act {
    display: inline-flex;
    align-items: center;
    gap: var(--gv-space-1);
    height: var(--gv-control-sm);
    padding: 0 var(--gv-space-2);
    border-radius: var(--gv-radius-sm);
    border: var(--gv-border-width) solid var(--gv-border-strong);
    background: var(--gv-surface);
    color: var(--gv-text-subtle);
    font-size: var(--gv-text-xs);
    font-weight: var(--gv-weight-medium);
    line-height: var(--gv-leading-none);
    cursor: pointer;
    transition: background-color var(--gv-dur-fast), color var(--gv-dur-fast), border-color var(--gv-dur-fast);
}
.mb-fmt-act:hover { background: var(--gv-surface-sunk); color: var(--gv-text); }
/* Attach is icon-only and that is allowed here: a paperclip is one of the few marks
 * DESIGN-SYSTEM.md's "universally understood" test actually passes. Rewrite is not, so it
 * keeps its word. */
.mb-fmt-att { width: var(--gv-control-sm); padding: 0; justify-content: center; }
/* The AI register, from the token the product ships rather than the gold the design doc
 * still describes. Outlined, not filled -- this sits among eleven quiet icons. */
.mb-fmt-ai { border-color: var(--gv-ai-fg); color: var(--gv-ai-fg); }
.mb-fmt-ai:hover:not(:disabled) { background: var(--gv-ai-bg); color: var(--gv-ai-fg-hover); border-color: var(--gv-ai-fg-hover); }
.mb-fmt-act:disabled,
.mb-fmt-act[aria-disabled="true"] {
    color: var(--gv-text-disabled);
    border-color: var(--gv-border);
    background: var(--gv-surface);
    cursor: default;
    pointer-events: none;
}

/* ── narrow and short ─────────────────────────────────────────────────────── */
/* Under 1040px the stage is one column and the panel goes, exactly as Discussions does it.
 * The video and its transport stay: on a narrow screen the recording is still the reason
 * this column exists, and the panel's contents are all reachable from the document itself.
 *
 * The stage also gives up its viewport lock here. A single column that is still
 * height-locked would put the document in a short scrolling box under the video with the
 * rest of the page empty below it -- the lock only earns its keep while there are two
 * columns to keep level. */
/* Both of this tab's remaining viewport rules are the shared stage's now (#466).
 *
 * The 1040px gap tweak is simply dead: the stage stacks at 1180px, so nothing reaches a
 * two-column layout at 1040 to tighten the gutter of.
 *
 * The short-viewport video cap moved to components.css and kept THIS file's number. All
 * three tabs disagreed about it -- 26vh here, 34vh on Discussions, nothing at all on
 * Motions -- and 26vh is the careful one: on a 768px-tall laptop it is the difference
 * between the transport and the panel being on screen under the player and being under
 * the fold. The tab that thought hardest about a short window is the one whose answer the
 * other four now get.
 *
 * What did NOT survive is the argument that used to sit here: that this stage must never
 * collapse to one column, because a full-width 16/9 box stops being a player beside the
 * document and becomes a banner across it. That reasoning is still sound about this tab
 * alone -- see the note at the head of this file for why it lost anyway. */

/* ── the bar's sentence ───────────────────────────────────────────────────── */
/* The live references in the line and its second line are components.css's now:
 * .gv-barlink and .gv-subline (#448). They started as .mbtv-bar-link / -l2 on
 * Discussions, were briefly copied here as .mbdoc-bar-*, and were promoted the moment
 * a second surface wanted them rather than left as two copies drifting apart. */

/* ── the facts row under the step bar ─────────────────────────────────────── */
/* The two things on this tab that are statements rather than actions: which version of
 * the minutes the public has (#103, formerly the portal posting pill), and the
 * minute-order counter. GV.stepBar's `meta` is escaped plain text and cannot hold a pill
 * or an input, so they get their own quiet line under the bar instead of being forced
 * into a slot that will not take them. */
.mbdoc-facts {
    display: flex;
    align-items: center;
    gap: var(--gv-space-2);
    flex-wrap: wrap;
    margin: calc(var(--gv-space-2) * -1) 0 var(--gv-space-3);
    padding: 0 var(--gv-space-1);
    min-height: var(--gv-control-sm);
}
/* :empty was never going to fire. The row used to ALWAYS contain #mb-portal-status, which
   renderMinPortalStatus filled asynchronously so the element had to exist before the answer
   did, and an element with a child is not :empty
   however little that child says. So on every meeting without minute orders the row stood
   28px tall (its own min-height) plus 12px of margin, showing nothing: 48px of gap between
   the toolbar and the filter row, measured.

   :has() asks the question that was actually meant — is the one thing in here still empty?
   One child and that child empty means nothing has arrived, so the row goes. It comes back
   on its own the moment the portal status lands (the child stops being :empty) or minute
   orders are switched on (there is more than one child). Where :has() is unsupported the
   row simply stays, which is the behaviour this replaced.

   Still load-bearing under #103: the version state moved up onto the bar, so on a board
   with no minute orders this row now has nothing in it at all and this rule is what keeps
   it from reserving 48px between the toolbar and the filter row. */
.mbdoc-facts:empty,
.mbdoc-facts:has(> :only-child:empty) { display: none; }
.mbdoc-facts__lbl {
    font-size: var(--gv-text-xs);
    font-weight: var(--gv-weight-medium);
    color: var(--gv-text-subtle);
    white-space: nowrap;
}
.mbdoc-facts__num { width: 6rem; }
.mbdoc-facts__hint {
    font-size: var(--gv-text-xs);
    color: var(--gv-text-subtle);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.mbdoc-facts__hint b { color: var(--gv-text); font-weight: var(--gv-weight-semibold); }

/* The bar's left half: the status pill, then the sentence about it (#103).
 *
 * A row of its own, and it has to be — the card's text column is a plain span, so the two
 * were INLINE siblings inside it. That broke both ways: the column's flex gap never
 * reached between them, so the pill read as the first word of the sentence; and the
 * sub-line, being a block, broke out to the column's left edge and started under the pill
 * instead of under the text it belongs to. A flex row fixes both at once, because the
 * sentence and its sub-line are then one box that the pill sits beside.
 *
 * align-items:center, so the pill is centred against the sentence however many lines it
 * runs to. That is deliberately NOT what the card does with the buttons on the right —
 * #512 baseline-aligns those to the FIRST line — and the difference is what each one is:
 * the buttons are read alongside the sentence's opening words, the pill labels the whole
 * block. */
.mbdoc-bar__state {
    display: flex;
    align-items: center;
    gap: var(--gv-space-3);
    min-width: 0;
}
/* The sentence and its sub-line: one column, so the sub-line sits under the text rather
   than under the pill. min-width:0 is what lets it wrap instead of pushing the buttons
   off the row. */
.mbdoc-bar__say { min-width: 0; }

/* Approved minutes: a document, not a workspace (#103).
 *
 * The controls are already absent from the markup — handles, item menus, Add rows, Rewrite
 * and the section lock are not rendered while the minutes are approved. What is left is
 * the CHROME around the text: grey fills that mean "type here", hover borders that promise
 * a box will accept a caret, a placeholder inviting prose into an empty item. Each of those
 * is an affordance for editing, and on an approved record every one of them is a lie.
 *
 * So the fills go flat and the hovers stop. Nothing is dimmed: this is the finished
 * document and it should read as clearly as it prints, not as a greyed-out version of a
 * screen the clerk cannot use. Dimming says "not available to you"; flattening says "there
 * is nothing to do here", which is the true one. */
.mbdoc--locked .mb-body-box,
.mbdoc--locked .mb-rich,
.mbdoc--locked .mb-bullet-content {
    background: transparent;
    border-color: transparent;
    cursor: default;
}
.mbdoc--locked .mb-body-box:hover,
.mbdoc--locked .mb-rich:hover,
.mbdoc--locked .mb-bullet-content:hover {
    background: transparent;
    border-color: transparent;
}
/* The empty-item invitation. An item nobody wrote on stays empty on an approved record,
   and asking for prose that cannot be typed is the plainest false promise on the screen.
 *
 * TWO rules, because the body box does not use the shared placeholder. styles.css draws
 * that one off [contenteditable="true"][data-placeholder]:empty, which cannot fire on a
 * locked document anyway — the attribute is not there. The body box has its own, keyed on
 * an is-empty class that mbBodyBoxEmptyState sets from JS, and that one fires regardless
 * of whether anything can be typed into it. It was the one still showing. */
.mbdoc--locked .mb-body-box.is-empty::before,
.mbdoc--locked [data-placeholder]:empty::before { content: none; }
/* Reordering is off at the source — Sortable is never wired to a frozen section — but the
   grab cursor lives on the row, so it goes too rather than surviving its handle. */
.mbdoc--locked .cursor-grab { cursor: default; }

/* ── the Rewrite popover ──────────────────────────────────────────────────── */
/* Anchored to the button's own wrapper. The popover existed before this change and was
 * unreachable (see mbOpenFillOptions); what is new is that it is on the design system
 * rather than in the indigo it was written in. */
.mb-fill-anchor { position: relative; display: inline-flex; }
.mb-fill-popover {
    position: absolute;
    right: 0;
    top: 100%;
    margin-top: var(--gv-space-1);
    z-index: var(--gv-z-dropdown);
    /* Wide enough for "Change where in the transcript this was discussed" to sit on two
       comfortable lines. The menu this replaced was 9.5rem, sized for four one-word levels
       and nothing else; that width cannot hold the three controls added to it. */
    min-width: 21rem;
    /* A cap is worth having: the panel is anchored under its button and does not flip, so
       a tall one scrolls inside itself instead of running past the bottom of the window. */
    max-height: min(70vh, 30rem);
    overflow-y: auto;
    /* NO HORIZONTAL PADDING. The rows run edge to edge and light up whole, the way a menu
       row does; each block below supplies its own side padding instead. A row that stops
       12px short of the panel wall does not read as a menu row. */
    padding: var(--gv-space-1) 0;
    background: var(--gv-surface);
    border: var(--gv-border-width) solid var(--gv-border);
    border-radius: var(--gv-radius-lg);
    box-shadow: var(--gv-shadow-lg);
    text-align: left;
}
/* "Level of detail" -- GV.menu's own heading treatment, since this is its arrangement. */
.mb-fill-head {
    margin: 0;
    padding: var(--gv-space-1) var(--gv-space-3);
    font-size: var(--gv-text-2xs);
    color: var(--gv-text-muted);
}
.mb-fill-sep {
    height: 1px;
    margin: var(--gv-space-1) 0;
    background: var(--gv-border);
}

.mb-fill-lbl {
    display: block;
    margin-bottom: var(--gv-space-2);
    font-size: var(--gv-text-xs);
    font-weight: var(--gv-weight-medium);
    color: var(--gv-text-subtle);
}
.mb-fill-lbl .opt { font-weight: var(--gv-weight-normal); color: var(--gv-text-disabled); }
/* The detail levels, as GV.choices wearing .gv-picker__opt's clothes.
 *
 * TWO THINGS ARE BEING HELD APART HERE. The SEMANTICS stay the component's: a radiogroup
 * with one tab stop, arrow keys between the options, Space and Enter wired -- which is what
 * these four are, four settings of one value, and what the GV.menu they replaced was not.
 * The METRICS come from .gv-picker__opt, the row GV.menu itself is built on: 4px by 12px,
 * an 8px gap, a 12px gutter for the glyph, 12px text. So the list reads as the menu a clerk
 * already knows and behaves as the control it actually is.
 *
 * Every number below is copied from that rule rather than chosen, and that is the point --
 * a second set of values for a row that is meant to look identical is the drift this
 * stylesheet exists to stop. If .gv-picker__opt moves, this should move with it.
 *
 * The component's own card treatment comes off entirely: no border, no fill, no radius, no
 * 28px badge box. The chosen row is marked the way the picker marks its chosen option --
 * brand-900 semibold label, brand-700 glyph, NO background -- so nothing is tinted before
 * the clerk has chosen anything, and what marks the choice is the same mark the rest of the
 * app uses for it. */
.mb-fill-detail .gv-choices { gap: 0; }
.mb-fill-detail .gv-choice {
    border: 0;
    background: none;
    border-radius: 0;
}
.mb-fill-detail .gv-choice:hover,
.mb-fill-detail .gv-choice.is-selected { background: none; box-shadow: none; }
.mb-fill-detail .gv-choice__pick {
    gap: var(--gv-space-2);
    padding: var(--gv-space-1) var(--gv-space-3);
    font-size: var(--gv-text-xs);
    color: var(--gv-text);
}
.mb-fill-detail .gv-choice__pick:hover { background: var(--gv-surface-sunk); }
.mb-fill-detail .gv-choice.is-selected .gv-choice__pick {
    color: var(--gv-brand-900);
    font-weight: var(--gv-weight-semibold);
}
/* The glyph gutter, not a badge box. --gv-control-sm is 28px, sized to hold a letter. */
.mb-fill-detail .gv-choice__mark--icon {
    width: var(--gv-space-3);
    height: auto;
    font-size: var(--gv-text-2xs);
    color: var(--gv-text-disabled);
}
.mb-fill-detail .gv-choice.is-selected .gv-choice__mark--icon { color: var(--gv-brand-700); }
.mb-fill-detail .gv-choice__text { font-size: inherit; font-weight: inherit; }

/* "With instructions…", the link the three-line box hides behind.
 *
 * [hidden] NEEDS THE RULE. .gv-btn sets display:inline-flex, and any author display
 * declaration outbids the UA's [hidden]{display:none} -- so without this the button stays
 * on screen after it has been pressed, under the box it just opened. .mb-fmt-actions[hidden]
 * carries the same rule for the same reason; the wrapper below needs none, being a plain
 * div with no display of its own. */
/* THE PANEL'S OTHER TWO ROWS -- "With instructions…" and "Change where this was
 * discussed" -- on the same metrics as a detail level above them.
 *
 * One rule worn by both, and the numbers are .gv-picker__opt's again (4px by 12px, an 8px
 * gap, a 12px glyph gutter, 12px text), so all six rows in this panel come from one source
 * rather than three that agree today.
 *
 * Neither is a fifth setting: one opens a field, the other opens a picker. They sit below
 * the hairline for that reason, which is exactly where GV.menu put "With instructions…"
 * and where a clerk has already met it. */
.mb-fill-rowbtn {
    display: flex;
    align-items: center;
    gap: var(--gv-space-2);
    width: 100%;
    padding: var(--gv-space-1) var(--gv-space-3);
    border: 0;
    background: none;
    font-family: var(--gv-font-ui);
    font-size: var(--gv-text-xs);
    color: var(--gv-text);
    text-align: left;
    cursor: pointer;
}
.mb-fill-rowbtn i {
    flex: 0 0 auto;
    width: var(--gv-space-3);
    font-size: var(--gv-text-2xs);
    color: var(--gv-text-disabled);
}
.mb-fill-rowbtn__t { flex: 1; min-width: 0; }
.mb-fill-rowbtn:hover { background: var(--gv-surface-sunk); }
.mb-fill-rowbtn:focus-visible {
    outline: var(--gv-focus-width) solid var(--gv-focus-color);
    outline-offset: calc(-1 * var(--gv-focus-width));
}
/* WHAT THE ITEM IS POINTED AT, under the row that changes it.
 *
 * One line: the stretch it covers and who was speaking. Indented past the glyph gutter so
 * it hangs off the row's label rather than starting a new column -- it is that row's
 * detail, not a sixth entry in the list. Nothing drawn around it: a bordered box would make
 * the panel's last group read as a card sitting under five menu rows, and this is a
 * caption, not a control.
 *
 * The middot is drawn rather than typed, so nothing is left hanging when a fact is missing
 * -- a meeting with no speaker recorded would otherwise read "7:41-25:57 ·". */
.mb-fill-ref {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--gv-space-2);
    margin: 0;
    padding: 0 var(--gv-space-3) var(--gv-space-2)
             calc(var(--gv-space-3) * 2 + var(--gv-space-2));
    /* SUBTEXT, and sized as such. A step below the 11px the rows above it use, because it
       is not a row: nothing is pressed here, it is the answer the row is offering to
       change. One weight and one colour across the whole line -- the range was a shade
       darker than the speaker beside it, which made a caption look like two facts of
       different importance when they are one sentence. */
    font-size: var(--gv-text-3xs);
    line-height: var(--gv-leading-snug);
    color: var(--gv-text-disabled);
}
.mb-fill-ref > span + span::before {
    content: "\00b7";
    margin-right: var(--gv-space-2);
    color: var(--gv-border-strong);
}
.mb-fill-ref .num { font-variant-numeric: tabular-nums; }

.mb-fill-extra-toggle[hidden] { display: none; }
/* The field itself is not a menu row, so it takes the side padding back. */
.mb-fill-extra-wrap { padding: var(--gv-space-2) var(--gv-space-3) 0; }

.mb-fill-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--gv-space-2);
    margin-top: var(--gv-space-3);
    padding: 0 var(--gv-space-3);
}
/* ── the re-point modal (#529) ────────────────────────────────────────────────
 * "Where was this discussed?" on an item's menu, opening the Discussions candidate
 * picker in a dialog.
 *
 * The picker brings its own appearance with it: .gv-choice from components.css, and
 * .mbtv-tpq / .mbtv-tphint / .mbtv-tpb from discussions.css, both of which app.html
 * already loads on every screen. That is the whole promise of the feature, so there is
 * deliberately NOT a rule here for a candidate card, a letter or a play control. One
 * override and the two screens stop being the same control.
 *
 * What is left is what a DIALOG needs and a two-pane layout gave for free: a height to
 * scroll inside. GV.modal appends its panel to <body>, outside .mbdoc, so these resolve
 * against the global tokens rather than this file's palette block -- the local --line and
 * --gd would come back empty out there.
 */
.mbdoc-rp {
    display: flex;
    flex-direction: column;
    min-height: 0;
    gap: var(--gv-space-3);
    /* READING HEIGHT, not list height. This was sized for a candidate list -- ten short
       cards, scanned. What it holds now is the transcript, and a clerk is reading speech
       to find the line an item starts on: too short a window and they are scrolling a
       paragraph at a time through a two-hour meeting to place one mark.
       72vh leaves the dialog's own head and foot on screen at 800px tall, which is the
       shortest laptop this has to work on. */
    max-height: min(72vh, 720px);
}

/* Which item this is about. The modal's title asks the question; this answers "about
   what", which the roster row supplied on Discussions and nothing supplies here. */
.mbdoc-rp-who {
    flex: 0 0 auto;
    margin: 0;
    font-size: var(--gv-text-xs);
    font-weight: 700;
    color: var(--gv-brand-900);
}

/* The candidate list, the escape-hatch buttons and the "back to the list" control are all
   gone: this modal IS the transcript now, and never renders a list to go back to. */

/* The transcript view: a search row, a hint line and a scrolling body, stacked as one
   bordered box. On Discussions these three sit flush inside a panel that already has an
   edge; a dialog has none to borrow, so the box draws its own. */
.mbdoc-rp--tr { gap: 0; }
.mbdoc-rp--tr .mbdoc-rp-who { margin-bottom: var(--gv-space-2); }
.mbdoc-rp--tr .mbtv-tpq {
    border: var(--gv-border-width) solid var(--gv-border);
    border-radius: var(--gv-radius-sm) var(--gv-radius-sm) 0 0;
}
.mbdoc-rp--tr .mbtv-tphint {
    border: var(--gv-border-width) solid var(--gv-border);
    border-top: 0;
}
.mbdoc-rp--tr .mbtv-tpb {
    /* NO SMOOTH SCROLL HERE. .mbtv-tpb sets scroll-behavior: smooth, which is right on
       Discussions where the transcript follows the recording and a glide shows the clerk
       it moved. This modal opens already scrolled to the item's current stretch, and a
       dialog that animates its own contents into place on open reads as the page settling
       rather than as the answer being shown. */
    scroll-behavior: auto;
    border: var(--gv-border-width) solid var(--gv-border);
    border-top: 0;
    border-radius: 0 0 var(--gv-radius-sm) var(--gv-radius-sm);
    min-height: 0;
}

/* ── find and replace (#77) ───────────────────────────────────────────────────
 * The box on the filter row FINDS: it highlights every match in the document and walks
 * them. It used to narrow the document instead (#443), and the two cannot both be true --
 * narrowing drops the header, the free text and the signature blocks whole, so the matches
 * in them could be neither shown nor stepped to.
 *
 * So the controls live in two places, and which one a thing goes in is decided by what it
 * belongs to rather than by how much room is left:
 *
 *   in the search box   how many matches, the arrows, what counts as a match, and Replace.
 *                       All of it belongs to FINDING, and all of it appears only once
 *                       something has been typed -- an empty box has nothing to count.
 *   the row below       the replacement itself. Drawn only when Replace is pressed.
 */

/* The box grows once it is carrying the find controls. .gv-filterbar__search caps itself at
 * 360px, which is right for a lone input on a shared toolbar and too narrow the moment a
 * count, two arrows, two toggles and a button are inside it. Overridden here rather than in
 * components.css because it is this tab's markup that puts them there.
 *
 * A wider cap, though, not `none` (#602). `none` was safe while the chips sat beside the
 * box and took the slack off it; with the replace row taking its own line of the bar the
 * box is alone on ITS line, and unbounded it grew to the full width of the bar -- which put
 * the count, the arrows and Replace hard against the right edge, a hand's width from the
 * word they are about. 32rem is the CONTENT box, so the box draws about 530px with its
 * padding and rule -- near enough to the 481px it measured when the chips were holding it
 * in that the controls sit where a clerk has been finding them.
 *
 * The cap is on .is-finding, so it holds whether or not Replace is open. Closed, it binds
 * only on a bar wide enough that the box would have passed 530px anyway (around 950px), so
 * the row a clerk sees most of the time is unchanged; it just stops growing past the point
 * where the controls start drifting away from the word. */
.mbdoc-find.is-finding {
    max-width: 32rem;
    flex: 1 1 22rem;
    gap: var(--gv-space-1);
}

/* The count and the arrows are one control, so they wrap as one. Left loose on a row that
 * wraps, the flex line broke between them and stranded an arrow at each end. */
.mbdoc-fr__step {
    display: inline-flex; align-items: center; gap: var(--gv-space-1); flex: 0 0 auto;
}
/* Tabular so "9 of 26" and "10 of 26" do not shove the arrows sideways while the clerk is
 * pressing them, and right-aligned so the number sits against them rather than drifting. */
.mbdoc-fr__n {
    flex: 0 0 auto;
    min-width: 4.5rem;
    text-align: right;
    font-size: var(--gv-text-xs);
    font-variant-numeric: tabular-nums;
    color: var(--gv-text-subtle);
}
.mbdoc-fr__nav {
    flex: 0 0 auto;
    width: var(--gv-control-xs); height: var(--gv-control-xs);
    display: inline-flex; align-items: center; justify-content: center;
    border: var(--gv-border-width) solid transparent;
    background: transparent;
    color: var(--gv-text-subtle);
    border-radius: var(--gv-radius-sm);
    font-size: var(--gv-text-2xs);
    cursor: pointer;
}
.mbdoc-fr__opt {
    flex: 0 0 auto;
    min-width: var(--gv-control-xs); height: var(--gv-control-xs);
    padding: 0 var(--gv-space-2);
    border: var(--gv-border-width) solid transparent;
    background: transparent;
    color: var(--gv-text-subtle);
    font-size: var(--gv-text-xs); font-weight: var(--gv-weight-medium);
    border-radius: var(--gv-radius-sm);
    cursor: pointer;
}
.mbdoc-fr__nav:hover, .mbdoc-fr__opt:hover {
    background: var(--gv-surface-sunk);
    color: var(--gv-ink-700);
}
.mbdoc-fr__opt.is-on {
    border-color: var(--gv-brand-border);
    background: var(--gv-brand-bg);
    color: var(--gv-brand-fg);
}
/* Replace, at the end of the box. Ruled off from the find controls beside it because it is
 * the one thing in there that does not answer "where is this word" -- it is what to do
 * next, and a clerk scanning the box should be able to see the join. */
.mbdoc-frt {
    display: inline-flex; align-items: center; gap: var(--gv-space-1); flex: 0 0 auto;
    height: var(--gv-control-xs);
    margin-left: var(--gv-space-1);
    padding: 0 var(--gv-space-2);
    border: var(--gv-border-width) solid transparent;
    border-left: var(--gv-border-width) solid var(--gv-border);
    border-radius: 0 var(--gv-radius-sm) var(--gv-radius-sm) 0;
    background: transparent;
    color: var(--gv-text-subtle);
    font-size: var(--gv-text-xs); font-weight: var(--gv-weight-medium);
    white-space: nowrap;
    cursor: pointer;
}
.mbdoc-frt:hover { background: var(--gv-surface-sunk); color: var(--gv-ink-700); }
/* Pressed is the brand, not the gold: the gold is spoken for by the matches themselves, and
 * a toggle wearing the same colour as the thing it reveals reads as one more match. */
.mbdoc-frt.is-on {
    background: var(--gv-brand-bg);
    color: var(--gv-brand-fg);
}
.mbdoc-frt:focus-visible,
.mbdoc-fr__opt:focus-visible,
.mbdoc-fr__nav:focus-visible,
.mbdoc-fr__btn:focus-visible,
.mbdoc-fr__x:focus-visible,
.mbdoc-fr__undo:focus-visible {
    outline: var(--gv-focus-width) solid var(--gv-focus-color);
    outline-offset: var(--gv-focus-offset);
}

/* The replace row, BESIDE the search box rather than under it (#602). It used to be a
 * standalone block below the whole bar -- a sunk, ruled panel, so it read as something
 * opened over the minutes rather than as more of the toolbar. That was the right treatment
 * for a block sitting on the paper. Inline it is a box drawn inside a box, and the border
 * it needed to separate itself from the document now only separates it from the toolbar it
 * is part of.
 *
 * So: no fill, no border, no radius, no padding of its own -- one rule on the left, the
 * same device already dividing Replace from the find controls inside the search box
 * (.mbdoc-frt). The row still says "this half is different from that half"; it just says
 * it at the weight the distance now calls for.
 *
 * `flex: 1 1 24rem` is what puts it on that line. 24rem is the smallest the group reads at
 * -- a 100px input floor, Replace, Replace all, the close -- so the bar can hold both it
 * and the search box down to about 770px, and under that the line cannot take both and the
 * row wraps beneath the search box on its own. That wrap IS the narrow layout; there is no
 * breakpoint here to keep true, and the row it falls back to is the one this started as.
 *
 * box-sizing stays, and not optionally: this app has no border-box reset, so once the row
 * is wrapped and growing into a whole line its padding and rule would be added ON TOP of
 * that line -- overflow hanging off the right edge of a column that keeps a 2px gutter so
 * the bar and the document do not miss their edges by even that much (#mbdoc-bar).
 *
 * No margin-bottom: the bar's own gap is a ROW-gap as well as a column one, and the 8px
 * under the group is the document scroller's padding-top (#603 moved it off the bar so it
 * scrolls away), so the margin this row carried as a standalone block below the bar would
 * now be spacing on top of spacing. */
.mbdoc-fr {
    display: flex; align-items: center; gap: var(--gv-space-2); flex-wrap: wrap;
    flex: 1 1 24rem;
    box-sizing: border-box;
    margin-left: var(--gv-space-1);
    padding-left: var(--gv-space-3);
    border-left: var(--gv-border-width) solid var(--gv-border);
}

/* The chips and the More button as one line of the bar (#602). Loose in the bar they are
 * two flex items among four, and with the replace row taking its share of the first line
 * they wrapped separately -- the chips onto one line and the lone More button onto the next,
 * which reads as a third row of toolbar with one button in it.
 *
 * At `auto` they sit beside the search box exactly as they do today, which is the state the
 * tab is in almost all of the time -- nothing typed, nothing to replace, one row of bar.
 * mbDocFilterHtml adds is-own-line only while the replace row is actually drawn, because
 * that is the only moment the filters have to be pushed down: left to wrap on their own
 * they would climb back up beside the search box on a wide enough bar, with Replace open,
 * which is the arrangement this is fixing. */
.mbdoc-filters {
    display: flex; align-items: center; gap: var(--gv-space-2); flex-wrap: wrap;
    flex: 1 1 auto;
    box-sizing: border-box;
}
.mbdoc-filters.is-own-line { flex: 0 0 100%; }
/* Shaped like the find box above it on purpose: one is what you are looking for and the
 * other is what to put in its place, and two halves of one job should not look like two
 * different kinds of control. */
.mbdoc-fr__in {
    display: flex; align-items: center; gap: var(--gv-space-2);
    border: var(--gv-border-width) solid var(--gv-border);
    background: var(--gv-surface);
    border-radius: var(--gv-radius-md);
    padding: var(--gv-space-1) var(--gv-space-2);
    flex: 1 1 120px; min-width: 100px; max-width: 360px;
}
.mbdoc-fr__in i { color: var(--gv-text-disabled); font-size: var(--gv-text-xs); }
.mbdoc-fr__q {
    border: 0; outline: 0; background: transparent;
    font: inherit; font-size: var(--gv-text-sm); flex: 1; min-width: 0; color: var(--gv-text);
}
.mbdoc-fr__btn {
    flex: 0 0 auto;
    height: var(--gv-control-xs);
    padding: 0 var(--gv-space-3);
    border: var(--gv-border-width) solid var(--gv-border);
    background: var(--gv-surface);
    color: var(--gv-ink-700);
    font-size: var(--gv-text-xs); font-weight: var(--gv-weight-medium);
    border-radius: var(--gv-radius-sm);
    cursor: pointer;
}
.mbdoc-fr__btn:hover { border-color: var(--gv-border-strong); color: var(--gv-text); }
/* Disabled rather than dropped. A row whose buttons come and go as the count changes
 * teaches nobody where anything is, and the count beside them already says why. */
.mbdoc-fr__nav:disabled, .mbdoc-fr__btn:disabled {
    color: var(--gv-text-disabled);
    border-color: var(--gv-border);
    background: transparent;
    cursor: default;
}
.mbdoc-fr__btn:disabled { background: var(--gv-surface); }
.mbdoc-fr__x {
    margin-left: auto; flex: 0 0 auto;
    width: var(--gv-control-xs); height: var(--gv-control-xs);
    display: inline-flex; align-items: center; justify-content: center;
    border: var(--gv-border-width) solid transparent;
    background: transparent;
    color: var(--gv-text-disabled);
    border-radius: var(--gv-radius-md);
    cursor: pointer;
}
.mbdoc-fr__x:hover { border-color: var(--gv-border); background: var(--gv-surface); color: var(--gv-ink-700); }
/* The result of a press that has already happened, not another control to reach for -- but
 * ON the row, not under it (#602). `flex: 1 0 100%` gave this a line of its own inside
 * .mbdoc-fr, which cost nothing while the row was a block below the whole bar -- it grew
 * downward into space nothing else wanted. Inline in the bar, that same line pushes the
 * filter chips down and the document with them, so a clerk pressing Replace watched the
 * paragraph they were reading walk down the screen. Nothing about replacing a word should
 * move the words around it.
 *
 * nowrap because the count and Undo are one statement -- this many changed, and here is the
 * way back -- and a break between them leaves an Undo attached to nothing. It does not grow
 * either: the width it is not taking belongs to the replacement field. */
.mbdoc-fr__res {
    flex: 0 1 auto;
    display: flex; align-items: center; gap: var(--gv-space-2);
    white-space: nowrap;
    font-size: var(--gv-text-xs);
    color: var(--gv-text-subtle);
}
.mbdoc-fr__undo {
    border: 0; background: transparent; padding: 0;
    color: var(--gv-brand-fg);
    font-size: var(--gv-text-xs); font-weight: var(--gv-weight-medium);
    text-decoration: underline;
    cursor: pointer;
}

/* The match itself. Gold, because amber down this screen already means "this item still
 * owes minutes" and a second amber in the prose would be two different warnings in one
 * column. The current one is the solid stop, so a document with forty matches still shows
 * which of them the arrows are standing on. */
.mbdoc-hit {
    background: var(--gv-gold-border);
    border-radius: var(--gv-radius-sm);
    padding: 0 1px;
    color: inherit;
}
.mbdoc-hit.is-current {
    background: var(--gv-gold);
    color: var(--gv-ink-900);
    box-shadow: 0 0 0 2px var(--gv-gold);
}
