/* =========================================================================
   Vitality Stacks — the manual builder (/build/)

   Loaded after style.css on page-build.php only, so every token and every
   shared class is already defined: --ink, --sage, --sage-2, --line, --slate,
   --mute, --green, --amber, --amber-bg, --max, --col, plus .wrap, .btn,
   .chip, .chips.inline, .notice, .fine and .sr. Nothing here redefines any of
   them — this sheet only draws the pieces that are new.

   Two named borrowings, deliberate, so the builder reads as the same site:
     · the selected-row treatment is the concept's .bl button[data-in="true"]
       from style.css §15 (green hairline on #F4FAF7);
     · the category-tag pill is .product .cats span from §14;
     · the clinician line uses the amber caution pair (--amber-bg / #6b4a12)
       the quality page's .bad panel already uses.

   Desktop-first, like style.css: two columns by default, one column with a
   fixed bottom tray under 1000px. Every class here is one page-build.php
   prints or assets/builder.js creates — the runtime-only ones included (§5b),
   because a class the client invents and this sheet has never heard of ships
   as unstyled text where a button should be. The few hooks with no rule of
   their own are the ones something else already draws: .vsb-cat is the theme's
   .chip, .is-hidden duplicates the [hidden] rule below, and .is-on duplicates aria-pressed.

   --mute is decoration only. Dose and fine print are content on this page, so
   they take --slate, which clears 4.5:1 on white, on #F4FAF7 and on --sage.
   ========================================================================= */

/* Author styles beat the UA [hidden] rule, so the filter's hide sticks. */
.vsb [hidden] { display: none !important; }

.vsb { padding-top: 72px; }
.vsb-h { font-family: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif; font-size: clamp(26px, 2.6vw, 34px); font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; }
.vsb-sub { font-size: 16px; color: var(--slate); margin-top: 10px; max-width: 62ch; }

/* =========================================================================
   1. Template row — the ten validated packs
   Renders only once a vs_learn_packs() helper exists; styled now so the row
   does not land unstyled the day it does.
   ========================================================================= */
.vsb-templates { margin-bottom: 56px; }
.vsb-tpl-row { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; margin-top: 24px; }
.vsb-tpl { display: grid; gap: 6px; align-content: start; text-align: left; padding: 18px 20px; background: #fff; border: 1.5px solid var(--line); border-radius: 16px; transition: border-color .15s, background .15s; }
.vsb-tpl:hover { border-color: var(--ink); }
.vsb-tpl b { font-size: 16px; font-weight: 600; }
.vsb-tpl-n { font-size: 13px; font-weight: 600; color: var(--green); }
.vsb-tpl-items { font-size: 13px; color: var(--slate); line-height: 1.45; }

/* =========================================================================
   2. The two-column frame
   ========================================================================= */
.vsb-grid { display: grid; grid-template-columns: minmax(0, 1fr) 372px; gap: 56px; align-items: start; }
.vsb-main { min-width: 0; }

/* =========================================================================
   3. Search and category chips
   The chips are the theme's .chips.inline .chip — ink fill when pressed —
   so nothing about their look is restated here.
   ========================================================================= */
.vsb-filters { margin: 26px 0 14px; display: grid; gap: 14px; }
.vsb-search-field { max-width: 340px; }
.vsb-search { width: 100%; }
.vsb-cats { row-gap: 8px; }

/* How many rows the filter left showing. A live region, so it is never hidden
   when empty and never taken out of the tree: it must already be there for a
   screen reader to hear the next filter. The server prints the opening count
   into it for the same reason. */
.vsb-results { font-size: 13px; color: var(--slate); margin-bottom: 12px; min-height: 19px; }

/* =========================================================================
   4. The formulary list
   One row per compound: name, shipped dose, category tags, the clinician
   line where one applies, the Learn link, and the Add control. No
   description copy and no price — by design, both live elsewhere.
   ========================================================================= */
.vsb-list { list-style: none; display: grid; gap: 8px; margin: 0; padding: 0; }
.vsb-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 16px; align-items: center; padding: 16px 18px; background: #fff; border: 1.5px solid var(--line); border-radius: 14px; transition: border-color .15s, background .15s; }
.vsb-item:hover { border-color: var(--ink); }
.vsb-item-body { min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; }
.vsb-name { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.vsb-dose { font-size: 14px; color: var(--slate); font-variant-numeric: tabular-nums; }
.vsb-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.vsb-tag { font-size: 12px; font-weight: 500; padding: 4px 10px; border-radius: 999px; background: var(--sage); color: var(--slate); }
.vsb-more { font-size: 13px; font-weight: 600; color: var(--green); box-shadow: inset 0 -1px 0 rgba(30, 107, 88, .3); }
.vsb-more:hover { box-shadow: inset 0 -2px 0 var(--green); }

/* The caution line. It is a "talk to someone" line, never a "this is
   dangerous" line, and these compounds stay addable — the builder is the
   deliberate path for them. It takes the whole row so it is not read as one
   more category tag. */
.vsb-clinician { flex: 1 0 100%; font-size: 13px; line-height: 1.45; color: #6b4a12; background: var(--amber-bg); border-radius: 9px; padding: 7px 11px; }

/* The toggle. Its word does not change — aria-pressed carries the chosen
   state and draws the ink fill — except on a row locked by the shared-SKU
   rule, where it reads "Same product". A locked row stays readable and its
   control stays focusable: only the action is refused, and activating it says
   why (builder.js). aria-disabled is the only disabled state used here; the
   [disabled] selector is kept for a control we did not draw. */
.vsb-add { flex: none; padding: 9px 18px; border: 1.5px solid var(--line); border-radius: 999px; background: #fff; color: var(--ink); font-size: 14px; font-weight: 600; line-height: 1.2; white-space: nowrap; transition: background .15s, color .15s, border-color .15s; }
.vsb-add:hover { border-color: var(--ink); }
.vsb-add[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }
.vsb-add[aria-pressed="true"]:hover { background: var(--green-deep); border-color: var(--green-deep); }
.vsb-add[disabled], .vsb-add[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
.vsb-add[disabled]:hover, .vsb-add[aria-disabled="true"]:hover { border-color: var(--line); }

/* Chosen row: the concept's green hairline on a pale green ground. Two
   selectors for one look — .is-picked is the class builder.js sets, the
   :has() form covers a browser where the class write is somehow missed. */
.vsb-item.is-picked { border-color: var(--green); background: #F4FAF7; }
.vsb-item:has(.vsb-add[aria-pressed="true"]) { border-color: var(--green); background: #F4FAF7; }

/* The other half of a shared-SKU pair: one store product, so it cannot also
   be chosen while its twin is in. Not a warning — a locked row reads as
   inactive, which is why it fades rather than colouring. */
.vsb-item.is-same-product { background: var(--sage); border-color: var(--line); }
.vsb-item.is-same-product:hover { border-color: var(--line); }
.vsb-item.is-same-product .vsb-name, .vsb-item.is-same-product .vsb-dose { color: var(--slate); }

/* A compound the backend cannot sell right now. Same locked look as the
   shared-SKU twin: listed, readable, not choosable. */
.vsb-item.is-unavailable { background: var(--sage); border-color: var(--line); }
.vsb-item.is-unavailable:hover { border-color: var(--line); }
.vsb-item.is-unavailable .vsb-name, .vsb-item.is-unavailable .vsb-dose { color: var(--slate); }

/* =========================================================================
   5. The pack panel
   Sticky beside the list on desktop; a fixed tray under 1000px (§7).
   ========================================================================= */
/* top clears the 68px sticky nav. The panel scrolls inside itself rather than
   growing past the viewport, so the continue button stays reachable with a
   full eight-compound pack on a short screen. */
.vsb-panel { position: sticky; top: 92px; }
.admin-bar .vsb-panel { top: 124px; }
.vsb-panel-in { background: var(--sage); border: 1.5px solid var(--line); border-radius: 20px; padding: 26px 24px; display: grid; gap: 14px; max-height: calc(100vh - 116px); overflow-y: auto; }
.admin-bar .vsb-panel-in { max-height: calc(100vh - 148px); }
.vsb-panel-title { font-family: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif; font-size: 26px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
.vsb-count { font-size: 14px; font-weight: 600; color: var(--green); font-variant-numeric: tabular-nums; }

/* The chosen list. Every class in this block is one builder.js emits: the
   rows are built at runtime, so the sheet has to name them exactly. */
.vsb-picked, .vsb-picked-list { list-style: none; display: grid; gap: 6px; margin: 0; padding: 0; }
.vsb-picked:empty { display: none; }
.vsb-picked-item { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; align-items: center; background: #fff; border-radius: 10px; padding: 10px 12px; font-size: 15px; }
.vsb-picked-body { min-width: 0; display: grid; gap: 2px; }
.vsb-picked-name { font-weight: 600; line-height: 1.3; }
.vsb-picked-dose { font-size: 12px; color: var(--slate); font-variant-numeric: tabular-nums; }
/* The same amber "talk to someone" line the list card carries, at panel size.
   It is the one place in the panel that pair is used. */
.vsb-picked-caution { font-size: 12px; line-height: 1.4; color: #6b4a12; background: var(--amber-bg); border-radius: 7px; padding: 5px 8px; margin-top: 4px; }
/* The per-row remove control carries the word, not a glyph, so it is sized
   for text; its accessible name names the compound (builder.js). */
.vsb-picked-remove { padding: 5px 10px; border-radius: 999px; color: var(--slate); font-size: 13px; font-weight: 600; line-height: 1.2; white-space: nowrap; transition: background .15s, color .15s; }
.vsb-picked-remove:hover { background: var(--sage-2); color: var(--ink); }
/* The empty state is a row of the same list; it must not read as a compound. */
.vsb-picked-empty { font-size: 13px; color: var(--slate); line-height: 1.45; }

.vsb-note { font-size: 13px; color: var(--slate); line-height: 1.45; }

/* The tier line. It is either the flat monthly price for the pack size or
   "Pricing confirmed at checkout" — never a per-item price and never a
   saving. The number is one span the server already formatted, or, for a pack
   size no tier names, a number and a "per month" of our own. */
.vsb-price { font-size: 19px; font-weight: 600; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; padding-top: 14px; border-top: 1px solid var(--line); }
.vsb-price-num { color: var(--ink); }
.vsb-price-per { font-size: 14px; font-weight: 500; color: var(--slate); margin-left: 6px; }
.vsb-price-pending { font-size: 15px; font-weight: 600; color: var(--slate); }

/* The live region. Redundancy messages — "these overlap" — plus add, remove
   and copy confirmations. Deliberately NOT the amber caution pair that
   .vsb-clinician and .vsb-picked-caution use: nothing announced here is a
   safety warning, and colouring it as one would say otherwise.

   It keeps its box when empty rather than being removed from the tree: a
   region that appears and is filled in the same tick is never announced, and
   blanking it to re-announce a repeated message would take it out again. */
.vsb-msg { font-size: 13px; line-height: 1.45; color: var(--slate); background: var(--sage-2); border-radius: 10px; padding: 10px 12px; min-height: 39px; }
.vsb-msg:empty { padding: 0; min-height: 0; background: none; }

.vsb-continue, .vsb-share { width: 100%; }
.vsb-fine { line-height: 1.5; color: var(--slate); }

/* =========================================================================
   5b. What builder.js inserts
   The checkout block replaces the continue button once a pack has been sent,
   and the share note and manual-copy field appear under the share button.
   None of these exist in the server markup, which is exactly why they are
   drawn here: the primary conversion control must not land as bare text.
   ========================================================================= */
.vsb-result { display: grid; gap: 8px; }
/* The theme's primary button, which is what it stands in for. builder.js also
   gives it .btn.btn-ink, so the fill, the hover and the aria-disabled fade all
   come from style.css; only the width is ours. */
.vsb-checkout { width: 100%; }
.vsb-checkout-pending { font-size: 13px; color: var(--slate); line-height: 1.45; }

.vsb-share-help { font-size: 12px; }
.vsb-share-url { width: 100%; font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }

/* =========================================================================
   6. Empty state
   ========================================================================= */
.vsb .notice a { color: var(--green); font-weight: 600; }

/* =========================================================================
   7. Responsive
   Under 1000px the panel leaves the flow and becomes a fixed tray: count and
   price on one line, then any message, the screening checkbox and the two
   buttons. The picked list drops out of it (see below), so the tray is a
   fixed height and the page's bottom padding can be sized to clear it. The
   tray sits under the sticky nav (z-index 40), not over it.
   ========================================================================= */
@media (max-width: 1000px) {
	.vsb-grid { grid-template-columns: 1fr; gap: 0; }

	.vsb-panel { position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 35; }
	.admin-bar .vsb-panel { top: auto; }
	.vsb-panel-in { border: 0; border-top: 1.5px solid var(--line); border-radius: 18px 18px 0 0; background: #fff; box-shadow: 0 -12px 32px rgba(15, 42, 34, .13); padding: 14px 20px calc(14px + env(safe-area-inset-bottom, 0px)); gap: 10px; max-height: 62vh; overflow-y: auto; -webkit-overflow-scrolling: touch; }

	/* The tray is a summary and an action, not the whole panel. The picked
	   list comes out of it: a growing list makes the tray eat the screen and
	   makes the reserve below unknowable, and nothing is lost — on one column
	   the list itself is the record of what is in, each chosen row green with
	   its toggle pressed. The count carries the total. (.vsb-share-help is a
	   .vsb-note, so it leaves with them.) */
	.vsb-panel-title, .vsb-note, .vsb-fine, .vsb-picked { display: none; }
	.vsb-price { padding-top: 0; border-top: 0; }

	/* Count on the left, tier on the right, on one line. The tier line is the
	   longest thing in the tray while pricing is unconfirmed ("Pricing
	   confirmed at checkout"), so it drops a step and the count is held on
	   one line rather than being squeezed into two. */
	.vsb-panel-in { grid-template-columns: auto minmax(0, 1fr); }
	.vsb-count { grid-column: 1; align-self: center; white-space: nowrap; }
	.vsb-price { grid-column: 2; text-align: right; align-self: center; font-size: 16px; }
	/* Everything that is not the count or the tier spans the tray, the nodes
	   builder.js inserts included — squeezed into the narrow auto column, the
	   checkout button would be unreachable in practice. */
	.vsb-msg, .vsb-continue, .vsb-share,
	.vsb-result, .vsb-share-help, .vsb-share-url { grid-column: 1 / -1; }

	/* Room for the tray, so the last row of the list and the footer both
	   clear it. Because the picked list is out of the tray, the tray's height
	   no longer depends on pack size: it is measured with a two-line message
	   showing, which is the tallest it gets. Scoped to the body class
	   page-build.php adds. */
	body.vs-build { padding-bottom: 264px; }
}

@media (max-width: 900px) {
	.vsb { padding-top: 48px; }
	.vsb-item { grid-template-columns: minmax(0, 1fr) auto; padding: 14px 15px; gap: 12px; }
	.vsb-add { padding: 9px 15px; }
	.vsb-search-field { max-width: none; }
	.vsb-tpl-row { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 560px) {
	.vsb-tpl-row { grid-template-columns: 1fr; }
	.vsb-item-body { gap: 6px 10px; }
	body.vs-build { padding-bottom: 300px; }
}

@media (prefers-reduced-motion: reduce) {
	.vsb-item, .vsb-add, .vsb-tpl, .vsb-picked-remove { transition: none; }
}
