/* ==========================================================================
   CLIENT VIEW — the page read at one client's prices
   ==========================================================================
   Shared by pricing.html and stock.html, because the control has to look and
   behave identically on both: it is one mode the user is in, not two features
   that happen to resemble each other. Anything page-specific (which columns
   collapse, which cells re-mark) stays in the page.

   The colour is the portal's warn ground rather than the primary one. Client
   View changes what every number on the page MEANS, so leaving it on by
   accident is the failure mode worth designing against — a blue band reads as
   decoration, an amber one reads as a state you are standing in.
   ========================================================================== */

/* ---- The mode's colour, in one place ----------------------------------
   Client Mode paints five surfaces: the header wash, the band, the client's
   mark, the rule chip, and the saving on every re-marked price. They are all
   derived from ONE hue here, so the mode can be re-coloured in three lines and
   can never end up half amber and half blue.

   Mixed against `--color-surface` rather than written as fixed hexes — the
   method app.css sets out for the warmth ramp: "USE THEM AS A HUE, NOT AS INK
   ... so the chip follows every theme instead of needing a second palette."
   That is why evening needs no second set of these.

   The header wash is the one value that cannot be a mix: `.header` is
   `backdrop-filter: blur(8px)`, so it needs real alpha or the blur dies. It is
   the hue at ~8% over the surface, written out.

   ⚠ The first cut of this used an invented cream (#FCF4E4 / #FDF8EE / #8A5A12).
   Three problems: the hexes were in no token family; per memory.md rule 19 a
   warm ground reads as **warn** ("in motion, or someone owes an action"), and a
   viewing scope is not that; and it clashed with everything else on the page. */
:root {
  --cv-hue:      var(--color-primary);       /* edges, chips, the switch's own lit colour */
  --cv-ink:      var(--color-primary-700);   /* type on light fills — the saving under a price */
  --cv-tint:     rgba(208, 219, 232, 0.94);  /* the header wash: the hue at 26%, alpha kept */
}

/* ⚠ EVENING INVERTS THE ROLES, and getting this wrong is not a cosmetic bug —
   it is an unreadable bar. On a dark theme `--color-primary-700` is **#8AB0FF**,
   a LIGHT blue: app.css says it outright — "On a dark fill the base token IS the
   legible ink, so each -ink points back at it rather than darkening further." So
   in evening that token is the INK and cannot also be the ground, and the white
   type the light theme puts on it would be white-on-light-blue.

   The ground is mixed instead. On a near-black page there is a ceiling on how
   far a readable panel can stand off the ground — at 45% the band is 2.33:1 off
   `--color-bg` and carries its type at 7.13:1, while going bright enough for a
   6:1 ground separation drops the type to 2.72:1. 45% is the balance, and 2.33:1
   is a clear step on dark (the theme's own surface is 1.27:1 off its page).

   `--cv-hue` and `--cv-ink` need no override: both already point at theme tokens
   that evening redefines, which is the whole reason they are tokens. */
[data-theme="evening"] {
  --cv-tint: rgba(34, 52, 88, 0.92);
}
/* Elegant needs its own for the same reason and none of the same numbers: its
   accent is a DARKER blue and its surface is a neutral #212121, so the wash
   above would be both the wrong hue over the wrong ground. Same construction —
   the theme's own hue at ~23% over its own surface, written as a literal rgba
   because `.header`'s backdrop-filter needs real alpha.

   ⚠ IT STAYS BLUE. Elegant is the achromatic theme, and the temptation is to
   wash the bar in grey to match — but a grey wash on a grey header is a mode
   nobody can see they are in, and Client Mode being left on by accident is the
   failure this colour exists to prevent. Hue is what the wash is FOR. */
[data-theme="elegant"] {
  --cv-tint: rgba(36, 51, 80, 0.92);
}
/* Derived, never re-picked.

   The header is the ONE surface that carries a wash. It is chrome — the bar you
   see out of the corner of your eye all day — so a faint colour on it is exactly
   the right amount of "you are in a client's prices". Everything else the mode
   touches stays in the page's own palette; see the note above `.cv-bar` for why
   that took three attempts to get right.

   26% is as far as the wash goes with the label still legible, and the label
   darkens to `--color-text` to buy that much. */
body[data-cv-on] .header { border-bottom-color: color-mix(in srgb, var(--cv-hue) 45%, var(--color-surface)); }
/* The label stops being muted while the mode is on: it is a live state now, and
   at 26% the muted grey would drop under 4.5:1 anyway. */
body[data-cv-on] .cv-mode { color: var(--color-text); }
/* The mode's chips, in the header's selector and again in the band: one mix,
   one ink, so the two read as the same object seen twice. 5.19:1 light,
   6.31:1 evening. */
.cv-pick__mark,
.cv-pick__rule { background: color-mix(in srgb, var(--cv-hue) 18%, var(--color-surface)); color: var(--cv-ink); }

/* ---- The control: one switch, and who it is switched to ----------------
   It rides the header's action cluster, immediately right of the search box
   (owner's call). Two earlier homes and why neither held: the page toolbar was
   already carrying a search, a filter and four status tabs; and the header's
   empty LEFT is where the live strips land — Next up and Talk now — so a mode
   switch has no business taking a seat a waiting client needs.

   It is a SWITCH, not a two-state segmented control. "Not in client mode" is the
   absence of a mode rather than a mode of its own, and naming it made the reader
   pick between two things when there is only one to turn on. A switch says it in
   half the room, in the shape this portal already uses for every Y/N.

   The header is sticky, which is the property that matters once the mode is on:
   the client stays on screen after the band below has scrolled away. */
.cv-head { display: flex; align-items: center; gap: 10px; min-width: 0; }
/* ⚠ Label LEFT of the switch — the reverse of memory.md rule 5, on the owner's
   explicit call (2026-09-03), and the only toggle in the portal laid out this
   way. Rule 5's shape is right for a settings column, where a rail of switches
   down the left edge is what makes the column scannable. This is a lone control
   in a header row read left to right beside a search box, and there the name has
   to arrive before the thing it names. The non-bold half of rule 5 still holds —
   `.switch-field` sets it. Order comes from the markup, not from `order:`, since
   there is no input to keep in place. */
.cv-mode { font-size: 13px; color: var(--color-text-muted); white-space: nowrap; }

/* Who the mode is switched to. It shares one seat with the words "Client Mode",
   both of them left of the switch: off, the words are there and this is hidden;
   on, they trade. The switch never moves, so the control does not jump under the
   cursor at the moment it is being used — only what it is switching changes.

   And the name is not lost by hiding the words: the switch carries the same
   string in `aria-label`, so a screen reader still meets a named control. */
body[data-cv-on] .cv-mode > span:not(.switch) { display: none; }
/* The edge takes the hue rather than the generic control border: this button
   stands on the mode's own tinted header, and a neutral grey hairline is the
   one thing that disappears against a tint — in either theme. Ground stays
   `--color-surface`, so it reads raised on a light header and recessed on a
   dark one, which is what each theme expects of a control. */
.cv-pick { display: inline-flex; align-items: center; gap: 7px; min-width: 0; max-width: 300px; height: 30px; padding: 0 8px; border: 1px solid color-mix(in srgb, var(--cv-hue) 30%, var(--color-border-strong)); border-radius: 6px; background: var(--color-surface); font: inherit; font-size: 13px; color: var(--color-text); cursor: pointer; }
.cv-pick:hover { border-color: var(--color-primary); }
.cv-pick[hidden] { display: none; }
.cv-pick__mark { width: 20px; height: 20px; flex: none; display: inline-flex; align-items: center; justify-content: center; border-radius: 5px; font-size: 10px; font-weight: 700; }
.cv-pick__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
/* The rule in the shape the pricing table's own column chips use, so the header
   and the table say it the same way. */
.cv-pick__rule { flex: none; padding: 1px 5px; border-radius: 4px; font-size: 10px; font-weight: 700; letter-spacing: .02em; white-space: nowrap; }
.cv-pick svg { width: 14px; height: 14px; flex: none; color: var(--color-text-subtle); }

/* ---- Sharing the header with a live strip -----------------------------
   The bar is a shared row, and two other things can land in it: **Next up**
   (`.nextup`, `margin-right: auto`, up to 520px — the countdown, and the call
   card that drops out of its underside) and **Talk now** (`.talknow`, which
   inserts itself at `[data-toggle-sidebar]`'s next sibling). Both take the
   header's LEFT; this control sits at the right, inside `.header-actions`, so
   the two no longer contend for the same seat — which is half of why it moved.

   ⚠ Neither can COVER it either: `.nextup__call` is `top: 100%`, so it paints
   over the page BELOW the header, never across it.

   What they still share is total width. The order of yielding is the order of
   urgency — a client waiting on a reply, then a call about to start, then which
   prices are on screen — so with a strip in the header this control drops its
   words and keeps the switch and the client's initials. */
/* `> span:not(.switch)` is the WORDS — never a position. This was
   `span:last-child`, which silently became the switch the moment the label
   moved in front of it, and would have hidden the control instead of its
   name. */
.header:has(.nextup) .cv-mode > span:not(.switch),
.header:has(.talknow) .cv-mode > span:not(.switch),
.header:has(.nextup) .cv-pick__name,
.header:has(.talknow) .cv-pick__name { display: none; }

/* ---- The header wears the mode ----------------------------------------
   While Client Mode is on, the top bar itself goes off white and takes a warm
   wash. The switch and the selector say WHICH client; this says THAT you are in
   somebody's prices at all, and it says it from the corner of the eye on a bar
   that never scrolls away — which is the one place a mode can be forgotten
   from. Same warm family as the `.cv-bar` band below and the client's mark, so
   the three read as one state rather than three decorations.

   Faint on purpose. This is ambient, not a warning: the band below carries the
   statement and the per-row lines carry the numbers, so a header loud enough to
   compete with them would be a third voice saying the same thing. The alpha is
   kept because `.header` is `backdrop-filter: blur(8px)` — an opaque colour here
   would kill the blur the bar is built on.

   Both palettes. `--color-header-bg` is themed (app.css:149 light, :202
   evening), so a single hardcoded tint would light up a dark header. The theme
   attribute sits on <html>, not on <body> (app.css:165), which is why the dark
   rule reads `[data-theme] body[data-cv-on]` and not both on one element. */
body[data-cv-on] .header { background: var(--cv-tint); }
/* Flipping the switch should read as the bar changing state, not as a flash. */
.header { transition: background-color 180ms ease, border-color 180ms ease; }

/* ---- The picker ------------------------------------------------------- */
/* Anchored under the switch. Fixed rather than absolute: the toolbar it sits
   in is inside a scrolling column on both pages, and an absolute popover gets
   clipped by the first ancestor with overflow set. */
.cv-picker { position: fixed; z-index: 60; width: 320px; max-height: 380px; display: none; flex-direction: column; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 7px; box-shadow: 0 10px 28px rgba(15, 23, 42, .16); overflow: hidden; }
.cv-picker.is-open { display: flex; }
.cv-picker__head { padding: 9px 10px; border-bottom: 1px solid var(--color-border); }
.cv-picker__search { width: 100%; }
.cv-picker__list { overflow: auto; padding: 5px; }
.cv-picker__group { padding: 7px 7px 3px; font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--color-text-subtle); }
.cv-opt { display: flex; align-items: center; gap: 8px; width: 100%; padding: 6px 7px; border: 0; border-radius: 5px; background: transparent; font: inherit; text-align: left; cursor: pointer; }
.cv-opt:hover, .cv-opt:focus-visible { background: var(--color-primary-50); outline: none; }
.cv-opt__mark { width: 24px; height: 24px; flex: none; display: inline-flex; align-items: center; justify-content: center; border-radius: 5px; background: var(--color-primary-50); color: var(--color-primary-700); font-size: 10px; font-weight: 700; }
.cv-opt__text { min-width: 0; flex: 1; }
.cv-opt__name { display: block; font-size: 13px; font-weight: 600; color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-opt__meta { display: block; font-size: 11px; color: var(--color-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cv-picker__empty { padding: 14px 10px; font-size: 12px; color: var(--color-text-muted); text-align: center; }

/* ---- The bar ---------------------------------------------------------- */
/* Says who, what they are, which price list names that kind of buyer, and the
   rule in words — the four facts the prices below it depend on. It is not
   collapsible: a mode you cannot see you are in is the whole problem. */
/* ⚠ TWO WRONG ANSWERS CAME BEFORE THIS ONE, and the pair of them is the rule.

   First the band was a TINT, the hue at 8% over the surface, and it vanished —
   arithmetic, not taste: the page ground is #E7EAEE, itself a blue-grey, so a
   blue tint over white lands on top of it. It scored 1.09:1 against that ground
   at 8% and still only 1.43:1 at 40%. No percentage rescues a pale panel on a
   pale ground.

   Then it became a SOLID bar in the hue's ink. That read perfectly and was far
   too loud: a dark blue slab across the page, shouting a fact the reader only
   needs to be aware of while they are concentrating on something else.

   The answer was never a strength between those two. It is to stop making the
   band a coloured object at all — it is a CARD, on the page's own surface, in
   the page's own type, exactly like the KPI cards above it. A card on this
   ground is legible without being loud, which is what the whole page is already
   built on. The mode shows in one accent edge and two small chips.

   Which leaves the sticky header carrying the ambience and the band carrying the
   facts, and that is the right way round: the wash is what you see out of the
   corner of your eye all day; the band is what you read once. */
.cv-bar { display: none; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 12px; padding: 10px 13px; border: 1px solid var(--color-border); border-left: 4px solid var(--cv-hue); border-radius: 7px; background: var(--color-surface); color: var(--color-text); box-shadow: var(--shadow-sm); }
body[data-cv-on] .cv-bar { display: flex; }
.cv-bar__who { display: flex; align-items: center; gap: 9px; min-width: 0; }
/* The mark and the rule chip are the only two coloured things in the band, and
   they take the same 18% mix and the same ink as the chips in the header's
   selector — so the control up top and the band down here read as one object
   seen twice, not as two different blues. */
.cv-bar__mark { width: 30px; height: 30px; flex: none; display: inline-flex; align-items: center; justify-content: center; border-radius: 6px; background: color-mix(in srgb, var(--cv-hue) 18%, var(--color-surface)); color: var(--cv-ink); font-size: 11px; font-weight: 700; }
.cv-bar__name { display: block; font-size: 14px; font-weight: 600; color: var(--color-text); }
.cv-bar__sub { display: block; font-size: 11px; color: var(--color-text-muted); }
/* Client type → price list. The arrow is the point: it is the only relationship
   in the model that decides what this client pays. */
.cv-bar__chain { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--color-text-muted); }
.cv-bar__chain svg { width: 13px; height: 13px; flex: none; }
.cv-bar__list { font-weight: 600; color: var(--color-text); }
/* The sales line, and the reason the bar exists: the number leads, the words
   qualify it (memory.md rule 6). */
.cv-bar__rule { display: inline-flex; align-items: baseline; gap: 5px; padding: 3px 8px; border-radius: 6px; background: color-mix(in srgb, var(--cv-hue) 18%, var(--color-surface)); color: var(--cv-ink); }
.cv-bar__off { font-size: 15px; font-weight: 700; line-height: 1.1; }
.cv-bar__against { font-size: 11px; }
.cv-bar__actions { margin-left: auto; display: flex; align-items: center; gap: 7px; flex: none; }
.cv-bar__note { flex-basis: 100%; font-size: 11px; color: var(--color-text-muted); }
/* No button overrides here any more. The band stands on `--color-surface`, so
   `.btn-outline` and `.btn-ghost` are already right — re-inking them was only
   ever needed to survive a coloured ground. */

/* ---- The re-marked prices --------------------------------------------- */
/* One rule for every price on either page: the client's figure takes the line
   the base used to hold, and the base drops under it with what the client is
   saving on THIS item. The percentage is the same on every row — it belongs to
   the price list, and the bar says it once — so what the line adds is the
   amount, which is the thing that changes product to product.

   One `display:block` rule covers both places the script puts it: inside the
   price node (a <dd>, a card footer's price, a fact row's value) and, in a
   table cell's `.cell-stack`, as its own line after the unit. 11px is the
   sub-line step (memory.md rule 4). */
.cv-delta { display: block; margin-top: 1px; font-size: 11px; font-weight: 400; color: var(--color-text-muted); white-space: nowrap; }
.cv-delta__save { color: var(--cv-ink); font-weight: 600; }
/* A price the client's list cannot produce a figure for — a fixed-price list
   with nothing set for this item, or an item with no base at all. */
.cv-delta--none { color: var(--color-text-subtle); }

/* ---- Touch ------------------------------------------------------------
   Mobile audit: every tappable thing here is 44px at or below the tablet
   breakpoint. The switch loses no width for it — it is two words, and the
   height is what a thumb needs. */
/* The header has never had room for words down here — its own search drops to a
   bare magnifier at 720px. The switch and the client's initials stay; the words
   go, and the mode is still readable, because a lit switch and a client's mark
   are the two things it is actually saying. */
@media (max-width: 720px) {
  .cv-mode > span:not(.switch), .cv-pick__name { display: none; }
  .cv-pick { max-width: none; }
}

@media (max-width: 768px) {
  .cv-pick { height: 44px; }
  .cv-opt { padding: 10px 8px; min-height: 44px; }
  .cv-picker { width: 320px; max-width: calc(100vw - 16px); }
  /* The bar stacks: who they are, then the chain, then the rule, then the two
     buttons on a row of their own rather than squeezed against the edge. */
  .cv-bar__actions { margin-left: 0; flex-basis: 100%; }
  .cv-bar__actions .btn { min-height: 44px; }
}
