/*
 * SavvyVault — customer portal styles (Phase 5, Subgroup C).
 *
 * DROP PATH: savvyvault/assets/css/portal.css
 *
 * Scope & philosophy
 * ------------------
 * The portal renders INSIDE the active theme (get_header()/get_footer() wrap
 * the body), so this stylesheet is deliberately self-contained and namespaced
 * under .savvy-vault-portal: it never restyles the theme, and the theme's own
 * typography/background still show through around the portal shell. The look
 * is refined-professional to match the practitioner audience (CFAs, CIPMs,
 * compliance officers) and the navy brand of the [savvy_vault_buy] button.
 *
 * All visuals are driven by CSS custom properties scoped to the portal and
 * chrome wrappers, so a theme (or a later white-label) can re-skin the portal
 * by re-declaring any token at higher specificity — same pattern as
 * frontend.css.
 *
 * TOKEN STEM — CHANGED AT W14-2B (2.135.0)
 * ----------------------------------------
 * This sheet's tokens are `--savvy-portal-*`. They were `--sc-*`, from the
 * pre-migration naming: the directory, the text domain, the namespace, the
 * tables and the REST namespace all moved and this did not. Retired at
 * source; see docs/DESIGN-TOKEN-RECONCILIATION.md §6.
 *
 * WHAT IS RETIRED IS THE TOKEN STEM AND THE `data-sc-*` ATTRIBUTES, AND NOT
 * THE WHOLE `sc-` ESTATE. Backlog §5.20 item 4 names tokens and data
 * attributes; `sc-` CSS CLASS and HTML ID names survive and are NOT this
 * iteration's — `sc-tut-media`, `sc-cpreview`, `sc-notice` (customer-facing,
 * includes/class-notice-renderer.php), `sc-mkt-topbar`, `sc-markers`,
 * `sc-picker`, and the `sc-add-email` / `sc-phone` ids in
 * templates/portal/account.php. Enumerated here rather than left implied,
 * because an earlier draft of this comment called the token stem "the last
 * surviving identifier" and that overstated it. They need a wave number.
 *
 * BREAKING FOR SITE CUSTOM CSS. Any rule a site wrote against the old names —
 * most likely the documented width override — no longer resolves. It fails
 * silently, as an unresolved custom property always does. The complete mapping
 * is docs/2026-09-02-W14-2B-HANDOFF.md §1; CHANGELOG.md summarises it.
 * No compatibility shim ships: Wave 13Y §1.1 ruled that the
 * retirement should REMOVE a layer rather than rewrite one, and a shim is the
 * layer.
 *
 * Nine names in the old stem were never declared anywhere and always resolved
 * to a per-site literal fallback — which is why the catalogue, marketplace and
 * emails sections stayed light in dark mode. They are merged onto the declared
 * token that carries their meaning, so those sections now follow the mode.
 * Every resulting colour change is tabulated in docs/2026-09-02-W14-2B-HANDOFF.md §1.4.
 *
 * Accessibility
 * -------------
 * - 44px minimum tap targets on interactive controls (WCAG 2.5.5).
 * - Two-tone fixed focus indicator (Public Website Specification §6.2.6):
 *   outline for forced-colors survival, plus a halo. Neither half is optional.
 * - No form control below 16px (Brand & Design §4.5, normative).
 * - prefers-reduced-motion disables transitions.
 * - Tables collapse to stacked rows under 640px without hiding any data.
 */
/* THE 16px FLOOR IN THIS FILE IS max(1rem, 16px), NOT A HARD 16px.
 * 2.136.2 converted admin.css and admin-studio.css and STOPPED, leaving five
 * hard values here and one in savvyvault-forms.css -- four of them on
 * CUSTOMER-FACING controls. code-qa caught it, and §5.16a SET TWO says the
 * thing directly: "portal.css APPEARING AT ALL means this half is NOT confined
 * to the admin estate. Any report calling this 'the admin remediation' is
 * wrong." That is the failure, in the release that quoted the warning.
 *
 * WHY RELATIVE: a hard 16px CAPS a reader who has raised their browser font
 * size (WCAG 1.4.4). The canonical control reset at
 * `14 - AfsavvyApps design system/design-system-export/tokens/tokens.css:201`
 * is max(1rem, ...) for exactly this reason. It never renders below 16px, so
 * Brand & Design 4.5's floor is unaffected. */

/* W14-2B: the portal token set. Declared on the portal wrapper AND on the
   standalone-chrome wrapper, which is a SIBLING of it rather than a
   descendant (PortalChrome::open_document emits header / main / footer as
   three top-level elements). Declaring on `.savvy-vault-portal` alone would
   leave every chrome rule resolving nothing.

   NOT on :root, deliberately. Before W14-2B the width knob sat on :root while
   every other portal token sat on the wrapper, so a card rendered outside the
   portal resolved one token by accident and the rest not at all.

   OVERRIDING THE WIDTH IN CUSTOM CSS: the selector changed with the stem.
     .savvy-vault-portal,
     .savvy-vault-portal-chrome { --savvy-portal-max: 1248px; }
   A `:root` rule written against the retired stem no longer reaches this, and
   that is a breaking change for any site that wrote one — recorded in
   CHANGELOG.md rather than shimmed, per Wave 13Y §1.1: the retirement removes
   a layer instead of rewriting one. */
.savvy-vault-portal,
.savvy-vault-portal-chrome {
	/* Layout */
	--savvy-portal-max: var(--sv-portal-max-width, 1200px);

	/* Palette */
	--savvy-portal-ink: #1a2332;              /* brand navy — headings, primary buttons */
	--savvy-portal-ink-hover: #0f1620;
	/* W14-67: derive component emphasis from the published strong foreground.
	   This is an internal pairing, not a new public decorative-colour role.
	   Without a host override, retain a mode-safe value for text/progress/borders.
	   The operator's saved primary remains on actions and the premium badge. */
	--savvy-portal-emphasis-default: #1a2332;
	--savvy-portal-emphasis: var(--sv-portal-text-strong, var(--savvy-portal-emphasis-default));
	--savvy-portal-text: var(--sv-portal-text, #2b3340);
	--savvy-portal-muted: var(--sv-portal-text-muted, #5b6675);
	--savvy-portal-line: var(--sv-portal-line, #e4e7ec);
	--savvy-portal-surface: var(--sv-portal-surface, var(--savvy-color-surface, #ffffff));
	--savvy-portal-surface-2: var(--sv-portal-surface-raised, #f7f8fa);
	--savvy-portal-accent: var(--sv-portal-link, #1a4f8f);          /* links / secondary accents */
	--savvy-portal-accent-hover: var(--sv-portal-link-hover, #123a6b);
	--savvy-portal-ok-bg: #e7f4ec;
	--savvy-portal-ok-ink: var(--savvy-color-success-fg, #1a7f37);
	--savvy-portal-error-bg: #fdecea;
	--savvy-portal-error-ink: var(--savvy-color-error, #b32d2e);
	--savvy-portal-warn-bg: #fff6e5;
	--savvy-portal-warn-ink: #8a5a00;

	/* Focus ring GEOMETRY only. The two colours are the canonical fixed role
	   tokens --af-color-focus-ring-fixed-outer / -inner, declared on :root in
	   assets/css/savvyvault-tokens.css, and they are never re-pointed by mode.
	   Only the geometry is local, so prefers-contrast can thicken the
	   indicator without touching a value the specification fixes. The pre-W14-2B colour token
	   (#4d8eff light / #8ab4ff dark) is RETIRED — it is C.4, and it measured
	   2.47:1 over the dark-mode primary button fill. */
	--savvy-portal-focus-width: 2px;
	--savvy-portal-focus-halo: 6px;

	/* Shape */
	--savvy-portal-radius: var(--savvy-radius-lg, 8px);
	--savvy-portal-radius-sm: var(--savvy-radius-md, 6px);
	--savvy-portal-shadow: var(--sv-portal-shadow, 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.08));
	--savvy-portal-tap: 44px;

	/* The canonical ecosystem role tokens this sheet reads —
	   --af-color-border-control and the two fixed focus-ring halves — are
	   declared ONCE, on :root in assets/css/savvyvault-tokens.css, which is
	   enqueued alongside this file at frontend/class-portal-assets.php:57.
	   Declaring a second copy here would put one value under one name in two
	   files, which docs/DESIGN-TOKEN-RECONCILIATION.md section 2 names as the
	   drift surface. */
}

/* 7F.6: the portal previously capped at 940px, which left wide empty margins
   on desktop (“squashed in the middle”). It follows --savvy-portal-max,
   declared above — tune it with one line of custom CSS; the selector is in
   the block comment above. */
.savvy-vault-portal {
	max-width: var(--savvy-portal-max, 1200px);
	margin: 40px auto 64px;
	padding: 0 20px;
	background: var(--sv-portal-wrapper-bg, transparent);
	color: var(--savvy-portal-text);
	line-height: var(--savvy-leading-base, 1.6);
	box-sizing: border-box;
}

.savvy-vault-portal *,
.savvy-vault-portal *::before,
.savvy-vault-portal *::after {
	box-sizing: border-box;
}

/* The login view is narrower and centred. */
.savvy-vault-portal--login {
	max-width: 480px;
}

/* ── Typography ─────────────────────────────────────────────────────────── */

.savvy-vault-portal h1 {
	color: var(--sv-portal-heading-ink, var(--savvy-portal-ink));
	font-size: var(--savvy-text-3xl, 1.875rem);
	line-height: var(--savvy-leading-3xl, 1.3);
	margin: 0 0 6px;
}

.savvy-vault-portal h2 {
	color: var(--sv-portal-heading-ink, var(--savvy-portal-ink));
	font-size: var(--savvy-text-xl, 1.25rem);
	line-height: var(--savvy-leading-xl, 1.4);
	margin: 32px 0 12px;
}

.savvy-vault-portal h3 {
	line-height: var(--savvy-leading-base, 1.6);
}

.savvy-vault-portal__lede {
	color: var(--savvy-portal-muted);
	margin: 0 0 24px;
	font-size: var(--savvy-text-lg, 1.125rem);
	line-height: var(--savvy-leading-lg, 1.55);
}

.savvy-vault-portal__hint {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-base, 1rem);
	line-height: var(--savvy-leading-base, 1.6);
	margin-top: 16px;
}

.savvy-vault-portal a {
	color: var(--savvy-portal-accent);
	text-decoration: none;
}

.savvy-vault-portal a:hover {
	color: var(--savvy-portal-accent-hover);
	text-decoration: underline;
}

/* ── C.4 (W14-2B): the focus indicator is TWO-TONE, and fixed ────────────
 * Public Website Specification §6.2.6, adopted for this family by WP UI
 * Specification §25.3. A 2px fixed-inner outline at 2px offset, inside a 6px
 * fixed-outer halo. The two halves separate from each other at 17.85:1, so
 * the indicator reads as an indicator on any ground.
 *
 * WHY NOT RE-TUNE THE OLD RING. The single-colour ring measured 2.47:1 over
 * the dark-mode primary button fill (#8ab4ff on #2563eb) against the 3.0
 * floor of SC 1.4.11 — one live failure. Re-tuning fixes the surfaces you can
 * see and is UNFALSIFIABLE AT THE THIRD, because nothing in a stylesheet
 * records which fills a ring was tuned against. This project's own case
 * proves it: the dark-mode button fill was a correct accessibility repair
 * that silently invalidated a ring which had been right before it.
 *
 * NEITHER HALF MAY BE REMOVED (§6.2.6, normative). Across the eight band and
 * surface families in both modes, each half carries surfaces the other fails
 * on — #FFFFFF measures 1.00:1 on the light page and #0F172A measures 1.07:1
 * on the dark one.
 *
 * The outline paints above the box-shadow, so the reading outward from the
 * control is: 2px gap, 2px dark outline, then the white halo out to 6px. */
.savvy-vault-portal a:focus-visible,
.savvy-vault-portal button:focus-visible,
.savvy-vault-portal input:focus-visible,
.savvy-vault-portal select:focus-visible,
.savvy-vault-portal summary:focus-visible,
.savvy-vault-portal textarea:focus-visible,
.savvy-vault-portal-chrome a:focus-visible,
.savvy-vault-portal-chrome button:focus-visible {
	/* The two colour reads carry a literal fallback and the geometry reads do
	   not, and the asymmetry is deliberate. An unresolved custom property in
	   `outline` is invalid at computed-value time, so the whole declaration
	   falls to `initial` — NO FOCUS RING AT ALL, an accessibility regression
	   rather than a cosmetic one. The geometry tokens are declared in this
	   same file and cannot be missing; the colours live in
	   savvyvault-tokens.css, which is a separate enqueue. Raised by the
	   W14-2B code review. The duplicated literal is the drift surface
	   docs/DESIGN-TOKEN-RECONCILIATION.md section 2 names, and it is accepted
	   here because the failure mode it guards against is worse. */
	outline: var(--savvy-portal-focus-width) solid var(--af-color-focus-ring-fixed-inner, #0F172A);
	outline-offset: 2px;
	box-shadow: 0 0 0 var(--savvy-portal-focus-halo) var(--af-color-focus-ring-fixed-outer, #FFFFFF);
}

/* ── Top navigation strip ───────────────────────────────────────────────── */

.savvy-vault-portal__nav {
	display: flex;
	flex-wrap: wrap;
	gap: 8px 18px;
	align-items: center;
	padding-bottom: 14px;
	margin-bottom: 24px;
	border-bottom: 1px solid var(--savvy-portal-line);
	font-size: var(--savvy-text-base, 1rem);
}

.savvy-vault-portal__nav a {
	display: inline-flex;
	align-items: center;
	min-height: var(--savvy-portal-tap);
	padding: 0 4px;
	color: var(--savvy-portal-muted);
	font-size: max(1rem, 16px);
	font-weight: 500;
}

.savvy-vault-portal__nav a.is-current {
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	font-weight: 700;
}

.savvy-vault-portal__nav-spacer {
	margin-left: auto;
}

/* ── 7F.1: collapsible nav (≤720px) ──
   portal.js adds .savvy-vault-portal__nav--js + injects the toggle button.
   Progressive enhancement: with JS absent the --js class never lands and
   the nav renders fully expanded at every width, exactly as before. */

.savvy-vault-portal__nav-toggle {
	display: none;
}


/* ── Notices ────────────────────────────────────────────────────────────── */

.savvy-vault-portal__notice {
	border-radius: var(--savvy-radius-md, 6px);
	padding: 12px 14px;
	margin: 0 0 18px;
	font-size: var(--savvy-text-base, 1rem);
	border: 1px solid transparent;
}

.savvy-vault-portal__notice--ok {
	background: var(--savvy-portal-ok-bg);
	color: var(--savvy-portal-ok-ink);
	border-color: #bfe3cc;
}

.savvy-vault-portal__notice--error {
	background: var(--savvy-portal-error-bg);
	color: var(--savvy-portal-error-ink);
	border-color: #f3c0bd;
}

.savvy-vault-portal__notice--warn {
	background: var(--savvy-portal-warn-bg);
	color: var(--savvy-portal-warn-ink);
	border-color: #f0dca8;
}

/* The update-available banner on the dashboard. */
.savvy-vault-portal__banner {
	display: flex;
	align-items: center;
	gap: 10px;
	background: var(--savvy-portal-warn-bg);
	color: var(--savvy-portal-warn-ink);
	border: 1px solid #f0dca8;
	border-radius: var(--savvy-radius-md, 6px);
	padding: 12px 14px;
	margin: 0 0 22px;
	font-size: var(--savvy-text-base, 1rem);
}

/* ── Forms ──────────────────────────────────────────────────────────────── */

.savvy-vault-portal__form {
	margin: 8px 0 0;
}

.savvy-vault-portal__field {
	margin-bottom: 16px;
}

.savvy-vault-portal label {
	display: block;
	font-weight: 600;
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	margin-bottom: 6px;
	font-size: var(--savvy-text-base, 1rem);
}

.savvy-vault-portal input[type="text"],
.savvy-vault-portal input[type="email"],
.savvy-vault-portal input[type="tel"],
.savvy-vault-portal select {
	display: block;
	width: 100%;
	max-width: 420px;
	min-height: var(--savvy-portal-tap);
	padding: 10px 12px;
	/* W14-2B: a border whose presence identifies a control. Removing it
	   leaves a white box on a white card, so it is border-control and not
	   the decorative --savvy-portal-line (Public Website Specification
	   §6.2.5, ruled a contradiction for us at ecosystem Wave 13Z §1). */
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--savvy-portal-surface);
	color: var(--savvy-portal-text);
	/* 1rem = 16px at the default root size. Brand & Design §4.5, normative. */
	font-size: max(1rem, var(--savvy-text-base, 1rem));
}

/* 7F.3: textareas were never in the shared field rule above, so message
   boxes (new support request) fell back to the browser's tiny default size
   instead of aligning with their sibling fields. Same treatment, vertical
   resize allowed. (Reply-thread textareas keep their own wider rule.) */
.savvy-vault-portal textarea {
	display: block;
	width: 100%;
	max-width: 420px;
	min-height: 96px;
	padding: 10px 12px;
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--savvy-portal-surface);
	color: var(--savvy-portal-text);
	font: inherit;
	font-size: max(1rem, var(--savvy-text-base, 1rem));
	resize: vertical;
}

.savvy-vault-portal input[readonly] {
	background: var(--savvy-portal-surface-2);
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__field-note {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
	margin-top: 6px;
}

/* ── Buttons ────────────────────────────────────────────────────────────── */

.savvy-vault-portal__btn,
.savvy-vault-portal a.savvy-vault-portal__btn,
.savvy-vault-portal button.savvy-vault-portal__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: var(--savvy-portal-tap);
	padding: 10px 20px;
	border: 1px solid transparent;
	border-radius: var(--sv-portal-button-radius, var(--savvy-radius-md, 6px));
	background: var(--sv-portal-action-primary-bg, var(--savvy-portal-action-primary-bg, var(--savvy-portal-ink)));
	/* W14-13: --savvy-portal-ink is the OPERATOR's primary_color once they set
	   one, and this ink was a white literal over it — 1.2454:1 at a light
	   choice such as #FDE68A, against SC 1.4.3's 4.5:1. The fallback is the
	   unchanged #fff, so a site that has set no colour renders exactly as
	   before; PortalAssets emits --savvy-portal-on-brand only when there is an
	   operator fill to decide against. */
	color: var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, var(--savvy-portal-on-brand, #fff)));
	font-size: max(1rem, var(--savvy-text-base, 1rem));
	font-weight: 600;
	line-height: 1;
	cursor: pointer;
	text-decoration: none;
	transition: background-color 0.15s ease;
}

.savvy-vault-portal__btn:hover,
.savvy-vault-portal a.savvy-vault-portal__btn:hover {
	background: var(--sv-portal-action-primary-bg-hover, var(--savvy-portal-action-primary-bg-hover, var(--savvy-portal-ink-hover)));
	/* The hover fill is a DIFFERENT colour (darken() of the operator's), so it
	   carries its own decided ink rather than reusing the resting one. */
	color: var(--sv-portal-action-primary-ink-hover, var(--savvy-portal-action-primary-ink-hover, var(--savvy-portal-on-brand-hover, var(--savvy-portal-on-brand, #fff))));
	text-decoration: none;
}

.savvy-vault-portal button.savvy-vault-portal__btn[disabled],
.savvy-vault-portal button.savvy-vault-portal__btn[aria-disabled="true"],
.savvy-vault-portal a.savvy-vault-portal__btn[aria-disabled="true"] {
	background: #9aa4b2;
	color: #fff;
	cursor: default;
}

.savvy-vault-portal__btn--secondary,
.savvy-vault-portal a.savvy-vault-portal__btn--secondary,
.savvy-vault-portal button.savvy-vault-portal__btn--secondary {
	background: var(--sv-portal-action-secondary-bg, var(--savvy-portal-surface));
	color: var(--sv-portal-action-secondary-ink, var(--sv-portal-text-strong, var(--savvy-portal-emphasis)));
	/* Unfilled button: its fill IS the card behind it, so the border is the
	   only thing identifying the control. §6.2.5 names unfilled buttons. */
	border-color: var(--af-color-border-control, #6B7280);
	border-width: var(--af-border-width-control, 1px);
}

.savvy-vault-portal__btn--secondary:hover,
.savvy-vault-portal a.savvy-vault-portal__btn--secondary:where(:not([aria-disabled="true"])):hover,
.savvy-vault-portal button.savvy-vault-portal__btn--secondary:where(:not([disabled]):not([aria-disabled="true"])):hover {
	background: var(--sv-portal-action-secondary-bg-hover, var(--savvy-portal-surface-2));
	color: var(--sv-portal-action-secondary-ink, var(--sv-portal-text-strong, var(--savvy-portal-emphasis)));
}

/* W14-77: disabled secondaries retain their surface role; the shared
   keyboard focus indicator above remains independent of action paint. */
.savvy-vault-portal button.savvy-vault-portal__btn--secondary[disabled],
.savvy-vault-portal button.savvy-vault-portal__btn--secondary[aria-disabled="true"],
.savvy-vault-portal a.savvy-vault-portal__btn--secondary[aria-disabled="true"] {
	background: var(--sv-portal-action-secondary-bg, var(--savvy-portal-surface));
	color: var(--sv-portal-action-secondary-ink, var(--sv-portal-text-muted, var(--savvy-portal-muted)));
	cursor: default;
}

.savvy-vault-portal button.savvy-vault-portal__btn--danger,
.savvy-vault-portal a.savvy-vault-portal__btn--danger {
	background: var(--savvy-portal-error-bg);
	color: var(--savvy-portal-error-ink);
	/* W14-67: button/anchor selectors must outrank the primary-action rule.
	   The error background and ink stay paired in both colour modes; the
	   canonical control border identifies the button independently of red. */
	border-color: var(--af-color-border-control, #6B7280);
}

.savvy-vault-portal button.savvy-vault-portal__btn--danger:hover,
.savvy-vault-portal a.savvy-vault-portal__btn--danger:hover {
	background: var(--savvy-portal-error-bg);
	color: var(--savvy-portal-error-ink);
}

.savvy-vault-portal__btn--small,
.savvy-vault-portal a.savvy-vault-portal__btn--small,
.savvy-vault-portal button.savvy-vault-portal__btn--small {
	min-height: 44px;
	padding: 6px 12px;
}

/* ── Product card grid (dashboard) ──────────────────────────────────────── */

.savvy-vault-portal__products {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr));
	gap: 18px;
	margin: 18px 0 0;
	padding: 0;
	list-style: none;
}

.savvy-vault-portal__card {
	display: flex;
	flex-direction: column;
	background: var(--savvy-portal-surface);
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	box-shadow: var(--savvy-portal-shadow);
	padding: 18px;
}

.savvy-vault-portal__card-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 10px;
}

.savvy-vault-portal__card-title {
	font-size: var(--savvy-text-lg, 1.125rem);
	font-weight: 700;
	color: var(--sv-portal-heading-ink, var(--savvy-portal-ink));
	margin: 0;
	line-height: var(--savvy-leading-lg, 1.55);
}

.savvy-vault-portal__card-meta {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
	margin: 8px 0 14px;
}

.savvy-vault-portal__card-actions {
	display: flex;
	flex-wrap: wrap;
	gap: 10px;
	margin-top: auto;
}

/* Tier badge. */
.savvy-vault-portal__badge {
	display: inline-block;
	/* W14-3: 0.72rem was 11.52px — below the 12px rung and below what
	   Brand & Design A.5.5 calls comfortable. It mattered less when the badge
	   said one of two known words; it carries an operator-authored tier name
	   now, so it moves onto the scale. */
	font-size: var(--savvy-text-xs, 0.75rem);
	font-weight: 700;
	letter-spacing: 0.02em;
	text-transform: uppercase;
	padding: 3px 8px;
	border-radius: 999px;
	white-space: nowrap;
}

.savvy-vault-portal__badge--premium {
	background: var(--savvy-portal-ink);
	/* W14-3: the ink must move WITH the fill.
	   `--savvy-portal-ink` inverts (#1a2332 light, #e8edf5 dark) and this was a hardcoded
	   #fff that did not, so the badge measured 1.176:1 in dark mode — the tier
	   invisible, and then carried by nothing at all. `--savvy-portal-surface` is the
	   token that inverts in the opposite direction, which is exactly the
	   pairing this needs: white on navy in light, near-black on pale in dark. */
	/* W14-13: the W14-3 pairing below is right while the fill INVERTS. It stops
	   being right the moment an operator pins --savvy-portal-ink to a fixed
	   hex, because then the fill stands still and this ink keeps moving —
	   Public Website Specification §6.2.6's exact geometry. The operator case
	   now falls through to the decided ink; the default keeps the pairing. */
	color: var(--savvy-portal-on-brand, var(--savvy-portal-surface));
}

/* Only a published strong role changes the premium badge's fill. Querying
   that role on an ancestor keeps a surface-only host override from replacing
   the saved operator's SurfaceInk pairing. All portal parents are style query
   containers; no size containment or markup change is needed. */
@container style(--sv-portal-text-strong) {
	.savvy-vault-portal__badge--premium {
		background: var(--savvy-portal-emphasis);
		color: var(--sv-portal-surface, var(--savvy-portal-surface));
	}
}

.savvy-vault-portal__badge--light {
	background: var(--savvy-portal-surface-2);
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	border: 1px solid var(--savvy-portal-line);
}

/* Status pill on a card / version row. */
.savvy-vault-portal__status {
	font-size: var(--savvy-text-sm, 0.875rem);
	font-weight: 600;
}

.savvy-vault-portal__status--current {
	color: var(--savvy-portal-ok-ink);
}

.savvy-vault-portal__status--update {
	color: var(--savvy-portal-warn-ink);
}

/* ── Marketplace strip ──────────────────────────────────────────────────── */

.savvy-vault-portal__market {
	margin-top: 36px;
	padding-top: 22px;
	border-top: 1px solid var(--savvy-portal-line);
}

.savvy-vault-portal__market-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
	gap: 14px;
	margin: 14px 0 0;
	padding: 0;
	list-style: none;
}

.savvy-vault-portal__market-item {
	background: var(--savvy-portal-surface-2);
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-md, 6px);
	padding: 14px;
}

.savvy-vault-portal__market-item h3 {
	font-size: var(--savvy-text-base, 1rem);
	margin: 0 0 4px;
	color: var(--sv-portal-heading-ink, var(--savvy-portal-ink));
}

.savvy-vault-portal__price {
	font-weight: 700;
	color: var(--savvy-portal-emphasis);
}

/* ── Tables (orders, sessions, version history) ─────────────────────────── */

.savvy-vault-portal__table {
	width: 100%;
	border-collapse: collapse;
	margin: 10px 0 0;
	font-size: var(--savvy-text-base, 1rem);
}

.savvy-vault-license-unit { margin-block: var(--savvy-space-4, 1rem); }
.savvy-vault-license-unit form + form { margin-top: var(--savvy-space-3, 0.75rem); }
.savvy-vault-license-terms { max-height: 18rem; overflow: auto; padding: var(--savvy-space-3, 0.75rem); margin-block: var(--savvy-space-3, 0.75rem); border: 1px solid var(--savvy-portal-line); background: var(--savvy-portal-surface-muted); white-space: normal; }
.savvy-vault-license-terms:focus-visible { outline: 2px solid var(--af-color-focus-ring-fixed-inner, #0F172A); outline-offset: 2px; box-shadow: 0 0 0 6px var(--af-color-focus-ring-fixed-outer, #FFFFFF); }

.savvy-vault-portal__table th,
.savvy-vault-portal__table td {
	text-align: left;
	padding: 10px 12px;
	border-bottom: 1px solid var(--savvy-portal-line);
	vertical-align: top;
}

.savvy-vault-portal__table th {
	color: var(--savvy-portal-muted);
	font-weight: 600;
	font-size: var(--savvy-text-sm, 0.875rem);
	text-transform: uppercase;
	letter-spacing: 0.02em;
}

.savvy-vault-portal__table tbody tr:hover {
	background: var(--savvy-portal-surface-2);
}

/* Toolbar above a table (filters + actions). */
.savvy-vault-portal__toolbar {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
	margin: 18px 0 4px;
}

.savvy-vault-portal__toolbar label {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin: 0;
	font-weight: 500;
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__toolbar select {
	width: auto;
	min-width: 130px;
	min-height: 36px;
	padding: 6px 10px;
}

.savvy-vault-portal__toolbar-spacer {
	margin-left: auto;
}

/* ── Account: sessions + danger zone ────────────────────────────────────── */

.savvy-vault-portal__danger {
	margin-top: 36px;
	padding: 18px;
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--savvy-radius-lg, 8px);
	background: var(--savvy-portal-error-bg);
	color: var(--savvy-portal-error-ink);
}

.savvy-vault-portal__danger h2,
.savvy-vault-portal__danger .savvy-vault-portal__field-note {
	margin-top: 0;
	color: var(--savvy-portal-error-ink);
}

/* ── Empty states ───────────────────────────────────────────────────────── */

.savvy-vault-portal__empty {
	background: var(--savvy-portal-surface-2);
	border: 1px dashed var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 26px;
	text-align: center;
	color: var(--savvy-portal-muted);
}

/* Hidden helper for JS to toggle. */
.savvy-vault-portal [hidden] {
	display: none !important;
}

.savvy-vault-portal__spinner {
	font-size: var(--savvy-text-base, 1rem);
	color: var(--savvy-portal-muted);
}

/* ── Responsive ─────────────────────────────────────────────────────────── */

/* 7F.1: re-homed 640px → 720px (general stacking tier; widens coverage). */

@media (prefers-reduced-motion: reduce) {
	.savvy-vault-portal__btn {
		transition: none;
	}
}

/* ── Portal chrome (0.12.3): minimal mode header/footer ─────────────────── */
/* Used when the portal "Header & footer" mode is Minimal. Scoped to its own
   wrapper so it never collides with theme or Avada chrome (custom mode). */
.savvy-vault-portal-chrome {
	background: var(--savvy-portal-surface);
	border-bottom: 1px solid var(--savvy-portal-line);
}
.savvy-vault-portal-chrome--footer {
	border-bottom: 0;
	border-top: 1px solid var(--savvy-portal-line);
	margin-top: 40px;
}
.savvy-vault-portal-chrome__inner {
	/* 7F.6: tracks the content width (+20px keeps the historical edge
	   alignment between chrome and content padding). */
	max-width: calc(var(--savvy-portal-max, 1200px) + 20px);
	margin: 0 auto;
	padding: 16px 20px;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
}
.savvy-vault-portal-chrome__brand img {
	max-height: 40px;
	width: auto;
	display: block;
}
.savvy-vault-portal-chrome__site-name {
	font-weight: 700;
	font-size: var(--savvy-text-lg, 1.125rem);
	color: var(--sv-portal-heading-ink, var(--savvy-portal-ink));
	text-decoration: none;
}
.savvy-vault-portal-chrome__menu {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 18px;
}
.savvy-vault-portal-chrome__menu a {
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	text-decoration: none;
	font-weight: 600;
	display: inline-block;
	min-height: 44px;
	line-height: 44px;
}
.savvy-vault-portal-chrome__menu a:hover,
.savvy-vault-portal-chrome__menu a:focus-visible {
	color: var(--sv-portal-link-hover, var(--savvy-portal-ink-hover));
	text-decoration: underline;
}
.savvy-vault-portal-chrome__copy {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
}
.savvy-vault-portal-main {
	display: block;
}

/* ── 7F.1: sticky footer (minimal / custom standalone modes) ──
   The standalone document (PortalChrome::open_document) stamps this body
   class; header + main + footer become a full-viewport flex column so the
   footer sits at the bottom of the screen even when the page content is
   short, instead of floating up after it. Theme mode is untouched. */
body.savvy-vault-portal-standalone {
	min-height: 100vh;
	display: flex;
	flex-direction: column;
}

body.savvy-vault-portal-standalone .savvy-vault-portal-main {
	flex: 1 0 auto;
	width: 100%;
}
/* 7F.1: re-homed 640px → 720px (general stacking tier; widens coverage). */

/* ── Catalog card feature image (0.12.9) ───────────────────────────────── */
.savvy-vault-portal__card-media {
	margin-bottom: 14px;
	border-radius: var(--sv-portal-media-radius, var(--savvy-radius-md, 6px));
	overflow: hidden;
	background: var(--savvy-portal-surface-2);
}
.savvy-vault-portal__card-image {
	display: block;
	width: 100%;
	height: 160px;
	object-fit: cover;
}

/* ── Minimal-mode footer links (0.12.10) ───────────────────────────────── */
.savvy-vault-portal-chrome__footer-links {
	display: flex;
	flex-wrap: wrap;
	gap: 18px;
}
.savvy-vault-portal-chrome__footer-links a {
	color: var(--savvy-portal-muted);
	text-decoration: none;
	font-size: var(--savvy-text-sm, 0.875rem);
}
.savvy-vault-portal-chrome__footer-links a:hover,
.savvy-vault-portal-chrome__footer-links a:focus-visible {
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	text-decoration: underline;
}

/* ── Phase 7B: catalog toolbar, bundles, support, video ────────────────── */

/* Catalog search + tier filter toolbar */
.savvy-vault-portal__catalog-toolbar {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
	margin: 12px 0 20px;
}
.savvy-vault-portal__catalog-toolbar .savvy-vault-portal__search {
	flex: 1 1 240px;
	min-width: 0;
}
.savvy-vault-portal__catalog-toolbar input[type="search"],
.savvy-vault-portal__catalog-toolbar select {
	width: 100%;
	box-sizing: border-box;
	/* 7F.3: the select inherited the 44px tap-target minimum from the shared
	   field rule but input[type="search"] never did, leaving the two controls
	   at different heights. Pin both to the same minimum. */
	min-height: var(--savvy-portal-tap);
	padding: 9px 12px;
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--savvy-radius-lg, 8px);
	/* W14-3: raised from 15px, and stated because a first attempt at this fix
	   ADDED a 16px declaration higher in the same rule and left the 15px below
	   it — later declaration wins, so the fix was inert and the control was
	   still 15px.
	   W14-2B / ecosystem E.54: the authority CITED here was wrong and is
	   corrected rather than quietly replaced. It read "Public Website
	   Specification 6.3.2". That document governs afsavvyapps.com and is not
	   a parent of any WordPress-family product; the Layer 1 authority that
	   binds this plugin by inheritance is BRAND & DESIGN PRINCIPLES §4.5,
	   restated in the operative paragraphs of WP UI Specification §3.5 and
	   §10.1. A product conforming to the right value for the wrong reason
	   conforms only while the two documents agree. */
	font-size: max(1rem, 16px);
	background: var(--sv-portal-catalog-control-bg, var(--sv-portal-surface, #fff));
	color: var(--sv-portal-text, var(--savvy-portal-text));
}
.savvy-vault-portal__catalog-toolbar .savvy-vault-portal__filter {
	flex: 0 0 auto;
}
.savvy-vault-portal__group-heading {
	margin: 26px 0 10px;
}

/* Bundle card */
.savvy-vault-portal__card--bundle {
	border-color: var(--savvy-portal-emphasis);
}
.savvy-vault-portal__bundle-includes {
	margin: 4px 0 10px 0;
	padding-left: 18px;
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
}
.savvy-vault-portal__bundle-includes li {
	margin: 2px 0;
}

/* Support threads */
.savvy-vault-portal__thread {
	border: 1px solid var(--savvy-portal-line);
	border-radius: 10px;
	padding: 10px 14px;
	margin-bottom: 10px;
	background: #fff;
}
.savvy-vault-portal__thread > summary {
	cursor: pointer;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	list-style: none;
}
.savvy-vault-portal__thread > summary::-webkit-details-marker { display: none; }
.savvy-vault-portal__thread-subject { font-weight: 600; }
.savvy-vault-portal__thread-meta {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-xs, 0.75rem);
	margin-left: auto;
}
.savvy-vault-portal__messages {
	list-style: none;
	margin: 12px 0 0;
	padding: 0;
}
.savvy-vault-portal__message {
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 10px 12px;
	margin-bottom: 8px;
	background: #f8fafc;
}
.savvy-vault-portal__message--out {
	background: #eef4fb;
}
.savvy-vault-portal__message-head {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-bottom: 4px;
	font-size: var(--savvy-text-sm, 0.875rem);
	line-height: var(--savvy-leading-sm, 1.5);
}
.savvy-vault-portal__message-body {
	/* W14-2B: 15px -> 16px. This is the BODY of a support message on a
	   customer-facing thread — not metadata, and not a form input. Brand &
	   Design §4.5: "Body text is 16px at every viewport. There is no reduced
	   mobile base." The adjacent 12px thread meta and 13px message HEAD are
	   metadata, were surfaced to the ecosystem as such, and are deliberately
	   left (backlog §5.20 item 5). This line is neither, and no register held
	   it. */
	font-size: max(1rem, 16px);
	line-height: 1.6;
}
.savvy-vault-portal__banner--ok {
	background: #e6f4ea;
	border: 1px solid #8ed1a8;
	color: #1c6b3f;
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 12px 14px;
}

/* Video chapters */
.savvy-vault-portal__videos { margin-top: 12px; }
.savvy-vault-portal__videos-section { margin: 18px 0 8px; }
.savvy-vault-portal__video {
	margin-bottom: 18px;
}
.savvy-vault-portal__video-head {
	display: flex;
	align-items: center;
	gap: 10px;
	margin-bottom: 4px;
}
.savvy-vault-portal__video-frame {
	position: relative;
	width: 100%;
	padding-top: 56.25%; /* 16:9 */
	border-radius: 10px;
	overflow: hidden;
	background: #000;
}
.savvy-vault-portal__video-frame iframe,
.savvy-vault-portal__video-frame video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}
.savvy-vault-portal__video-locked {
	padding: 14px 16px;
	border: 1px dashed var(--savvy-portal-line);
	border-radius: 10px;
	color: var(--savvy-portal-muted);
	background: #f8fafc;
	font-size: var(--savvy-text-sm, 0.875rem);
	line-height: var(--savvy-leading-sm, 1.5);
}

/* ── 0.19.2: owned-version chips + in-portal support reply ─────────────── */

.savvy-vault-portal__owned-versions {
	margin: 8px 0 0;
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	align-items: center;
	font-size: var(--savvy-text-sm, 0.875rem);
}
.savvy-vault-portal__owned-versions-label {
	color: var(--savvy-portal-muted);
}
.savvy-vault-portal__version-chip {
	display: inline-block;
	padding: 2px 9px;
	border-radius: 999px;
	background: #eef2f7;
	border: 1px solid #dbe3ec;
	color: #334155;
	font-size: var(--savvy-text-xs, 0.75rem);
	line-height: 1.5;
}
.savvy-vault-portal__version-chip.is-current {
	background: var(--sv-portal-version-current-bg, #1F3A5F);
	border-color: var(--sv-portal-version-current-bg, #1F3A5F);
	color: var(--sv-portal-version-current-ink, #fff);
}

.savvy-vault-portal__reply-form {
	margin: 12px 0 2px;
}
.savvy-vault-portal__reply-form textarea {
	width: 100%;
	box-sizing: border-box;
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 9px 12px;
	font: inherit;
	/* W14-2B: 14px -> 16px. Brand & Design §4.5, normative — no form input
	   renders below 16px, at any viewport, in any surrounding density. */
	font-size: max(1rem, 16px);
	resize: vertical;
}
.savvy-vault-portal__reply-actions {
	margin-top: 8px;
	display: flex;
	justify-content: flex-end;
}
.savvy-vault-portal__thread[open] {
	border-color: #cdd8e4;
	box-shadow: 0 1px 3px rgba(15, 23, 42, .06);
}

/* ── 0.19.4 — Product / bundle detail pages + owned-product sidebar ─────── */

/* Long-form description (renders wpautop output). */
.savvy-vault-portal__prose {
	margin: 6px 0 4px;
	color: var(--savvy-portal-text);
}
.savvy-vault-portal__prose p {
	margin: 0 0 0.85em;
}
.savvy-vault-portal__prose p:last-child {
	margin-bottom: 0;
}

/* Two-column detail layout: aside (owned products) + main content. With no
   aside the layout collapses to a single full-width column automatically. */
.savvy-vault-portal__detail--has-aside {
	display: flex;
	align-items: flex-start;
	gap: 28px;
}
.savvy-vault-portal__detail-aside {
	flex: 0 0 220px;
	max-width: 220px;
	position: sticky;
	top: 24px;
	background: var(--savvy-portal-surface-2);
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 16px;
}
.savvy-vault-portal__detail-main {
	flex: 1 1 auto;
	min-width: 0;
}
.savvy-vault-portal__aside-title {
	margin: 0 0 10px;
	font-size: var(--savvy-text-xs, 0.75rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--savvy-portal-muted);
}
.savvy-vault-portal__aside-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
.savvy-vault-portal__aside-link {
	display: block;
	padding: 8px 10px;
	border-radius: var(--savvy-radius-md, 6px);
	color: var(--savvy-portal-accent);
	text-decoration: none;
	font-size: var(--savvy-text-base, 1rem);
	line-height: 1.35;
}
.savvy-vault-portal__aside-link:hover,
.savvy-vault-portal__aside-link:focus-visible {
	background: var(--savvy-portal-surface);
	color: var(--savvy-portal-accent-hover);
}
.savvy-vault-portal__aside-link.is-current {
	background: var(--sv-portal-nav-current-bg, var(--sv-portal-action-primary-bg, var(--savvy-portal-action-primary-bg, var(--savvy-portal-ink))));
	/* W14-13: same operator fill as the primary button. */
	color: var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, var(--savvy-portal-on-brand, #fff))));
	font-weight: 600;
}
.savvy-vault-portal__aside-link.is-current:hover {
	background: var(--sv-portal-nav-current-bg-hover, var(--sv-portal-action-primary-bg-hover, var(--savvy-portal-action-primary-bg-hover, var(--savvy-portal-ink-hover))));
	/* W14-13 round 2, found by design QA. The resting state seven lines above
	   was given the decided ink and THIS HOVER WAS NOT, while
	   --savvy-portal-on-brand-hover -- created by this same iteration precisely
	   because the hover fill is a different colour -- was emitted and read
	   nowhere. On a light primary_color the resting state was legible and the
	   hover was not. */
	color: var(--sv-portal-nav-current-ink-hover, var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink-hover, var(--savvy-portal-action-primary-ink-hover, var(--savvy-portal-on-brand-hover, var(--savvy-portal-on-brand, #fff))))));
}

/* Detail feature image (bundle detail). */
.savvy-vault-portal__detail-media {
	margin: 4px 0 18px;
	border-radius: var(--savvy-radius-lg, 8px);
	overflow: hidden;
	background: var(--savvy-portal-surface-2);
}
.savvy-vault-portal__detail-image {
	display: block;
	width: 100%;
	height: auto;
}

/* Detail price + action row. */
.savvy-vault-portal__detail-price {
	font-size: var(--savvy-text-xl, 1.25rem);
	margin: 16px 0 12px;
}
.savvy-vault-portal__detail-actions {
	margin-top: 0;
	margin-bottom: 4px;
	align-items: center;
}

/* Bundle component list. */
.savvy-vault-portal__component-list {
	list-style: none;
	margin: 12px 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 10px;
}
.savvy-vault-portal__component {
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	background: var(--savvy-portal-surface);
	padding: 12px 14px;
}
.savvy-vault-portal__component-name {
	display: inline-block;
	font-weight: 600;
	color: var(--savvy-portal-accent);
	text-decoration: none;
}
.savvy-vault-portal__component-name:hover,
.savvy-vault-portal__component-name:focus-visible {
	color: var(--savvy-portal-accent-hover);
	text-decoration: underline;
}
.savvy-vault-portal__component-desc {
	display: block;
	margin-top: 3px;
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-base, 1rem);
}

/* Releases (versions) with file lists + per-version download. */
.savvy-vault-portal__releases {
	display: flex;
	flex-direction: column;
	gap: 14px;
	margin-top: 12px;
}
.savvy-vault-portal__release {
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	background: var(--savvy-portal-surface);
	padding: 14px 16px;
}
.savvy-vault-portal__release.is-owned {
	border-color: var(--savvy-portal-emphasis);
	box-shadow: var(--savvy-portal-shadow);
}
.savvy-vault-portal__release-head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 8px;
}
.savvy-vault-portal__release-version {
	font-weight: 700;
	color: var(--sv-portal-heading-ink, var(--savvy-portal-ink));
	font-size: var(--savvy-text-lg, 1.125rem);
	line-height: var(--savvy-leading-lg, 1.55);
}
.savvy-vault-portal__release-date {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
	margin-left: auto;
}
.savvy-vault-portal__release-notes {
	margin: 8px 0 0;
	color: var(--savvy-portal-text);
	font-size: var(--savvy-text-base, 1rem);
}
.savvy-vault-portal__files {
	list-style: none;
	margin: 10px 0 0;
	padding: 0;
	border-top: 1px solid var(--savvy-portal-line);
}
.savvy-vault-portal__file {
	display: flex;
	justify-content: space-between;
	gap: 14px;
	padding: 7px 0;
	border-bottom: 1px solid var(--savvy-portal-surface-2);
	font-size: var(--savvy-text-base, 1rem);
}
.savvy-vault-portal__file:last-child {
	border-bottom: 0;
}
.savvy-vault-portal__file-name {
	color: var(--savvy-portal-text);
	word-break: break-word;
}
.savvy-vault-portal__file-size {
	color: var(--savvy-portal-muted);
	white-space: nowrap;
	flex: 0 0 auto;
}
.savvy-vault-portal__release-delivery {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, auto);
	gap: var(--savvy-space-2, 8px) var(--savvy-space-3, 12px);
	align-items: start;
}
.savvy-vault-portal__release-meta {
	grid-column: 1;
	min-width: 0;
	overflow-wrap: anywhere;
	font-family: var(--savvy-font-mono, inherit);
}
.savvy-vault-portal__release-meta .savvy-vault-portal__release-version {
	font-size: max(12px, var(--sv-library-release-meta-size, var(--savvy-text-lg, 1.125rem)));
}
.savvy-vault-portal__release-file-count {
	font-size: max(12px, var(--sv-library-release-meta-size, var(--savvy-text-sm, 0.875rem)));
	color: var(--savvy-portal-muted);
}
.savvy-vault-portal__release-action {
	grid-column: 2;
	grid-row: 1 / span 2;
	min-width: 0;
	max-width: 100%;
}
.savvy-vault-portal__release-caption,
.savvy-vault-portal__release-delivery > .savvy-vault-portal__release-note {
	grid-column: 1 / -1;
	margin: 0;
}
.savvy-vault-portal__release-caption {
	grid-column: 1;
}
.savvy-vault-portal__release-caption,
.savvy-vault-portal__release-state,
.savvy-vault-portal__release-note {
	font-size: max(16px, var(--savvy-text-base, 1rem));
	line-height: 1.6;
	color: var(--savvy-portal-muted);
	overflow-wrap: anywhere;
}
.savvy-vault-portal .savvy-vault-portal__btn.savvy-vault-portal__release-zip {
	padding: var(--sv-library-zip-padding, var(--savvy-space-2, 8px) var(--savvy-space-3, 12px));
	font-weight: var(--sv-library-zip-weight, 600);
	white-space: normal;
	max-width: 100%;
}
.savvy-vault-portal--product.sv-library-stack-active .savvy-vault-portal__release-delivery {
	display: flex;
	flex-direction: column;
	align-items: stretch;
}
.savvy-vault-portal.savvy-vault-portal--product.sv-library-stack-active .savvy-vault-portal__release-action .savvy-vault-portal__btn.savvy-vault-portal__release-zip {
	width: 100%;
	min-height: max(48px, 3rem);
}
@media (max-width: 479px) {
	.savvy-vault-portal__release-delivery {
		display: flex;
		flex-direction: column;
		align-items: stretch;
	}
}
.savvy-vault-portal__download {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 10px;
	margin-top: 12px;
}
.savvy-vault-portal__download-note {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
}
.savvy-vault-portal__download-note--pending {
	color: var(--savvy-portal-warn-ink);
	background: var(--savvy-portal-warn-bg);
	border-radius: var(--savvy-radius-md, 6px);
	padding: 8px 12px;
}

/* "Included in these bundles" list. */
.savvy-vault-portal__in-bundles {
	list-style: none;
	margin: 12px 0 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.savvy-vault-portal__in-bundles li {
	display: flex;
	align-items: baseline;
	gap: 10px;
}
.savvy-vault-portal__in-bundles a {
	color: var(--savvy-portal-accent);
	text-decoration: none;
	font-weight: 600;
}
.savvy-vault-portal__in-bundles a:hover,
.savvy-vault-portal__in-bundles a:focus-visible {
	color: var(--savvy-portal-accent-hover);
	text-decoration: underline;
}
.savvy-vault-portal__in-bundles-price {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
}

/* Deliverable 5: catalogue cards — Buy + View details remain equal width,
   equal height and centred when checkout is available. The row may wrap so a
   fail-closed availability notice can take a readable line of its own. */
.savvy-vault-portal--catalog .savvy-vault-portal__card-actions {
	flex-wrap: wrap;
	align-items: stretch;
}
.savvy-vault-portal--catalog .savvy-vault-portal__card-actions > .savvy-vault-portal__btn {
	flex: 1 1 0;
	min-width: 0;
}

/* W14-48: shortcode failure notices are direct action-row children. Keep the
   buyer-facing refusal and the remaining action on separate rows instead of
   squeezing the action below the 44px control floor. This also covers the
   same shortcode output on public product-detail licence cards. */
.savvy-vault-portal--catalog .savvy-vault-portal__card-actions > .savvy-vault-checkout-unavailable,
.savvy-vault-portal--catalog-product .savvy-vault-portal__card-actions > .savvy-vault-checkout-unavailable {
	flex: 1 0 100%;
	min-width: 0;
	max-width: 100%;
	margin: 0;
}

/* Detail layout stacks under 720px: the aside drops above the main column
   and its links flow as a horizontal wrap. 7F.1: re-homed 720px → 800px
   (wide two-column tier, matching the LMS layout; widens coverage). */

/* ── Support attachments (0.19.5) ──────────────────────────────────────── */
.savvy-vault-portal__banner--warn {
	background: var(--savvy-portal-warn-bg, #fff4e5);
	border: 1px solid var(--savvy-portal-warn-ink, #b26b00);
	color: var(--savvy-portal-warn-ink, #8a5200);
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 12px 14px;
}

/* File field (new-ticket + reply forms) */
.savvy-vault-portal__field--file input[type="file"] {
	display: block;
	margin-top: 4px;
	max-width: 100%;
	/* W14-2B: 14px -> 16px. Brand & Design §4.5, normative. */
	font-size: max(1rem, 16px);
}
.savvy-vault-portal__field--reply-file {
	margin: 8px 0 4px;
}

/* Attachment chips under a message */
.savvy-vault-portal__attachments {
	list-style: none;
	margin: 8px 0 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
}
.savvy-vault-portal__attachments li {
	margin: 0;
}
.savvy-vault-portal__attachment {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	max-width: 100%;
	padding: 5px 10px;
	border: 1px solid var(--savvy-portal-line, #d7d7dd);
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--savvy-portal-surface, #fff);
	text-decoration: none;
	color: var(--savvy-portal-accent);
	font-size: var(--savvy-text-sm, 0.875rem);
	line-height: var(--savvy-leading-sm, 1.5);
}
.savvy-vault-portal__attachment::before {
	content: "\2193"; /* downwards arrow — download affordance */
	font-weight: 700;
	opacity: .8;
}
.savvy-vault-portal__attachment:hover,
.savvy-vault-portal__attachment:focus {
	border-color: var(--savvy-portal-accent);
	background: var(--savvy-portal-surface-2);
}
.savvy-vault-portal__attachment-name {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 18rem;
}
.savvy-vault-portal__attachment-size {
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-xs, 0.75rem);
	flex: 0 0 auto;
}


/* ── Phase 7C: tutorials ──────────────────────────────────────────────── */

.savvy-vault-portal__tutorial-list {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
	gap: 16px;
	margin-top: 16px;
}

.savvy-vault-portal__tutorial-card-title {
	margin: 0 0 8px;
	font-size: var(--savvy-text-lg, 1.125rem);
}

.savvy-vault-portal__tutorial-card-title a {
	text-decoration: none;
}

.savvy-vault-portal__tutorial-section {
	margin-top: 28px;
}

.savvy-vault-portal__tutorial-section-title {
	margin: 0 0 12px;
}

.savvy-vault-portal__tutorial-chapter {
	margin-top: 16px;
}

.savvy-vault-portal__tutorial-chapter-title {
	margin: 0 0 8px;
}

.savvy-vault-portal__tutorial-text {
	margin: 12px 0;
	line-height: 1.6;
}

.savvy-vault-portal__tutorial-text p:first-child {
	margin-top: 0;
}

.savvy-vault-portal__tutorial-text p:last-child {
	margin-bottom: 0;
}

.savvy-vault-portal__tutorial-resource,
.savvy-vault-portal__tutorial-link {
	margin: 12px 0 0;
}


/* ── Phase 7D: LMS tutorial layout + progress ─────────────────────────── */

.savvy-vault-portal__lms {
	display: grid;
	grid-template-columns: 280px minmax(0, 1fr);
	gap: 24px;
	margin-top: 20px;
	align-items: start;
}

.savvy-vault-portal__lms-nav {
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	padding: 16px;
	background: var(--savvy-portal-surface);
	position: sticky;
	top: 16px;
}

.savvy-vault-portal__lms-progress {
	margin-bottom: 14px;
}

.savvy-vault-portal__lms-progress-label {
	display: block;
	font-size: var(--savvy-text-xs, 0.75rem);
	font-weight: 600;
	margin-bottom: 6px;
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__lms-progress-bar {
	height: 8px;
	border-radius: var(--savvy-radius-sm, 4px);
	/* The host surface pairs with its strong progress fill; the fixed border
	   still identifies the unfilled track against that same surface. */
	border: 1px solid var(--af-color-border-control, #6B7280);
	box-sizing: border-box;
	background: var(--sv-portal-surface, var(--savvy-portal-line));
	overflow: hidden;
}

.savvy-vault-portal__lms-progress-fill {
	height: 100%;
	border-radius: var(--savvy-radius-sm, 4px);
	background: var(--savvy-portal-emphasis);
	transition: width 0.2s ease;
}

.savvy-vault-portal__lms-nav-section {
	margin: 14px 0 6px;
	font-size: var(--savvy-text-xs, 0.75rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__lms-nav-list {
	list-style: none;
	margin: 0 0 6px;
	padding: 0;
}

.savvy-vault-portal__lms-nav-link {
	display: flex;
	align-items: baseline;
	gap: 8px;
	padding: 6px 8px;
	border-radius: var(--savvy-radius-md, 6px);
	text-decoration: none;
	line-height: 1.4;
}

.savvy-vault-portal__lms-nav-link:hover {
	background: var(--savvy-portal-line);
}
.savvy-vault-portal__lms-nav-link.is-current {
	background: var(--sv-portal-nav-current-bg, var(--sv-portal-action-primary-bg, var(--savvy-portal-action-primary-bg, var(--savvy-portal-ink))));
	/* W14-13, AND THIS ONE FAILED ON THE DEFAULT PATH, not only under an
	   operator colour. Unlike the primary button and the current aside link,
	   this rule has NO entry in the dark-mode override block below, so in dark
	   mode it filled with --savvy-portal-ink at its pale #e8edf5 and painted
	   #fff on it: 1.1756:1. W14-3 fixed precisely this on the premium badge
	   and this selector was added afterwards, so the fix never reached it —
	   the per-element shape that WP UI §25.4 warns re-opens the defect every
	   time a component is added. The fallback is now the INVERTING surface
	   token, which is the W14-3 pairing, and the operator case falls through
	   to the decided ink ahead of it. */
	color: var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, var(--savvy-portal-on-brand, var(--savvy-portal-surface)))));
}
.savvy-vault-portal__lms-nav-link.is-current .savvy-vault-portal__lms-nav-check {
	/* W14-13 round 2: this glyph sits INSIDE .is-current, so its ground is the
	   same operator fill the rule above just had decided for it. Found by
	   design QA ONE SELECTOR BELOW that fix, carrying the identical literal —
	   which is the census failure the comment above describes, demonstrated. */
	color: var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, var(--savvy-portal-on-brand, var(--savvy-portal-surface)))));
}

.savvy-vault-portal__lms-nav-check {
	flex: 0 0 14px;
	font-weight: 700;
	color: var(--savvy-portal-ok-ink);
}

.savvy-vault-portal__lms-nav-link.is-done:not(.is-current) .savvy-vault-portal__lms-nav-text {
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__lms-eyebrow {
	margin: 0 0 4px;
	font-size: var(--savvy-text-xs, 0.75rem);
	font-weight: 700;
	text-transform: uppercase;
	letter-spacing: 0.04em;
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__lms-chapter-title {
	margin: 0 0 10px;
}

.savvy-vault-portal__lms-controls {
	display: flex;
	align-items: center;
	gap: 12px;
	margin-top: 24px;
	padding-top: 16px;
	border-top: 1px solid var(--savvy-portal-line);
}

.savvy-vault-portal__lms-controls-prev {
	flex: 0 0 auto;
}

.savvy-vault-portal__lms-controls-mark {
	flex: 1 1 auto;
	text-align: center;
}

.savvy-vault-portal__lms-controls-next {
	flex: 0 0 auto;
	margin-left: auto;
}

.savvy-vault-portal__lms-done-state {
	font-weight: 600;
	color: var(--savvy-portal-ok-ink);
}

.savvy-vault-portal__lms-unmark {
	margin-left: 10px;
	font-size: var(--savvy-text-xs, 0.75rem);
	color: var(--savvy-portal-muted);
}

/* List-page progress badges. */
.savvy-vault-portal__tutorial-progress {
	margin: 4px 0 10px;
	font-size: var(--savvy-text-xs, 0.75rem);
	font-weight: 600;
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__tutorial-progress.is-progress {
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
}

.savvy-vault-portal__tutorial-progress.is-done {
	color: var(--savvy-portal-ok-ink);
}

/* Product/bundle detail-page tutorial signals. */
.savvy-vault-portal__tutorial-signals {
	margin: 14px 0 4px;
}

.savvy-vault-portal__tutorial-signal-note {
	margin: 6px 0 0;
	font-size: var(--savvy-text-sm, 0.875rem);
	font-weight: 600;
}


/* ── Phase 7E: pages, chapter groups, preview mode, reset ─────────── */

.savvy-vault-portal__lms-nav-chapter {
	display: flex;
	align-items: baseline;
	gap: 8px;
	margin: 10px 0 2px;
	padding: 2px 8px;
	font-weight: 600;
	font-size: var(--savvy-text-sm, 0.875rem);
	line-height: var(--savvy-leading-sm, 1.5);
}

.savvy-vault-portal__lms-nav-chapter .savvy-vault-portal__lms-nav-check {
	flex: 0 0 14px;
}

.savvy-vault-portal__lms-nav-list--pages {
	margin: 0 0 4px;
}

.savvy-vault-portal__lms-nav-list--pages .savvy-vault-portal__lms-nav-link {
	padding-left: 26px;
	font-size: var(--savvy-text-sm, 0.875rem);
	line-height: var(--savvy-leading-sm, 1.5);
}

.savvy-vault-portal__lms-nav-link.is-locked {
	opacity: 0.55;
	cursor: default;
}

.savvy-vault-portal__lms-preview-note {
	margin: 0 0 12px;
	padding: 8px 10px;
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--savvy-portal-line);
	font-size: var(--savvy-text-xs, 0.75rem);
	line-height: 1.45;
}

.savvy-vault-portal__lms-sample-note {
	margin-top: 24px;
	padding-top: 16px;
	border-top: 1px solid var(--savvy-portal-line);
}

.savvy-vault-portal__lms-reset {
	display: inline-block;
	margin-top: 8px;
	font-size: var(--savvy-text-xs, 0.75rem);
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__tutorial-progress.is-preview {
	color: var(--savvy-portal-muted);
	font-style: italic;
}

/* ── Phase 7E.1: collapsible contents menu ───────────────────── */

.savvy-vault-portal__lms-nav details {
	margin: 0;
}

.savvy-vault-portal__lms-nav summary {
	list-style: none;
	display: flex;
	align-items: baseline;
	gap: 6px;
	cursor: pointer;
	user-select: none;
}

.savvy-vault-portal__lms-nav summary::-webkit-details-marker {
	display: none;
}

.savvy-vault-portal__lms-nav summary::before {
	content: "\25B8";
	flex: 0 0 auto;
	font-size: var(--savvy-text-xs, 0.75rem);
	line-height: 1;
	color: var(--savvy-portal-muted);
	transition: transform 0.15s ease;
}

.savvy-vault-portal__lms-nav details[open] > summary::before {
	transform: rotate(90deg);
}

.savvy-vault-portal__lms-nav-chap {
	margin-left: 4px;
}

.savvy-vault-portal__lms-nav-chap .savvy-vault-portal__lms-nav-list--pages {
	margin-left: 10px;
}

/* ── 7F.3: support page tabs ──
   The tab bar ships hidden; portal.js reveals it and switches the two
   sections. Without JS both sections render stacked, exactly as before. */

.savvy-vault-portal__tabs {
	display: flex;
	gap: 4px;
	margin: 18px 0;
	border-bottom: 1px solid var(--savvy-portal-line);
}

.savvy-vault-portal__tab {
	appearance: none;
	background: none;
	border: 0;
	border-bottom: 3px solid transparent;
	margin-bottom: -1px;
	padding: 10px 14px;
	font: inherit;
	font-size: max(1rem, var(--savvy-text-base, 1rem));
	font-weight: 600;
	color: var(--savvy-portal-muted);
	cursor: pointer;
}

.savvy-vault-portal__tab.is-active {
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	border-bottom-color: #1F3A5F;
}

/* W14-2B: the hardcoded 2px #2E6CA4 ring that used to sit here has been
   removed. It bypassed the token entirely, so it was invisible to every
   audit of the ring, and it measured 3.16:1 on the dark page background —
   passing SC 1.4.11 by 0.16 and by accident. The global :focus-visible rule
   above now carries this control with the two-tone fixed indicator. */

/* ── 7F.3: marketplace cross-sell strip ──
   Horizontal scroll-snap track: 3 cards at desktop width, 2 ≤800px,
   one-and-a-peek ≤600px. Arrows + auto-advance are portal.js
   enhancements; without JS this is a native scrollable row. */

.savvy-vault-portal--strip {
	margin-top: 8px;
	padding-top: 8px;
	border-top: 1px solid var(--savvy-portal-line);
}

.savvy-vault-portal__market-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
}

.savvy-vault-portal__market-head h2 {
	margin: 12px 0;
}

.savvy-vault-portal__market-arrows {
	display: flex;
	gap: 8px;
}

.savvy-vault-portal__market-arrow {
	width: 36px;
	height: 36px;
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--sv-portal-button-radius, 50%);
	background: var(--savvy-portal-surface);
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	font-size: max(1rem, 16px);
	line-height: 1;
	cursor: pointer;
}

.savvy-vault-portal__market-arrow:hover,
.savvy-vault-portal__market-arrow:focus-visible {
	background: var(--savvy-portal-surface-2);
}

.savvy-vault-portal__market-track {
	display: grid;
	grid-auto-flow: column;
	grid-auto-columns: calc((100% - 32px) / 3);
	gap: 16px;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	list-style: none;
	margin: 4px 0 28px;
	padding: 4px 2px 12px;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: thin;
}

.savvy-vault-portal__market-card {
	display: flex;
	flex-direction: column;
	gap: 8px;
	scroll-snap-align: start;
	min-width: 0;
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--savvy-portal-surface);
	padding: 14px;
}

.savvy-vault-portal__market-card--bundle {
	border-color: var(--savvy-portal-emphasis);
}

.savvy-vault-portal__market-media {
	height: 110px;
	border-radius: var(--sv-portal-media-radius, var(--savvy-radius-md, 6px));
	overflow: hidden;
	background: var(--savvy-portal-surface-2);
}

.savvy-vault-portal__market-image {
	display: block;
	width: 100%;
	height: 110px;
	object-fit: cover;
}

.savvy-vault-portal__market-media-empty {
	display: block;
	height: 100%;
	background: linear-gradient(135deg, var(--savvy-portal-surface-2), #e7edf3);
}

.savvy-vault-portal__market-title {
	margin: 0;
	font-size: var(--savvy-text-lg, 1.125rem);
}

.savvy-vault-portal__market-meta {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0;
}

.savvy-vault-portal__market-desc {
	margin: 0;
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-base, 1rem);
	display: -webkit-box;
	-webkit-line-clamp: 2;
	-webkit-box-orient: vertical;
	overflow: hidden;
}

.savvy-vault-portal__market-card .savvy-vault-portal__price {
	margin: 0;
}

.savvy-vault-portal__market-actions {
	margin: auto 0 0;
}



/* =========================================================================
 * CV-7a — Accessibility / visual conformance (WCAG 2.2 AA)
 * Appended block. All rules scoped to .savvy-vault-portal so nothing leaks
 * into the theme. Token-driven: dark mode and high-contrast only re-point the
 * existing --savvy-portal-* custom properties, so every component follows automatically.
 * ========================================================================= */

/* --- Dark mode: re-point tokens on the wrapper (respects OS preference).
 *     Components already read these tokens, so no per-component overrides. --- */
@media (prefers-color-scheme: dark) {
	.savvy-vault-portal,
	.savvy-vault-portal-chrome {
		--savvy-portal-ink: #e8edf5;            /* headings / primary button text on tinted */
		--savvy-portal-ink-hover: #ffffff;
		--savvy-portal-emphasis-default: #e8edf5;
		--savvy-portal-text: var(--sv-portal-text, #d6dde6);
		--savvy-portal-muted: var(--sv-portal-text-muted, #9aa6b4);
		--savvy-portal-line: var(--sv-portal-line, #2b3340);
		--savvy-portal-surface: var(--sv-portal-surface, #141a22);
		--savvy-portal-surface-2: var(--sv-portal-surface-raised, #1c232d);
		--savvy-portal-accent: var(--sv-portal-link, #6fa8ff);        /* links lift on dark for >= 4.5:1 */
		--savvy-portal-accent-hover: var(--sv-portal-link-hover, #9cc4ff);
		--savvy-portal-ok-bg: #11281b;
		--savvy-portal-ok-ink: #58d68d;
		--savvy-portal-error-bg: #2a1514;
		--savvy-portal-error-ink: #ff8a87;
		--savvy-portal-warn-bg: #2a2310;
		--savvy-portal-warn-ink: #e8c266;
		--savvy-portal-shadow: var(--sv-portal-shadow, 0 1px 2px rgba(0, 0, 0, 0.5), 0 1px 3px rgba(0, 0, 0, 0.6));
		background: var(--savvy-portal-surface);
	}
	/* Content-wrapper property does not repaint the sibling chrome. */
	.savvy-vault-portal {
		background: var(--sv-portal-wrapper-bg, var(--savvy-portal-surface));
	}
}

/* --- High contrast: keep focus + structure visible in Windows High Contrast /
 *     forced-colors and when the user asks for more contrast. --- */
@media (forced-colors: active) {
	.savvy-vault-portal a:focus-visible,
	.savvy-vault-portal button:focus-visible,
	.savvy-vault-portal input:focus-visible,
	.savvy-vault-portal select:focus-visible,
	.savvy-vault-portal summary:focus-visible,
	.savvy-vault-portal textarea:focus-visible {
		outline: 3px solid CanvasText;
		outline-offset: 2px;
	}
	.savvy-vault-portal__notice,
	.savvy-vault-portal__card,
	.savvy-vault-portal table {
		border: 1px solid CanvasText;
	}
}

@media (prefers-contrast: more) {
	.savvy-vault-portal,
	.savvy-vault-portal-chrome {
		--savvy-portal-muted: var(--sv-portal-text-muted, var(--savvy-portal-text));
		--savvy-portal-line: var(--sv-portal-line, var(--savvy-portal-text));
		/* Geometry only. The two ring COLOURS are fixed by Public Website
		   Specification §6.2.6 and are not re-pointed here; thickening both
		   halves keeps their 17.85:1 separation while making the indicator
		   easier to find. */
		--savvy-portal-focus-width: 4px;
		--savvy-portal-focus-halo: 8px;
	}
}

/* --- Reflow (WCAG 2.2 SC 1.4.10): no horizontal scroll at 320px, baseline at
 *     375px. Complements the existing 800/720/600 tiers. --- */

/* RETAINED tiny-screen wrap guard (max-width:320). Universal selector;
   inverting would clobber descendants' own max-width at 321-600px, so kept
   as a documented max-width residual (Track C6). */
@media (max-width: 320px) {
	.savvy-vault-portal,
	.savvy-vault-portal * {
		max-width: 100%;
	}
	.savvy-vault-portal img,
	.savvy-vault-portal table,
	.savvy-vault-portal pre,
	.savvy-vault-portal code {
		max-width: 100%;
		overflow-wrap: anywhere;
		word-break: break-word;
	}
	.savvy-vault-portal pre,
	.savvy-vault-portal code {
		white-space: pre-wrap;
	}
}

/* --- Print: legible black-on-white, drop interactive chrome, expose link
 *     targets so a printed portal page is self-describing. --- */
@media print {
	.savvy-vault-portal {
		max-width: 100%;
		margin: 0;
		padding: 0;
		color: #000;
		background: #fff;
		box-shadow: none;
	}
	.savvy-vault-portal__nav,
	.savvy-vault-portal__nav-toggle,
	.savvy-vault-marketplace-strip,
	.savvy-vault-portal button,
	.savvy-vault-portal .savvy-vault-portal__notice--ok {
		display: none !important;
	}
	.savvy-vault-portal a {
		color: #000;
		text-decoration: underline;
	}
	.savvy-vault-portal a[href^="http"]::after {
		content: " (" attr(href) ")";
		font-size: 0.85em;
		word-break: break-all;
	}
	.savvy-vault-portal table,
	.savvy-vault-portal img {
		page-break-inside: avoid;
	}
}

/* =========================================================================
 * CV-7a-fix — dark-mode contrast corrections (QA follow-up).
 * The base CV-7a dark block re-points --savvy-portal-ink to a light value for text /
 * heading legibility, but --savvy-portal-ink is ALSO used as the fill of primary
 * buttons (with #fff text) and the catalog toolbar hardcodes a white
 * background — both went white-on-white in dark mode. These explicit
 * overrides restore accessible contrast (WCAG 2.2 AA, >= 4.5:1).
 * ========================================================================= */
@media (prefers-color-scheme: dark) {/* Primary / current buttons: solid accessible fill,
white text. */
	.savvy-vault-portal button.savvy-vault-portal__btn:not(.savvy-vault-portal__btn--secondary):not(.savvy-vault-portal__btn--danger):not([disabled]):not([aria-disabled="true"]),
.savvy-vault-portal a.savvy-vault-portal__btn:not(.savvy-vault-portal__btn--secondary):not(.savvy-vault-portal__btn--danger):not([aria-disabled="true"]) {
		background: var(--sv-portal-action-primary-bg, var(--savvy-portal-action-primary-bg, #2563eb));
		color: var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, #fff));
	}
.savvy-vault-portal__aside-link.is-current {
		background: var(--sv-portal-nav-current-bg, var(--sv-portal-action-primary-bg, var(--savvy-portal-action-primary-bg, #2563eb)));
		color: var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, #fff)));
	}
.savvy-vault-portal__btn:not(.savvy-vault-portal__btn--secondary):not(.savvy-vault-portal__btn--danger):not([disabled]):not([aria-disabled="true"]):hover,
.savvy-vault-portal a.savvy-vault-portal__btn:not(.savvy-vault-portal__btn--secondary):not(.savvy-vault-portal__btn--danger):not([aria-disabled="true"]):hover {
		background: var(--sv-portal-action-primary-bg-hover, var(--savvy-portal-action-primary-bg-hover, #1d4ed8));
		color: var(--sv-portal-action-primary-ink-hover, var(--savvy-portal-action-primary-ink-hover, #fff));
	}
.savvy-vault-portal__aside-link.is-current:hover {
		background: var(--sv-portal-nav-current-bg-hover, var(--sv-portal-action-primary-bg-hover, var(--savvy-portal-action-primary-bg-hover, #1d4ed8)));
		color: var(--sv-portal-nav-current-ink-hover, var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink-hover, var(--savvy-portal-action-primary-ink-hover, #fff))));
	}
	/* Disabled primary button: muted fill, still-legible text. */
	.savvy-vault-portal button.savvy-vault-portal__btn[disabled],
	.savvy-vault-portal button.savvy-vault-portal__btn[aria-disabled="true"],
	.savvy-vault-portal a.savvy-vault-portal__btn[aria-disabled="true"] {
		background: #3a4452;
		color: #c8d0da;
	}
	/* Secondary button reads --savvy-portal-ink as text on a surface fill. */
	.savvy-vault-portal__btn--secondary,
	.savvy-vault-portal a.savvy-vault-portal__btn--secondary,
	.savvy-vault-portal button.savvy-vault-portal__btn--secondary {
		background: var(--sv-portal-action-secondary-bg, var(--sv-portal-surface-raised, var(--savvy-portal-surface-2)));
		color: var(--sv-portal-action-secondary-ink, var(--sv-portal-text-strong, var(--savvy-portal-emphasis)));
	}
	.savvy-vault-portal a.savvy-vault-portal__btn--secondary:where(:not([aria-disabled="true"])):hover,
	.savvy-vault-portal button.savvy-vault-portal__btn--secondary:where(:not([disabled]):not([aria-disabled="true"])):hover {
		background: var(--sv-portal-action-secondary-bg-hover, var(--sv-portal-surface-raised, var(--savvy-portal-surface-2)));
	}
	.savvy-vault-portal button.savvy-vault-portal__btn--secondary[disabled],
	.savvy-vault-portal button.savvy-vault-portal__btn--secondary[aria-disabled="true"],
	.savvy-vault-portal a.savvy-vault-portal__btn--secondary[aria-disabled="true"] {
		background: var(--sv-portal-action-secondary-bg, var(--sv-portal-surface-raised, var(--savvy-portal-surface-2)));
		color: var(--sv-portal-action-secondary-ink, var(--sv-portal-text-muted, var(--savvy-portal-muted)));
	}
	/* Catalog toolbar hardcodes background:#fff — restore dark surface + text. */
	.savvy-vault-portal__catalog-toolbar input[type="search"],
	.savvy-vault-portal__catalog-toolbar select {
		background: var(--sv-portal-catalog-control-bg, var(--sv-portal-surface, var(--savvy-portal-surface)));
		color: var(--sv-portal-text, var(--savvy-portal-text));
		/* border-control holds ONE value in both modes by design; this
		   override exists for the hardcoded white fill above it and must not
		   re-point the border to the decorative token. */
		border-color: var(--af-color-border-control, #6B7280);
	}
}


/* ==========================================================================
 * Mobile-first responsive layout (Track C6, 2.123.20).
 *
 * Ten of the eleven former max-width blocks are inverted to mobile-first: the
 * small-screen values below are the base, and four min-width blocks
 * (376 / 601 / 721 / 801) restore the wider layouts. Rendering is unchanged at
 * every width (376/601/721/801 are the exact complements of 375/600/720/800).
 *
 * The hamburger nav and stacked-table transforms invert here too: the mobile
 * presentation is the base, and min-width:721 returns the inline nav and the
 * normal table (display:table-cell/table-row, header back on-screen, pseudo
 * label removed). The portal JS toggles --js/--open, but hide/show is CSS-
 * gated, so widening past 720 always restores the inline nav - min-width:721
 * reproduces that exactly.
 *
 * RESIDUAL: the max-width:320 tiny-screen wrap guard is RETAINED (see above).
 * It uses a universal selector (.savvy-vault-portal *); a mobile-first
 * inversion would clobber descendants carrying their own max-width (e.g.
 * __attachment-name) between 321-600px, so it stays a documented max-width
 * residual (accepted divergence for the Protocol Gap Analysis).
 * ========================================================================== */

/* Mobile base (small screens): stacked nav, stacked table, single columns. */
.savvy-vault-portal__nav-toggle {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	min-height: var(--savvy-portal-tap);
	margin: 0 0 10px;
	padding: 8px 14px;
	border: 1px solid var(--af-color-border-control, #6B7280);
	border-radius: var(--sv-portal-button-radius, var(--savvy-radius-md, 6px));
	background: var(--savvy-portal-surface);
	color: var(--sv-portal-text-strong, var(--savvy-portal-ink));
	font: inherit;
	font-size: max(1rem, 16px);
	font-weight: 600;
	cursor: pointer;
}

.savvy-vault-portal__nav-toggle::after {
	content: "▾";
	color: var(--savvy-portal-muted);
}

.savvy-vault-portal__nav-toggle[aria-expanded="true"]::after {
	content: "▴";
}

.savvy-vault-portal__nav--js {
	display: none;
}

.savvy-vault-portal__nav--js.savvy-vault-portal__nav--open {
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 0;
}

.savvy-vault-portal__nav--js.savvy-vault-portal__nav--open a {
	padding: 10px 4px;
	border-bottom: 1px solid var(--savvy-portal-surface-2);
}

.savvy-vault-portal__nav--js.savvy-vault-portal__nav--open a:last-child {
	border-bottom: 0;
}

.savvy-vault-portal__nav--js .savvy-vault-portal__nav-spacer {
	margin-left: 0;
}

.savvy-vault-portal {
	margin-top: 24px;
}

.savvy-vault-portal h1 {
	font-size: var(--savvy-text-2xl, 1.5rem);
	line-height: var(--savvy-leading-2xl, 1.35);
}

/* Stack table rows: each cell becomes a label/value line. */
.savvy-vault-portal__table--stack thead {
	position: absolute;
	left: -9999px;
}

.savvy-vault-portal__table--stack tr {
	display: block;
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-md, 6px);
	padding: 6px 10px;
	margin-bottom: 12px;
}

.savvy-vault-portal__table--stack td {
	display: flex;
	justify-content: space-between;
	gap: 14px;
	border-bottom: 1px solid var(--savvy-portal-surface-2);
	padding: 8px 0;
}

.savvy-vault-portal__table--stack td:last-child {
	border-bottom: 0;
}

.savvy-vault-portal__table--stack td::before {
	content: attr(data-label);
	font-weight: 600;
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
	text-transform: uppercase;
	letter-spacing: 0.02em;
}

.savvy-vault-portal__toolbar-spacer {
	margin-left: 0;
	width: 100%;
}

.savvy-vault-portal__btn {
	width: 100%;
}

.savvy-vault-portal__card-actions .savvy-vault-portal__btn {
	width: auto;
	flex: 1 1 auto;
}

.savvy-vault-portal-chrome__inner {
	flex-direction: column;
	align-items: flex-start;
}

.savvy-vault-portal__detail--has-aside {
	flex-direction: column;
	gap: 18px;
}
.savvy-vault-portal__detail-aside {
	flex: 1 1 auto;
	max-width: none;
	width: 100%;
	position: static;
}
.savvy-vault-portal__aside-list {
	flex-direction: row;
	flex-wrap: wrap;
	gap: 6px;
}
.savvy-vault-portal__aside-link {
	border: 1px solid var(--savvy-portal-line);
	background: var(--savvy-portal-surface);
}

.savvy-vault-portal__attachment-name { max-width: 11rem; }

.savvy-vault-portal__tutorial-list {
	grid-template-columns: 1fr;
}

.savvy-vault-portal__lms {
	grid-template-columns: 1fr;
}

.savvy-vault-portal__lms-nav {
	position: static;
}

.savvy-vault-portal__lms-controls {
	flex-wrap: wrap;
}

.savvy-vault-portal__market-track {
	grid-auto-columns: calc((100% - 16px) / 2);
}

.savvy-vault-portal__market-track {
	grid-auto-columns: 84%;
}

.savvy-vault-portal {
	padding-left: 14px;
	padding-right: 14px;
}

/* >= 376px: restore the portal's side padding. */
@media screen and (min-width: 376px) {
	.savvy-vault-portal { padding-left: 20px; padding-right: 20px; }
}

/* >= 601px: restore the two-up market track, multi-column tutorials, wide filename. */
@media screen and (min-width: 601px) {
	.savvy-vault-portal__attachment-name { max-width: 18rem; }
	.savvy-vault-portal__tutorial-list { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }
	.savvy-vault-portal__market-track { grid-auto-columns: calc((100% - 16px) / 2); }
}

/* >= 721px: restore the inline nav, the normal (un-stacked) table, and desktop spacing. */
@media screen and (min-width: 721px) {
	.savvy-vault-portal { margin-top: 40px; }
	.savvy-vault-portal h1 { font-size: var(--savvy-text-3xl, 1.875rem); line-height: var(--savvy-leading-3xl, 1.3); }
	.savvy-vault-portal-chrome__inner { flex-direction: row; align-items: center; }
	.savvy-vault-portal__toolbar-spacer { margin-left: auto; width: auto; }
	.savvy-vault-portal__btn { width: auto; }
	.savvy-vault-portal__card-actions .savvy-vault-portal__btn { flex: 0 1 auto; }
	.savvy-vault-portal__nav-toggle { display: none; }
	.savvy-vault-portal__nav--js { display: flex; }
	.savvy-vault-portal__nav--js.savvy-vault-portal__nav--open { flex-direction: row; align-items: center; gap: 8px 18px; }
	.savvy-vault-portal__nav--js.savvy-vault-portal__nav--open a { padding: 0; border-bottom: 0; }
	.savvy-vault-portal__nav--js .savvy-vault-portal__nav-spacer { margin-left: auto; }
	.savvy-vault-portal__table--stack thead { position: static; left: auto; }
	.savvy-vault-portal__table--stack tr { display: table-row; border: 0; border-radius: var(--savvy-radius-none, 0); padding: 0; margin-bottom: 0; }
	.savvy-vault-portal__table--stack td { display: table-cell; border-bottom: 1px solid var(--savvy-portal-line); padding: 10px 12px; }
	.savvy-vault-portal__table--stack td:last-child { border-bottom: 1px solid var(--savvy-portal-line); }
	.savvy-vault-portal__table--stack td::before { content: none; }
}

/* >= 801px: restore the docked detail aside, the vertical aside list, and the LMS grid. */
@media screen and (min-width: 801px) {
	.savvy-vault-portal__detail--has-aside { flex-direction: row; gap: 28px; }
	.savvy-vault-portal__detail-aside { flex: 0 0 220px; max-width: 220px; width: auto; position: sticky; }
	.savvy-vault-portal__aside-list { flex-direction: column; flex-wrap: nowrap; gap: 2px; }
	.savvy-vault-portal__aside-link { border: 0; background: transparent; }
	.savvy-vault-portal__lms { grid-template-columns: 280px minmax(0, 1fr); }
	.savvy-vault-portal__lms-nav { position: sticky; }
	.savvy-vault-portal__lms-controls { flex-wrap: nowrap; }
	.savvy-vault-portal__market-track { grid-auto-columns: calc((100% - 32px) / 3); }
}

/* ---------- WP UI 6.4 target size (W14-16) ---------------------------
   The portal renders two attestation checkboxes on the affiliate settings
   screen -- a US-person declaration and an accuracy declaration -- and this
   stylesheet sized neither. 6.4 sets 44x44 for a checkbox or radio; the
   glyph goes to 24px (a 44px glyph looks broken) and the 44px comes from the
   label row, which is what a finger actually hits. */
/* CHILD-1: keep the statement readable and its terms link operable. */
.savvy-vault-portal .savvy-vault-terms-acceptance { min-width: 0; max-width: 100%; overflow-wrap: anywhere; }
.savvy-vault-portal .savvy-vault-terms-acceptance :is(label, a, input) { font-size: max(1rem, 16px); line-height: 1.5; }
.savvy-vault-portal .savvy-vault-terms-acceptance span { min-width: 0; }
.savvy-vault-portal .savvy-vault-terms-acceptance a { display: inline-flex; align-items: center; min-height: 44px; color: inherit; text-decoration: underline; }
.savvy-vault-portal input[type="checkbox"],
.savvy-vault-portal input[type="radio"] {
	width: 24px;
	height: 24px;
	min-width: 24px;
	flex: 0 0 auto;
}
.savvy-vault-portal label:has(input[type="checkbox"]),
.savvy-vault-portal label:has(input[type="radio"]) {
	min-height: 44px;
	display: flex;
	align-items: center;
	gap: 8px;
}

/* W14-21 testimonials ----------------------------------------------------- */
.savvy-vault-testimonial-list {
	display: grid;
	gap: var(--savvy-space-6, 24px);
	margin-top: var(--savvy-space-6, 24px);
}

.savvy-vault-testimonial-card {
	padding: clamp(var(--savvy-space-5, 20px), 3vw, var(--savvy-space-8, 32px));
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-xl, 12px);
	background: var(--savvy-portal-surface);
}

.savvy-vault-testimonial-card > header {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--savvy-space-4, 16px);
	margin-bottom: var(--savvy-space-5, 20px);
}

.savvy-vault-testimonial-card h2 { margin: 0; }

.savvy-vault-testimonial-card__eyebrow {
	margin: 0 0 var(--savvy-space-1, 4px);
	color: var(--savvy-portal-muted);
	font-size: var(--savvy-text-sm, 0.875rem);
}

.savvy-vault-testimonial-state {
	display: inline-flex;
	align-items: center;
	min-height: 32px;
	padding: var(--savvy-space-1, 4px) var(--savvy-space-3, 12px);
	border: 1px solid currentColor;
	border-radius: var(--savvy-radius-full, 9999px);
	font-size: var(--savvy-text-sm, 0.875rem);
	font-weight: 700;
}

.savvy-vault-testimonial-consent {
	display: flex;
	align-items: center;
	gap: var(--savvy-space-3, 12px);
	margin: var(--savvy-space-5, 20px) 0;
	font-size: var(--savvy-text-base, 1rem);
	line-height: 1.5;
}

.savvy-vault-testimonial-consent input { font-size: max(1rem, 16px); }

.savvy-vault-testimonial-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--savvy-space-3, 12px);
}

.savvy-vault-testimonial-card textarea,
.savvy-vault-testimonial-card input[type="text"] {
	width: 100%;
	font-size: max(1rem, 16px);
}

/* W14-22: native radio semantics with measured 44px targets. The visible
 * digit and sentence keep colour/star shape from carrying meaning alone. */
.savvy-vault-star-input {
	margin: var(--savvy-space-5, 20px) 0;
	padding: 0;
	border: 0;
}

.savvy-vault-star-input legend {
	font-size: max(1rem, 16px);
	font-weight: 700;
}

.savvy-vault-star-input__options {
	display: flex;
	flex-wrap: wrap;
	gap: var(--savvy-space-2, 8px);
}

.savvy-vault-portal .savvy-vault-star-input label.savvy-vault-star-input__option {
	position: relative;
	display: inline-grid;
	grid-template-columns: auto auto;
	align-items: center;
	justify-content: center;
	gap: 2px;
	width: 44px;
	height: 44px;
	box-sizing: border-box;
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--savvy-portal-surface);
	font-size: max(1rem, 16px);
	font-weight: 700;
	margin: 0;
	cursor: pointer;
}

.savvy-vault-portal .savvy-vault-star-input__option input[type="radio"] {
	position: absolute;
	inset: 0;
	width: 44px;
	height: 44px;
	min-width: 44px;
	margin: 0;
	opacity: 0;
	font-size: max(1rem, 16px);
	cursor: pointer;
}

.savvy-vault-star-input__option:has(input:checked) {
	border-width: 2px;
	border-color: var(--savvy-portal-accent);
	background: var(--savvy-portal-surface-2);
}

.savvy-vault-star-input__option:focus-within {
	outline: 3px solid var(--af-color-focus-ring, #005fcc);
	outline-offset: 2px;
}

.savvy-vault-star-input__glyph { color: var(--savvy-portal-accent); }

.savvy-vault-rating {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--savvy-space-2, 8px);
	margin: var(--savvy-space-2, 8px) 0;
	font-size: max(1rem, 16px);
	line-height: 1.5;
}

.savvy-vault-rating__stars { display: inline-flex; }
.savvy-vault-rating__star { color: var(--savvy-portal-muted); }
.savvy-vault-rating__star--full { color: var(--savvy-portal-accent); }
.savvy-vault-rating__star--half {
	background: linear-gradient(90deg, var(--savvy-portal-accent) 50%, var(--savvy-portal-muted) 50%);
	-webkit-background-clip: text;
	background-clip: text;
	color: transparent;
}
.savvy-vault-rating__text { font-weight: 700; }
.savvy-vault-rating--unrated { color: var(--savvy-portal-muted); }
.savvy-vault-testimonial-rating-readonly { font-size: max(1rem, 16px); }

/* W14-68: tenant-neutral dashboard regions; published hooks are dashboard scoped. */
.savvy-vault-portal.savvy-vault-portal--dashboard :is(.savvy-vault-portal__just-added, .savvy-vault-portal__recent-orders, .savvy-vault-portal__purchase-action, .savvy-vault-portal__receipt-link), .savvy-vault-portal:is(.savvy-vault-portal--receipt, .savvy-vault-portal--orders) a.savvy-vault-portal__btn { font-size: max(1rem, 16px); }
.savvy-vault-portal--dashboard :is(.savvy-vault-portal__region-title, .savvy-vault-portal__library-title) { font-size: max(16px, var(--savvy-text-xl, 1.25rem)); }
.savvy-vault-portal--dashboard .savvy-vault-portal__region-title,
.savvy-vault-portal--dashboard .savvy-vault-portal__library-title,
.savvy-vault-portal--receipt h1 {
	color: var(--sv-portal-heading-ink, var(--sv-portal-text-strong, var(--savvy-portal-emphasis)));
}
.savvy-vault-portal--dashboard a.savvy-vault-portal__receipt-link,
.savvy-vault-portal:is(.savvy-vault-portal--receipt, .savvy-vault-portal--orders) a.savvy-vault-portal__btn--secondary {
	background: var(--sv-portal-action-secondary-bg, var(--savvy-portal-surface));
}
.savvy-vault-portal--dashboard a.savvy-vault-portal__receipt-link:hover,
.savvy-vault-portal:is(.savvy-vault-portal--receipt, .savvy-vault-portal--orders) a.savvy-vault-portal__btn--secondary:hover {
	background: var(--sv-portal-action-secondary-bg-hover, var(--sv-portal-surface-raised, var(--sv-portal-surface, var(--savvy-portal-surface-2))));
}
.savvy-vault-portal--dashboard .savvy-vault-portal__just-added,
.savvy-vault-portal--dashboard .savvy-vault-portal__recent-orders,
.savvy-vault-portal--dashboard .savvy-vault-portal__figure {
	margin: 0 0 var(--savvy-space-6, 24px);
	padding: var(--savvy-space-5, 20px);
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-lg, 8px);
	background: var(--savvy-portal-surface);
	color: var(--savvy-portal-text);
	min-width: 0;
	overflow-wrap: anywhere;
}
.savvy-vault-portal--dashboard .savvy-vault-portal__figures {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
	gap: var(--savvy-space-4, 16px);
	margin: 0;
}
.savvy-vault-portal--dashboard .savvy-vault-portal__figure-value {
	margin: var(--savvy-space-2, 8px) 0 0;
	font-size: max(16px, var(--savvy-text-xl, 1.25rem));
	font-weight: 700;
}
.savvy-vault-portal--dashboard .savvy-vault-portal__figure-label,
.savvy-vault-portal--dashboard .savvy-vault-portal__purchase-action,
.savvy-vault-portal--dashboard .savvy-vault-portal__receipt-link,
.savvy-vault-portal--receipt {
	font-size: max(1rem, 16px);
	line-height: var(--savvy-leading-base, 1.6);
}
.savvy-vault-portal--dashboard .savvy-vault-portal__orders-list {
	list-style: none;
	padding: 0;
	margin: 0;
}
.savvy-vault-portal--dashboard .savvy-vault-portal__order-row + .savvy-vault-portal__order-row {
	border-top: 1px solid var(--savvy-portal-line);
	margin-top: var(--savvy-space-4, 16px);
	padding-top: var(--savvy-space-4, 16px);
}
.savvy-vault-portal--dashboard .savvy-vault-portal__order-product,
.savvy-vault-portal--dashboard .savvy-vault-portal__purchase-name { font-weight: 700; }
.savvy-vault-portal--receipt .savvy-vault-portal__receipt-details {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
	gap: var(--savvy-space-4, 16px);
	overflow-wrap: anywhere;
}
.savvy-vault-portal--receipt .savvy-vault-portal__receipt-details dt { font-weight: 700; }
.savvy-vault-portal--receipt .savvy-vault-portal__receipt-details dd { margin: 0; }
@media screen and (max-width: 640px) {
	.savvy-vault-portal--receipt .savvy-vault-portal__receipt-details { grid-template-columns: minmax(0, 1fr); gap: var(--savvy-space-2, 8px); }
	.savvy-vault-portal--receipt .savvy-vault-portal__receipt-details dd { margin-bottom: var(--savvy-space-4, 16px); }
}

.savvy-vault-testimonial-card blockquote {
	margin: var(--savvy-space-5, 20px) 0;
	padding: var(--savvy-space-4, 16px) var(--savvy-space-5, 20px);
	border-left: 4px solid var(--savvy-portal-accent);
	background: var(--savvy-portal-surface-2);
	font-size: var(--savvy-text-lg, 1.125rem);
	line-height: 1.65;
}

@media screen and (max-width: 640px) {
	.savvy-vault-testimonial-card > header { display: block; }
	.savvy-vault-testimonial-state { margin-top: var(--savvy-space-3, 12px); }
	.savvy-vault-testimonial-actions > * { width: 100%; }
}

/* W14-69: public family token; inherited metadata keeps its unset family.
 * Value wrappers keep mobile table labels and ordinary prose outside the role.
 * Selectors remain internal except the already-published dashboard hooks. */
.savvy-vault-portal .savvy-vault-portal__order-number,
.savvy-vault-portal .savvy-vault-portal__order-date,
.savvy-vault-portal .savvy-vault-portal__order-total,
.savvy-vault-portal .savvy-vault-portal__order-status,
.savvy-vault-portal .savvy-vault-portal__version-chip,
.savvy-vault-portal .savvy-vault-portal__release-version,
.savvy-vault-portal .savvy-vault-portal__release-date,
.savvy-vault-portal .savvy-vault-portal__file-meta,
.savvy-vault-portal .savvy-vault-portal__thread-meta,
.savvy-vault-portal .savvy-vault-portal__mono-meta {
	font-family: var(--savvy-font-mono, inherit);
}
.savvy-vault-portal code {
	font-family: var(--savvy-font-mono, monospace);
}

/* W14-75: promised component roles, left unset for host inheritance. */
.savvy-vault-portal--product .savvy-vault-portal__file {
	flex-wrap: wrap;
	align-items: flex-start;
	gap: var(--sv-library-row-gap, 14px);
	padding: var(--sv-library-row-padding, 7px 0);
	background: var(--sv-library-row-bg, transparent);
	border-bottom-color: var(--sv-library-row-divider, var(--savvy-portal-surface-2));
	border-radius: var(--sv-library-row-radius, 0px);
}
.savvy-vault-portal--product .savvy-vault-portal__file-details { flex: 1 1 180px; min-width: 0; overflow-wrap: anywhere; }
.savvy-vault-portal--product .savvy-vault-portal__file-name { display: block; font-family: inherit; font-size: max(1rem, 16px, var(--savvy-text-base, 1rem)); }
.savvy-vault-portal--product .savvy-vault-portal__file-meta {
	display: block;
	color: var(--savvy-portal-muted);
	font-size: max(.75rem, 12px, var(--savvy-text-sm, .875rem));
	line-height: var(--savvy-leading-base, 1.6);
	overflow-wrap: anywhere;
}
.savvy-vault-portal--product .savvy-vault-portal__file-license,
.savvy-vault-portal--product .savvy-vault-portal__file-grant {
	display: inline-block;
	font-size: max(.75rem, 12px, var(--savvy-text-sm, .875rem));
	line-height: var(--savvy-leading-base, 1.6);
	padding: var(--savvy-space-1, 4px) var(--savvy-space-2, 8px);
	margin-block-start: var(--savvy-space-2, 8px);
	border: 1px solid var(--savvy-portal-line);
	border-radius: var(--savvy-radius-sm, 4px);
	background: var(--savvy-portal-surface-2);
	color: var(--savvy-portal-text);
}
.savvy-vault-portal--product .savvy-vault-portal__file-grant { border-style: dashed; background: transparent; }
.savvy-vault-portal--product .savvy-vault-portal__file-action { flex: 0 1 auto; min-width: 0; max-width: 100%; }
.savvy-vault-portal--product .savvy-vault-portal__file-action .savvy-vault-portal__btn {
	font-size: max(1rem, 16px, var(--savvy-text-base, 1rem));
	min-width: 44px;
	min-height: 44px;
	padding: var(--sv-library-action-padding, var(--savvy-space-2, 8px) var(--savvy-space-3, 12px));
	white-space: normal;
	overflow-wrap: anywhere;
}
.savvy-vault-portal--product .savvy-vault-portal__file-state,
.savvy-vault-portal--product .savvy-vault-portal__file-note,
.savvy-vault-portal--product .savvy-vault-portal__library-note,
.savvy-vault-portal--product .savvy-vault-portal__printing-note {
	font-size: max(1rem, 16px, var(--savvy-text-base, 1rem));
	line-height: var(--savvy-leading-base, 1.6);
	color: var(--savvy-portal-text);
	overflow-wrap: anywhere;
}
.savvy-vault-portal--product .savvy-vault-portal__file-note { margin: var(--savvy-space-2, 8px) 0 0; max-width: 28ch; }
.savvy-vault-portal--catalog { padding-inline: var(--sv-catalog-padding-inline, 20px); }
@media screen and (max-width: 640px) {
	.savvy-vault-portal--catalog { padding-inline: var(--sv-catalog-padding-inline, 14px); }
}
.savvy-vault-portal--catalog .savvy-vault-portal__products {
	grid-template-columns: repeat(auto-fill, minmax(min(var(--sv-catalog-grid-min, 260px), 100%), 1fr));
	gap: var(--sv-catalog-grid-gap, 18px);
}
.savvy-vault-portal--catalog .savvy-vault-portal__card { padding: var(--sv-catalog-card-padding, 18px); }
.savvy-vault-portal--catalog .savvy-vault-portal__card-title {
	font-family: var(--sv-catalog-title-font-family, inherit);
	font-size: max(1rem, 16px, var(--sv-catalog-title-size, var(--savvy-text-lg, 1.125rem)));
	font-weight: var(--sv-catalog-title-weight, 700);
	color: var(--sv-catalog-title-ink, var(--sv-portal-heading-ink, var(--savvy-portal-ink)));
}
.savvy-vault-portal--catalog .savvy-vault-portal__price {
	font-family: var(--sv-catalog-price-font-family, inherit);
	font-size: max(1rem, 16px, var(--sv-catalog-price-size, var(--savvy-text-base, 1rem)));
	font-weight: var(--sv-catalog-price-weight, 700);
	color: var(--sv-catalog-price-ink, var(--savvy-portal-emphasis));
}

.savvy-vault-portal--catalog h1 { font-size: max(1rem, 16px, var(--sv-catalog-heading-size, var(--savvy-text-2xl, 1.5rem))); }
@media (min-width: 641px) {
	.savvy-vault-portal--catalog h1 { font-size: max(1rem, 16px, var(--sv-catalog-heading-size, var(--savvy-text-3xl, 1.875rem))); }
}

/* W14-76: LMS current hover is a separately promised navigation role. */
.savvy-vault-portal__lms-nav-link.is-current:hover {
	background: var(--sv-portal-nav-current-bg-hover, var(--sv-portal-nav-current-bg, var(--sv-portal-action-primary-bg-hover, var(--savvy-portal-action-primary-bg-hover, var(--savvy-portal-ink)))));
	color: var(--sv-portal-nav-current-ink-hover, var(--sv-portal-nav-current-ink, var(--sv-portal-action-primary-ink, var(--savvy-portal-action-primary-ink, var(--savvy-portal-on-brand, var(--savvy-portal-surface))))));
}
.savvy-vault-portal__lms-nav-link.is-current:hover .savvy-vault-portal__lms-nav-check { color: inherit; }

/* Current navigation labels retain the control floor under smaller host roots. */
.savvy-vault-portal__aside-link.is-current,
.savvy-vault-portal__lms-nav-link.is-current { font-size: max(16px, var(--savvy-text-base, 1rem)); }

/* W14-83: discovery roles on the catalogue root; public roles remain unset. */
.savvy-vault-portal--catalog .savvy-vault-portal__catalog-toolbar {
	gap: var(--sv-catalog-filter-gap, 12px);
}
.savvy-vault-portal--catalog .savvy-vault-portal__catalog-toolbar label {
	display: flex;
	flex-direction: column;
	gap: var(--savvy-space-1, 4px);
	font-size: max(1rem, 16px);
	font-weight: var(--sv-catalog-label-weight, 600);
	min-width: 0;
	flex: 1 1 180px;
}
.savvy-vault-portal--catalog .savvy-vault-portal__catalog-toolbar .savvy-vault-portal__catalog-control {
	width: 100%;
	min-width: 44px;
	min-height: 44px;
	box-sizing: border-box;
	font-size: max(1rem, 16px);
	line-height: var(--savvy-leading-base, 1.6);
	padding: var(--sv-catalog-filter-padding, 8px 12px);
	border: var(--af-border-width-control, 1px) solid var(--af-color-border-control, #6B7280);
	border-radius: var(--savvy-radius-md, 6px);
	background: var(--sv-portal-catalog-control-bg, var(--sv-portal-surface, var(--savvy-portal-surface)));
	color: var(--sv-portal-text-strong, var(--savvy-portal-text));
}
.savvy-vault-portal--catalog .savvy-vault-portal__catalog-count {
	font-size: max(1rem, 16px);
	color: var(--sv-catalog-count-ink, var(--sv-portal-text-strong, var(--savvy-portal-text)));
}
.savvy-vault-portal--catalog .savvy-vault-portal__catalog-eyebrow {
	font-size: max(1rem, 16px);
	margin: 0 0 var(--savvy-space-2, 8px);
	color: var(--sv-catalog-eyebrow-ink, var(--sv-portal-text-strong, var(--savvy-portal-text)));
}
.savvy-vault-portal--catalog .savvy-vault-portal__catalog-badge {
	display: inline-block;
	font-size: max(1rem, 16px);
	padding: var(--sv-catalog-badge-padding, 4px 8px);
	border-radius: var(--sv-catalog-badge-radius, var(--savvy-radius-md, 6px));
	background: var(--sv-catalog-badge-bg, var(--sv-portal-surface-raised, var(--savvy-portal-surface-2)));
	color: var(--sv-catalog-badge-ink, var(--sv-portal-text-strong, var(--savvy-portal-text)));
	max-width: 100%;
	box-sizing: border-box;
	overflow-wrap: anywhere;
}
.savvy-vault-portal--catalog [hidden] { display: none !important; }

/* PX24-1: optional catalogue-only host presentation; defaults stay unchanged. */
.savvy-vault-portal--catalog.savvy-vault-portal .savvy-vault-portal__btn {
	font-weight: var(--sv-catalog-button-weight, 600);
}
.savvy-vault-portal--catalog.savvy-vault-portal .savvy-vault-buy-btn {
	font-weight: var(--sv-catalog-button-weight, var(--sv-buy-font-weight, var(--savvy-vault-btn-font-weight, 600)));
}
.savvy-vault-portal--catalog .savvy-vault-portal__card-image {
	aspect-ratio: var(--sv-catalog-media-aspect-ratio, auto);
	height: var(--sv-catalog-media-height, 160px);
}
.savvy-vault-portal--catalog .savvy-vault-portal__card-media {
	margin-inline: calc(-1 * max(0px, var(--sv-catalog-media-bleed, 0px)));
	margin-block-start: calc(-1 * max(0px, var(--sv-catalog-media-bleed, 0px)));
}
.savvy-vault-portal--catalog.sv-catalog-stack-active .savvy-vault-portal__catalog-toolbar {
	flex-direction: column;
}
.savvy-vault-portal--catalog.sv-catalog-stack-active .savvy-vault-portal__catalog-toolbar label {
	flex: 0 1 auto;
	width: 100%;
}

/* W13-6 shared metric/termination panels; body and controls retain absolute floors. */
#wpbody-content .sv-affiliate-metrics,.savvy-vault-portal .sv-affiliate-metrics{font-size:var(--savvy-affiliate-body,16px);line-height:var(--savvy-leading-base,1.6);overflow-wrap:anywhere;margin:var(--savvy-space-6,24px) 0}
#wpbody-content .sv-affiliate-metrics p,.savvy-vault-portal .sv-affiliate-metrics p{font-size:var(--savvy-affiliate-body,16px);line-height:var(--savvy-leading-base,1.6)}
.sv-affiliate-metrics dl{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--savvy-space-2,8px) var(--savvy-space-4,16px)}.sv-affiliate-metrics dd{margin:0}
#wpbody-content .sv-affiliate-metrics h2,.savvy-vault-portal .sv-affiliate-metrics h2{font-size:var(--savvy-affiliate-heading,20px);line-height:var(--savvy-affiliate-leading,1.5)}
#wpbody-content .sv-affiliate-metrics label,#wpbody-content .sv-affiliate-metrics legend{display:block;font-size:var(--savvy-affiliate-body,16px);line-height:var(--savvy-leading-base,1.6);min-height:var(--savvy-affiliate-target,44px)}
#wpbody-content .sv-affiliate-metrics select,#wpbody-content .sv-affiliate-metrics textarea,#wpbody-content .sv-affiliate-metrics button,#wpbody-content .sv-affiliate-metrics a{font-size:var(--savvy-affiliate-body,16px);min-height:var(--savvy-affiliate-target,44px);max-width:100%;white-space:normal;overflow-wrap:anywhere}
#wpbody-content .sv-affiliate-metrics textarea{display:block;width:100%}
.sv-affiliate-metrics a{display:inline-flex;align-items:center;min-height:var(--savvy-affiliate-target,44px);font-size:var(--savvy-affiliate-body,16px)}
@media(max-width:768px){.sv-affiliate-metrics dl{grid-template-columns:minmax(0,1fr)}}
.savvy-vault-portal .sv-affiliate-tier-status,.savvy-vault-portal .sv-affiliate-tier-status p{font-size:max(16px,var(--savvy-font-size-base,16px));line-height:var(--savvy-leading-base,1.6);overflow-wrap:anywhere}

/* Tenant letters: inherited control faces, type floors and shared touch targets. */
.savvy-vault-portal.savvy-vault-portal :is(input, select, textarea, button:not(.savvy-vault-buy-btn)) {
	font-family: inherit;
}
.savvy-vault-portal.savvy-vault-portal :is(label, input, select, textarea, button:not(.savvy-vault-buy-btn), a:not(.savvy-vault-buy-btn), summary) {
	font-size: max(16px, 1rem, 1em);
}
.savvy-vault-portal.savvy-vault-portal :is(input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]), select, textarea, button:not(.savvy-vault-buy-btn), summary) {
	min-height: max(44px, var(--savvy-portal-tap, 44px));
	max-width: 100%;
}
.savvy-vault-portal.savvy-vault-portal :is(button, summary) { white-space: normal; overflow-wrap: anywhere; }
.savvy-vault-portal label:has(input:is([type="checkbox"], [type="radio"])) { min-height: max(44px, var(--savvy-portal-tap, 44px)); }
.savvy-vault-portal { overflow-wrap: anywhere; }
.savvy-vault-portal .savvy-vault-portal__bundle-includes a { display: inline-flex; align-items: center; min-height: max(44px, var(--savvy-portal-tap, 44px)); }
.savvy-vault-portal .savvy-vault-portal__card-title { line-height: var(--savvy-leading-lg, 1.55); }
.savvy-vault-portal .savvy-vault-portal__badge { font-size: max(12px, .75rem, var(--savvy-text-xs, .75rem)); }
.savvy-vault-portal .savvy-vault-portal__mono { font-family: var(--savvy-font-mono, inherit); }
.savvy-vault-portal--licenses :is(small, [id^="sv-scope-help-"]) { font-size: max(16px, 1rem); }

/* Orders-only host override; unset/no-JS retains the original media-query cascade. */
.savvy-vault-portal--orders .sv-orders-stack-host thead { position: static; left: auto; }
.savvy-vault-portal--orders .sv-orders-stack-host tr { display: table-row; border: 0; border-radius: var(--savvy-radius-none, 0); padding: 0; margin-bottom: 0; }
.savvy-vault-portal--orders .sv-orders-stack-host td { display: table-cell; border-bottom: 1px solid var(--savvy-portal-line); padding: 10px 12px; }
.savvy-vault-portal--orders .sv-orders-stack-host td:last-child { border-bottom: 1px solid var(--savvy-portal-line); }
.savvy-vault-portal--orders .sv-orders-stack-host td::before { content: none; }
.savvy-vault-portal--orders .sv-orders-stack-active thead { position: absolute; left: -9999px; }
.savvy-vault-portal--orders .sv-orders-stack-active tr { display: block; border: 1px solid var(--savvy-portal-line); border-radius: var(--savvy-radius-md, 6px); padding: 6px 10px; margin-bottom: 12px; }
.savvy-vault-portal--orders .sv-orders-stack-active td { display: flex; justify-content: space-between; gap: 14px; border-bottom: 1px solid var(--savvy-portal-surface-2); padding: 8px 0; }
.savvy-vault-portal--orders .sv-orders-stack-active td:last-child { border-bottom: 0; }
.savvy-vault-portal--orders .sv-orders-stack-active td::before { content: attr(data-label); font-weight: 600; color: var(--savvy-portal-muted); font-size: var(--savvy-text-sm, 0.875rem); text-transform: uppercase; letter-spacing: 0.02em; }

/* PX23-2: additive host roles; preserve the distinct unset identifier faces. */
.savvy-vault-portal--orders .savvy-vault-portal__order-number {
	font-family: var(--sv-portal-order-id-font-family, var(--savvy-font-mono, monospace));
}
.savvy-vault-portal--dashboard .savvy-vault-portal__order-number {
	font-family: var(--sv-portal-order-id-font-family, var(--savvy-font-mono, inherit));
}
/* Match the shared text-control floor's specificity without weakening it. */
.savvy-vault-portal.savvy-vault-portal--licenses :is(input[type="text"], input[type="email"]):not([type="checkbox"]):not([type="radio"]):not([type="hidden"]) {
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	max-width: min(100%, max(44px, var(--sv-portal-unit-field-max-width, 100%)));
}
.savvy-vault-portal--licenses .savvy-vault-portal__unit-status {
	font-family: inherit;
	font-size: max(16px, 1rem);
}

/* Keep the stacked header accessible without extending the RTL scroll area. */
@media (max-width: 720px) {
	.savvy-vault-portal--orders .savvy-vault-portal__orders-table:not(.sv-orders-stack-host) thead {
		left: auto;
		width: 1px;
		height: 1px;
		overflow: hidden;
		clip-path: inset(50%);
	}
}
.savvy-vault-portal--orders .savvy-vault-portal__orders-table.sv-orders-stack-active thead {
	left: auto;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip-path: inset(50%);
}
