/*
 * Stetworth /app — the Verified-chat screen (new-ux lane 13; CONTRACTS §12.9).
 *
 * Every rule here styles the `vch-` vocabulary app_ctrl_chat.js
 * emits — the heading, the conversation list with its status rows and
 * the New chat menu, the thread with its checking, failed and answer
 * sheets, the decision card and the composer — plus the module's own
 * data-vchat-* / aria state hooks as attribute selectors.  Token
 * vocabulary only (every colour, space, type, radius and shadow value
 * through var(--…)), and the media set is the split-sheet pair: one
 * narrow-viewport block and one reduced-motion block, nothing else.
 */

/* The screen: the page h1, then two columns — the conversation rail and
   the thread panel — on the page's own ground.  Sizes are the Claude
   Design mockup's (dc.html :575-595), authoritative for this screen's
   look by the product owner's 2026-09-14 direction. */
.vch-screen {
    display: flex;
    flex-direction: column;
    gap: 22px;
    min-width: 0;
}

.vch-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--s-4);
}

.vch-title {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--fs-hero);
    font-weight: 400;
    letter-spacing: normal;
    color: var(--c-ink);
}

.vch-disabled {
    margin: 0;
    font-size: var(--fs-ui);
    color: var(--c-ink-muted);
}

.vch-body {
    display: grid;
    grid-template-columns: minmax(180px, 220px) minmax(0, 1fr);
    gap: 28px;
    align-items: start;
    min-width: 0;
}

/* The list column: the rail is sticky, so the conversations stay with
   the reader down a long thread (dc.html :596).  The 14px rhythm is set
   on both the column and the region inside it — the region is the flex
   parent that actually holds the head, the list and the foot. */
.vch-list-col {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
    position: sticky;
    top: var(--s-5);
}

.vch-list-region {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
}

.vch-list-head,
.vch-list-foot {
    display: flex;
    align-items: center;
    gap: var(--s-2);
}

/* The New chat block: the oxblood slab and, in flow beneath it, the two
   mode tiles.  The column is what makes the slab the rail's full width
   and what pushes the rows down when the menu opens (dc.html :597). */
.vch-new-wrap {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    flex: 1 1 auto;
    min-width: 0;
    position: relative;
}

.vch-new,
.vch-show-archived,
.vch-unarchive,
.vch-retry,
.vch-mode-btn,
.vch-send,
.vch-copy,
.vch-toggle-original,
.vch-talk,
.vch-confirm-yes,
.vch-confirm-no {
    appearance: none;
    font: inherit;
    font-size: var(--fs-ui);
    color: var(--c-ink);
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--radius-pill);
    padding: var(--s-1) var(--s-3);
    cursor: pointer;
    transition: background-color var(--t-fast), border-color var(--t-fast);
}

/* The slab: full width, left-aligned, with the plus at one end and the
   caret at the other (dc.html :597).  It lifts on hover; the shadow
   stays put, so the movement reads as the card rising, not swelling. */
.vch-new {
    display: flex;
    justify-content: space-between;
    align-items: center;
    text-align: left;
    font-size: var(--fs-13-5);
    font-weight: 500;
    line-height: normal;
    padding: 11px 14px;
    color: var(--c-sand-card);
    background: var(--c-accent);
    border: 0;
    border-radius: var(--r-10);
    box-shadow: var(--sh-slab);
    transition: background-color var(--t-base), transform var(--t-base);
}

.vch-new:hover {
    background: var(--c-accent-hover);
    transform: translateY(-1px);
}

.vch-new-label {
    display: inline-flex;
    align-items: center;
    gap: 9px;
}

/* The caret is decoration: aria-expanded on the button is the state. */
.vch-new-caret {
    font-size: var(--fs-micro);
    opacity: 0.7;
}

.vch-new:focus-visible,
.vch-show-archived:focus-visible,
.vch-unarchive:focus-visible,
.vch-retry:focus-visible,
.vch-mode-btn:focus-visible,
.vch-send:focus-visible,
.vch-copy:focus-visible,
.vch-toggle-original:focus-visible,
.vch-talk:focus-visible,
.vch-confirm-yes:focus-visible,
.vch-confirm-no:focus-visible,
.vch-new-option:focus-visible,
.vch-row-open:focus-visible,
.vch-archive:focus-visible,
.vch-rename:focus-visible,
.vch-decide:focus-visible,
.vch-rename-input:focus-visible,
.vch-original-tab:focus-visible,
.vch-picker:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

/* The slab keeps its resting lift while focused — a ring on its own
   would drop the card to the page for as long as the focus is on it. */
.vch-new:focus-visible {
    box-shadow: var(--sh-slab), var(--focus-ring);
}

/* The New chat menu: two tiles in flow under the slab, hidden at rest
   (dc.html :599-601).  The container carries no chrome of its own —
   each tile is its own card. */
.vch-new-menu {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--s-2);
    min-width: 0;
}

/* A mode tile: a card with the mode's icon disc above its name and its
   one-line caption (dc.html :600-601).  The hover warms the edge to the
   accent and lifts; the ground does not move. */
.vch-new-option {
    appearance: none;
    font: inherit;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--s-2);
    min-width: 0;
    padding: var(--s-3) 10px;
    text-align: center;
    color: var(--c-ink);
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--r-10);
    box-shadow: var(--sh-menu);
    cursor: pointer;
    transition: border-color var(--t-base), transform var(--t-base);
}

.vch-new-option:hover {
    border-color: var(--c-accent);
    transform: translateY(-1px);
}

/* The icon disc: the mode's colour, never its meaning — the tile's
   accessible name is the name and the caption beneath it. */
.vch-new-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: var(--radius-pill);
}

[data-vchat-new-fast] .vch-new-icon {
    background: var(--c-gold-tint);
    color: var(--c-burnished-text);
}

[data-vchat-new-deliberate] .vch-new-icon {
    background: var(--c-moss-tint);
    color: var(--c-status-complete);
}

.vch-new-text {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    min-width: 0;
}

.vch-new-text b {
    font-weight: 500;
    font-size: var(--fs-12-5);
    line-height: normal;
}

.vch-new-text span {
    font-size: var(--fs-micro);
    line-height: 1.3;
    color: var(--c-ink-faint);
}

/* Show archived has no mockup counterpart, and a second sand pill under
   the slab competes with it: it reads as a quiet link instead. */
.vch-show-archived {
    padding: 0;
    font-size: var(--fs-meta);
    color: var(--c-ink-muted);
    background: none;
    border: 0;
    border-radius: 0;
}

.vch-show-archived:hover {
    color: var(--c-ink);
    text-decoration: underline;
}

.vch-show-archived[aria-pressed="true"] {
    color: var(--c-accent);
    background: none;
}

/* The rows: one bordered card each, holding the open control — status
   disc, title, status label — beside the archive control, with the
   delete confirm revealed underneath inside the same card. */
.vch-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    min-width: 0;
}

/* The card (dc.html :606).  The resting border is transparent rather
   than absent, so selecting a row never shifts the column by a pixel;
   the hover lifts rather than tints, and lands on the same elevation
   the selected row rests at.  The card wraps: the open control and the
   trash share its first line, and the confirm strip takes a full second
   one beneath them, inside the same card. */
.vch-row {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 10px;
    row-gap: 0;
    min-width: 0;
    border: 1px solid transparent;
    border-radius: var(--r-10);
    transition: transform var(--t-base), box-shadow var(--t-base);
}

.vch-row:hover {
    transform: translateY(-1px);
    box-shadow: var(--sh-select);
}

.vch-row--selected,
.vch-row[aria-current="true"] {
    background: var(--c-sand-card);
    border-color: var(--c-sand-rule);
    box-shadow: var(--sh-select);
}

/* An open confirm gives its row the selected card even when the row is
   not selected: the strip's rule needs a surface to sit on. */
.vch-row[data-vchat-confirming] {
    background: var(--c-sand-card);
    border-color: var(--c-sand-rule);
}

/* The 30 / 1fr / 18 rhythm of the mockup's row grid (dc.html :607),
   split across two elements because a button may not contain a button:
   the disc and the text stack are this control's own grid, and the
   trash is the row's third column. */
.vch-row-open {
    appearance: none;
    font: inherit;
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr);
    grid-template-rows: auto;
    align-items: center;
    column-gap: 10px;
    /* A zero basis, not `auto`: a flex line breaks on the hypothetical
       main size, so an open control sized by its own title would wrap
       the trash onto a second line before it ever shrank. */
    flex: 1 1 0;
    min-width: 0;
    padding: 11px 0 11px 12px;
    text-align: left;
    color: var(--c-ink);
    background: transparent;
    border: 0;
    border-radius: var(--r-10);
    cursor: pointer;
}

/* The status disc (dc.html :608).  content-box keeps the ring exactly
   30px across at every border width, the way the mockup draws it; the
   disc is aria-hidden, so its colour is decoration and the state is
   read from the label beside it. */
.vch-row-glyph {
    box-sizing: content-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 1.5px solid var(--c-sand-rule);
    border-radius: var(--radius-pill);
    font-size: var(--fs-meta);
    font-weight: 700;
    line-height: normal;
    color: var(--c-burnished-text);
}

.vch-row-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.vch-row-title {
    font-family: var(--font-text);
    font-size: var(--fs-body);
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The label wraps where the disc's colour would not carry: the mockup
   lets "Changed since you read it" run to two lines rather than clip. */
.vch-row-status {
    font-size: var(--fs-micro);
    line-height: normal;
    color: var(--c-ink-muted);
}

/* Disc and label part company here — but only in the FILL.  The disc's
   ground and ring take the mockup's own tints, which are decoration under
   the 3:1 floor; its mark is hairline line-art and its label is 11px
   prose, so both take the AA-passing rung of the same hue — the gold
   statuses burnish, the error reads brick.  The design's own error red is
   3.87:1 on the page a row rests on and 4.17:1 on a selected row's card,
   both under the 4.5:1 floor, which is why the row's mark and label read
   brick (`--c-status-failed`) while the failed CARD in the thread, where
   the red is a disc fill and a heading rather than small prose, keeps the
   chat red.  Same as the composer note's (style.css, `--c-chat-alert`). */
.vch-row[data-vchat-status="error"] .vch-row-glyph {
    background: var(--c-chat-alert-tint);
    border-color: var(--c-chat-alert-rule);
    color: var(--c-status-failed);
}

.vch-row[data-vchat-status="error"] .vch-row-status {
    color: var(--c-status-failed);
}

.vch-row[data-vchat-status="checking"] .vch-row-glyph {
    border: 2px solid var(--c-soft);
    border-top-color: var(--c-gold);
    animation: vch-spin 1.1s linear infinite;
}

.vch-row[data-vchat-status="needs-answer"] .vch-row-glyph {
    background: var(--c-gold-tint);
    border-color: var(--c-gold-rule-card);
}

.vch-row[data-vchat-status="changed"] .vch-row-glyph {
    background: var(--c-gold);
    border-color: var(--c-gold);
    color: var(--c-sand-card);
}

.vch-row[data-vchat-status="checking"] .vch-row-status,
.vch-row[data-vchat-status="needs-answer"] .vch-row-status,
.vch-row[data-vchat-status="changed"] .vch-row-status {
    color: var(--c-burnished-text);
}

.vch-row[data-vchat-status="verified"] .vch-row-glyph {
    background: var(--c-status-complete);
    border-color: var(--c-status-complete);
    color: var(--c-sand-card);
}

.vch-row[data-vchat-status="verified"] .vch-row-status {
    color: var(--c-status-complete);
}

.vch-row[data-vchat-archived] .vch-row-title {
    color: var(--c-ink-muted);
    font-style: italic;
}

/* The archive control: the mockup's 13px trash in an 18px box, quiet
   until the pointer is on it (dc.html :610).  It still archives — the
   icon and the label are the mockup's, the action is the app's. */
.vch-archive {
    appearance: none;
    font: inherit;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 18px;
    height: 18px;
    padding: 2px;
    margin-right: 8px;
    color: var(--c-rule-strong);
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

/* The mockup's dismissal red is the one --c-dismiss-hover carries, and
   this repo's hover-tint census admits that colour only at sites it can
   cite to its own in-tree design export — which holds no Verified chat
   screen.  The brick --c-status-failed is the same hue a step down and
   is what this control already used. */
.vch-archive:hover {
    color: var(--c-status-failed);
}

/* The archive confirm ("Delete?" Delete / Keep), hidden at rest; the
   row's one recorded exception to the dialog rule.  It is a strip under
   the row inside the same card, divided from it by a hairline
   (dc.html :613) — not a second thing crowding the row's own line. */
.vch-confirm {
    display: flex;
    flex: 1 1 100%;
    align-items: center;
    gap: var(--s-2);
    padding: 8px 12px 10px;
    border-top: 1px solid var(--c-soft);
    font-size: var(--fs-meta);
    color: var(--c-ink-soft);
}

/* The confirm keeps the app's brick rather than the mockup's brighter
   red: that colour is the hover-census-locked dismiss tint, and the
   rail lane's recorded departure 2 holds.  Only the corner is the
   mockup's (dc.html :613). */
.vch-confirm-yes {
    margin-left: auto;
    padding: 3px 8px;
    font-size: var(--fs-meta);
    color: var(--c-status-failed);
    background: none;
    border: 1px solid var(--c-status-failed);
    border-radius: 5px;
}

.vch-confirm-yes:hover {
    color: var(--c-sand-card);
    background: var(--c-status-failed);
}

.vch-confirm-no {
    padding: 0;
    font-size: var(--fs-meta);
    color: var(--c-ink-muted);
    background: none;
    border: 0;
}

.vch-confirm-no:hover {
    color: var(--c-ink);
}

.vch-unarchive {
    flex: none;
    font-size: var(--fs-meta);
    margin-right: var(--s-1);
}

/* The thread column: a single enclosing panel holding the head, the
   turns and the composer (dc.html :620).  The panel edge is the only
   enclosure — every child spaces itself with its own padding, so both
   flex parents run at zero gap (the retired admin bundle's empty slot
   used to sit beneath). */
.vch-thread-col {
    display: flex;
    flex-direction: column;
    gap: 0;
    min-width: 0;
}

.vch-thread {
    display: flex;
    flex-direction: column;
    gap: 0;
    min-width: 0;
    padding: 22px 26px 18px;
    background: var(--c-chat-panel);
    border: 1px solid var(--c-soft);
    border-radius: var(--r-16);
    box-shadow: var(--sh-sheen);
}

/* The panel is the enclosure around its contents, so with nothing to
   enclose there is nothing to draw.  The controller empties this node and
   leaves it in place on the flag-off screen (``_paintDisabled``), where the
   whole answer is one line of copy beside the heading; without this the
   padded, bordered card still paints beneath it as an empty frame.  It also
   covers the first frame, before the first read settles.  `:empty` is exact
   here because every paint writes the node's children with `innerHTML`, so
   an unpainted thread holds no whitespace text node either. */
.vch-thread:empty {
    display: none;
}

/* The two empty states, which the mockup draws differently: the panel
   with no conversation chosen is one centred line of type (dc.html
   :759); a chosen conversation with no turns yet is a dashed shield
   mark above its line (dc.html :633-635).  Both lines take the muted
   rung, not the design's own stone: that stone is 2.44:1 on this panel,
   and these are the only words on the screen. */
.vch-none {
    margin: 80px 0;
    padding: 0;
    text-align: center;
    font-family: var(--font-text);
    font-size: var(--fs-18);
    line-height: normal;
    color: var(--c-ink-muted);
}

.vch-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    margin: 0;
    padding: 56px 20px 40px;
    text-align: center;
}

/* The mark is decoration: the paragraph below carries the whole of the
   meaning, and the mark itself is aria-hidden. */
.vch-empty-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    border: 1.5px dashed var(--c-rule-strong);
    border-radius: var(--radius-pill);
    color: var(--c-gold);
}

.vch-empty-copy {
    margin: 0;
    max-width: 320px;
    font-family: var(--font-text);
    font-size: var(--fs-18);
    line-height: 1.4;
    color: var(--c-ink-muted);
}

.vch-thread-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 14px;
    padding-bottom: 14px;
}

/* The thread title IS the rename control (dc.html :623): a text cursor,
   a warm hover ground and a faint pencil sitting after the title.  The
   negative margin cancels the padding so the title keeps the panel's
   own text edge. */
.vch-rename {
    appearance: none;
    font: inherit;
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-family: var(--font-display);
    font-size: var(--fs-section);
    min-width: 0;
    padding: 2px 6px;
    margin-left: -6px;
    text-align: left;
    color: var(--c-ink);
    background: transparent;
    border: 0;
    border-radius: var(--r-6);
    cursor: text;
    transition: background-color var(--t-base);
}

.vch-rename:hover {
    background: var(--c-chat-track);
}

/* The pencil does not take the title's ink — it is faint on purpose,
   and it is decoration: the button's name is the title itself. */
.vch-rename-pencil {
    flex: none;
    color: var(--c-ink-faint);
}

.vch-thread-title {
    display: block;
    line-height: normal;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vch-rename-input {
    font: inherit;
    color: var(--c-ink);
    background: var(--c-sand-card);
    border: 1px solid var(--c-rule-strong);
    border-radius: var(--r-6);
    padding: 2px var(--s-2);
    min-width: 0;
    flex: 1 1 200px;
    font-family: var(--font-display);
    font-size: var(--fs-section);
}

/* The mode switch: a warm track with no border of its own, and the
   pressed side filled oxblood over a tinted lift (dc.html :625-627).
   The fill is decoration — aria-pressed is what announces the mode. */
.vch-mode {
    display: inline-flex;
    gap: 2px;
    border-radius: var(--radius-pill);
    padding: 3px;
    background: var(--c-chat-track);
}

.vch-mode-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: 0;
    padding: 6px var(--s-3);
    border-radius: var(--radius-pill);
    background: transparent;
    color: var(--c-ink-muted);
    font-size: var(--fs-12-5);
    font-weight: 500;
    line-height: normal;
    transition: background-color var(--t-base), color var(--t-base),
        box-shadow var(--t-base);
}

.vch-mode-btn[aria-pressed="true"] {
    color: var(--c-sand-card);
    background: var(--c-accent);
    box-shadow: var(--sh-pill);
}

/* The selected pill carries a shadow at rest, and `box-shadow` is one
   property: without this the resting lift would win over the ring above
   on specificity and the chosen mode would be the one control on the
   screen that shows no focus at all.  Same shape as `.vch-new`. */
.vch-mode-btn[aria-pressed="true"]:focus-visible {
    box-shadow: var(--sh-pill), var(--focus-ring);
}

/* The turns: the list is the scrolling region the frames pin to the
   bottom of while the reader is already there. */
.vch-turns {
    list-style: none;
    margin: 0;
    padding: 10px 0 26px;
    display: flex;
    flex-direction: column;
    gap: 30px;
    max-height: calc(100vh - 16rem);
    overflow-y: auto;
    min-width: 0;
}

.vch-turn {
    display: flex;
    flex-direction: column;
    gap: 14px;
    min-width: 0;
    animation: vch-ease 0.35s both;
}

/* What the reader asked: an oxblood bubble on the right, its one square
   corner pointing back at them (dc.html :640). */
.vch-user {
    align-self: flex-end;
    max-width: 76%;
    padding: 11px var(--s-4);
    font-family: var(--font-text);
    font-size: var(--fs-base);
    line-height: 1.45;
    color: var(--c-sand-card);
    background: var(--c-accent);
    border-radius: var(--r-16) var(--r-16) var(--radius-sm) var(--r-16);
    box-shadow: var(--sh-bubble);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* The sheets: one raised card per answer state, filling the thread
   column (dc.html :644, :690, :699).  The mockup draws no left rule on
   any of them — the shadow is what lifts a turn off the panel, the
   failed card states itself in its own hairline, and the revised answer
   says so in words on the .vch-changed line rather than in a colour. */
.vch-sheet {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 18px;
    padding: 26px 28px 22px;
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-card);
    overflow-wrap: anywhere;
}

.vch-sheet--failed {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-4);
    padding: 18px 22px;
    border-color: var(--c-chat-alert-rule);
}

.vch-eyebrow {
    margin: 0;
    font-family: var(--font-ui);
    font-size: var(--fs-micro);
    font-weight: 500;
    line-height: normal;
    letter-spacing: var(--track-tag);
    text-transform: uppercase;
    color: var(--c-burnished-text);
}

.vch-text {
    font-family: var(--font-text);
    font-size: var(--fs-lede);
    line-height: 1.5;
    color: var(--c-ink);
}

.vch-text p {
    margin: 0 0 var(--s-2);
}

.vch-text p:last-child {
    margin-bottom: 0;
}

.vch-text .vch-list-items {
    margin: 0 0 var(--s-2);
    padding-left: var(--s-5);
}

.vch-text code {
    font-family: var(--font-mono);
    font-size: var(--fs-ui);
    padding: 0 var(--s-1);
    background: var(--c-paper-warm);
    border-radius: var(--radius-sm);
}

/* The two checking sheets carry their padding on the body, the bar stack
   and the footer rather than on the shell, so the "Not yet checked" tab
   can hang off the top edge and the blurred bars can run out to it
   (dc.html :644-649, :664-667). */
.vch-sheet--draft,
.vch-sheet--sealed {
    gap: 0;
    padding: 0;
    overflow: hidden;
}

/* That tab is the one cue that what you are reading has not been checked
   yet (dc.html :647), so it stays real text in the document: never a
   ::before, never a background image. */
[data-vchat-draft] > .vch-eyebrow {
    position: absolute;
    top: -1px;
    right: 28px;
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-10-5);
    letter-spacing: var(--track-tab);
    padding: 5px 10px 6px;
    background: var(--c-chat-track);
    border-radius: 0 0 var(--r-7) var(--r-7);
}

/* An unchecked draft reads a shade lighter than a settled answer
   (dc.html :648 against :712); the 30px top clears the tab. */
[data-vchat-draft] > .vch-text {
    padding: 30px 28px 18px;
    line-height: 1.6;
}

[data-vchat-draft] > .vch-text p {
    color: var(--c-ink-soft);
}

/* The sealed sheet: four bars where the answer will land, blurred and
   half-lit because deliberate mode shows nothing until it is checked
   (dc.html :665).  They do not pulse — the rail below carries the state,
   and a blurred placeholder that moves reads as content. */
.vch-sealed {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    padding: 28px 28px var(--s-2);
    filter: blur(2px);
    opacity: 0.5;
}

.vch-bar {
    display: block;
    height: var(--s-3);
    background: var(--c-sand-rule);
    border-radius: var(--r-6);
}

.vch-bar:nth-child(1) {
    width: 92%;
}

.vch-bar:nth-child(3) {
    width: 78%;
}

.vch-bar:nth-child(4) {
    width: 64%;
}

/* The fast sheet's footer: the pending seal and the step chips over a
   rule softer than the sheet's own edge (dc.html :649). */
.vch-sheet-foot {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-4);
    padding: 14px 28px 20px;
    border-top: 1px solid var(--c-soft);
}

/* The seal is a 52px dashed ring in both of its lives: empty and turning
   slowly while the checks run (dc.html :650), and holding the 38px disc
   and its check once they are done (dc.html :714).  It stays out of the
   accessibility tree in both — the tab, the chips and the VERIFIED word
   are what announce the state. */
.vch-seal {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 52px;
    height: 52px;
    background: transparent;
    border: 1.5px dashed var(--c-status-complete);
    border-radius: var(--radius-pill);
}

.vch-seal--pending {
    border-color: var(--c-rule-strong);
    animation: vch-spin 6s linear infinite;
}

/* The settled seal lands like a wax stamp and keeps the tilt it landed
   at: the keyframe's resting frame holds the -8deg. */
[data-vchat-seal] {
    animation: vch-stamp 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.vch-seal-disc {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    color: var(--c-sand-card);
    background: var(--c-status-complete);
    border-radius: var(--radius-pill);
}

/* The three checks: a chip each on the fast sheet (dc.html :652-654).
   The icon is decoration — the label beside it is the announced state. */
.vch-steps {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-2);
    min-width: 0;
}

/* The chip at rest — and, in the rail, the node's label under it — takes
   the muted rung rather than the design's own stone, which is 2.57:1 on
   this ground and carries 11px prose. */
.vch-step {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 10px 5px 6px;
    font-size: var(--fs-11-5);
    color: var(--c-ink-muted);
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--radius-pill);
}

/* The chip's label takes the face's own leading, like the mockup's
   (dc.html :654); the rail's stacked label declares its own 1.3 below
   (dc.html :676). */
.vch-step-label {
    line-height: normal;
}

.vch-step-glyph {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 20px;
    height: 20px;
    color: var(--c-rule-strong);
    background: var(--c-chat-track);
    border-radius: var(--radius-pill);
}

.vch-step[data-vchat-check="running"] {
    color: var(--c-ink);
    background: var(--c-gold-tint);
    border-color: var(--c-gold);
}

/* The halo is a surface's own spreading shadow, growing out of its edge
   and fading (dc.html :38, :654) — the two rests are tokens, so the sheet
   still spends no literal elevation.  WHICH surface differs by variant,
   and the mockup is explicit about it: on the fast strip the chip is the
   whole control and carries the animation (dc.html :654); in the rail the
   44px node carries it and the column around it is unpainted (dc.html
   :671).  Hung on `.vch-step` for both, the rail's column — which keeps
   the chip's pill radius — would draw a gold capsule around the node AND
   its label, a surface the mockup has nowhere. */
.vch-steps:not(.vch-steps--stacked) .vch-step[data-vchat-check="running"],
.vch-steps--stacked .vch-step[data-vchat-check="running"] .vch-step-glyph {
    animation: vch-breathe 1s ease-in-out infinite alternate;
}

.vch-step[data-vchat-check="running"] .vch-step-glyph {
    color: var(--c-burnished-text);
    background: var(--c-gold-badge);
}

.vch-step[data-vchat-check="done"] {
    color: var(--c-status-complete);
    background: var(--c-moss-tint);
    border-color: var(--c-status-complete);
}

.vch-step[data-vchat-check="done"] .vch-step-glyph {
    color: var(--c-sand-card);
    background: var(--c-status-complete);
}

/* No mockup state: the chat's alert red, borrowed from the failed card so
   the screen has one error colour rather than two. */
.vch-step[data-vchat-check="error"] {
    color: var(--c-chat-alert);
    background: var(--c-chat-alert-tint);
    border-color: var(--c-chat-alert-rule);
}

.vch-step[data-vchat-check="error"] .vch-step-glyph {
    color: var(--c-sand-card);
    background: var(--c-chat-alert);
}

.vch-step[data-vchat-check="skipped"] {
    opacity: 0.7;
}

/* The deliberate rail: the same three checks as a row of nodes, with a
   connector whose green length is how much of the checking is done
   (dc.html :667-676). */
.vch-steps--stacked {
    position: relative;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
    gap: 0;
    padding: 18px 28px 26px;
}

.vch-steps--stacked::before {
    content: "";
    position: absolute;
    top: 40px;
    left: calc(16.66% + 22px);
    right: calc(16.66% + 22px);
    height: 1.5px;
    background: var(--c-sand-rule);
}

.vch-steps--stacked[data-vchat-progress="50"]::before {
    background: linear-gradient(90deg, var(--c-status-complete) 0, var(--c-status-complete) 50%, var(--c-sand-rule) 50%, var(--c-sand-rule) 100%);
}

.vch-steps--stacked[data-vchat-progress="100"]::before {
    background: var(--c-status-complete);
}

.vch-steps--stacked .vch-step {
    flex-direction: column;
    gap: 10px;
    padding: 0;
    text-align: center;
    background: none;
    border: 0;
}

.vch-steps--stacked .vch-step-glyph {
    width: 44px;
    height: 44px;
    background: var(--c-sand-card);
    border: 1.5px solid var(--c-sand-rule);
}

.vch-steps--stacked .vch-step-label {
    max-width: 120px;
    line-height: 1.3;
}

.vch-steps--stacked .vch-step[data-vchat-check="running"] .vch-step-glyph {
    background: var(--c-gold-tint);
    border-color: var(--c-gold);
}

.vch-steps--stacked .vch-step[data-vchat-check="done"] .vch-step-glyph {
    background: var(--c-status-complete);
    border-color: var(--c-status-complete);
}

.vch-steps--stacked .vch-step[data-vchat-check="error"] .vch-step-glyph {
    background: var(--c-chat-alert);
    border-color: var(--c-chat-alert);
}

/* The failed card: an alert disc, what happened, and Try again at the far
   end — pushed there by the body's own growth, not by a margin, so the
   button wraps under the sentence rather than onto a line of its own
   (dc.html :690-693). */
.vch-failed-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 44px;
    height: 44px;
    color: var(--c-chat-alert);
    background: var(--c-chat-alert-tint);
    border-radius: var(--radius-pill);
}

.vch-failed-body {
    display: flex;
    flex-direction: column;
    flex: 1 1 200px;
    gap: 3px;
    min-width: 0;
}

.vch-failed-lede {
    margin: 0;
    font-family: var(--font-text);
    font-size: var(--fs-16-5);
    font-weight: 400;
    line-height: normal;
    color: var(--c-ink);
}

.vch-failed-error {
    margin: 0;
    font-size: var(--fs-13);
    line-height: 1.4;
    color: var(--c-ink-muted);
}

.vch-retry {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 9px 14px;
    font-size: var(--fs-13-5);
    font-weight: 500;
    line-height: normal;
    color: var(--c-sand-card);
    background: var(--c-accent);
    border: 1px solid var(--c-accent);
    border-radius: var(--radius);
    box-shadow: var(--sh-retry);
}

.vch-retry:hover {
    background: var(--c-accent-hover);
    border-color: var(--c-accent-hover);
}

/* The answer footer: the seal, the VERIFIED word over its confidence
   meter, the footnotes, then Copy (dc.html :713-717).  The confidence
   level is written once on the footer and read from there by the ring,
   the disc, the word and the filled pips — the mockup drives all four
   from one variable, and they are siblings, not descendants, of the
   element that carries `data-vchat-confidence`. */
.vch-answer-foot {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--s-4);
    padding-top: 14px;
    border-top: 1px solid var(--c-soft);
    color: var(--c-ink-muted);
}

.vch-answer-foot[data-vchat-level="low"] .vch-seal {
    border-color: var(--c-burnished-text);
}

.vch-answer-foot[data-vchat-level="low"] .vch-seal-disc {
    background: var(--c-burnished-text);
}

.vch-answer-foot[data-vchat-level="low"] .vch-verified-word {
    color: var(--c-burnished-text);
}

.vch-answer-foot[data-vchat-level="low"] .vch-dot--on {
    background: var(--c-burnished-text);
}

.vch-verified {
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.vch-verified-word {
    font-size: var(--fs-meta);
    font-weight: 500;
    line-height: normal;
    letter-spacing: var(--track-tag);
    text-transform: uppercase;
    color: var(--c-status-complete);
}

.vch-confidence {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
}

.vch-meter {
    display: inline-flex;
    align-items: center;
    gap: var(--s-1);
}

.vch-dot {
    width: 18px;
    height: 5px;
    background: var(--c-sand-rule);
    border-radius: 3px;
}

.vch-dot--on {
    background: var(--c-status-complete);
}

/* The design's own faint stone is 2.57:1 on this ground; the word keeps
   the muted rung it can be read at. */
.vch-confidence-word {
    margin-left: var(--s-1);
    font-size: var(--fs-micro);
    line-height: normal;
    color: var(--c-ink-muted);
}

.vch-footnote {
    margin-left: auto;
    max-width: 40%;
    font-family: var(--font-ui);
    font-size: var(--fs-meta);
    line-height: 1.4;
    text-align: right;
    color: var(--c-ink-muted);
}

.vch-copy {
    display: inline-flex;
    align-items: center;
    flex: none;
    gap: 6px;
    margin-left: auto;
    padding: 6px 9px;
    font-size: var(--fs-meta);
    color: var(--c-ink-muted);
    border-radius: var(--radius);
}

/* Two `auto` margins would split the free space; a footnote takes it and
   Copy sits flush after it (dc.html :717). */
.vch-footnote ~ .vch-copy {
    margin-left: 0;
}

/* The mockup warms the border to the resting gold here, which the app's
   hover law reserves for its focus indicator, so only the label moves. */
.vch-copy:hover {
    color: var(--c-accent);
}

.vch-copy[data-vchat-copied] {
    color: var(--c-status-complete);
}

/* The settled answer rises into place, a shade darker than the draft it
   replaced (dc.html :699, :712). */
.vch-sheet--answer {
    animation: vch-ease 0.35s both;
}

[data-vchat-answer] > .vch-text {
    line-height: 1.6;
}

[data-vchat-answer] > .vch-text p {
    color: var(--c-ink);
}

/* The revised answer says so in words under a gold rule, with the
   disclosure as a plain underlined link at the end of the sentence
   (dc.html :719).  The rule is a ::before, not a border-left: the sheet
   draws no left edges any more. */
.vch-changed {
    position: relative;
    margin: 0;
    padding-left: 15px;
    font-size: var(--fs-13);
    line-height: 1.45;
    color: var(--c-ink-muted);
}

.vch-changed::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 3px;
    background: var(--c-gold);
    border-radius: 2px;
}

.vch-changed-label {
    font-weight: 500;
    color: var(--c-burnished-text);
}

.vch-changed:hover,
.vch-changed:hover .vch-toggle-original {
    color: var(--c-ink);
}

/* The revised answer wears its own corner tab: a second control on the
   same disclosure, so a reader who never reaches the changed line still
   sees that this answer was rewritten (dc.html :700).  The pencil is
   decoration; the word and aria-expanded are what announce the state.
   The ground is the mockup's darker gold rather than its lighter
   resting one: near-white on that lighter gold is 3.7:1, under the text
   floor, and this screen already substitutes down the same channel for
   gold-as-text. */
.vch-original-tab {
    position: absolute;
    top: -1px;
    right: 28px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 10px 6px;
    font-family: inherit;
    font-size: var(--fs-10-5);
    font-weight: 500;
    line-height: normal;
    letter-spacing: var(--track-tab);
    text-transform: uppercase;
    color: var(--c-sand-card);
    background: var(--c-gold-press);
    border: 0;
    border-radius: 0 0 var(--r-7) var(--r-7);
    box-shadow: var(--sh-tab);
    transition: background-color var(--t-base);
}

.vch-original-tab:hover,
.vch-original-tab[aria-expanded="true"] {
    background: var(--c-burnished-text);
}

.vch-toggle-original {
    padding: 0;
    font-size: inherit;
    color: var(--c-burnished-text);
    text-decoration: underline;
    background: none;
    border: 0;
    border-radius: 0;
}

/* The original draft on demand: a dashed gold panel above the answer it
   was revised into (dc.html :702-705). */
.vch-original {
    display: flex;
    flex-direction: column;
    gap: 10px;
    margin-top: var(--s-2);
    padding: 16px 18px 14px;
    background: var(--c-sand-page);
    border: 1px dashed var(--c-gold-dash);
    border-radius: var(--r-10);
    animation: vch-ease 0.25s both;
}

.vch-original > .vch-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
}

.vch-original .vch-text {
    font-size: var(--fs-base);
    line-height: 1.55;
    color: var(--c-ink-muted);
}

.vch-original .vch-text p {
    color: var(--c-ink-muted);
}

.vch-changed-foot {
    position: relative;
    margin: 0;
    padding-left: 15px;
    font-size: var(--fs-12-5);
    line-height: 1.45;
    color: var(--c-ink-muted);
}

.vch-changed-foot::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 3px;
    background: var(--c-gold);
    border-radius: 2px;
}

/* The one decision card: a gold-edged card inset inside the turn and
   lifted over the answer it interrupts (dc.html :726).  Positioned, so
   the negative top margin puts it above the sheet rather than under. */
.vch-decision {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--s-4);
    margin: -10px 18px 0;
    padding: 20px 22px 22px;
    background: var(--c-sand-card);
    border: 1px solid var(--c-gold-rule-card);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-raise);
    animation: vch-rise 0.4s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

/* The head: a breathing gold disc, the eyebrow and the assumption itself
   (dc.html :727-729).  The disc's halo is the same spreading shadow the
   running step wears, at the card's own slower pace. */
.vch-decision-head {
    display: flex;
    align-items: flex-start;
    gap: 14px;
}

.vch-decision-icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 40px;
    height: 40px;
    color: var(--c-burnished-text);
    background: var(--c-gold-tint);
    border-radius: var(--radius-pill);
    animation: vch-breathe 1.1s ease-in-out infinite alternate;
}

.vch-decision-text {
    display: flex;
    flex-direction: column;
    gap: var(--s-1);
    min-width: 0;
}

.vch-decision-title {
    margin: 0;
    font-size: var(--fs-micro);
    font-weight: 500;
    line-height: normal;
    letter-spacing: var(--track-tag);
    text-transform: uppercase;
    color: var(--c-burnished-text);
}

.vch-decision-lead {
    margin: 0;
    font-family: var(--font-text);
    font-size: var(--fs-lede);
    line-height: 1.45;
    color: var(--c-ink);
}

/* The two answers are tiles, each led by its own disc: a check for the
   assumption standing, a re-run arrow for it being corrected (dc.html
   :731-739).  They lift to their own colour on hover — green for the one
   that keeps the answer, oxblood for the one that sends it back. */
.vch-decision-actions {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 10px;
}

.vch-decide {
    appearance: none;
    font: inherit;
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr);
    align-items: start;
    gap: var(--s-3);
    padding: 14px 16px;
    text-align: left;
    color: var(--c-ink);
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--r-12);
    cursor: pointer;
    transition: transform var(--t-base), box-shadow var(--t-base), border-color var(--t-base);
}

.vch-decide[data-vchat-decide="confirmed"]:hover {
    transform: translateY(-2px);
    border-color: var(--c-status-complete);
    box-shadow: var(--sh-tile);
}

.vch-decide[data-vchat-decide="denied"]:hover {
    transform: translateY(-2px);
    border-color: var(--c-accent);
    box-shadow: var(--sh-tile);
}

.vch-decide-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-pill);
}

.vch-decide[data-vchat-decide="confirmed"] .vch-decide-icon {
    color: var(--c-status-complete);
    background: var(--c-moss-tint);
}

.vch-decide[data-vchat-decide="denied"] .vch-decide-icon {
    color: var(--c-accent);
    background: var(--c-accent-tint);
}

.vch-decide-text {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.vch-decide b {
    font-weight: 500;
    font-size: var(--fs-14-5);
    line-height: normal;
}

.vch-decide-text span {
    font-size: var(--fs-12-5);
    line-height: 1.4;
    color: var(--c-ink-muted);
}

/* Neither answer: a quiet link under the tiles, not a third tile
   (dc.html :741). */
.vch-talk {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 0 2px;
    font-size: var(--fs-13);
    line-height: normal;
    color: var(--c-ink-muted);
    background: transparent;
    border: 0;
    border-radius: 0;
    transition: color var(--t-fast);
}

.vch-talk:hover {
    color: var(--c-accent);
}

/* Once answered, one line with a small filled disc: green when the answer
   stood, burnished when it is being re-checked (dc.html :744-746). */
.vch-resolved {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding-left: 6px;
    font-size: var(--fs-13);
    color: var(--c-status-complete);
}

.vch-resolved-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 18px;
    height: 18px;
    color: var(--c-sand-card);
    background: var(--c-status-complete);
    border-radius: var(--radius-pill);
}

.vch-resolved[data-vchat-resolved="denied"] {
    color: var(--c-burnished-text);
}

.vch-resolved[data-vchat-resolved="denied"] .vch-resolved-mark {
    background: var(--c-burnished-text);
}

/* The composer and its note: no rule above it — the thread fades into
   the panel's own ground under a card that stays with the reader
   (dc.html :751-753).  The mockup's `z-index: 5` is deliberately NOT
   copied: the /app z-index ladder (style.css `--z-*`, pinned by
   tests/test_app_design_tokens.py::test_z_index_ladder) admits no literal
   rung, and none of its rungs means "inside one panel".  Nothing is lost
   today — the turns are unpositioned, so this sticky footer paints above
   them whatever the source order; a turn that ever takes `position` +
   `z-index` has to take a ladder rung with it. */
.vch-composer {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    padding: 14px 0 var(--s-1);
    background: linear-gradient(to bottom, transparent, var(--c-chat-panel) 22%);
}

/* The note under the thread: a dot that pulses while something is running
   and the sentence beside it (dc.html :752).  The height is reserved even
   when there is nothing to say, so the composer never jumps; the dot is
   drawn only for a note that has words, or it would blink at nothing. */
.vch-note {
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--s-2);
    padding-left: var(--s-1);
    min-height: 1.25em;
    font-size: var(--fs-12-5);
    line-height: normal;
    color: var(--c-ink-muted);
}

.vch-note[data-vchat-note-kind="status"]::before,
.vch-note[data-vchat-note-kind="verifying"]::before,
.vch-note[data-vchat-note-kind="error"]::before {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    background: currentColor;
    border-radius: var(--radius-pill);
    animation: vch-pulse 0.7s ease infinite alternate;
}

.vch-note[data-vchat-note-kind="verifying"] {
    color: var(--c-burnished-text);
}

/* The design's alert red is 3.96:1 as text on the panel; the note keeps
   the brick red it can be read at. */
.vch-note[data-vchat-note-kind="error"] {
    color: var(--c-status-failed);
}

.vch-composer-row {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    padding: var(--s-2) var(--s-2) var(--s-2) var(--s-4);
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--sh-composer);
}

/* The well is the row, so the focus ring goes there — the textarea has
   no edge of its own to carry it. */
.vch-composer-row:focus-within {
    box-shadow: var(--sh-composer), var(--focus-ring);
}

.vch-input {
    flex: 1 1 auto;
    padding: var(--s-2) 0;
    min-height: 3.25rem;
    font-family: var(--font-text);
    font-size: var(--fs-16-5);
    line-height: 1.45;
    color: var(--c-ink);
    background: transparent;
    border: 0;
    border-radius: 0;
    resize: none;
}

/* The mockup's textarea carries `outline: 0` unconditionally (dc.html
   :754) and the ring above is what indicates focus, so the field must
   put the browser's own ring down or a hard UA rectangle paints inside
   a card the pass deliberately left borderless.  `:focus` rather than
   `:focus-visible`: the UA draws that rectangle on a mouse click into
   the field as well as on Tab, and `.vch-composer-row:focus-within`
   lights on both paths, so the designed indicator is never the one
   being removed.  Same composition as the brief composer's borderless
   field inside its ringed card (`.nt-brief:focus`, composer.css). */
.vch-input:focus {
    outline: none;
}

/* Disabled is the whole well fading, not a repaint of the two controls
   inside it: the mockup puts `opacity` on the composer row (dc.html
   :753), bound to a `chatInputOpacity` its own script drops to `.55`
   while a run — or an archived conversation — holds it, and leaves the
   field transparent and Send oxblood.  The stamp comes from the
   controller: a sheet cannot reach up from a descendant's `[disabled]`
   to its parent under this sheet's selector discipline. */
.vch-composer-row[data-vchat-composer-disabled] {
    opacity: 0.55;
}

.vch-input[disabled],
.vch-send[disabled] {
    cursor: not-allowed;
}

/* Send is the arrow alone; `aria-label` is what names it (dc.html :755). */
.vch-send {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--c-sand-card);
    background: var(--c-accent);
    border: 0;
    border-radius: var(--r-10);
    box-shadow: var(--sh-accent);
    transition: background-color var(--t-fast), transform var(--t-fast);
}

.vch-send:hover {
    background: var(--c-accent-hover);
    transform: translateY(-1px);
}

/* The phone bar (narrow only): New chat and Show archived, then the
   picker with the selected conversation's archive control. */
.vch-phone {
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
}

.vch-phone-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-2);
    min-width: 0;
}

/* The picker takes the bar's whole line less the trash beside it: its
   basis is zero, not its option text, so a long title cannot push the
   trash onto a line of its own now that the bar wraps. */
.vch-picker {
    font: inherit;
    font-size: var(--fs-ui);
    flex: 1 1 0;
    min-width: 0;
    padding: var(--s-2) var(--s-3);
    color: var(--c-ink);
    background: var(--c-sand-card);
    border: 1px solid var(--c-sand-rule);
    border-radius: var(--radius);
}

/* The host carries no box of its own: the trash keeps the picker's line
   while the confirm strip, which is `flex: 1 1 100%`, wraps onto a line
   of its own under it with its hairline across the bar (dc.html :613).
   Nested inside a box the strip would have to share the picker's line
   and squeeze the title to a few characters. */
.vch-phone-archive {
    display: contents;
}

/* Motion (dc.html :29-38): the entrance both settled surfaces share, the
   decision card's rise, the pending seal's turn, the verified seal's
   stamp, the halo a running check breathes and the note's pulse.  The two
   repeating pairs run a half-cycle and `alternate` — the same rendered
   loop as the mockup's 0/50/100 keyframes, in the from/to vocabulary this
   sheet is held to.  Every one of them is cut in the reduced-motion block
   at the foot of the sheet. */
@keyframes vch-ease {
    from {
        opacity: 0;
        transform: translateY(6px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes vch-rise {
    from {
        opacity: 0;
        transform: translateY(10px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* The ring is the whole of the checking state's paint, so it is the one
   thing in the rail that must hold still when motion is turned down. */
@keyframes vch-spin {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}

@keyframes vch-stamp {
    from {
        opacity: 0;
        transform: rotate(-8deg) scale(1.35);
    }

    to {
        opacity: 1;
        transform: rotate(-8deg) scale(1);
    }
}

@keyframes vch-breathe {
    from {
        box-shadow: var(--sh-breathe);
    }

    to {
        box-shadow: var(--sh-halo);
    }
}

@keyframes vch-pulse {
    from {
        opacity: 0.35;
    }

    to {
        opacity: 1;
    }
}

/* Phone (§7.8): one column, ≥ 44 px targets, the steps strip wraps,
   the decision buttons stack full-width and the answer footer wraps
   with Copy last. */
@media (max-width: 720px) {
    .vch-body {
        grid-template-columns: minmax(0, 1fr);
    }

    /* The rail stops riding the scroll once it is a bar above the
       thread rather than a column beside it. */
    .vch-list-col {
        position: static;
    }

    /* The panel keeps its surface, softened to fit 368px. */
    .vch-thread {
        padding: var(--s-4);
        border-radius: var(--r-12);
    }

    .vch-new,
    .vch-show-archived,
    .vch-send,
    .vch-retry,
    .vch-talk,
    .vch-picker,
    .vch-mode-btn,
    .vch-rename,
    .vch-toggle-original,
    .vch-confirm-yes,
    .vch-confirm-no {
        min-height: 44px;
    }

    .vch-archive {
        width: 44px;
        height: 44px;
    }

    .vch-send {
        width: 44px;
        height: 44px;
    }

    /* The three controls that only reach 44px by growing their own box:
       a link inside a sentence, and the two words of the confirm. */
    .vch-toggle-original {
        padding-block: var(--s-2);
    }

    .vch-confirm-yes,
    .vch-confirm-no {
        padding-inline: var(--s-3);
    }

    /* The title is the rename control's own label, so the control's
       finger height is the title's line; one rung down stops a 24px
       Caslon title ellipsising at 206px. */
    .vch-rename {
        font-size: var(--fs-title);
    }

    /* Beside the slab the quiet link keeps to one line and to the slab's
       own row, so opening the mode menu does not drag it down the bar. */
    .vch-phone-row > .vch-show-archived {
        align-self: flex-start;
        display: inline-flex;
        align-items: center;
        white-space: nowrap;
    }

    .vch-turns {
        max-height: none;
        overflow-y: visible;
    }

    .vch-sheet {
        padding: var(--s-3) var(--s-4);
    }

    .vch-user {
        max-width: 88%;
    }

    .vch-decision {
        margin-inline: 0;
        padding: var(--s-4);
    }

    /* The checking sheets' own paddings, brought in from the desktop's
       28px so the tab and the rail still clear a 364px column. */
    [data-vchat-draft] > .vch-eyebrow {
        right: var(--s-4);
    }

    [data-vchat-draft] > .vch-text {
        padding: 24px 18px 14px;
    }

    .vch-sealed {
        padding: 20px 18px var(--s-2);
    }

    .vch-sheet-foot {
        padding: 12px 18px 16px;
    }

    /* The rail stays three columns — a 44px node is already a finger —
       and the captions give up their 120px cap to fit beside it. */
    .vch-steps--stacked {
        padding: 16px var(--s-2) 20px;
    }

    .vch-steps--stacked .vch-step-label {
        max-width: 100%;
    }

    .vch-steps {
        gap: var(--s-1) var(--s-2);
    }

    .vch-decision-actions {
        grid-template-columns: minmax(0, 1fr);
    }

    .vch-decide,
    .vch-talk {
        min-height: 44px;
    }

    /* Copy stays the right-pinned chip it is on the desktop (dc.html
       :717) — a full-width bar would read as the answer's main action. */
    .vch-answer-foot .vch-copy {
        flex: none;
        margin-left: auto;
        min-width: 44px;
        min-height: 44px;
    }

    /* The footnote's 40% cap earns its keep beside a 740px meter, not a
       296px one, where it wraps into the meter's own line. */
    .vch-footnote {
        max-width: none;
    }

    /* The corner tab is a second handle on a disclosure the answer's own
       sentence already carries, and a 25px tab is not a finger.  On the
       narrow screen the sentence's link is the handle, at 44px; the tab
       steps aside rather than shrink the reader's target. */
    .vch-original-tab {
        display: none;
    }

    .vch-input {
        min-height: 44px;
    }
}

/* Reduced motion: nothing turns, rises, breathes or pulses.  The seal keeps
   the tilt its stamp ends on, because that is its resting shape and not the
   motion, and every rail lift — the row, the slab, the mode tiles — is cut. */
@media (prefers-reduced-motion: reduce) {
    .vch-sheet--answer,
    .vch-original,
    .vch-decision,
    .vch-seal--pending,
    [data-vchat-seal],
    .vch-note[data-vchat-note-kind]::before,
    .vch-turn,
    .vch-decision-icon,
    .vch-steps:not(.vch-steps--stacked) .vch-step[data-vchat-check="running"],
    .vch-steps--stacked .vch-step[data-vchat-check="running"] .vch-step-glyph,
    .vch-row[data-vchat-status="checking"] .vch-row-glyph {
        animation: none;
    }

    [data-vchat-seal] {
        transform: rotate(-8deg);
    }

    .vch-row,
    .vch-new,
    .vch-new-option,
    .vch-decide,
    .vch-talk,
    .vch-send,
    .vch-original-tab {
        transition: none;
        transform: none;
    }

    /* The lifts live on :hover, which outranks the resting rule above
       however the cascade is ordered — so the hover states are cut by
       name as well, or the movement survives the cut and only its
       easing goes. */
    .vch-row:hover,
    .vch-new:hover,
    .vch-new-option:hover,
    .vch-decide[data-vchat-decide="confirmed"]:hover,
    .vch-decide[data-vchat-decide="denied"]:hover,
    .vch-send:hover {
        transform: none;
    }
}
