.deal-side-nav-group-title {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 8px;
    padding-left: 8px;
    color: #000000;
}

.deal-side-nav-link {
    border: 0;
    background: transparent;
    color: #334155;
    font-weight: 600;
    margin-bottom: 8px;
    display: block;
    width: 100%;
    text-align: left;
    padding: 0;
    cursor: pointer;
}

.deal-side-nav-link.is-active {
    color: #2563eb;
}

/*
 * A tab whose pane is empty — today only the four POC sections when `poc_required` is No, applied
 * by `resources/js/features/deal/pocNav.js`. Still readable, plainly unavailable, and inert to the
 * mouse; the keyboard is handled by `tabindex="-1"` and `deal-modal.js` refuses the switch.
 */
.deal-side-nav-link.is-disabled {
    color: #94a3b8;
    opacity: 0.55;
    pointer-events: none;
}

/*
 * ONE bounded modal; each column scrolls on its own.
 *
 * THE PROBLEM. `.modal-body` is a flex row, so its height is whatever its TALLEST column needs —
 * and that column is the side nav, which is 16 entries plus 5 group titles and has no cap
 * (`#cardModalTabMenuMultiple` carries `overflow-y: auto` in custom.css but nothing to overflow
 * against). It therefore ran past the viewport, `.modal-content` grew its own scrollbar, and the
 * content column — now showing ONE tab instead of the whole form — was left as a short card
 * floating in a screen and a half of empty background.
 *
 * THE FIX. Give the row a definite height and let all three columns scroll inside it. Definite
 * and not `max-height`: a percentage height on a child resolves to `auto` against a parent that
 * only has a maximum, so the columns would stretch past the cap instead of scrolling within it.
 *
 * This is the standard shape for a nav-plus-detail modal — the frame stays put, each pane moves
 * independently, and nothing outside the modal scrolls. The nav gets its own scrollbar when the
 * entry list is longer than the frame, which is what the empty space was really telling us.
 *
 * SCOPED TO ≥1024px on purpose. Below that, custom.css stacks the modal — the nav collapses to a
 * horizontal strip of inline-block entries and `.card-left-panel` is capped at `max-height: 50%`
 * with its own scroll (`@media (max-width: 1023px)`). Three side-by-side scrolling columns is not
 * that layout, so the breakpoint here matches theirs and mobile is left exactly as it was.
 */
@media (min-width: 1024px) {
    #dealFullDetailsWrapper .modal-body {
        height: 70vh;
        /* Past this the frame stops growing with the screen: a nav column two thousand pixels tall
           is harder to scan than a scrolling one, and the fields would sit alone in the middle
           again. */
        max-height: 680px;
        align-items: stretch;
    }

    /* The maximise toggle (app.js) takes the dialog to the full window — let the body use it. */
    .is-maximized #dealFullDetailsWrapper .modal-body {
        height: 100%;
        max-height: none;
    }

    /* `min-height: 0` is load-bearing: a flex item defaults to `min-height: auto`, which refuses
       to shrink below its content and so cancels the overflow before it can scroll. */
    #dealFullDetailsWrapper #cardModalTabMenuMultiple {
        min-height: 0;
        max-height: 100%;
        overflow-y: auto;
    }

    /*
     * ⚠️ THE RIGHT PANEL IS NO LONGER THE SCROLLER — its inner content region is, and the swap is
     * what pins the deal's action buttons to the foot of the column.
     *
     * While the panel itself scrolled, everything in it scrolled: the buttons sat wherever the
     * content above them happened to end, which on a deal with a long task title was somewhere in
     * the middle of the column and on a short one left a screenful of white space beneath them.
     * Moving the overflow one level in puts `.deal-right-panel-actions` OUTSIDE the scrolling box,
     * so it cannot move however tall the card above it grows.
     *
     * `overflow: hidden` and not `visible`: the panel is the flex item that has to be capped, and a
     * visible overflow here lets the inner region grow past the cap instead of scrolling within it.
     */
    #dealFullDetailsWrapper .card-right-panel {
        min-height: 0;
        max-height: 100%;
        overflow: hidden;
    }

    /* `height: 100%` so the column below can divide it; `min-height: 0` so the division can shrink
       below its content, which is what allows the scroll at all. */
    #dealFullDetailsWrapper .deal-right-panel {
        height: 100%;
        min-height: 0;
    }

    /*
     * ONE scroller per column. `.card-left-panel` also carries `overflow-y: auto` (custom-ui.css),
     * and it sits inside the container that `deal-modal.js` resets on every tab switch — nested,
     * that is two scrollbars on one column and a tab that opens half-scrolled.
     */
    #dealFullDetailsWrapper .card-left-panel {
        overflow: visible;
    }

    /* 100% of the bounded row above, NOT of the viewport: the row is what it must fit inside. */
    #dealFullDetailsWrapper .deal-sections-container {
        max-height: 100%;
        min-height: 0;
    }

    /*
     * Shrink the SHELL to the bounded body, or the saving above is invisible.
     *
     * The shared modal wrapper (modals/common-modal-wrapper.blade.php) is built to fill the
     * window: `.modal-dialog` is `height: 100vh`, `#commonModalForm` is `height: 100% !important`
     * for the lg/xl dialogs, and `.modal-content` is `height: 100%` again. Capping the deal's own
     * `.modal-body` therefore did not shorten the popup at all — it just left the difference as a
     * blank white band under the three columns.
     *
     * `:has()` because the deal markup is INJECTED into that shared shell — `#dealFullDetailsWrapper`
     * is a descendant, so there is no ancestor selector and no class of our own to hang this on.
     * The alternative was tagging the dialog from JS, which means remembering to untag it when the
     * next modal reuses the same shell; a browser that lacks `:has()` simply keeps today's
     * behaviour instead of inheriting that bug.
     *
     * The dialog itself KEEPS its 100vh — it is the flex parent whose `justify-content: center`
     * is what centres the now-shorter modal in the window.
     */
    .card-modal .modal-dialog:has(#dealFullDetailsWrapper) > form {
        height: auto !important;
    }

    .card-modal .modal-dialog:has(#dealFullDetailsWrapper) .modal-content {
        height: auto;
    }
}

.deal-sections-container {
    max-height: 100vh;
    overflow-y: auto;
    scroll-behavior: smooth;
    padding-right: 18px;
    position: relative;
}

/*
 * Every tab starts at the same distance under the breadcrumb.
 *
 * The panes open one at a time now, so the first element of each is the first thing the eye lands
 * on — and they do not agree: most sections open on a `.form-row` or a `.card` with no top margin,
 * while `google-registration` and `ms-support` open on an `<h6 class="mt-3">` group heading. That
 * 1rem is the whole discrepancy, and it moved the fields on those two tabs visibly lower than on
 * Deal info.
 *
 * Zeroed here rather than by dropping `mt-3` in those components, because the SAME heading style
 * repeats further down each section, where the top margin is exactly what separates one group of
 * fields from the last. Only the first one should be flush, and `:first-child` says that once for
 * every section — including any added later — instead of asking each one to remember.
 *
 * Both the Blade island container and the section div React renders inside it match `[id^=…]`;
 * the outer match is a no-op, since its only child is the inner div.
 *
 * `!important` is not a specificity fight that can be won any other way: Bootstrap generates every
 * spacing utility as `margin-top: 1rem !important` (vendor/css/bootstrap/bootstrap.min.css), and
 * no selector outranks that without matching it.
 */
.deal-sections-container [id^="section-"] > :first-child {
    margin-top: 0 !important;
}

/*
 * THE SECTION CARDS ARE TIGHTER THAN THE APP'S DEFAULT CARD, and only inside this modal.
 *
 * A `.card-body` is `padding: 1.25rem` from Bootstrap with `padding-left/right: 25px` layered on
 * by the theme — 20px/25px, which is right for a page-width card carrying one thing and wrong
 * here: the modal shows ONE pane at a time inside an already-inset column, so every tab paid for
 * that inset twice and the fields sat in a narrow channel down the middle.
 *
 * Scoped to `.deal-sections-container`, which exists only in full-details.blade.php — every other
 * card in the application keeps the padding it has. Both axes are named explicitly because the
 * theme sets the horizontal pair on its own; a shorthand alone would be overridden on those two
 * sides and silently keep 25px.
 */
.deal-sections-container .card > .card-body {
    padding: 14px 16px;
}

/*
 * A heading that OPENS a card sits flush with it.
 *
 * Same reasoning as the `:first-child` rule above, one level deeper: the section titles are
 * `<h6 class="mt-3">`, and that 1rem is what separates one group of fields from the previous one
 * further down a section. At the TOP of a card body it is not separating anything — it just adds
 * to the padding, which is what made these cards read as loose even after the padding came down.
 *
 * `!important` for the same reason the rule above needs it: Bootstrap's spacing utilities are all
 * `margin-top: 1rem !important`, and no selector outranks that without matching it.
 */
.deal-sections-container .card > .card-body > :first-child {
    margin-top: 0 !important;
}

/*
 * A CARD WHOSE FIELDS ALL RENDERED NOTHING SAYS SO, instead of showing a title over white space.
 *
 * Every section card is `<h6>` + `<div class="form-row">…fields…</div>` (raw in
 * CustomerRequirements, via the shared `Card` in Documents / Proposal / DealCards), and a `Field`
 * returns null when the field matrix does not apply it at this stage. So a whole group can hide
 * and leave the row an element with no children at all — which is what `:empty` matches, since
 * React renders no whitespace text nodes for null children or JSX comments.
 *
 * ⚠️ THE SELECTOR IS DELIBERATELY NARROW: `:nth-child(2):last-child` means the card body holds
 * the heading and this row and nothing else. A card with a table, a list or a second populated
 * row therefore never gets the notice, so it cannot appear beneath content that plainly exists.
 *
 * ⚠️ IT IS GENERATED CONTENT, WHICH IS A TRADE. One rule covers all ~20 tabs including any added
 * later, and no component has to remember; the cost is that the sentence is not selectable and
 * cannot be translated. If it ever needs to be either, the place to move it is the shared `Card`
 * plus CustomerRequirements — four files for the same result in real DOM.
 */
.deal-sections-container .card > .card-body > .form-row:empty:nth-child(2):last-child::after {
    content: "No data available";
    display: block;
    width: 100%;
    padding: 18px 0 6px;
    text-align: center;
    font-size: var(--text-12, 12px);
    color: var(--slate-400, #94a3b8);
}

/*
 * NO CARET RULE HERE — deliberately. `resources/css/shared/select2.css` draws the multi-select
 * caret for every island, sized and positioned to match the bootstrap theme's single-select arrow,
 * and flips it while the panel is open.
 *
 * This file used to draw its own copy under `#dealFullDetailsWrapper`, and an ID outranks the
 * shared rule's classes, which broke it in two visible ways:
 *
 *   · the triangle was 6px where every other dropdown's is 0.25rem, so the deal form's carets were
 *     plainly bigger than the rest of the app's;
 *   · it set `border-top` but never `border-bottom`, so on open the shared flip rule's
 *     `border-bottom: .25rem solid #6c757d` landed while this rule's coloured `border-top`
 *     survived — a box with BOTH edges coloured, which renders as TWO arrows, one pointing down
 *     and one up.
 *
 * Every deal section is a React island now, so `.ui-island` covers all of them. If a Blade
 * multi-select is ever added back to this modal, give it the caret in the shared file rather than
 * re-creating a higher-specificity copy here.
 *
 * That copy was resurrected once already: `sku-tab-ui`'s merge (3aed1482) reinstated it directly
 * under this comment. If it reappears in a conflict here, dropping it is the resolution.
 */
#dealFullDetailsWrapper .select2-selection--multiple .select2-search__field {
    cursor: pointer;
}

/* =========================================================================
 * THE DEAL MODAL'S OWN HEADER ROW, FOOTER AND CURRENT-TASK CARD
 * (resources/js/features/deal/sections/DealTitleBar.jsx,
 *  DealTaskActions.jsx, DealCurrentTask.jsx)
 * ========================================================================= */

/*
 * Hide the SHARED modal header for this modal only.
 *
 * `modals/common-modal-wrapper.blade.php` renders one `.modal-header` for every modal in the
 * application: a title string plus the maximise and close buttons. The deal form needs a stage
 * badge and three figures up there, and there is no way to put them in a shell every other modal
 * shares — so the deal renders its own bar as the first child of its body, and this rule takes
 * the shared one out of the layout.
 *
 * `:has()` for the same reason the shell rules above use it: the deal markup is INJECTED into
 * that shared shell, so `#dealFullDetailsWrapper` is a descendant and there is no ancestor
 * selector to hang this on. A browser without `:has()` simply keeps both headers — visibly odd,
 * never broken.
 *
 * `!important` is NOT a specificity fight that could be won another way: `events.js` shows the
 * header with jQuery `.show()` on every modal open, which writes `display: block` as an INLINE
 * style, and no selector outranks that. It is also what makes the hiding self-reverting — the
 * next modal's `.show()` still runs, and its own header is not inside a dialog matching this
 * selector, so nothing has to remember to undo anything.
 */
.card-modal .modal-dialog:has(#dealFullDetailsWrapper) #commonModalHeader {
    display: none !important;
}

.deal-titlebar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 20px;
    border-bottom: 1px solid var(--slate-200);
    background-color: var(--color-white);
    /* Chrome: it must not shrink or scroll while the columns under it do. */
    flex: 0 0 auto;
}

.deal-titlebar-title {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: var(--slate-900);
    white-space: nowrap;
}

/*
 * NO CHIP CLASSES OF ITS OWN. The header's chip is the customer's commercial badge, and it renders
 * as `status-pill status-pill-<class>` — the same pair the customers list and the customer card use
 * for the same badge. A `.deal-titlebar-chip` with its own `wf-chip-amber/green/red/slate` palette
 * lived here while the chip showed the deal STAGE; it went with the stage, rather than being left
 * as a second way to colour a Lead.
 */

/*
 * ⚠️ THE TITLE BAR'S FIGURE STYLES ARE GONE (2026-08-24) — `.deal-titlebar-figures`,
 * `.deal-titlebar-figure`, its label/value pair, `.deal-titlebar-margin` and `.deal-titlebar-pct`.
 *
 * The bar carries no figures any more: Total Deal Value and Size moved to the right-hand rail
 * (`.deal-rail-*` below). Deleted rather than left behind, because an unused rule with a plausible
 * name is what gets re-adopted by the next thing that needs a figure in a header.
 *
 * ⚠ CORRECTION (epic merge): this note used to say Total Margin "appeared in neither Mazen's
 * mockup nor any `Deal_US_*` story". Half right. The mockup carries no deal-level margin — true —
 * but Deal_US_020 §1 defines Total Margin After Discount and Original Margin as header fields and
 * AC 10 makes them QC-verifiable. They now render in the RAIL, beside Total Deal Value, which is
 * what §1's "next to Total Deal Value" resolves to once the value itself lives there. See
 * `.deal-rail-sub` below and the note in `DealHeader.jsx`.
 */

.deal-titlebar-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 6px;
    border: 0;
    background: transparent;
    color: var(--slate-400);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: color 0.15s ease, background-color 0.15s ease;
}

.deal-titlebar-icon-btn:hover {
    color: var(--slate-600);
    background-color: var(--slate-100);
}

/* --- the right panel's two regions -------------------------------------- */

/*
 * The scrolling half. Outside the ≥1024px media query it is an ordinary block with no cap, which
 * is deliberate: below that breakpoint custom.css stacks the modal and the panel is capped at
 * `max-height: 50%` with its own scroll, so a second scroller here would be two scrollbars on one
 * stacked column.
 */
.deal-right-panel-scroll {
    min-height: 0;
}

/*
 * ⚠️ `.deal-right-panel-actions` USED TO BE STYLED HERE and its rules are gone with the markup
 * (AI-10 rule 1, 2026-08-23). The deal's own actions — Save progress, Mark as Won, Mark as Lost —
 * moved out of the right panel and into `.deal-form-footer-record` at the bottom of the form, so
 * the panel is now a pure scrolling read surface with nothing pinned beneath it.
 *
 * The class-rewrite warning that lived here has moved with the button, to `agreement-model.js`'s
 * `updateDealActionButton`, which is where it actually applies.
 */

@media (min-width: 1024px) {
    .deal-right-panel-scroll {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        /* Room for the scrollbar so the card's right edge is not sliced by it. */
        padding-right: 4px;
    }
}

/* --- the footer ---------------------------------------------------------- */

/*
 * ONE ROW: the required-field stepper on the left, the workflow's actions on the right.
 *
 * The deal's own buttons are NOT here — they are pinned to the foot of the right panel. The split
 * is by whose action it is: the panel's act on the record and never move, this row's are the
 * engine's and change with every stage.
 *
 * Wraps rather than compressing, because the workflow half can grow an outcome panel (a comment
 * box, a signatory field, a reassign picker) and a squeezed stepper is worse than a taller row.
 *
 * ⚠️ THE ROW MUST SURVIVE BOTH HALVES BEING ABSENT — and both routinely are. The stepper hides
 * itself when the stage has no mandatory fields, and the workflow island renders nothing for a
 * viewer with no eligible action; on a won deal viewed by a colleague, neither exists. `:empty`
 * below collapses the row entirely rather than leaving a bordered 62px band of nothing.
 */
.deal-form-footer {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 12px;
    padding: 12px 20px;
    border-top: 1px solid var(--slate-200);
    background-color: var(--color-white);
}

/*
 * ⚠️ NOT `:empty` — the row always HAS children (the stepper div is present but display:none, and
 * the island div is present but renders nothing), so `:empty` never matches it. `:not(:has(...))`
 * asks the question that is actually being asked: is anything in here visible? A browser without
 * `:has()` keeps today's behaviour, which is an empty bordered strip: cosmetic, not broken.
 */
.deal-form-footer:not(:has(#deal-required-progress[style*="flex"])):not(:has(.deal-footer-actions)):not(:has(.deal-form-footer-record > *)) {
    display: none;
}

/* The stepper, now the row's left half. It carries `align-items-center gap-8` from Blade and
   `display: flex` from `requiredProgress.js`; everything else it used to draw (its own padding,
   border and background) is the row's job now. */
#deal-required-progress {
    flex: 0 0 auto;
}

/*
 * The action notice's own line, above the stepper and the workflow buttons.
 *
 * `DealTaskActions` portals its success/error banner here (see that file) because the workflow slot
 * is only the row's right-hand half: a banner rendered there was squeezed into a third of the width
 * with the stepper still beside it, and a sentence like "No manager found up the chain to escalate
 * to" wrapped over three lines.
 *
 * `flex-basis: 100%` claims a whole line of the wrapping row; `order: -1` puts that line FIRST.
 * Without the order it would land between the stepper's line and the buttons' line and split a
 * single row into three.
 */
#deal-footer-notice {
    flex: 1 1 100%;
    order: -1;
}

/*
 * Empty is the normal state — there is no message until an action is taken. Hidden rather than
 * merely zero-height so it contributes no `gap` either, and so it cannot hold the footer row open
 * on its own (the row's collapse rule asks about the stepper and the action bar, not about this).
 */
#deal-footer-notice:empty {
    display: none;
}

/*
 * With the banner portalled out, this wrapper is genuinely empty whenever the viewer has a message
 * but no eligible action left — which is the ordinary state right after reassigning a task away.
 * Without this it would still claim its 320px flex basis and push the stepper off centre.
 */
.deal-footer-actions:empty {
    display: none;
}

/*
 * SIZED TO ITS BUTTONS, so the engine's actions sit BESIDE the record's own instead of across a gap
 * from them.
 *
 * It was `flex: 1 1 320px` — elastic, to give the outcome panels room. The cost was the row it
 * produced when no panel was open: `.deal-form-footer-record` is pushed right by its `margin-left:
 * auto`, and this slot then claimed the remaining 320px+ AFTER it, so Save progress and Change
 * status ended up stranded in the middle of the footer with Registered / Return for correction
 * right-aligned far to their right. Two groups of buttons that act on one record, reading as two
 * unrelated clusters.
 *
 * `0 0 auto` collapses that: the four buttons now form one right-aligned cluster.
 */
.deal-form-footer-workflow {
    flex: 0 0 auto;
    display: flex;
    justify-content: flex-end;
}

/*
 * …EXCEPT while a panel is open. `Panel` (`.wf-fieldset`) is the comment box, the reassign picker
 * and the signatory field, and it renders INSIDE this slot, underneath the buttons — at button
 * width a textarea would be unusable. `:has()` gives it the slack back for exactly as long as it
 * needs it, and a browser without `:has()` keeps a narrow-but-working panel.
 */
.deal-form-footer-workflow:has(.wf-fieldset) {
    flex: 1 1 320px;
}

/*
 * The island renders nothing when the viewer cannot act. Without this the empty flex child still
 * claims its 320px basis, and the deal's own buttons sit off to the left of a gap.
 *
 * `:empty` is exact here rather than approximate: React mounts into this div and returning null
 * leaves it with no child nodes at all — not a comment, not whitespace, which are the two things
 * that would defeat the selector.
 */
.deal-form-footer-workflow:empty {
    display: none;
}

/*
 * THE RECORD'S OWN ACTIONS — Save progress and Change status (AI-10 rule 1).
 *
 * A ROW, not the full-width stack they wore in the old right-hand column: that stack existed
 * because the column was ~260px wide, and this footer is the width of the modal. `flex: 0 0 auto`
 * so the group takes only what its buttons need and the workflow slot beside it keeps the slack —
 * the engine's buttons are the ones that change in number, so they get the elastic half.
 *
 * `margin-left: auto` pushes the pair right when the required-field stepper is hidden, which is
 * most stages; with the stepper visible, `justify-content: space-between` on the row has already
 * done it and this is a no-op.
 */
.deal-form-footer-record {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
}

/* Empty whenever the form is frozen AND neither terminal action is offered — a colleague reading
   a won deal. Collapsed so it contributes no `gap`, and so the footer's own collapse rule above
   can tell "nothing to show" from "nothing rendered". */
.deal-form-footer-record:empty {
    display: none;
}

/*
 * ⚠️ THE STATUS MENU OPENS UPWARD. It lives in the footer, at the bottom of a modal that is
 * itself near the bottom of the viewport, so Bootstrap's default downward `.dropdown-menu`
 * rendered at y=838 in an 833px viewport — open, correct, and entirely off screen. Measured in
 * the browser 2026-08-24.
 *
 * `bottom: 100%` rather than a `.dropup` class on the wrapper: the direction is a property of
 * WHERE THIS MENU IS, not a choice the template should be able to get wrong, and `agreement-model.js`
 * rewrites class lists in this area (see `updateDealActionButton`).
 */
.deal-form-footer-record .dropdown-menu {
    top: auto;
    bottom: 100%;
    margin-bottom: 4px;
}

/* ============================================================================
 * THE RIGHT-HAND RAIL — laid out to Mazen's deal-form mockup (2026-08-24).
 *
 * A stack of labelled blocks: a small uppercase micro-label over its value. The rail carried
 * three different heading treatments before (a 1rem bold paragraph, a `text-xs` span, an `h4`),
 * which is why the customer id read as a stray number — nothing said what it was.
 *
 * Sizes are the mockup's: 9.5px label at .07em tracking, 14px/700 value. They are written here
 * rather than as utility classes because they are this rail's typography, not the app's.
 * ========================================================================== */
.deal-rail-block {
    margin-bottom: 16px;
}

/* The pair sits on one line — Size is the value beside it, banded. */
.deal-rail-row {
    display: flex;
    gap: 14px;
    margin-bottom: 18px;
}

/* `min-width: 0` so a long money string ellipsises inside the column instead of pushing the Size
   pill out of the 290px rail. */
.deal-rail-col {
    flex: 1 1 0;
    min-width: 0;
}

.deal-rail-label {
    font-size: 9.5px;
    font-weight: 400;
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--slate-400);
    margin-bottom: 4px;
}

.deal-rail-value {
    font-size: 14px;
    font-weight: 700;
    line-height: 1.35;
    color: var(--slate-900);
    word-break: break-word;
}

/* Tabular figures so ids and money line up down the column rather than jittering per digit. */
.deal-rail-num {
    font-variant-numeric: tabular-nums;
}

/*
 * The percentage under a rail money figure — Total margin and Original margin each carry one
 * (Deal_US_020 §1 rule 7). Secondary to the amount above it: same column, smaller and muted, so the
 * pair reads as one figure rather than two competing ones.
 */
.deal-rail-sub {
    font-size: 11px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--slate-400);
    margin-top: 2px;
}

/* An empty value block must not collapse to nothing and shunt the label into the next one. */
.deal-rail-value:empty::after {
    content: "\00a0";
}

/*
 * THE PAGE BEHIND THE MODAL DOES NOT ALSO SHOW ITS BUTTONS (AI-10 rule 1, second half).
 *
 * `DealActiveTasksPanel` on the deal page renders a `TaskActionBar` per open task, and the deal
 * modal renders one for the SAME task in its footer. Both were on screen at once — the review
 * frame caught four identical buttons in the modal and four more visible on the page beside it.
 *
 * Both surfaces are kept, by decision: the page list is how you act without opening the modal.
 * They simply must not be simultaneous, and "the modal is open" is exactly what Bootstrap's
 * `body.modal-open` already states — so this needs no new state, no JS and no prop threading.
 *
 * SCOPED TO THE ACTION BAR, not the panel. The reader keeps the task's name, stage, assignee and
 * SLA behind the dialog; it is only the duplicate CONTROLS that go.
 */
body.modal-open [data-wf-island="deal-active-tasks"] .wf-task-action-bar {
    display: none;
}

.deal-footer-actions {
    width: 100%;
}

/* TaskActionBar lays its buttons out in a flex row; in a footer that row belongs on the right. */
.deal-footer-actions .d-flex.flex-wrap {
    justify-content: flex-end;
}

/* --- the right panel's Current Task card -------------------------------- */

/*
 * The block carries the gap on BOTH sides now.
 *
 * `margin-bottom` rather than padding on the scroller, because the card is the last thing in the
 * right rail: without it the white card ended flush against the bottom of the scrolling column,
 * which reads as a card that has been cut off rather than one that has finished. On the panel's
 * own margin it would also disappear the moment anything is added below this card, while here it
 * travels with the thing it is spacing.
 */
.deal-task-block {
    margin-top: 4px;
    margin-bottom: 16px;
}

.deal-task-title {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-bottom: 10px;
    font-size: 9px; /* below --text-xs (10px): the card's own smallest step, matching the face tooltips */
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--slate-900);
}

.deal-task-card {
    padding: 14px;
    background-color: var(--color-white);
    border: 1px solid var(--slate-200);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-primary);
}

/*
 * The 9px name tooltip on a collaborator face (requested design).
 *
 * ⚠️ NOT `title`. A native tooltip cannot be sized, positioned or themed, and the design asks
 * for 9px — so the name rides on `data-name` and this draws it. `pointer-events: none` keeps it
 * from eating the hover that summoned it, and the stack sits high enough in the card that the
 * bubble opens upward into the row above rather than off the panel.
 */
.deal-task-face {
    position: relative;
    display: inline-flex;
}

.deal-task-face::after {
    content: attr(data-name);
    position: absolute;
    bottom: calc(100% + 6px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    padding: 3px 6px;
    font-size: 9px;
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    color: var(--color-white, #ffffff);
    background-color: var(--slate-900, #0f172a);
    border-radius: var(--radius-sm, 4px);
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.12s;
    pointer-events: none;
}

.deal-task-face:hover::after,
.deal-task-face:focus-visible::after {
    opacity: 1;
    visibility: visible;
}

/*
 * `More details` — a link in a row of labels, so it carries the label's size and nothing a
 * button would bring.
 *
 * ⚠️ EVERY RESET HERE IS LOAD-BEARING, because this used to be a `.btn.btn-link`: the border and
 * background are the button's, `box-shadow: none` kills the `.btn:focus` ring that Bootstrap
 * leaves on screen after a click (the "border" this was reported as), and `text-decoration: none`
 * holds through hover, which `.btn-link` underlines.
 *
 * Focus stays PERCEIVABLE without either: the underline comes back on `:focus-visible` only, so
 * keyboard users get a mark and a mouse click leaves nothing behind.
 */
.deal-task-morelink {
    padding: 0;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
    color: var(--blue-700, #1d4ed8);
    background: none;
    border: 0;
    box-shadow: none;
    text-decoration: none;
    cursor: pointer;
}

.deal-task-morelink:hover,
.deal-task-morelink:focus {
    text-decoration: none;
    box-shadow: none;
    outline: none;
}

.deal-task-morelink:focus-visible {
    text-decoration: underline;
}

/* 🚧 The stage-flow pair is static (see DealCurrentTask.jsx) — the styling is not, and will
   serve the real value unchanged. */
.deal-task-stageflow {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 8px;
    font-size: 11px;
    color: var(--slate-500, #64748b);
    background-color: var(--slate-100, #f1f5f9);
    border-radius: var(--radius-sm, 4px);
}

.deal-task-stageflow__to {
    font-weight: 700;
    color: var(--color-black, #0f172a);
}

.deal-task-card-muted {
    font-size: 12px;
    color: var(--slate-500);
}

.deal-task-name {
    margin: 0 0 4px;
    font-size: 12px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--slate-900);
}

.deal-task-code {
    margin-bottom: 16px;
    font-size: var(--text-xs);
    font-family: monospace;
    color: var(--slate-400);
}

.deal-task-rows {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.deal-task-row-label {
    font-size: 9px; /* see .deal-task-title */
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--slate-400);
    margin-bottom: 2px;
}

.deal-task-row-value {
    font-size: 12px;
    font-weight: 600;
    color: var(--slate-900);
    word-break: break-word;
}

/* Due date and the remaining span on one line — they are one fact, read together. */
.deal-task-due {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 8px;
}

.deal-task-remaining {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--blue-600);
    white-space: nowrap;
}

.deal-task-remaining.is-overdue {
    color: var(--red-700);
}

.deal-task-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    flex: 0 0 24px;
    border-radius: 50%;
    background-color: var(--green-300);
    color: var(--color-white);
    font-size: var(--text-xs);
    font-weight: 700;
}

/*
 * "2 other tasks open on this deal". The card shows ONE task by design — a deal can genuinely
 * have several — and this line is how it says so instead of looking like the whole answer.
 */
.deal-task-more {
    margin-top: 14px;
    padding-top: 10px;
    border-top: 1px solid var(--slate-100);
    font-size: var(--text-sm);
    color: var(--slate-500);
    line-height: 1.5;
}

/*
 * MAXIMISED — the height chain from the dialog down to the three columns.
 *
 * ⚠️ THIS PATH HAD NEVER RUN BEFORE 2026-08-22, which is why it needed fixing the moment it did.
 * `#cardModalMaximize`'s listener resolves its target once at load as
 * `document.querySelector('.card-modal .modal-dialog')`, and `layout/wrapper.blade.php` includes
 * `modals/actions-modal-wrapper` BEFORE `modals/common-modal-wrapper` — so every click put
 * `.is-maximized` on `#actionsModal` and the deal modal could not be maximised at all. The
 * `.is-maximized #dealFullDetailsWrapper .modal-body` rule further up this file was dead code.
 * `DealTitleBar.jsx` toggles the class on the dialog it is itself inside, so it runs now.
 *
 * WHAT IT HAD TO FIX. That rule sets the columns' row to `height: 100%`, and the two shell rules
 * above it deliberately set `> form` and `.modal-content` to `height: auto` — which is right for
 * the windowed modal (it shrinks the shared shell around a 70vh body) and exactly wrong maximised:
 * a percentage height resolves to `auto` against an `auto` parent, so nothing bounded the columns,
 * their three scrollbars vanished, and the title bar and footer scrolled off the top and bottom of
 * the window with the fields. Reproduced on /deals/29 the first time the toggle worked.
 *
 * WHY THE WHOLE CHAIN. The deal markup is injected into `#commonModalBody`, so between the dialog
 * and the columns there are five elements — `#commonModalForm`, `.modal-content`,
 * `#commonModalBody`, `#dealForm`, `#dealFullDetailsWrapper` — and a percentage height stops dead
 * at the first one that is `auto`. Making each a flex column that fills its parent means the
 * height comes from the window once, and `calc()` never has to guess at the bar and footer.
 *
 * Same `@media` as the rules it corrects: below 1024px custom.css stacks this modal and three
 * side-by-side scrolling columns is not that layout.
 */
@media (min-width: 1024px) {
    .card-modal .modal-dialog.is-maximized:has(#dealFullDetailsWrapper) > form,
    .card-modal .modal-dialog.is-maximized:has(#dealFullDetailsWrapper) .modal-content,
    .card-modal .modal-dialog.is-maximized:has(#dealFullDetailsWrapper) #commonModalBody,
    .card-modal .modal-dialog.is-maximized:has(#dealFullDetailsWrapper) #dealForm,
    .card-modal .modal-dialog.is-maximized #dealFullDetailsWrapper {
        display: flex;
        flex-direction: column;
        /* `!important` only because it is overriding the `auto` shell rules above, which carry it
           for their own reason (Bootstrap's `height: 100% !important` on lg/xl dialog forms). */
        height: 100% !important;
        /* Load-bearing: a flex item defaults to `min-height: auto` and refuses to shrink below its
           content, which cancels every overflow underneath it before it can scroll. */
        min-height: 0;
    }

    .is-maximized #dealFullDetailsWrapper .modal-body {
        /*
         * REPLACES the `height: 100%` above. The columns' row is one of THREE children — the title
         * bar, itself, and the footer row (which now carries the required-field stepper as its left
         * half) — so it takes what is left rather than all of it. `100%` would make it as tall as
         * the whole content box and push the footer out of the window, which is the difference
         * between a maximised form and a broken one.
         */
        height: auto;
        max-height: none;
        flex: 1 1 auto;
        min-height: 0;
    }
}

/* ---------------------------------------------------------------------------
 * The ⭐ Summary / 🔎 Reviewer cards (GEN_US_006 / GEN_US_007).
 *
 * A label/value grid rather than a table: the rows are a definition list, the
 * count runs from 16 to 114 depending on stage, and a table would need a header
 * nobody reads.
 *
 * ⚠️ NO VIEWPORT MEDIA QUERY DECIDES THE COLUMN COUNT. Side-by-side is Bootstrap's
 * job (`col-lg-6` in DealCards.jsx), because these cards do not own their width —
 * they sit in whatever column the page gives them. A `@media (min-width: 1200px)`
 * two-column rule fired on viewport width alone, which is wrong the moment the
 * cards are inside a narrower container: at 1456px it gave each card ~200px,
 * less than the label column's own minimum, and the value column collapsed so
 * that "New service" wrapped one character per line. Caught in the browser.
 *
 * The label minimum is therefore modest (120px, not 180px) and expressed as a
 * ceiling too, so a half-width column still leaves the value something to use.
 * ------------------------------------------------------------------------- */
.deal-card-rows {
    margin: 0;
}

.deal-card-row {
    display: grid;
    grid-template-columns: minmax(120px, 45%) minmax(0, 1fr);
    gap: 12px;
    padding: 7px 0;
    border-bottom: 1px solid rgba(0, 0, 0, .05);
}

.deal-card-row:last-child {
    border-bottom: 0;
}

.deal-card-row-label,
.deal-card-row-value {
    margin: 0;
    font-size: 13px;
    line-height: 1.45;
    /* The matrix labels run long ("Contract — GCP value mirrors (consumption /
       commitment / CUD / PAYG / total / payment / dates / length)"), so both
       sides wrap rather than clip — a truncated label names nothing. */
    overflow-wrap: anywhere;
}

.deal-card-row-label {
    font-weight: 400;
    color: #6b7280;
}

.deal-card-row-value {
    color: #111827;
}

/* SKU section — capped table with a pinned header.
   Two classes, because custom.css's `.table-responsive { max-height: 100vh }` is a later rule at
   equal specificity. The cap has to sit on the table's OWN container: `position: sticky` resolves
   against the nearest scrolling ancestor, so a separate wrapper would leave the header nothing to
   stick to. `thead th` already carries its slate background in custom.css. */
.table-responsive.deal-scroll-table {
    max-height: 300px;
}

.table-responsive.deal-scroll-table table thead th {
    position: sticky;
    top: 0;
    z-index: 3;
}

/* SKU pills in the resource table.

   Everything expressible as a utility is one, on the markup: `d-inline-flex align-items-center`
   for the pill, `text-truncate` for the name, `flex-shrink-0 border-left ml-1 pl-1 text-slate-500
   font-weight-normal` for the qty, `text-decoration-none text-slate-700` / `text-blue-700 text-xs
   align-self-center text-nowrap` for the two toggle states. These four rules are what has no
   utility: there is no `max-w-*` family, `status-pill` is uppercase by design, and Bootstrap 4 has
   no underline utility (that one arrived in BS5). */

/* The pill cap is what makes the cell wrap after two; the cell cap keeps the column off the rest
   of the table. `min-w-240px` is the nearest existing utility and is 10px short of the floor. */
.deal-sku-cell {
    min-width: 250px;
    max-width: 300px;
}

.deal-sku-pill {
    max-width: 215px;
    text-transform: none;
}

.deal-sku-toggle:hover {
    background-color: var(--slate-200);
}

.deal-sku-less,
.deal-sku-less:hover,
.deal-sku-less:focus {
    text-decoration: underline;
}
