/* ============================================================================
   Guided quote builder (waves 2-6) — shared styles for the step components
   under Pages/Quoting/Guided/. Owns every `oc-guided-*` class the steps emit
   (the shell's own chrome is component-isolated in GuidedFlow.razor.css).
   Nothing here hardcodes a color: every value is an --oc-* token, per
   design-system/dark-mode.md.
   ========================================================================== */

/* Segment picker (type step) */
.oc-guided-segments {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 0.75rem;
}

.oc-guided-segment {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 4.25rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface);
    color: var(--oc-text);
    font-family: var(--oc-font-sans);
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    cursor: pointer;
    text-align: center;
    transition: border-color 120ms ease, background 120ms ease;
}

.oc-guided-segment:hover {
    border-color: var(--oc-primary);
    background: var(--oc-surface-hover);
}

.oc-guided-segment.is-selected {
    border-color: var(--oc-primary);
    background: var(--oc-primary-soft);
    color: var(--oc-orange-text);
}

.oc-guided-segment:focus-visible {
    outline: 2px solid var(--oc-primary);
    outline-offset: 2px;
}

/* Count inputs (scale + roles steps) */
.oc-guided-roles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1rem;
}

.oc-guided-count {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.oc-guided-count-label {
    font-weight: 600;
    color: var(--oc-text);
    font-size: var(--oc-fs-sm);
}

.oc-guided-count-hint {
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    min-height: 2.1em;
}

.oc-guided-count-input {
    max-width: 8rem;
}

/* PHONE: these count boxes are plain Bootstrap .form-control / .form-select,
   so their height is Bootstrap's default 38px - under the 44px touch-target
   floor. Measured on iPhone 13: six of them at 128x38 (and one select at
   128x36) across the roles, features and networking steps. The 8rem cap stays
   - a two-digit count does not want a full-width box, and the short side that
   fails the floor is the height, not the width. */
@media (max-width: 480px) {
    .oc-guided-count-input {
        min-height: 44px;
    }
}

/* The site's-last-station answer, said INLINE under the box that just snapped
   back (CEO 2026-08-14: "i dont need a warning"). Muted and quiet on purpose:
   nothing went wrong, nothing needs fixing, and error styling here would be the
   modal's tone in a different shape. It sits inside the .oc-guided-count flex
   column, so it stacks under the input with the column's own gap.

   Capped at 68ch like every other line of prose in this flow — see the measure
   rule in GuidedFlow.razor.css — because it is a sentence, not a control. */
.oc-guided-count-note {
    max-width: 68ch;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
}

.oc-guided-roles-total {
    margin: 1rem 0 0;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
}

/* Payment step: the recommendation line and the PAX inline gate */
.oc-guided-reco-badge {
    margin-left: 0.4rem;
}

.oc-guided-reco-reason {
    margin: 0.9rem 0 0;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
}

.oc-guided-pax-warning {
    margin-top: 1rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--oc-amber-700);
    border-radius: var(--oc-radius-md);
    background: var(--oc-warning-bg);
    color: var(--oc-warning-text);
    font-size: var(--oc-fs-sm);
}

.oc-guided-pax-warning-text {
    margin: 0 0 0.75rem;
    white-space: pre-line; /* the CEO's warning keeps its own line breaks */
}

.oc-guided-pax-ack {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    font-weight: 600;
    cursor: pointer;
}

/* Hardware step: role blocks, banners, totals */
.oc-guided-hw-start {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.75rem;
}

.oc-guided-hw-note {
    margin: 0.75rem 0 0;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
}

.oc-guided-hw-removed {
    margin-bottom: 1rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--oc-amber-700);
    border-radius: var(--oc-radius-md);
    background: var(--oc-warning-bg);
    color: var(--oc-warning-text);
    font-size: var(--oc-fs-sm);
}

.oc-guided-hw-removed-title {
    margin: 0 0 0.4rem;
    font-weight: 600;
}

.oc-guided-hw-removed ul {
    margin: 0;
    padding-left: 1.2rem;
}

.oc-guided-hw-problem {
    margin: 0 0 0.75rem;
    padding: 0.6rem 0.9rem;
    border: 1px solid var(--oc-danger-100);
    border-radius: var(--oc-radius-md);
    background: var(--oc-danger-bg);
    color: var(--oc-danger-text);
    font-size: var(--oc-fs-sm);
}

/* Informational, deliberately NOT the warning amber: the build succeeded and
   nothing was lost — the merchant just already owns some of what it plans. */
.oc-guided-hw-owned {
    margin-bottom: 1rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--oc-info-border);
    border-radius: var(--oc-radius-md);
    background: var(--oc-info-bg);
    color: var(--oc-info-text);
    font-size: var(--oc-fs-sm);
}

.oc-guided-hw-owned-title {
    margin: 0 0 0.4rem;
    font-weight: 600;
}

.oc-guided-hw-owned ul {
    margin: 0;
    padding-left: 1.2rem;
}

.oc-guided-hw-group {
    margin: 1rem 0 0;
}

.oc-guided-hw-group-title {
    margin: 0 0 0.4rem;
    font-size: var(--oc-fs-md);
    color: var(--oc-text);
}

.oc-guided-hw-group-count {
    margin-left: 0.5rem;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
    font-weight: 400;
}

/* ── the hardware money column ───────────────────────────────────────────────
   ONE grid — name | qty | price | actions — shared by every row that carries a
   price: a part line and the totals, on the hardware list AND inside the station
   editor, which renders the same rows through the same classes.

   The previous shape was `display:flex; justify-content:space-between` over
   three content-sized children with no flex-grow, so the free space split into
   two equal gaps and the price landed at `name width + freeSpace/2` — a
   different x-position on every row, which is the "pricing is inconsistently
   placed" the CEO reported.

   Each row declares the columns itself rather than inheriting them from a
   wrapper, so no ancestor is load-bearing: a row keeps its columns wherever the
   markup moves it — which is exactly why the editor dialog could adopt them
   unchanged. Any new priced row joins THIS selector list rather than inventing
   columns of its own. (.oc-guided-swap-option was the third member until the
   inline swap panel it belonged to was replaced by the dialog; the alternatives
   are option cards now, and a card is not a priced row.)

   The actions column carries the info popover and the editor's opener, and is
   sized for two 44px tap targets.
   ------------------------------------------------------------------------- */
.oc-guided-hw-line,
.oc-guided-hw-totals {
    --oc-hw-cols: minmax(0, 1fr) 3.5rem 6.5rem 5.5rem;
    --oc-hw-gap: 0.75rem;
    display: grid;
    grid-template-columns: var(--oc-hw-cols);
    gap: var(--oc-hw-gap);
    align-items: baseline;
}

/* One horizontal inset for the part lines and the totals, which is what puts
   their price columns on a single right edge. The lines list is inset a further
   1px by its own border; the totals reproduce that with a transparent one. */
.oc-guided-hw-line,
.oc-guided-hw-totals {
    padding-left: 0.85rem;
    padding-right: 0.85rem;
}

.oc-guided-hw-lines {
    margin: 0;
    padding: 0;
    list-style: none;
    border: 1px solid var(--oc-border-soft);
    border-radius: var(--oc-radius-md);
}

.oc-guided-hw-line {
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text);
}

.oc-guided-hw-line + .oc-guided-hw-line {
    border-top: 1px solid var(--oc-border-soft);
}

/* Had no rule at all until now: a long product description overflowed its
   share of the row instead of wrapping inside its own column. */
.oc-guided-hw-line-name {
    min-width: 0;
    color: var(--oc-text);
    overflow-wrap: anywhere;
}

/* ── what the part IS, and which MODEL it is ─────────────────────────────────
   The row used to lead with the raw catalog name — "EU POSBANK Edge Widescreen
   Touch AIO 8GB N95" — which is a string a rep has to translate out loud before
   a merchant understands it. The plain part name leads now, and the model
   follows underneath in the muted token: still answerable, no longer competing
   for the eye. Both are BLOCK so they stack inside the name column rather than
   running together, and both inherit the cell's overflow-wrap above.
   ------------------------------------------------------------------------- */
.oc-guided-hw-line-part {
    display: block;
    color: var(--oc-text);
}

.oc-guided-hw-line-model {
    display: block;
    margin-top: 0.1rem;
    font-size: var(--oc-fs-xs);
    color: var(--oc-text-muted);
}

.oc-guided-hw-line-qty {
    text-align: right;
    color: var(--oc-text-muted);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Money: right-aligned in its own column and tabular, so the decimal points
   line up down the page and a $1,199.00 does not shift a $99.00. */
.oc-guided-hw-line-price {
    text-align: right;
    color: var(--oc-text);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.oc-guided-hw-line-actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 0.35rem;
    /* The cell is baseline-aligned with the rest of the row (see the grid above).
       Centred and non-shrinking now that it holds TWO controls: baseline-aligning
       a 44px button against a name that wraps to three lines puts the button at
       the first line's baseline, and a shrinkable info icon loses its tap area to
       a long product name. */
    align-self: center;
}

.oc-guided-hw-line-actions > * {
    flex: 0 0 auto;
}

.oc-guided-hw-totals {
    row-gap: 0.3rem;
    margin: 1.25rem 0 1rem;
    border: 1px solid transparent; /* matches the lines list's border inset */
    font-size: var(--oc-fs-md);
    color: var(--oc-text);
}

/* Label spans name+qty; the amount takes the price column, which is what puts
   it under the prices it sums. */
.oc-guided-hw-totals-label {
    grid-column: 1 / 3;
    color: var(--oc-text-muted);
}

.oc-guided-hw-totals .oc-guided-hw-line-price {
    grid-column: 3;
    font-size: var(--oc-fs-md);
}

/* ── cadence, on the rows that mix it (FL2) ──────────────────────────────────
   The software & licensing group holds monthly licenses AND their one-time
   activation fees, and two rows of "$120.00" say nothing about which is which.
   The word rides UNDER the name rather than beside the price so it cannot push
   the money out of its column — the whole point of the grid above.
   ------------------------------------------------------------------------- */
.oc-guided-hw-line-cadence {
    display: block;
    margin-top: 0.1rem;
    font-size: var(--oc-fs-xs);
    color: var(--oc-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

/* The honest note when licensing could not be priced for this partner. Muted,
   not amber: nothing is broken, nothing here is the rep's to fix, and the note
   exists only so an incomplete monthly figure is never read as the whole one. */
.oc-guided-hw-license-gap {
    margin: 0.25rem 0 1rem;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--oc-border-soft);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface-subtle);
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
    max-width: 70ch;
}

/* Features + networking steps: switch rows */
.oc-guided-features {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

/* Each switch is a CARD, like every other choice in this flow. It was a bare
   row with a browser-default checkbox: a 13x13 box on a phone, and a tap target
   only as tall as the text beside it. The whole label IS the control, so the
   min-height is a real 44px target rather than decoration; the count box and
   the select nested inside are interactive descendants and keep their own
   clicks, which is why they do not toggle the switch. */
.oc-guided-feature {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    min-height: 2.75rem;
    padding: 0.75rem 0.9rem;
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface);
    cursor: pointer;
    transition: border-color 120ms ease, background 120ms ease;
}

/* Hover and focus on the TAPPABLE ones only. The backup-internet row is a div
   holding a select, so lighting the whole card up would promise a tap that does
   nothing. Same treatment the segment cards above use. */
label.oc-guided-feature:hover {
    border-color: var(--oc-primary);
    background: var(--oc-surface-hover);
}

label.oc-guided-feature:focus-within {
    border-color: var(--oc-primary);
}

.oc-guided-feature input[type="checkbox"] {
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.1rem;
    flex: none;
    accent-color: var(--oc-primary);
}

.oc-guided-feature-body {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    /* Takes the slack and yields it: min-width 0 is what lets a long hint wrap
       instead of pushing the price cell off the edge of the card. */
    flex: 1 1 auto;
    min-width: 0;
}

/* The price on a switch (F2.3). Its own end-aligned cell in tabular figures,
   for the same reason the hardware list has one shared money column: a price
   whose left edge moves with the length of the text beside it is the "pricing
   is inconsistently placed" complaint this wave started from. Nothing renders
   at all when the price could not be resolved — see GuidedOptionPrice. */
.oc-guided-option-price {
    display: flex;
    flex: none;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.1rem;
    margin-left: auto;
    padding-left: 0.75rem;
    text-align: right;
}

.oc-guided-option-price-value {
    font-weight: 600;
    color: var(--oc-text);
    font-size: var(--oc-fs-sm);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.oc-guided-option-price-cadence {
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    white-space: nowrap;
}

/* What a figure leaves out, and why a switch carries no figure at all. Both sit
   in the same right-hand cell as the cadence and read like it: this is the
   caption's job, not a second money line. They wrap — an excluded part's name is
   not a number, and pinning it to one line would push the card wide. */
.oc-guided-option-price-excludes {
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    font-style: italic;
    max-width: 12rem;
}

.oc-guided-option-price-note .oc-guided-option-price-cadence {
    white-space: normal;
    font-style: italic;
    max-width: 12rem;
}

.oc-guided-feature-name {
    font-weight: 600;
    color: var(--oc-text);
    font-size: var(--oc-fs-sm);
}

.oc-guided-feature-hint {
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
}

.oc-guided-callerid-opts {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.35rem;
}

.oc-guided-callerid-ip {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--oc-text);
    font-size: var(--oc-fs-sm);
    cursor: pointer;
}

/* Online ordering page */
.oc-guided-oo-list {
    margin: 0 0 1rem;
    padding-left: 1.2rem;
    color: var(--oc-text);
    font-size: var(--oc-fs-sm);
}

.oc-guided-oo-list li {
    margin-bottom: 0.4rem;
}

/* ── Review step: THE MONEY (F2.2) ───────────────────────────────────────────
   The two totals are the largest thing on the page, in the customer's own split
   and the customer's own words. They sit ABOVE the answers list because the
   question a rep has at this point is "what does it cost", and until FL2 this
   page answered it with a build timestamp.

   Two cards rather than a label/value grid: the monthly figure is a different
   KIND of number from the one-time one — one is due, one recurs — and a shared
   two-row grid makes them read as a subtotal and a total of the same thing.
   ------------------------------------------------------------------------- */
.oc-guided-review-money {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--oc-space-3, 12px);
    margin: 0 0 1.25rem;
}

.oc-guided-review-total {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.85rem 1rem;
    border: 1px solid var(--oc-border-soft);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface-subtle);
}

.oc-guided-review-total-label {
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    color: var(--oc-text-muted);
}

.oc-guided-review-total-value {
    font-size: var(--oc-fs-h2);
    font-weight: 700;
    color: var(--oc-text);
    font-variant-numeric: tabular-nums;
}

.oc-guided-review-total-caption {
    font-size: var(--oc-fs-xs);
    color: var(--oc-text-muted);
}

/* Smaller and lighter than the amount it follows — "/mo" is a unit, not part of
   the number, and at full weight it reads as more digits. */
.oc-guided-review-permo {
    font-size: var(--oc-fs-body);
    font-weight: 600;
    color: var(--oc-text-muted);
}

/* The monthly card keeps the same size and weight as the one-time card: the
   customer's page gives both totals equal billing, and quieting this one here
   would coach the rep to under-sell the part of the deal that recurs. Its own
   class exists for the caption and for anything a later phase needs. */
.oc-guided-review-total-monthly {
    border-color: var(--oc-border-soft);
}

.oc-guided-review-covered {
    margin: -0.75rem 0 1.25rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
    max-width: 70ch;
}

/* Demoted from the page's most prominent line to a caption (F2.2). */
.oc-guided-review-built {
    color: var(--oc-text-muted);
}

/* Review step */
.oc-guided-review {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.oc-guided-review-row {
    display: grid;
    grid-template-columns: 11rem 1fr;
    gap: 1rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--oc-border-soft);
    font-size: var(--oc-fs-sm);
}

.oc-guided-review-row dt {
    color: var(--oc-text-muted);
    font-weight: 600;
}

.oc-guided-review-row dd {
    margin: 0;
    color: var(--oc-text);
}

.oc-guided-review-note {
    color: var(--oc-text-muted);
}

.oc-guided-review-warn {
    color: var(--oc-warning-text);
    font-weight: 600;
}

/* Review step: the in-place rebuild under the staleness warning, and the way
   out of the two dead ends (F2.7) — a refused rebuild and a never-built quote
   each get a real control to the station hardware step, where the parts and the
   build's gaps actually live. */
.oc-guided-review-rebuild {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: var(--oc-space-2, 0.5rem);
}

/* OcButton is sized by padding only (see OcButton.razor.css), which lands it
   under the 44px floor. Same targeted lift the editor and confirm surfaces
   take, for the same reason: raising the component's DEFAULT is another PR's
   job. min-height rather than height so it clamps whatever the component's
   scoped CSS computes, without depending on stylesheet order. */
.oc-guided-review-rebuild .oc-btn {
    min-height: 2.75rem;
}

/* A refusal message belongs UNDER the buttons it explains, on its own full row,
   rather than squeezed beside them. */
.oc-guided-review-rebuild .oc-guided-rebuild-error {
    flex: 1 1 100%;
}

/* ── the review gate's verdict (CEO 2026-08-16) ───────────────────────────────
   Finish now checks the quote before it lets go of it. The verdict sits at the
   BOTTOM of the page, immediately above the button that produced it — a refusal
   rendered at the top would scroll out of sight on the one press that needs to
   explain itself.
   -------------------------------------------------------------------------- */
.oc-guided-review-audit {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--oc-border);
}

.oc-guided-review-audit-head {
    margin: 0 0 0.5rem;
    font-weight: 600;
}

/* A refusal. Same danger colour as a refused rebuild — the rep is being told
   "no" by the same page for the same kind of reason. */
.oc-guided-review-audit-fail {
    color: var(--oc-danger, #b3261e);
}

/* Advisories argue, they don't alarm: they never stop the flow, so they must not
   look like the thing that does. */
.oc-guided-review-audit-advice {
    color: var(--oc-text-muted);
}

.oc-guided-review-findings {
    margin: 0 0 0.5rem;
    padding-left: 1.25rem;
}

.oc-guided-review-findings li {
    margin-bottom: 0.35rem;
}

/* The two figures on a broken total, so the rep can see WHICH way it is wrong
   rather than only that it is. Tabular so the pair lines up. */
.oc-guided-review-figures {
    display: block;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
    font-variant-numeric: tabular-nums;
}

/* ── "Recommended for {type}" (CEO 2026-08-16) ────────────────────────────────
   The marker beside a question this kind of business is known to want. It sits
   in the same row as the question's price, so it is sized and coloured to read
   as an ANNOTATION rather than as a second call to action: a badge that competed
   with the switch would be a recommendation that pre-ticks itself by force of
   design, which is precisely what the CEO ruled out.

   ⚠️ --oc-orange-text, not --oc-primary. The brand orange is ~3.5:1 on this
   surface and fails as text at this size; the accessible token exists for
   exactly this and the standing rule is that orange AS TEXT always uses it.
   -------------------------------------------------------------------------- */
.oc-guided-reco-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    white-space: nowrap;
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    color: var(--oc-orange-text);
}

/* Small enough not to shout, large enough to find. */
.oc-guided-reco-badge .bi {
    font-size: 0.7em;
}

/* Payment step: the standing "why we recommend DataCap" case — quieter than the
   warning (it argues, it doesn't alarm). The reasoning leads, the points follow
   as a list (CEO 2026-08-08). Only this wrapper sets a colour: the same lead +
   list fragment also renders INSIDE the amber override gate, so the parts must
   inherit whatever surface they land on rather than fight it. */
.oc-guided-reco-why {
    margin: 0.9rem 0 0;
    font-size: 0.9rem;
    color: var(--oc-text-muted);
    max-width: 60ch;
}

.oc-guided-reco-why-lead {
    margin: 0 0 0.5rem;
    font-weight: 600;
}

.oc-guided-reco-why-points {
    display: grid;
    gap: 0.4rem;
    margin: 0;
    padding-left: 1.25rem;
}

.oc-guided-reco-why-text {
    margin: 0;
    white-space: pre-line; /* the CEO's paragraph keeps its own line breaks */
}

/* In the gate the case sits directly above the acknowledgement checkbox, which
   needs the same breathing room the single warning paragraph used to give it. */
.oc-guided-pax-warning .oc-guided-reco-why-points,
.oc-guided-pax-warning .oc-guided-reco-why-text {
    margin-bottom: 0.75rem;
}

/* Stations step: a failed build says so inline, where the button is. */
.oc-guided-hw-error {
    margin-top: 1rem;
    padding: 0.9rem 1rem;
    border: 1px solid var(--oc-danger, #b3261e);
    border-radius: var(--oc-radius-md, 8px);
    color: var(--oc-danger, #b3261e);
}

/* Combined counts page: the devices row (handhelds + kiosks) sits apart from
   the station roles so the page reads as two questions, not six inputs. */
.oc-guided-devices {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--oc-border);
}

/* Review step: a refused rebuild is an error, not a staleness warning. */
.oc-guided-rebuild-error {
    margin: 0.5rem 0 0;
    color: var(--oc-danger, #b3261e);
    font-size: var(--oc-fs-sm);
}

/* ── the hardware list's per-part controls ────────────────────────────────────
   Two controls per row: the info popover and the editor's opener. The trashcan
   is deliberately NOT here — it lives inside the dialog, because the phone-UX
   prober clicks every overlay opener it finds unattended and skips any control
   with a destructive name, so a destructively-labelled opener would leave the
   whole dialog unaudited.
   -------------------------------------------------------------------------- */

/* The opener's own sizing is PR 11505's and is left alone — it measures a uniform
   61x44 on a phone through the min-height in this file's 640px block. Only the
   disabled state is new, because the opener can now be disabled while a build is
   in flight. */
.oc-guided-hw-change {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: none;
    color: var(--oc-orange-text);
    cursor: pointer;
    font-size: var(--oc-fs-sm);
    padding: 0 0.25rem;
}

.oc-guided-hw-change:hover:not(:disabled) { text-decoration: underline; }
.oc-guided-hw-change:disabled { color: var(--oc-text-muted); cursor: default; }

/* The info icon is the walkthrough's popover primitive, which sizes itself for a
   labelled trigger in a form. In a list row it is an icon on its own, and it has
   to be a real tap target. */
.oc-guided-hw-line-actions .oc-wt-help-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 2.75rem;
    min-width: 2.75rem;
    padding: 0;
}

.oc-guided-hw-group-hint {
    margin-left: 0.5rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
    font-weight: normal;
}

/* ── the station roster ──────────────────────────────────────────────────────
   One role's stations, named, each with its own trashcan, plus an Add station
   button. It REPLACED the station-count number box on both surfaces (CEO
   2026-08-09): a station is a thing, not a quantity, and a count could only ever
   drop the tail of a role. Shared by the counts page and the station editor, so
   the two cannot look like different features.
   -------------------------------------------------------------------------- */
.oc-guided-rosters {
    display: flex;
    flex-direction: column;
    gap: 1.1rem;
}

.oc-guided-roster + .oc-guided-roster {
    margin-top: 1.1rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--oc-border-soft);
}

.oc-guided-roster-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
}

.oc-guided-roster-title {
    margin: 0;
    font-size: var(--oc-fs-md);
    color: var(--oc-text);
}

/* ⚠️ THE TYPED COUNT'S RULES WERE DELETED FROM HERE, 2026-08-14. The roster could
   briefly draw a number field per role (.oc-guided-roster-count-input / -count-unit);
   the CEO removed both the counts page's roster and the shrink confirm the same day
   ("i dont need it to add stations to a list. i just need to be entering a qty." /
   "i dont need a warning"), and the counts page now owns its own boxes through the
   plain .oc-guided-count rules at the top of this file. The roster is the HARDWARE
   step's list, where a number beside a list of parts is a different screen's
   question. Do not re-add a count control here without re-reading GuidedStationRoster.razor.

   How many of this role — a READOUT, not a control. Tabular so a column of them
   lines up. */
.oc-guided-roster-count {
    min-width: 1.75rem;
    padding: 0 0.4rem;
    border-radius: 999px;
    background: var(--oc-surface-hover);
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.oc-guided-roster-hint {
    flex: 1 1 12rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
}

.oc-guided-roster-list {
    list-style: none;
    margin: 0.5rem 0 0;
    padding: 0;
}

.oc-guided-roster-station + .oc-guided-roster-station {
    margin-top: 0.6rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--oc-border-soft);
}

/* Name and trashcan on one line, the trashcan pinned to the right edge with an
   auto margin so a long station name never pushes it off. */
.oc-guided-roster-station-head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.75rem;
}

.oc-guided-roster-station-name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    color: var(--oc-text);
}

.oc-guided-roster-station-head > .oc-guided-se-trash {
    margin-left: auto;
    flex: 0 0 auto;
}

.oc-guided-roster-add { margin-top: 0.6rem; }

/* ── confirm-before-destroy ──────────────────────────────────────────────────
   Removing a station destroys its label, its add-ons and its identity, and can
   switch caller ID off or clear a payment acknowledgement; removing a part can
   take a hand-edited line with it. Both get a confirm that NAMES that first.
   Shape follows AcChart's seat removal — the one confirm-before-destroy pattern
   in this repo.

   z-index sits above .modal (10501 in oc-theme.css) because this confirm can be
   raised from inside the station editor, and a confirm you cannot see is worse
   than no confirm at all.
   -------------------------------------------------------------------------- */
.oc-guided-confirm-scrim {
    position: fixed;
    inset: 0;
    background: rgba(17, 20, 24, 0.45);
    z-index: 10600;
}

/* ⚠️ THE WIDTH IS A GUARD, NOT A LOOK (2026-08-14, round 3).
   This dialog is `position: fixed` INSIDE the app shell's touch scroller, so it is
   one CSS property away from the WebKit trap recorded further down this file: an
   ancestor that grows a `transform`, `filter`, `perspective`, `contain` or a
   `will-change` naming any of them becomes the containing block for fixed
   descendants and starts clipping this dialog for real.

   Measured at 390px on 2026-08-14: main.oc-guided-body's content box is 331px (the
   shell's own clamp(1rem, 2.5vw, 2.5rem) gutter plus .oc-content-inner's 0.75rem),
   while `100vw - 2rem` is 358px. Today nothing establishes that containing block and
   the audit's screenshot shows the dialog whole — but a 358px dialog would lose 27px
   the day one appears, and it would lose it silently. `100vw - 4rem` is 326px, which
   fits inside the gutters the flow already keeps, so the guard holds either way.
   Never `position: absolute` in the scroller instead: this is a modal, and an
   absolutely positioned one lands at a document offset the rep has scrolled away
   from. */
.oc-guided-confirm {
    position: fixed;
    z-index: 10601;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(30rem, calc(100vw - 4rem));
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
    padding: 1.15rem 1.25rem calc(1.15rem + env(safe-area-inset-bottom, 0px));
    border-radius: var(--oc-radius-md, 8px);
    background: var(--oc-surface);
    color: var(--oc-text);
    box-shadow: 0 12px 40px rgba(17, 20, 24, 0.3);
}

.oc-guided-confirm-title {
    margin: 0 0 0.6rem;
    font-size: var(--oc-fs-md);
    color: var(--oc-text);
}

.oc-guided-confirm-body {
    font-size: var(--oc-fs-sm);
    color: var(--oc-text);
}

.oc-guided-confirm-body p { margin: 0 0 0.5rem; }
.oc-guided-confirm-body ul { margin: 0; padding-left: 1.1rem; }
.oc-guided-confirm-body li { margin-bottom: 0.35rem; }

/* The stations a typed shrink would take, named one per line. It is followed by
   more prose, so unlike the trailing consequences list it needs a bottom margin;
   the names are what the rep is actually being asked about, so they carry the
   body's own weight rather than the muted tone of a hint. */
.oc-guided-confirm-body ul.oc-guided-confirm-stations {
    margin: 0 0 0.6rem;
    font-weight: 600;
}

.oc-guided-confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 1rem;
}

/* OcButton is sized by padding only (see OcButton.razor.css), which lands it at
   36px — under the 44px floor. Raising its DEFAULT is another PR's job, so this
   raises it on these two surfaces alone. min-height rather than height on
   purpose: it clamps whatever the component's scoped CSS computes, so it wins
   without depending on which stylesheet loads last. */
.oc-guided-editor .oc-btn,
.oc-guided-confirm .oc-btn {
    min-height: 2.75rem;
}

/* ── the station editor dialog ───────────────────────────────────────────────
   Replaces the inline swap panel (PR 11480), which on a phone opened BELOW all
   the part rows: re-measured against current staging, 0 of its 245px were on
   screen and 310px sat below the fold, so tapping Change produced literally no
   visible result. (It was 34% visible when first measured; PR 11505's taller
   rows pushed it the rest of the way off.) It could also be silently retargeted
   from any other row. Hence: over the list, not below it.
   -------------------------------------------------------------------------- */
.oc-guided-editor .modal-content {
    background: var(--oc-surface);
    color: var(--oc-text);
}

/* Bootstrap's own close button is 32x32 — under the 44px floor, and it is the
   ONE control the phone-UX prober must be able to hit (a dialog it cannot close
   makes it hard-reload the route). Measured at 390px before this rule. */
.oc-guided-editor .btn-close {
    /* border-box explicitly: .btn-close carries its own 0.25em padding, and
       measured at 390px a content-box reading turned 2.75rem into a 60px glyph. */
    box-sizing: border-box;
    width: 2.75rem;
    height: 2.75rem;
    background-size: 1rem;
    margin: 0;
}

.oc-guided-se-lede {
    margin: 0 0 0.75rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
}

.oc-guided-se-role + .oc-guided-se-role {
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--oc-border-soft);
}

.oc-guided-se-role-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem;
}

.oc-guided-se-role-title {
    margin: 0;
    font-size: var(--oc-fs-md);
    color: var(--oc-text);
}

.oc-guided-se-station { margin-top: 0.85rem; }

.oc-guided-se-station-title {
    margin: 0 0 0.4rem;
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    color: var(--oc-text);
}

/* A station's parts hang under its name in the roster, indented just enough to
   read as belonging to it without stealing width from the part rows. */
.oc-guided-se-station-body {
    padding-left: 0.5rem;
    border-left: 2px solid var(--oc-border-soft);
}

/* The part a rep aimed at when they pressed Change on the list. Nothing is
   hidden, so this is a "you are here" marker rather than a disclosure. */
.oc-guided-hw-line.is-targeted {
    background: var(--oc-primary-soft);
    border-radius: var(--oc-radius-md, 8px);
}

/* Choices still in flight, in the one case where saying nothing would be worse:
   a part with alternatives is indistinguishable from a part without until the
   fetch lands. */
.oc-guided-se-loading {
    padding: 0.25rem 0.85rem 0.5rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
}

/* Stations removed from the configuration whose lines are still on the quote
   until the next build. Before this they fell out of the role sections and
   rendered at the very bottom beside the router and the platform fee, which read
   as a glitch. Muted and captioned: they are leaving, not being edited. */
.oc-guided-se-orphans {
    margin-top: 1.25rem;
    padding: 0.75rem 0.85rem;
    border: 1px dashed var(--oc-border);
    border-radius: var(--oc-radius-md, 8px);
    opacity: 0.85;
}

.oc-guided-se-trash {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    background: none;
    border: none;
    border-radius: var(--oc-radius-md, 8px);
    color: var(--oc-danger, #b3261e);
    cursor: pointer;
    font-size: var(--oc-fs-md);
    padding: 0;
}

.oc-guided-se-trash:hover:not(:disabled) { background: var(--oc-surface-hover); }
.oc-guided-se-trash:disabled { color: var(--oc-text-muted); cursor: default; }

/* The all-in-one's hint, in the parts list where its missing trashcan would be. */
.oc-guided-se-hint {
    padding: 0.35rem 0.85rem 0.6rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
}

.oc-guided-se-options { padding: 0.25rem 0.85rem 0.75rem; }

.oc-guided-se-choices-title {
    margin: 0 0 0.5rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
}

.oc-guided-se-choices-title strong { color: var(--oc-text); }

/* Option cards in a dialog: one per row so a product name and its price never
   compete for the same line, and short enough that three fit without scrolling
   the part they belong to off screen. */
.oc-guided-se-options .oc-guided-segments,
.oc-guided-se-add-body .oc-guided-segments {
    grid-template-columns: 1fr;
    gap: 0.4rem;
}

.oc-guided-se-options .oc-guided-segment,
.oc-guided-se-add-body .oc-guided-segment {
    min-height: 2.75rem;
    padding: 0.55rem 0.75rem;
    justify-content: flex-start;
    text-align: left;
}

/* The price on an option card. margin-left:auto rather than a container rule —
   an auto margin beats the cards' justify-content, so cards WITHOUT a price are
   untouched wherever else the component is used. */
.oc-guided-segment-meta {
    margin-left: auto;
    padding-left: 0.75rem;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The catalog's sentence about a product, under the option's label. The card is
   a flex row that centres its items, so a taller name cell keeps the price
   vertically centred against it without any change to the card itself. Cards
   with no Detail render no element at all, so every other call site of this
   component is untouched. */
.oc-guided-segment-detail {
    display: block;
    margin-top: 0.15rem;
    font-size: var(--oc-fs-xs);
    font-weight: 400;
    color: var(--oc-text-muted);
    white-space: normal;
    overflow-wrap: anywhere;
}

.oc-guided-se-choices-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.6rem;
}

/* Add an item: category first, then the item within it. */
.oc-guided-se-add { margin-top: 0.5rem; }

.oc-guided-se-add-open {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 2.75rem;
    padding: 0 0.75rem;
    border: 1px dashed var(--oc-border);
    border-radius: var(--oc-radius-md, 8px);
    background: none;
    color: var(--oc-orange-text);
    cursor: pointer;
    font-size: var(--oc-fs-sm);
}

.oc-guided-se-add-open:hover:not(:disabled) { border-color: var(--oc-primary); }
.oc-guided-se-add-open:disabled { color: var(--oc-text-muted); cursor: default; }

.oc-guided-se-add-body {
    margin-top: 0.6rem;
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-radius-md, 8px);
}

.oc-guided-se-add-step {
    margin: 0 0 0.45rem;
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    color: var(--oc-text);
}

.oc-guided-se-add-step + .oc-guided-segments { margin-bottom: 0.5rem; }

/* Category tabs — CatalogSidebar's tab-then-pick shape, with this file's tokens
   rather than its own qe-* classes, which have no CSS anywhere. */
.oc-guided-se-cats {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: 0.75rem;
}

.oc-guided-se-cat {
    min-height: 2.75rem;
    padding: 0 0.85rem;
    border: 1px solid var(--oc-border);
    border-radius: 999px;
    background: var(--oc-surface);
    color: var(--oc-text);
    cursor: pointer;
    font-size: var(--oc-fs-sm);
}

.oc-guided-se-cat:hover:not(:disabled) { border-color: var(--oc-primary); }

.oc-guided-se-cat.is-selected {
    border-color: var(--oc-primary);
    background: var(--oc-primary-soft);
    color: var(--oc-orange-text);
    font-weight: 600;
}

/* What an answer owes: a part that has been added or put back is not on the
   quote until the list is rebuilt, and the button to do it is right here. */
.oc-guided-se-owed {
    margin: 0 0 1rem;
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-radius-md, 8px);
    background: var(--oc-surface-hover);
    font-size: var(--oc-fs-sm);
}

.oc-guided-se-owed p { margin: 0 0 0.6rem; }

/* THE FOOTER holds the rebuild the answers owe, beside Done. It used to sit at
   the top of the body, and this modal is long enough that a rep reached Done
   having never scrolled back past it — then left believing an added part was on
   the quote when only the ANSWER was saved. Full-width row above the buttons so
   it cannot be mistaken for a caption on Done. */
.oc-guided-se-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
}

.oc-guided-se-foot > .oc-guided-se-owed {
    flex: 1 1 100%;
    margin: 0;
}

/* Hand-edited lines a removal took with it. Same shape as the rebuild's banner
   on the page behind, because it is the same news. */
.oc-guided-se-removed {
    margin: 0 0 1rem;
    padding: 0.75rem 0.85rem;
    border: 1px solid var(--oc-danger, #b3261e);
    border-radius: var(--oc-radius-md, 8px);
    color: var(--oc-danger, #b3261e);
    font-size: var(--oc-fs-sm);
}

.oc-guided-se-removed ul { margin: 0.35rem 0 0; padding-left: 1.1rem; }

/* ── who pays for a part (FL-P) ───────────────────────────────────────────────
   The chip rides UNDER the part's name inside the name cell — the cadence
   word's slot, for the cadence word's reason: it must never push the money out
   of its column. It is a real control, so unlike the cadence it holds the 44px
   floor. The panel opens INLINE below the chip (no overlay: this is one small
   answer, and a dialog-over-a-dialog inside the station editor is the thing
   being avoided), constrained to the name column's width, which at the phone
   breakpoint is the full row. */
.oc-guided-payer {
    display: block;
}

.oc-guided-payer-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    min-height: 2.75rem;
    margin-left: -0.4rem; /* optical: aligns the label with the name above it */
    padding: 0 0.4rem;
    background: none;
    border: none;
    border-radius: var(--oc-radius-md, 8px);
    color: var(--oc-orange-text);
    cursor: pointer;
    font-size: var(--oc-fs-xs);
    font-weight: 400; /* the phone layout bolds the whole name cell */
    text-align: left;
}

.oc-guided-payer-chip:hover:not(:disabled) { background: var(--oc-surface-hover); }
.oc-guided-payer-chip:disabled { color: var(--oc-text-muted); cursor: default; }

/* The split half's static tag: same slot under the name, information only. Flex
   with the floor's min-height so the pair's rows keep matching heights — a
   chip-bearing row and a tag-bearing row otherwise misalign by the padding. */
.oc-guided-payer-tag {
    display: inline-flex;
    align-items: center;
    min-height: 2.75rem;
    font-size: var(--oc-fs-xs);
    font-weight: 400;
    color: var(--oc-text-muted);
}

/* Walkthrough list: the in-progress-only toggle sits with the filter inputs. */
.oc-wt-list-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
    white-space: nowrap;
}

/* ----------------------------------------------------------------------------
   THE PERSISTENT HEADER (F2.5)

   One strip above the progress bar on every step: who the quote is for, what it
   comes to, whether it is saved, and the way out.

   Everything here WRAPS. That is the whole layout rule, and it is not decorative:
   this strip carries four independent groups whose widths are all runtime data —
   a merchant name, two to four currency figures, a timestamp and a button — and
   the last defect on this flow put the primary action 142px off the right edge of
   a 390px phone because a flex group could not wrap. So the strip wraps, the
   money group wraps inside it, and the state group wraps inside that; nothing
   here relies on any of them fitting.
   -------------------------------------------------------------------------- */
.oc-guided-topbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem 1.25rem;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--oc-border-soft);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface-subtle);
}

.oc-guided-topbar-who {
    display: flex;
    flex-direction: column;
    /* min-width:0 is what lets the name ellipsis instead of forcing the strip
       wider than its container — a flex item's default min-width is auto. */
    min-width: 0;
    flex: 1 1 10rem;
}

.oc-guided-topbar-customer {
    font-weight: 700;
    font-size: var(--oc-fs-body);
    color: var(--oc-text);
    /* Truncated in CSS, never in the string: the full name stays in the DOM for a
       screen reader and in the title tooltip. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.oc-guided-topbar-customer-unknown {
    font-weight: 600;
    color: var(--oc-text-muted);
}

.oc-guided-topbar-quoteno {
    font-size: var(--oc-fs-xs);
    color: var(--oc-text-muted);
    font-variant-numeric: tabular-nums;
}

.oc-guided-topbar-money {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 0.35rem 1.25rem;
    min-width: 0;
}

.oc-guided-topbar-figure {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.oc-guided-topbar-figure-label {
    font-size: var(--oc-fs-xs);
    font-weight: 600;
    color: var(--oc-text-muted);
    white-space: nowrap;
}

/* Tabular so the figure does not jitter as the rep adds stations, and nowrap so a
   price never breaks across two lines mid-number. */
.oc-guided-topbar-figure-value {
    font-size: var(--oc-fs-h3);
    font-weight: 700;
    color: var(--oc-text);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* "/mo" is a unit, not more digits — same treatment it gets on the review card. */
.oc-guided-topbar-permo {
    font-size: var(--oc-fs-sm);
    font-weight: 600;
    color: var(--oc-text-muted);
}

/* The partner's two figures are quieter than the merchant's: they are context for
   why the merchant's total reads as it does, not the number the rep is quoting. */
.oc-guided-topbar-figure-partner .oc-guided-topbar-figure-value {
    font-size: var(--oc-fs-body);
    font-weight: 600;
    color: var(--oc-text-muted);
}

/* The annual disclosure under "Total Monthly" (CEO 2026-09-26). Quieter than the
   figure and allowed to WRAP — the figure value above it is nowrap so a price never
   breaks mid-number, but this is a sentence fragment and nowrap would widen the whole
   sticky strip until the state group on the right wrapped off, which is exactly how the
   last phone defect on this flow happened. The max-width keeps it inside its own
   figure's column instead of stretching the flex row. */
.oc-guided-topbar-figure-caption {
    font-size: var(--oc-fs-xs);
    font-weight: 600;
    color: var(--oc-text-muted);
    white-space: normal;
    max-width: 16rem;
}

.oc-guided-topbar-note {
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
}

.oc-guided-topbar-state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto;
}

.oc-guided-saved {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text-muted);
    white-space: nowrap;
}

/* Saved is reassurance, not a status hue: it is true on every step and a green
   tick on every step is decoration the eye stops reading. */
.oc-guided-saved-ico {
    font-size: var(--oc-fs-body);
}

.oc-guided-saved-busy {
    color: var(--oc-text);
}

/* OcButton's ghost variant is Bootstrap's 38px — under the 44px floor this wave
   holds, and this is a permanently-visible control on a phone. Unconditional
   rather than media-scoped: it is a tap target at every width. */
.oc-guided-finish-later {
    min-height: 2.75rem;
}

/* ============================================================================
   Narrow viewports — this file's FIRST @media rules. Kept at the end of the
   file on purpose: every rule below overrides a base rule declared above it,
   and source order is the only thing that guarantees that (these selectors are
   the same specificity as their base rules).

   The only thing that degrades is the hardware money grid, because it is the
   only fixed-column layout here. Everything above is already auto-fill grid or
   wrapping flex.
   ========================================================================== */

/* Tablet / large phone in landscape: still one row per part, just tighter
   number columns so the description keeps most of the width. */
@media (max-width: 768px) {
    .oc-guided-hw-line,
    .oc-guided-hw-totals {
        /* The actions column carries TWO real tap targets now (info + Change), so
           it is 6.5rem rather than 5rem: 44px + 44px + the gap. The money column
           gives up the width, because a price is 7 characters and was never using
           6rem of it. */
        --oc-hw-cols: minmax(0, 1fr) 2.75rem 5.5rem 6.5rem;
        --oc-hw-gap: 0.5rem;
    }
}

/* Phone (the 390px target). Four columns are kept — so the price column is
   still ONE column shared by parts, alternatives and totals — but the
   description takes a row of its own above them, because at this width a 1fr
   name column would wrap every product name to three or four lines. The
   numbers then sit as a right-aligned strip: × 1   $599.00   Change.
   Every cell is placed explicitly: with the name spanning the full row, plain
   auto-placement would drop qty into column 1 and shift the money out of its
   column, which is the exact defect this whole change removes. */
@media (max-width: 640px) {
    .oc-guided-hw-line,
    .oc-guided-hw-totals {
        /* Column 1 holds nothing on the numbers row (the name spans the full row
           below), so the fixed tracks are what has to fit: 40 + 88 + 104 + gaps.
           The actions track is the widest of the three because it is the only one
           holding tap targets. */
        --oc-hw-cols: minmax(0, 1fr) 2.5rem 5.5rem 6.5rem;
        --oc-hw-gap: 0.5rem;
    }

    .oc-guided-hw-line {
        row-gap: 0.2rem;
    }

    .oc-guided-hw-line > .oc-guided-hw-line-name {
        grid-column: 1 / -1;
        font-weight: 600;
    }

    .oc-guided-hw-line-qty {
        grid-column: 2;
    }

    .oc-guided-hw-line-price {
        grid-column: 3;
    }

    .oc-guided-hw-line-actions {
        grid-column: 4;
    }

    /* A bare text button measures 57 × 16 — under the 44px tap target
       PhoneUxChecks flags as a warn. Now that the actions cell exists, giving
       it a real touch height costs one rule. (PR 11505; measured compliant at
       61 × 44 across all eight triggers, so it is untouched here.) */
    .oc-guided-hw-change {
        min-height: 2.75rem;
        padding: 0 0.4rem;
    }

    /* A differs-hint doubles the header's length — its own line, not a
       squeezed trailing fragment. */
    .oc-guided-hw-group-hint {
        display: block;
        margin-left: 0;
    }

    /* The role heading's chips wrap under the title rather than squeezing it:
       heading first, then "× 3 stations" and any differs-hint on their own line. */
    .oc-guided-hw-group-title {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.25rem;
    }

    /* The roster's hint takes its own line at this width — beside a title and a
       count chip it had about eight characters of room. */
    .oc-guided-roster-hint {
        flex: 1 1 100%;
    }

    /* A switch card's price takes a row of its own, still right-aligned — the
       same move the hardware list makes at this width and for the same reason:
       beside "Back-of-house displays" and its hint, a money cell wide enough for
       "One-time, each" leaves the question about eight characters. Right-aligned
       so the prices still share one edge down the page. */
    .oc-guided-feature {
        flex-wrap: wrap;
    }

    .oc-guided-option-price {
        flex-basis: 100%;
        flex-direction: row;
        align-items: baseline;
        justify-content: flex-end;
        gap: 0.4rem;
        margin-left: 0;
        padding-left: 0;
    }

    /* A station's parts give up the indent: at 390px the two-column part grid
       needs every pixel of the modal's width. */
    .oc-guided-se-station-body {
        padding-left: 0;
        border-left: none;
    }

    /* THE DIALOG AT 390px. Bootstrap's own margin is halved so the content gets
       the width, and the body scrolls inside the modal (modal-dialog-scrollable)
       rather than the page scrolling behind it. Nothing here is allowed to be
       wider than the viewport: the parts list inside reuses the same four-column
       grid as the list outside, which has already collapsed by this breakpoint. */
    .oc-guided-editor .modal-dialog {
        margin: 0.5rem;
        max-width: none;
    }

    .oc-guided-editor .modal-body {
        padding: 0.85rem 0.65rem;
    }

    .oc-guided-se-options,
    .oc-guided-se-hint {
        padding-left: 0.65rem;
        padding-right: 0.65rem;
    }

    /* Full-width actions: two buttons side by side at this width leave neither
       enough room to read. */
    .oc-guided-se-choices-actions {
        justify-content: stretch;
    }

    .oc-guided-se-choices-actions > * {
        flex: 1 1 8rem;
    }

        /* THE PERSISTENT HEADER AT 390px. It is on screen for the whole walkthrough,
       so every rule here is about it costing as little vertical space as it can
       while staying readable — a strip that eats the step content is a strip the
       rep resents on every one of nine pages. */

    .oc-guided-topbar {
        padding: 0.6rem 0.7rem;
        gap: 0.4rem 0.75rem;
    }

    /* The name takes the full width rather than competing with the money for a
       row: at this width sharing left the name about ten characters, and a
       truncated merchant name is the one thing this header exists to show. */
    .oc-guided-topbar-who {
        flex: 1 1 100%;
    }

    /* Money on its own row, label above value, wrapping between figures. Two
       figures fit side by side at 390px; a partner-paid pair wraps underneath
       rather than squeezing the merchant's total. */
    .oc-guided-topbar-money {
        flex: 1 1 100%;
        gap: 0.3rem 1rem;
    }

    /* The state group loses its auto margin — pushed right on its own row it left
       "Saved 10:42" hard against the button. Space-between instead: the timestamp
       reads from the left edge, the exit sits at the right, and the group wraps
       rather than pushing the button off the edge, which is exactly how the last
       phone defect on this flow happened. */
    .oc-guided-topbar-state {
        flex: 1 1 100%;
        margin-left: 0;
        justify-content: space-between;
    }
}

/* ── the dialog vs the phone app shell (≤768px, matching OcAppShell) ─────────
   At these widths the app shell is a 100dvh flex column — top bar, scrolling
   .oc-content, bottom tab bar — and this dialog is fixed-position markup INSIDE
   the scrolling content. Left at Bootstrap's inset-0 the dialog spans the whole
   viewport, but it can never PAINT over the shell's bars: the page it lives in
   is a stacking context of its own (the .oc-content-inner fade-in), so no
   z-index inside it escapes, and WebKit additionally clips fixed descendants of
   the touch scroller to the content box. Measured 2026-08-09 at 390x664: panel
   390x664 at top 0, title + Close X hidden under the top bar, Done under the
   bottom tab bar — the dialog could not be dismissed at all.

   So the dialog is laid out in the space the chrome actually leaves: the top
   inset clears the top bar, the bottom inset clears the tab bar (which already
   carries the home-indicator inset itself), and the height comes from the
   insets instead of Bootstrap's 100%. modal-dialog-scrollable keeps the header
   and footer pinned inside that box, so Close X and Done are always on screen
   and only the body scrolls. The bars stay visible and live — this dialog never
   owned them. The 0.5rem dialog margin (≤640 above; Bootstrap's own margin at
   641-768) absorbs a top bar that wraps a few px taller than its 60px min. */
@media (max-width: 768px) {
    /* Both bars carry a safe-area inset on top of their token height (the top
       bar the status bar, the bottom bar the home indicator), so the token alone
       parks the dialog's edges under them in the installed iPhone app. */
    .modal.oc-guided-editor {
        top: calc(var(--oc-shell-topbar-h) + env(safe-area-inset-top));
        bottom: var(--oc-shell-bottombar-total);
        height: auto;
    }

    /* The trashcan confirm rides the same page, so the same clipping applies:
       viewport-centered with a near-viewport max-height, a tall confirm's
       actions landed under the bottom tab bar. Cap it to the space between the
       bars — it stays viewport-centered, and the 1rem slack absorbs the bars'
       4px height difference. (Its scrim still spans the viewport; the visible
       part keeps its tap-to-cancel.) */
    .oc-guided-confirm {
        max-height: calc(100vh - var(--oc-shell-topbar-h) - env(safe-area-inset-top) - var(--oc-shell-bottombar-total) - 1rem);
        max-height: calc(100dvh - var(--oc-shell-topbar-h) - env(safe-area-inset-top) - var(--oc-shell-bottombar-total) - 1rem);
    }
}

/* ── Walkthrough Configuration Mode (the authoring twin of the rep's screens) ─
   Lives in this file, not a new one, on purpose: the authoring bodies render the
   SAME part rows through the SAME money column as the rep's screens, which is what
   makes them "the same screens". Only the chrome that is unique to authoring — the
   scope lens, the precedence notice, the provenance line under a price — is new.
   Every value is an --oc-* token, per design-system/dark-mode.md.
   ------------------------------------------------------------------------- */

/* The scope lens: two independent narrowings, side by side.

   Side by side rather than stacked BECAUSE they are independent. A vertical stack
   reads as a sequence, and a sequence is exactly the wrong mental model here: a
   station-type price beats a business-type one, so neither control is "inside" the
   other. They collapse to one column on a phone, where a row of two is unreadable
   before it is misleading. */
.oc-guided-cfg-lens {
    padding: 0.9rem 1rem;
    border: 1px solid var(--oc-border);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface-subtle);
}

.oc-guided-cfg-lens-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 0.75rem 1.25rem;
}

.oc-guided-cfg-lens-summary {
    margin: 0.75rem 0 0;
    color: var(--oc-text);
    font-size: var(--oc-fs-sm);
    font-weight: 600;
}

/* The API's precedence sentence. Quiet but always present: it is the one thing an
   author has to be told before they set a price, and hiding it behind a disclosure
   would mean the person who most needs it is the one who never opens it. */
.oc-guided-cfg-precedence {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 0.55rem 0 0;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    max-width: 80ch;
}

.oc-guided-cfg-precedence i {
    flex: 0 0 auto;
    margin-top: 0.15rem;
}

/* One part of a station. */
.oc-guided-cfg-slot {
    margin: 1.25rem 0 0;
}

.oc-guided-cfg-slot-title {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0 0 0.4rem;
    font-size: var(--oc-fs-md);
    color: var(--oc-text);
}

/* A part this station type cannot carry is DIMMED, never hidden. Hiding it would
   read as a bug to an author who came looking for it; offering it would let them
   author a price that can never reach a quote. */
.oc-guided-cfg-slot.is-not-carried .oc-guided-cfg-slot-title {
    color: var(--oc-text-muted);
}

.oc-guided-cfg-slot-notice {
    margin: 0;
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--oc-border-soft);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface-subtle);
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
    max-width: 80ch;
}

/* Which answer-driven variant of the part these rows are for. */
.oc-guided-cfg-scope-label {
    margin: 0.6rem 0 0.3rem;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    text-transform: uppercase;
    letter-spacing: 0.02em;
}

.oc-guided-cfg-empty {
    margin: 0 0 0.5rem;
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-sm);
    max-width: 80ch;
}

/* THE POINT OF JOINING THE ROW SELECTORS ABOVE rather than inventing columns: an
   authoring row's price lands on the same right edge as the price of the same part
   on the rep's own screen. The columns come from --oc-hw-cols, declared on this
   selector list beside .oc-guided-hw-line and .oc-guided-hw-totals — the actions
   column is empty here and is where the price editor's control lands next. */
.oc-guided-cfg-offer {
    --oc-hw-cols: minmax(0, 1fr) 3.5rem 6.5rem 5.5rem;
    --oc-hw-gap: 0.75rem;
    display: grid;
    grid-template-columns: var(--oc-hw-cols);
    gap: var(--oc-hw-gap);
    align-items: baseline;
    padding: 0.6rem 0.85rem;
    font-size: var(--oc-fs-sm);
    color: var(--oc-text);
}

.oc-guided-cfg-offer + .oc-guided-cfg-offer {
    border-top: 1px solid var(--oc-border-soft);
}

/* The name cell holds the badge, the SKU and the provenance line, so it stacks. */
.oc-guided-cfg-offer .oc-guided-hw-line-name {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
}

.oc-guided-cfg-offer-sku {
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    font-variant-numeric: tabular-nums;
}

/* WHY THIS PRICE. The resolver's own words for the rule that won, never a
   client-side guess at which narrowing did — the case that matters is a
   station-type rule beating a business-type one, which is exactly the case a guess
   gets backwards. */
.oc-guided-cfg-source {
    color: var(--oc-text-muted);
    font-size: var(--oc-fs-xs);
    max-width: 60ch;
}

.oc-guided-cfg-catalog-price {
    font-variant-numeric: tabular-nums;
}

/* WHAT EACH STATION TYPE IS REALLY QUOTED, when the flat price is quoted nowhere.

   A FLAT LIST, DELIBERATELY. No indent, no connector, no arrow, no ordering by
   "specificity": each row is an independent resolution of the same question, and the
   scopes do not sum and do not inherit. Any of those decorations would draw a
   hierarchy that does not exist, and an author who believes it will price wrongly
   off it. The station type is the label, the money lines up in its own column, and
   the resolver's own words for the rule sit underneath. */
.oc-guided-cfg-role-prices {
    list-style: none;
    margin: 0.35rem 0 0;
    padding: 0.4rem 0.6rem;
    width: 100%;
    border: 1px solid var(--oc-border-soft);
    border-radius: var(--oc-radius-md);
    background: var(--oc-surface-subtle);
}

.oc-guided-cfg-role-prices > li {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: 0.75rem;
    padding: 0.25rem 0;
    font-size: var(--oc-fs-xs);
}

.oc-guided-cfg-role-prices > li + li {
    border-top: 1px solid var(--oc-border-soft);
}

.oc-guided-cfg-role-name {
    color: var(--oc-text);
}

.oc-guided-cfg-role-price {
    text-align: right;
    color: var(--oc-text);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Under the station type, spanning both columns, so the sentence never squeezes the
   money out of alignment with the column above it. */
.oc-guided-cfg-role-source {
    grid-column: 1 / -1;
    color: var(--oc-text-muted);
}

/* "Varies" stands where a single price would, because there ISN'T a single price -
   printing one would be the misreport this whole branch exists to stop. Same weight
   and column as the money it replaces so the row does not reflow. */
.oc-guided-cfg-price-varies {
    color: var(--oc-text-muted);
    font-style: italic;
}

/* A row stored blank where it should be absent: pricing reads blank and absent as
   the same wildcard, storage counts them as different keys, so a duplicate can exist
   and tie. Amber rather than danger - the row is readable and quoting works, it is
   the catalog underneath that needs tidying. */
.oc-guided-cfg-data-warning {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    color: var(--oc-amber-700);
    font-size: var(--oc-fs-xs);
    max-width: 60ch;
}

.oc-guided-cfg-loading {
    display: flex;
    justify-content: center;
    padding: 2.5rem 0;
}

/* Phones: the money keeps its own column, but 5.5rem of empty actions and 3.5rem of
   unused quantity are two thirds of a 390px row. Measured at 390px, a part name plus
   its provenance line needs every pixel the two empty columns were holding. */
@media (max-width: 480px) {
    .oc-guided-cfg-offer {
        --oc-hw-cols: minmax(0, 1fr) 6.5rem;
        --oc-hw-gap: 0.5rem;
        padding-left: 0.6rem;
        padding-right: 0.6rem;
    }

    .oc-guided-cfg-offer .oc-guided-hw-line-actions {
        display: none;
    }

    .oc-guided-cfg-lens {
        padding: 0.75rem 0.7rem;
    }
}
