/* ============================================================
   Real, reusable components rebuilt from the BowerVault design
   system's Button / Logo / PricingCard component source
   (project/_ds/.../_ds_bundle.js) - same style contract, ported
   to production markup instead of the Claude Design preview
   runtime.
   ============================================================ */

/* ---- Button -------------------------------------------------
   Mirrors components/core/Button.jsx exactly: same padding,
   radius, type ramp and per-variant skins. Applied to native
   <button>/<a> elements (not a custom element) so focus,
   keyboard activation and screen-reader semantics stay native. */
.bv-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border-radius: 12px;
  border: 0;
  cursor: pointer;
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 500;
  line-height: 1.5;
  white-space: nowrap;
  text-decoration: none;
  background: transparent;
  padding: 6px 12px;
  transition: background-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard),
              opacity var(--duration-fast) linear;
}
.bv-btn:has(.bv-btn-icon) { gap: 12px; }
.bv-btn[disabled] { cursor: not-allowed; opacity: 0.4; }
.bv-btn--small { padding: 4px 10px; }
.bv-btn--link { padding: 0; }
/* Keyboard-focus ring, in brand navy rather than the browser's default
   blue - reported as looking out of place against the site's own
   colours (first spotted on the Cookies section's "Manage cookie
   preferences" button, but .bv-btn never had a focus style of its own
   anywhere, so this was true of every button on the site). Stays a
   real, clearly visible outline rather than being removed or
   suppressed - see the "Surfaces, not borders" section of CLAUDE.md:
   focus outlines are the one outline-shaped thing on this site that's
   never up for removal, since it's an accessibility requirement, not
   decoration. --alternate buttons sit on dark backgrounds, where navy
   would all but disappear, so those get a white ring instead - same
   default/on-dark split --ring-default/--ring-on-dark already draw for
   .bv-btn--secondary's resting state below. */
.bv-btn:focus-visible { outline: 2px solid var(--button-primary-bg); outline-offset: 2px; }
.bv-btn--alternate:focus-visible { outline-color: var(--color-neutral-white); }

/* primary */
.bv-btn--primary {
  background: var(--button-primary-bg);
  color: var(--button-primary-text);
  box-shadow: 0 0 0 1px var(--button-primary-ring);
  /* Wider horizontal breathing room than plain/secondary buttons get by
     default - overrides just the left/right of .bv-btn/.bv-btn--small's
     padding shorthand, so top/bottom (and the small variant's tighter
     vertical padding) are untouched. Comes after .bv-btn--small in the
     cascade so it wins regardless of class order in the markup. */
  padding-left: 22px;
  padding-right: 22px;
}
/* Every variant's hover state is a ghost/outline treatment - transparent
   fill, legible text, a crisp 1px border - modeled on how the "Get
   started" button (.bv-btn--secondary.bv-btn--alternate) already looks
   at rest: never a solid inverted fill. For the already-ghost variants
   (secondary/tertiary) hover just solidifies their already-present
   border rather than changing anything else, so the language stays
   quiet and consistent instead of buttons flashing to a hard fill. */
.bv-btn--primary:hover {
  background: transparent;
  color: var(--color-neutral-white);
  box-shadow: 0 0 0 1px var(--color-neutral-white);
}
/* .bv-btn--primary is used in a few light-background contexts (the
   pricing plan panel, the contact form, and any .section--mint or
   .section--canvas section - Vault's "Need a lead magnet built for
   you?" CTA, Pricing's trial CTA, the Mortgage Readiness Report's live
   demo CTA) where the white hover above would sit white/near-white-on-
   light and all but vanish - those keep the navy version instead.
   Scoped to .section--mint/.section--canvas generally rather than each
   individual button, so a future primary button added to a toned-light
   section gets correct contrast automatically instead of needing its
   own selector added here. */
.plan-panel__primary .bv-btn--primary:hover,
.contact-layout__form .bv-btn--primary:hover,
.section--mint .bv-btn--primary:hover,
.section--canvas .bv-btn--primary:hover {
  color: var(--button-primary-bg);
  box-shadow: 0 0 0 1px var(--button-primary-bg);
}
.bv-btn--primary.bv-btn--alternate {
  background: var(--color-neutral-white);
  color: var(--color-neutral-black);
  box-shadow: 0 0 0 1px var(--color-neutral-white);
}
.bv-btn--primary.bv-btn--alternate:hover {
  background: transparent;
  color: var(--color-neutral-white);
  box-shadow: 0 0 0 1px var(--color-neutral-white);
}

/* secondary (outline) */
.bv-btn--secondary {
  color: var(--color-neutral-black);
  box-shadow: var(--ring-default);
  padding-left: 22px;
  padding-right: 22px;
}
.bv-btn--secondary:hover { box-shadow: 0 0 0 1px var(--color-neutral-black); }
.bv-btn--secondary.bv-btn--alternate {
  color: var(--text-on-dark);
  box-shadow: var(--ring-on-dark);
}
.bv-btn--secondary.bv-btn--alternate:hover { box-shadow: 0 0 0 1px var(--color-neutral-white); }

/* tertiary (unused sitewide today, kept consistent for whenever it is) */
.bv-btn--tertiary { color: var(--color-neutral-black); }
.bv-btn--tertiary.bv-btn--alternate { color: var(--text-on-dark); }
.bv-btn--tertiary:hover { box-shadow: 0 0 0 1px var(--color-neutral-black); }
.bv-btn--tertiary.bv-btn--alternate:hover { box-shadow: 0 0 0 1px var(--color-neutral-white); }

/* link */
.bv-btn--link { color: var(--color-neutral-black); }
.bv-btn--link.bv-btn--alternate { color: var(--text-on-dark); }
.bv-btn--link:hover { text-decoration: underline; }

.bv-btn-icon { display: inline-flex; flex-shrink: 0; }

/* ---- Logo -----------------------------------------------------
   Mirrors components/core/Logo.jsx: native artwork is 548x161
   (ratio 3.404), height drives width. <bv-logo> is a tiny custom
   element (no shadow DOM) whose only job is that ratio math, so
   it lives in one place instead of being recomputed per use. */
bv-logo {
  display: inline-flex;
  line-height: 0;
}
bv-logo img {
  display: block;
  height: 100%;
  width: auto;
}

/* ---- Billing toggle + plan panel ---------------------------------
   Ported from Pricing.dc.html, first built for pricing.html - Home
   reuses the exact same markup/behaviour for its own pricing section
   rather than the older, simpler <bv-pricing-card> that used to live
   here (retired: two different pricing displays for the same plan was
   the inconsistency this replaced). Corner radius uses --radius-lg
   (the moderate rounding Home's old toggle had) rather than the fully-
   round 999px pricing.html's own toggle used to have - that mismatch
   was itself the design inconsistency being fixed, resolved by having
   one shared toggle rather than picking one page's shape over the
   other's. */
.billing-toggle { display: inline-flex; gap: 4px; padding: 4px; border-radius: var(--radius-lg); background: var(--card-bg); }
.billing-toggle__btn {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  border-radius: 8px;
  cursor: pointer;
  border: 0;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.5;
  color: var(--text-primary);
  transition: background-color var(--duration-base) var(--ease-standard), color var(--duration-base) var(--ease-standard);
}
.billing-toggle__btn.is-active { background: var(--color-pickled-bluewood); color: var(--text-on-dark); }
.billing-toggle__note { font-size: 13px; opacity: 0.6; }
.billing-toggle__btn.is-active .billing-toggle__note { opacity: 0.85; }

/* One card, two halves, told apart by tone rather than the rule that
   used to sit between them: the panel takes the section's card fill
   and .plan-panel__included takes the block fill a level in. Both
   flip together per section (see .section in page.css), so the split
   stays legible whether this lands on mist (Home) or white
   (pricing.html) - the earlier version left the left half transparent
   and leaned on the outline to frame the two as one thing. */
.plan-panel {
  width: 100%;
  max-width: 1080px;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--card-bg);
}
.plan-panel__primary { padding: 48px; display: flex; flex-direction: column; gap: 24px; }
.plan-panel__head { display: flex; flex-direction: column; gap: 8px; }
.plan-panel__head span { font-family: var(--font-ui); font-size: 14px; font-weight: 600; line-height: 1.5; color: var(--color-pickled-bluewood); }
.plan-panel__head h3 { margin: 0; font-family: var(--font-heading); font-weight: 400; font-size: 32px; line-height: 1.3; letter-spacing: -0.01em; color: var(--text-primary); }
.plan-panel__disclaimer { font-family: var(--font-ui); font-size: 14px; line-height: 1.5; color: var(--text-secondary); }
/* Mint rather than the F9F9F9 block tone the surface ladder would
   otherwise give it: this half runs to the card's own edge, so it sits
   directly against the canvas, and F9F9F9 on F2F2F2 left the panel
   with no right edge at all. Mint is the spec's own "positive state,
   or a quiet aside", which a ticked list of what you get is exactly. */
.plan-panel__included { padding: 48px; display: flex; flex-direction: column; gap: 20px; background: var(--surface-accent); }
.plan-panel__included > span { font-family: var(--font-ui); font-size: 14px; font-weight: 600; line-height: 1.5; color: var(--text-primary); }

.plan-price { display: flex; flex-direction: column; gap: 8px; }
.plan-price__row { display: flex; align-items: baseline; gap: 8px; }
.plan-price__value { font-family: var(--font-heading); font-weight: 400; font-size: 64px; line-height: 1; letter-spacing: -0.02em; color: var(--text-primary); }
.plan-price__unit { font-family: var(--font-ui); font-size: 16px; line-height: 1.5; color: var(--text-secondary); }
.plan-price__note { font-family: var(--font-body); font-size: 16px; line-height: 1.5; color: var(--text-primary); }

@media (max-width: 960px) {
  .plan-panel { grid-template-columns: 1fr; }
}
@media (max-width: 640px) {
  .billing-toggle { flex-direction: column; width: 100%; }
  .billing-toggle__btn { justify-content: center; }
  .plan-price__value { font-size: 48px; }
}

/* ---- Tick list ----------------------------------------------------
   A checkmarked list of short points - first used on The Vault's
   Bespoke section, reused as-is on How It Works' Configuration and
   Lead capture sections. */
.tick-list { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 16px; }
.tick-list li { display: flex; gap: 12px; align-items: flex-start; font-family: var(--font-body); font-size: 16px; line-height: 1.5; color: var(--text-primary); }
.tick-list .icon--check { width: 20px; height: 20px; margin-top: 2px; }

/* ---- Pill ----------------------------------------------------------
   A static label on a quiet ground (platform names, etc.) - not
   interactive. Same 5% neutral fill as .tag-soon and
   .feature-card__badge--included, so every small label on the site
   reads as one family. For a clickable/selectable version see
   .filter-chip in vault.css. */
.pill {
  display: inline-flex;
  align-items: center;
  padding: 8px 16px;
  border-radius: 999px;
  font-family: var(--font-ui);
  font-size: 14px;
  line-height: 1.5;
  color: var(--text-primary);
  background: var(--opacity-neutral-darkest-5);
}

/* ---- Rule list ------------------------------------------------------
   A title + short description, items separated by a bottom rule (no
   rule after the last one) - first used for Pricing's add-ons list,
   reused as-is for About's Philosophy points. */
.rule-list { display: flex; flex-direction: column; gap: 20px; margin-top: 8px; }
.rule-list__item { display: flex; flex-direction: column; gap: 6px; padding-bottom: 20px; border-bottom: var(--border-width) solid var(--border-default); }
.rule-list__item:last-child { padding-bottom: 0; border-bottom: 0; }
.rule-list__title { font-family: var(--font-ui); font-size: 16px; font-weight: 600; line-height: 1.5; color: var(--text-primary); }
.rule-list__desc { font-family: var(--font-body); font-size: 14px; line-height: 1.5; color: var(--text-secondary); text-wrap: pretty; }
/* Only needed on the handful of rule-list items that also carry a
   .tag-soon badge (see below) - .rule-list__title alone (a plain span)
   is fine sitting directly in the column flow for every other item. */
.rule-list__title-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ---- "Coming soon" tag ------------------------------------------------
   A quiet, informational label for a feature or add-on that's described
   on the site but not actually available yet - same visual weight as
   .feature-card__badge--included (a feature that's already active),
   deliberately not the louder .feature-card__badge--addon navy, since
   "not built yet" shouldn't read as more urgent than "you can buy this
   today". Shared across Home, How It Works and Pricing wherever a
   feature needs this caveat; not used on the Vault page, which already
   has its own live/coming-soon convention (.vault-card__status) scoped
   to actual catalogue tools. */
.tag-soon {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--opacity-neutral-darkest-5);
  color: var(--text-secondary);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}

/* ---- Form field --------------------------------------------------
   Ports the design system's Input/Select/Textarea (components/forms/
   *.jsx): a label above a bottom-rule row. First real form on the
   site (Contact) - kept here rather than page-scoped since any future
   form (signup, login) should reuse it. */
.field { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.field__label { font-family: var(--font-ui); font-size: 16px; line-height: 1.5; color: var(--text-primary); }
.field__required { color: var(--color-cinnabar-dark); margin-left: 4px; }
.field__row {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 8px 0;
  border-bottom: var(--border-width) solid var(--border-default);
}
.field__row input,
.field__row select {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: none;
  background: transparent;
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-primary);
  padding: 0;
}
.field__row select { appearance: none; }
.field__chevron { opacity: 0.6; flex-shrink: 0; }
.field textarea {
  resize: vertical;
  min-height: 180px;
  border: 0;
  border-bottom: var(--border-width) solid var(--border-default);
  background: transparent;
  outline: none;
  padding: 12px 12px 12px 0;
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-primary);
}

/* A field that reveals in response to another field's value (e.g. an
   "Other, please specify" follow-up) - animates open with the
   grid-template-rows 0fr->1fr trick so height:auto can transition.
   Closed it must take up no space at all: zero height AND zero
   margin, or each collapsed reveal leaves a phantom gap in the form
   (three of them stacked 96px of dead space above contact's Message
   field). So the 32px spacing lives on .is-open, not on the base
   rule - and must not be reintroduced as an inline style on the
   element, which would beat both. */
.field-reveal {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  overflow: hidden;
  margin-top: 0;
  transition: grid-template-rows 320ms var(--ease-standard), opacity 240ms var(--ease-standard), margin-top 320ms var(--ease-standard);
}
.field-reveal.is-open { grid-template-rows: 1fr; opacity: 1; margin-top: 32px; }
.field-reveal > div { min-height: 0; overflow: hidden; }

/* ---- Icon sprite ------------------------------------------------
   The step / trust icons are the DC's own hand-drawn thin-stroke
   set (1.25px strokes on a 24x24 grid). Defined once as <symbol>s
   in index.html and referenced with <use> so each icon has a
   single source of truth. */
.icon {
  display: block;
  flex-shrink: 0;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon--chevron { width: 20px; height: 20px; }

/* ---- Scroll reveal -------------------------------------------------
   Headings and card-grid rows fade + slide up into place on scroll,
   driven by initScrollReveal() in js/main.js. That script only ever
   adds .reveal to an element it is actively watching with a live
   IntersectionObserver, so nothing is hidden here unless something
   guarantees it will be revealed. No-JS visitors, unsupported browsers,
   prefers-reduced-motion and anyone inside a hidden container (e.g. the
   About page's bio modal) never get the class and just see the page in
   place - no animation, no risk of anything stuck invisible.

   This used to run on Animate.css, loaded async from a CDN - a genuine,
   confirmed source of the "stuck invisible" bugs documented below and
   in CLAUDE.md (a slow/blocked/failed request left .animate__animated
   doing precisely nothing, and even cancelled the CSS backstop meant to
   catch that, since its selector only checked whether those classes had
   been *added*, not whether they actually did anything). A first-party
   transition, defined right here, can't silently fail to load - .reveal
   sets the "from" state, .is-revealed (added by JS) transitions to the
   "to" state, both always present regardless of network conditions.
   Duration/easing/delay are set as inline styles by initScrollReveal()
   rather than here, so each element (staggered card rows, say) can
   still vary them individually. */
@media (prefers-reduced-motion: no-preference) {
  .reveal {
    opacity: 0;
    transform: translate3d(0, 34px, 0);
    transition-property: opacity, transform;
  }
  .reveal.is-revealed { opacity: 1; transform: none; }
}

/* A pure-CSS backstop, independent of every JS mechanism above (the
   IntersectionObserver, its various fail-safes in main.js) - after
   several rounds of JS-side timer/listener fixes for a heading getting
   stuck invisible on Under the Surface specifically (see CLAUDE.md)
   still didn't hold up in real use, this exists so the guarantee no
   longer depends on any JS running correctly at all: some time after
   .reveal is applied, this animation alone forces the element visible,
   using the browser's own animation timeline rather than a JS timer
   that can be frozen, throttled or wiped by a reload along with
   everything else. :not(.is-revealed) is what makes it inert the
   moment the real IntersectionObserver-driven reveal actually happens
   (that adds .is-revealed, which stops this selector matching,
   cancelling this animation) - safe to cancel now that .is-revealed is
   a first-party rule immediately above, guaranteed to already be
   taking over rather than a third-party class that might be doing
   nothing at all.
   The delay was 5s originally, timed against the single scene this was
   built for (Under the Surface, where content becomes "due" for a
   reveal within moments of the page loading). initScrollReveal() marks
   *every* heading and card row on a page with .reveal essentially all
   at once, right after DOMContentLoaded, regardless of how far down
   the page it sits - not lazily as each one nears the viewport - so on
   an ordinary content page this timer starts for everything
   simultaneously at load, not "whenever this element is actually due."
   A visitor reading at a normal pace easily takes more than 5s to
   scroll to a page's lower sections, so this was firing routinely in
   completely ordinary browsing: content still well below the fold got
   force-shown at full opacity with no transition the moment 5s
   elapsed, then sat there already "revealed" by the time the visitor
   actually scrolled to it - reported as entire sections from partway
   down a page onward never fading in, the exact opposite of a rare
   last-resort. 20s is comfortably past how long it takes to scroll
   through this whole site's longest page at a normal reading pace
   (the real JS-driven reveal in main.js should reveal everything
   within a fraction of a second of it actually entering the viewport,
   so this should still essentially never fire in ordinary use) while
   remaining a genuine safety net if JS fails outright. */
@keyframes utsRevealBackstop {
  to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
  .reveal:not(.is-revealed) {
    animation: utsRevealBackstop 0.01s 20s forwards;
  }
}

/* ---- Shared mock artefacts ---------------------------------------
   The branded email and the PDF report, both drawn in a fictional
   client's palette rather than BowerVault's - the point of each is
   that the lead never sees us. Written for the Mortgage Readiness
   Report page and now shared with Features, so they live here rather
   than in either page's stylesheet. Both are decorative: every name
   and figure in them is invented, and the markup hides them from
   assistive tech.

   Their shadow is conditional. White on a toned section needs none;
   white on a white section is the one case the no-borders brief
   keeps a shadow for, which is what the design's 1px border on these
   translates to. */
.mock-caption { font-family: var(--font-body); font-size: 14px; line-height: 1.5; color: rgba(12, 8, 1, 0.58); text-wrap: pretty; }

/* ---- The branded email: laptop artwork ---------------------------------
   Browser chrome, a webmail client, and the firm's branded message open
   in it. Everything inside the screen is sized in `cqi` against the
   screen's own inline size (container-type: inline-size), so the whole
   mock scales as one object at any width and needs no breakpoint.

   Written for the Mortgage Readiness Report page and now shared with
   Features (each with its own invented firm and copy), so it lives here
   rather than in either page's stylesheet. Decorative throughout: every
   name and figure is invented, and the markup hides it from assistive
   tech.

   The 1px lines inside are part of the illustration: this is a picture
   of somebody else's mail client, the same exception the mock radio
   dots get. The no-borders rule governs our own blocks, not artwork of
   other software. */
.laptop-frame { flex: 1 1 480px; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.laptop { display: flex; flex-direction: column; align-items: center; }
/* Lid narrower than the base on purpose. */
.laptop__lid {
  width: 94%;
  box-sizing: border-box;
  padding: 2.2% 2.2% 3%;
  border-radius: 14px 14px 3px 3px;
  background: #8D8A85;
  box-shadow: 0 34px 70px rgba(12, 8, 1, 0.28);
}
.laptop__camera { display: flex; align-items: center; justify-content: center; height: 12px; margin: -5px 0 7px; }
.laptop__camera span { width: 4px; height: 4px; border-radius: 999px; background: rgba(12, 8, 1, 0.4); }
.laptop__screen {
  container-type: inline-size;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  border-radius: 3px;
  background: #EEEDEB;
  display: flex;
  flex-direction: column;
  font-family: 'Poppins', system-ui, sans-serif;
}
.laptop__screen * { font-family: inherit; }
.laptop__base { width: 100%; display: flex; flex-direction: column; align-items: center; }
.laptop__base-bar { width: 100%; height: 12px; border-radius: 2px 2px 10px 10px; background: linear-gradient(#D3D0CC, #A19D98); }
.laptop__feet { width: 100%; display: flex; justify-content: space-between; padding: 0 9%; box-sizing: border-box; }
.laptop__feet span { width: 8%; height: 4px; border-radius: 0 0 3px 3px; background: rgba(12, 8, 1, 0.28); }

/* Browser chrome */
.browser { flex-shrink: 0; display: flex; flex-direction: column; background: #DEDCD8; }
.browser__tabs { display: flex; align-items: flex-end; gap: 1cqi; padding: 0.9cqi 1.4cqi 0; }
.browser__lights { display: flex; gap: 0.9cqi; padding-bottom: 1.2cqi; }
.browser__lights span { width: 1.1cqi; height: 1.1cqi; border-radius: 999px; }
.browser__lights span:nth-child(1) { background: #E4675A; }
.browser__lights span:nth-child(2) { background: #E9B44C; }
.browser__lights span:nth-child(3) { background: #65B77E; }
.browser__tab {
  display: flex;
  align-items: center;
  gap: 1.2cqi;
  max-width: 18cqi;
  padding: 1cqi 1.6cqi;
  border-radius: 0.8cqi 0.8cqi 0 0;
  background: rgba(255, 255, 255, 0.35);
  font-size: 1.6cqi;
  color: rgba(12, 8, 1, 0.62);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.browser__tab--active { max-width: 26cqi; background: #F7F6F4; color: #1B1339; }
.browser__favicon { width: 1.4cqi; height: 1.4cqi; border-radius: 0.3cqi; background: #2E1F63; flex-shrink: 0; }
.browser__bar { display: flex; align-items: center; gap: 1.4cqi; padding: 0.9cqi 1.4cqi; background: #F7F6F4; }
.browser__nav { display: flex; gap: 1.2cqi; }
.browser__nav span { width: 1.6cqi; height: 1.6cqi; border-radius: 999px; background: rgba(12, 8, 1, 0.18); }
.browser__nav span:last-child { background: rgba(12, 8, 1, 0.12); }
.browser__url { flex: 1; display: flex; align-items: center; gap: 1.2cqi; padding: 0.9cqi 1.6cqi; border-radius: 999px; background: #EAE8E4; font-size: 1.6cqi; color: rgba(12, 8, 1, 0.62); }
.browser__url .icon { width: 1.6cqi; height: 1.6cqi; }

/* The mail client */
.mailapp { flex: 1; min-height: 0; display: flex; background: #fff; }
.mail__sidebar { flex: 0 0 18cqi; display: flex; flex-direction: column; gap: 0.5cqi; padding: 1.6cqi 1.1cqi; background: #F4F2EF; }
.mail__user { display: flex; align-items: center; gap: 1.2cqi; margin-bottom: 1.4cqi; padding: 0 1.4cqi; }
.mail__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.4cqi;
  height: 3.4cqi;
  border-radius: 999px;
  background: #2E1F63;
  font-size: 1.5cqi;
  font-weight: 600;
  color: #fff;
  flex-shrink: 0;
}
.mail__user b { font-size: 1.7cqi; font-weight: 600; color: #1B1339; }
.mail__folder { display: flex; align-items: center; gap: 1.4cqi; padding: 0.9cqi 1.4cqi; border-radius: 0.8cqi; }
.mail__folder span:first-child { width: 1.4cqi; height: 1.4cqi; border-radius: 0.4cqi; background: rgba(12, 8, 1, 0.25); flex-shrink: 0; }
.mail__folder-name { flex: 1; font-size: 1.7cqi; color: rgba(12, 8, 1, 0.62); }
.mail__folder-count { font-size: 1.5cqi; color: rgba(12, 8, 1, 0.62); }
.mail__folder--active { background: rgba(46, 31, 99, 0.1); }
.mail__folder--active span:first-child { background: #2E1F63; }
.mail__folder--active .mail__folder-name { font-weight: 600; color: #1B1339; }

.mail__message { flex: 1; min-width: 0; display: flex; flex-direction: column; overflow: hidden; background: #fff; }
.mail__head { flex-shrink: 0; display: flex; flex-direction: column; gap: 1.1cqi; padding: 1.6cqi 2cqi; border-bottom: 1px solid rgba(12, 8, 1, 0.1); }
.mail__subject { font-size: 2.6cqi; font-weight: 600; line-height: 1.25; color: #1B1339; }
.mail__from { display: flex; align-items: center; gap: 1.4cqi; }
.mail__from .mail__avatar { width: 4cqi; height: 4cqi; font-size: 1.6cqi; }
.mail__sender { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.mail__sender b { font-size: 1.8cqi; font-weight: 600; color: #1B1339; }
.mail__sender span { font-size: 1.6cqi; color: rgba(12, 8, 1, 0.62); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mail__time { flex-shrink: 0; font-size: 1.6cqi; color: rgba(12, 8, 1, 0.62); }
.mail__attach { align-self: flex-start; display: flex; align-items: center; gap: 1.2cqi; padding: 1cqi 1.6cqi; border-radius: 0.8cqi; border: 1px solid rgba(12, 8, 1, 0.15); font-size: 1.6cqi; color: #1B1339; }
.mail__attach .icon { width: 1.6cqi; height: 1.6cqi; color: rgba(12, 8, 1, 0.55); stroke-width: 2; }
.mail__attach span:last-child { color: rgba(12, 8, 1, 0.62); }

/* The message body is pinned to the pane height so the legal line can
   never be the thing that clips - the handoff is explicit about this.
   Only the score paragraph flexes; everything below it is fixed. */
.mail__pane { flex: 1; min-height: 0; display: flex; overflow: hidden; padding: 1.6cqi 2.2cqi 0; }
.mail__card { flex: 1; min-height: 0; display: flex; flex-direction: column; border: 1px solid #E6DFF4; border-radius: 0.6cqi 0.6cqi 0 0; overflow: hidden; }
.mail__letterhead {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1.2cqi;
  padding: 1.6cqi 2.4cqi 1.3cqi;
  margin: 0 2cqi;
  background: #fff;
  border-bottom: 0.4cqi solid #2E1F63;
}
.mail__letterhead span:first-child { width: 2.2cqi; height: 2.2cqi; border-radius: 0.6cqi; background: #FF6B4A; }
.mail__letterhead span:last-child { font-size: 2cqi; font-weight: 600; letter-spacing: 0.02em; color: #2E1F63; }
.mail__body { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 1.2cqi; padding: 1.6cqi 2cqi; }
.mail__body h3 { flex-shrink: 0; margin: 0; font-size: 2.4cqi; font-weight: 700; line-height: 1.25; color: #2E1F63; }
/* align-items: center guarantees equal space above and below the line,
   regardless of the loaded font's own leading metrics - padding alone
   can't, since a font's half-leading isn't necessarily split evenly
   around its glyphs. */
.mail__score { flex: 1 1 auto; min-height: 0; overflow: hidden; display: flex; align-items: center; padding: 0 1.6cqi; background: #F4F0FC; border-left: 0.5cqi solid #FF6B4A; }
.mail__score span { font-size: 1.8cqi; line-height: 1.5; color: #4A4160; }
.mail__contact { flex-shrink: 0; font-size: 1.8cqi; line-height: 1.5; color: #4A4160; }
.mail__contact strong { color: #2E1F63; }
.mail__legal { flex-shrink: 0; padding-top: 1.1cqi; border-top: 1px solid #E6DFF4; font-size: 1.7cqi; line-height: 1.4; color: #635B7E; }

.mail__agenda { flex: 0 0 26cqi; display: flex; flex-direction: column; gap: 1.2cqi; padding: 1.6cqi; border-left: 1px solid rgba(12, 8, 1, 0.1); background: #fff; }
.mail__agenda-head { display: flex; flex-direction: column; gap: 0.2cqi; }
.mail__agenda-head span:first-child { font-size: 1.5cqi; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: rgba(12, 8, 1, 0.62); }
.mail__agenda-head span:last-child { font-size: 2cqi; font-weight: 600; color: #1B1339; }
.agenda-item { display: flex; gap: 1.2cqi; }
.agenda-item__rail { flex: 0 0 0.5cqi; border-radius: 999px; background: rgba(12, 8, 1, 0.25); }
.agenda-item__rail--navy { background: #2E1F63; }
.agenda-item__rail--orange { background: #FF6B4A; }
.agenda-item__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.3cqi; padding: 1.1cqi 1.2cqi; border-radius: 0.7cqi; background: #F7F6F4; }
.agenda-item__time { font-size: 1.5cqi; color: rgba(12, 8, 1, 0.62); }
.agenda-item__title { font-size: 1.7cqi; font-weight: 600; line-height: 1.3; color: #1B1339; }
.agenda-item__who { font-size: 1.5cqi; color: rgba(12, 8, 1, 0.62); }

/* ---- The PDF they take away --------------------------------------------
   Three sheets stacked back-to-front, the front one carrying the real
   content. Everything inside it is sized in `cqi` against the sheet's
   own inline size (container-type: inline-size on .pdf-sheet), so the
   whole page scales as one object at any column width instead of
   needing a breakpoint per element - a document mock only reads right
   if its type scales with the paper. Harlow & Finch's palette and
   Poppins throughout: it is their report, not ours. */
.spec-points { display: flex; flex-direction: column; gap: 14px; margin-top: 8px; }
.spec-point { display: flex; gap: 12px; align-items: baseline; }
.spec-point__num { flex: 0 0 auto; font-family: var(--font-ui); font-size: 14px; font-weight: 600; color: var(--color-pickled-bluewood); }
.spec-point span:last-child { font-family: var(--font-body); font-size: 15px; line-height: 1.5; color: var(--text-primary); text-wrap: pretty; }

.pdf-frame { flex: 1 1 520px; min-width: 0; display: flex; justify-content: center; }
.pdf-stack { position: relative; width: 100%; max-width: 586px; padding: 0 26px 26px 0; box-sizing: border-box; }
.pdf-stack__back,
.pdf-stack__mid { position: absolute; border-radius: 6px; background: #fff; box-shadow: 0 14px 34px rgba(12, 8, 1, 0.1); }
.pdf-stack__back { top: 26px; left: 26px; right: 0; bottom: 0; }
.pdf-stack__mid { top: 13px; left: 13px; right: 13px; bottom: 13px; }
.pdf-sheet {
  position: relative;
  container-type: inline-size;
  border-radius: 6px;
  overflow: hidden;
  background: #fff;
  box-shadow: 0 30px 64px rgba(12, 8, 1, 0.18);
  font-family: 'Poppins', system-ui, sans-serif;
}
.pdf-sheet * { font-family: inherit; }
.pdf-sheet__bar { display: flex; align-items: center; justify-content: space-between; padding: 3.6cqi 4.8cqi; background: #2E1F63; }
.pdf-sheet__brand { display: flex; align-items: center; gap: 1.6cqi; }
.pdf-sheet__mark { width: 3.4cqi; height: 3.4cqi; border-radius: 1cqi; background: #FF6B4A; }
.pdf-sheet__name { font-size: 2.4cqi; font-weight: 600; color: #fff; }
.pdf-sheet__doc { font-size: 2cqi; color: rgba(255, 255, 255, 0.78); }
.pdf-sheet__body { display: flex; flex-direction: column; gap: 2.6cqi; padding: 4cqi 4.8cqi; }
.pdf-sheet__title { display: flex; flex-direction: column; gap: 1cqi; }
.pdf-sheet__title b { font-size: 4.2cqi; font-weight: 600; line-height: 1.2; color: #1B1339; }
.pdf-sheet__title span { font-size: 2.2cqi; color: #635B7E; }
.pdf-score { display: flex; flex-direction: column; gap: 2cqi; padding: 3.2cqi 3.4cqi; border-radius: 2cqi; background: #F4F0FC; }
.pdf-score__row { display: flex; align-items: baseline; gap: 1.6cqi; }
.pdf-score__value { font-size: 6cqi; font-weight: 600; line-height: 1; color: #2E1F63; }
.pdf-score__unit { font-size: 2.4cqi; color: #4A4160; }
/* Darker than the site's usual accent orange (#FF6B4A) - that shade is
   too light for white text to clear 4.5:1 contrast at this badge's size. */
.pdf-score__tier { margin-left: auto; padding: 0.9cqi 2.2cqi; border-radius: 999px; background: #BF5038; font-size: 1.9cqi; font-weight: 600; color: #fff; white-space: nowrap; }
.pdf-score__track { height: 1.2cqi; border-radius: 999px; background: #E6DFF4; overflow: hidden; }
.pdf-score__track span { display: block; height: 100%; background: #2E1F63; }
.pdf-figures { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.2cqi; }
/* A tinted ground rather than the export's 1px lilac outline. */
.pdf-figure { display: flex; flex-direction: column; gap: 0.6cqi; padding: 2.6cqi 2.8cqi; border-radius: 1.6cqi; background: #FAF7FE; }
.pdf-figure__label { font-size: 1.8cqi; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase; color: #A83A1C; }
.pdf-figure__value { font-size: 2.8cqi; font-weight: 600; color: #1B1339; }
.pdf-sheet__subhead { font-size: 2.4cqi; font-weight: 600; line-height: 1.3; color: #1B1339; }
.pdf-note { font-size: 2.1cqi; line-height: 1.5; color: #4A4160; }
.pdf-note strong { color: #1B1339; }
.pdf-warning { display: flex; gap: 2cqi; align-items: flex-start; padding: 2.4cqi 2.6cqi; border-radius: 1.4cqi; background: #FBF0EC; }
.pdf-warning__bar { flex: 0 0 0.6cqi; align-self: stretch; border-radius: 999px; background: #FF6B4A; }
.pdf-warning p { margin: 0; font-size: 2.2cqi; line-height: 1.55; color: #4A4160; }
.pdf-warning strong { color: #1B1339; }
.pdf-page {
  position: absolute;
  right: 0;
  bottom: -8px;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--color-pickled-bluewood);
  font-family: var(--font-ui);
  font-size: 13px;
  font-weight: 600;
  color: #fff;
}


@media (max-width: 960px) {
  .laptop-frame,
  .pdf-frame { flex: 1 1 auto; }
}

/* ---- Live tool embed --------------------------------------------------
   The real tool, embedded exactly as a subscriber embeds it: a holder
   plus the app's one-line bootstrap (see CLAUDE.md's "The live demo on
   a tool page"). embed-loader.js finds the holder, mounts the iframe
   and sizes it itself, so this needs to do nothing but give it a
   column and stop it overflowing - no height rules, don't fight the
   loader. First built for /vault/mortgage-readiness-report's own live
   demo, shared here once mortgage-broker-demo.html needed the same
   thing with nothing else around it. */
.demo-embed { min-width: 0; }
.demo-embed iframe { display: block; width: 100%; border: 0; }

/* ---- CookieYes banner: brand styling ------------------------------
   The consent banner itself is injected at runtime by the third-party
   script in every page's <head> (cdn-cookieyes.com/client_data/...) -
   not markup this repo owns or controls, so this can't follow the
   "give it var(--card-bg)/.bv-btn" pattern the rest of the site's real
   components do. CookieYes's dashboard has a visual "Custom CSS" theme
   editor, but that's a paid-plan feature; this achieves the same result
   for free by overriding their own documented class names here instead,
   in a stylesheet this site already loads everywhere.
   .cky-consent-container, .cky-btn/.cky-btn-accept/.cky-btn-reject/
   .cky-btn-customize are CookieYes's own class names (used in their own
   customisation docs, e.g. the button-reorder guide targets exactly
   these three), not something inferred from this codebase. Their
   widget ships its own stylesheet which loads after this one and may
   carry higher specificity, so colour/radius/font are backed by
   !important - the usual, expected way to win against a third-party
   widget's own built-in theme (confirmed against CookieYes's own
   troubleshooting guidance, not a first choice for this codebase's own
   components). Verify against the live banner once deployed - this was
   written against CookieYes's documented class names, not a running
   instance of their script, since this sandbox can't reach
   cdn-cookieyes.com to inspect the real rendered DOM. */
.cky-consent-container {
  font-family: var(--font-ui) !important;
}
.cky-btn {
  border-radius: 12px !important;
  font-family: var(--font-ui) !important;
  font-weight: 500 !important;
  padding: 6px 22px !important;
  box-shadow: none !important;
  border: 0 !important;
  transition: background-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard) !important;
}
/* Accept: same filled-navy treatment as .bv-btn--primary. */
.cky-btn-accept {
  background: var(--button-primary-bg) !important;
  color: var(--button-primary-text) !important;
}
.cky-btn-accept:hover {
  background: transparent !important;
  color: var(--button-primary-bg) !important;
  box-shadow: 0 0 0 1px var(--button-primary-bg) !important;
}
/* Every other button (Reject, Customize, and the preference centre's
   "Save My Preferences" - confirmed live as a button CookieYes doesn't
   document a class name for, since .cky-btn-customize turned out to
   only cover the banner's own "Customize" button, not the modal's save
   action) gets the same outline treatment as .bv-btn--secondary. A
   :not() catch-all rather than naming each button individually, so any
   button class CookieYes ships that this page hasn't specifically seen
   still gets styled instead of falling back to their default blue. */
.cky-btn:not(.cky-btn-accept) {
  background: transparent !important;
  color: var(--color-neutral-black) !important;
  box-shadow: var(--ring-default) !important;
}
.cky-btn:not(.cky-btn-accept):hover {
  box-shadow: 0 0 0 1px var(--color-neutral-black) !important;
}
/* The floating "revisit consent" button - a separate circular widget
   CookieYes pins on screen so a visitor can reopen their preferences
   any time, not one of the in-banner/in-modal buttons above. Its own
   documented class, reported live showing CookieYes's default blue.
   Only the circle's fill changes here - the checkmark glyph inside it
   is a white SVG baked into CookieYes's own asset, not styleable via
   CSS, but white already reads fine against navy, same as every other
   on-dark icon/text on this site. */
.cky-btn-revisit-wrapper {
  background-color: var(--button-primary-bg) !important;
}

