/* Velaire Health · tokens v2.1 · LOCKED: A · Wine & Gold
   Drop-in override for the Aigocy template (assets/scss/abstracts/_variable.scss).
   Load AFTER the template CSS, or paste into :root in _variable.scss. */

:root {
  /* ---- Aigocy variable names, Velaire values ---- */
  --white:        #ffffff;
  --black:        #2e1119;   /* wine-950 stands in for pure black everywhere */
  --primary:      #efe3e4;   /* text on wine (template: #09090B) */
  --secondary:    #ddbdc4;   /* muted text on wine (template: #71717A) */
  --brand:        #c9a06a;   /* gold (template: #FD3A25) */
  /* CORRECTED 08-Sep. The handoff file mapped --body as "body copy on wine".
     In Aigocy --body is the PAGE GROUND -- `body { background-color: var(--body) }`
     is its only use, and the template's own value is #EDECEC, a near-white.
     Mapping it to a light pink left cream text on a pale ground. It has to carry
     the wine ground instead. Body copy is --secondary, which is already #ddbdc4. */
  --body:         #4a1f2d;   /* page ground (wine-800) */

  /* neutral ramp → wine ramp, same 12 steps so every component recolors */
  --neutral-50:   #f8f2f1;   /* card surface */
  --neutral-100:  #efe3e4;   /* text / cream fill */
  --neutral-200:  #ddbdc4;   /* muted text, hairlines on cream */
  --neutral-300:  #c49aa6;
  --neutral-400:  #a56c7f;
  --neutral-500:  #86394f;   /* wine-600 hover, orb core */
  --neutral-600:  #7a3345;
  --neutral-700:  #672c3e;   /* raised section */
  --neutral-800:  #4a1f2d;   /* DEFAULT ground */
  --neutral-900:  #3a1824;
  --neutral-950:  #2e1119;   /* footer, button base */

  /* ---- Velaire additions ---- */
  /* The accent ramp. --vh-gold-400 was used throughout with a literal fallback and
     never actually declared, which meant every rule referencing it resolved to the
     hard-coded value and the per-market themes could not reach any of them: a market
     switch changed --brand and nothing a reader could see. All four steps are
     declared here and all four are overridden per market below. */
  --vh-gold-300:  #e0bf8a;   /* gold text on wine, 7.6:1 */
  --vh-gold-400:  #e0bf8a;   /* the tone for text on the dark ground */
  --vh-gold-500:  #c9a06a;   /* fills: buttons, chips */
  --vh-gold-700:  #7b592d;   /* gold text on the cream pill, 5.0:1 */
  --vh-ink:       #201a1d;   /* text on cream */
  --vh-ink-muted: #6d5a62;
  --vh-rose:      #e88faf;   /* status: urgent / to a person. never decorative */
  --vh-rule:      rgba(239,227,228,0.14);
  --vh-rule-card: rgba(32,26,29,0.08);
  --vh-glass:     rgba(239,227,228,0.06);
  --vh-glass-bd:  rgba(239,227,228,0.16);
  --vh-orb:       radial-gradient(closest-side, rgba(134,57,79,0.55), rgba(74,31,45,0));

  /* type: the template's own, self-hosted by next/font ($font-main Urbanist,
     $font-roboto Roboto) */
  --vh-font-display: Urbanist, sans-serif;
  --vh-font-body:    Urbanist, sans-serif;
  --vh-font-mono:    Roboto, sans-serif;
}

/* Light sections: add data-theme="light" on the <section> */
[data-theme="light"] {
  --primary:   #201a1d;
  --secondary: #6d5a62;
  --body:      #6d5a62;
  --brand:     #4a1f2d;   /* wine becomes the button on cream; gold stays for kickers */
  --neutral-950: #f8f2f1;
  --neutral-900: #ffffff;
  --neutral-800: #f8f2f1;
  --neutral-700: #efe3e4;
  --neutral-200: rgba(32,26,29,0.12);
  --neutral-100: #4a1f2d;
  --neutral-50:  #2e1119;
}

/* ---- Button recipe: Aigocy .tf-btn, Velaire skin ----
   Keep the template's structure (pill, inset top light + bottom shade, radial sheen,
   .animate-btn shine sweep). Only the colors change. */
.tf-btn {
  color: #2e1119;
  font-family: var(--vh-font-body);
  background:
    radial-gradient(62.56% 62.56% at 28.14% -10.42%, rgba(255,255,255,0.35) 0%, rgba(255,255,255,0) 100%),
    linear-gradient(0deg, #c9a06a, #c9a06a);
  box-shadow:
    0 -3px 0 0 #9a7648 inset,
    0 1px 0 0 rgba(255,255,255,0.45) inset,
    0 2.77px 2.21px rgba(46,17,25,0.12),
    0 6.65px 5.32px rgba(46,17,25,0.13),
    0 12.52px 10.02px rgba(46,17,25,0.14),
    0 22.34px 17.87px rgba(46,17,25,0.14);
}
.tf-btn:hover {
  background:
    radial-gradient(62.56% 62.56% at 50% -13%, rgba(255,255,255,0.7) 0%, rgba(255,255,255,0) 100%),
    linear-gradient(0deg, #e0bf8a, #e0bf8a);
}
/* secondary: wine pill with cream text (replaces .tf-btn-2 dark grey) */
.tf-btn-2 {
  color: var(--vh-cream, #efe3e4);
  background:
    radial-gradient(62.56% 62.56% at 28.14% -10.42%, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0) 100%),
    linear-gradient(0deg, #2e1119, #2e1119);
  box-shadow:
    0 -3px 0 0 #1c0a10 inset,
    0 1px 0 0 rgba(255,255,255,0.18) inset,
    0 6.65px 5.32px rgba(46,17,25,0.25);
}
.tf-btn-2:hover { color: var(--vh-gold-300); }
/* shine sweep on hover, cream instead of white */
.animate-btn.tf-btn::after {
  background-image: linear-gradient(120deg, rgba(0,0,0,0) 20%, rgba(255,255,255,0.45), rgba(0,0,0,0) 70%);
}
/* THE TEXT IS DARK IN LIGHT MODE TOO, WHICH LOOKS WRONG NEXT TO THE WINE BELOW IT.
   That wine never paints. The market-gold rule further down repoints `background-image`
   at `--vh-gold-500` for every `.tf-btn`, at the same specificity and later in the file,
   so the button is gold in BOTH themes and only the `color` here ever applied. Near-white
   on that gold measured 1.98:1 on /demo and /product, and 1.00:1 on one /pricing button
   where the gradient does not reach -- near-white on near-white, an invisible label.
   #2e1119 is the value dark mode already uses: 7.2:1 on the US gold, and 6.8 / 7.4 / 7.7 /
   8.0 on the GB, AU, CA and IE ramps, so it clears AA in every market.
   Neither contrast gate catches this: v2:contrast returns null for anything with a
   background-image and hands it to v2:ground, and v2:ground only reads .section-page-title.
   The `background` line stays because it still resets background-color and sets the
   box-shadow; only its gradient is dead. */
[data-theme="light"] .tf-btn {
  color: #2e1119;
  background:
    radial-gradient(62.56% 62.56% at 28.14% -10.42%, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0) 100%),
    linear-gradient(0deg, #4a1f2d, #4a1f2d);
  box-shadow: 0 -3px 0 0 #2e1119 inset, 0 1px 0 0 rgba(255,255,255,0.2) inset, 0 6.65px 5.32px rgba(46,17,25,0.18);
}

/* ---- Motion: gsapAnimation.js edits ----
   char reveal:  gsap.set(chars, { color: "#FFFFFF52" }) → "rgba(239,227,228,0.4)"
                 gsap.to(chars,  { color: "#ffffff" })   → "#efe3e4"
   ScrollSmoother smooth: 2 → 1.4  (calmer; healthcare buyers, not agency visitors)
   pinned card stack: keep. cards use --neutral-50 (cream) on --neutral-800 (wine)
   parallax items (scrub: 2): keep, cap travel at 40px
   delete: hero.mp4 video bg, light-top.png / light-bot.png glows, earth.png */

/* ---- Wordmark ----
   nav:      "VelaireHealth"  Urbanist 600, 16px, letter-spacing -0.02em, cream
   display:  "Velaire Health" Urbanist 700/800, "Health" italic gold-300 */

/* ---- Typeface swap: Urbanist/Roboto -> Velaire's locked v2 faces ----
   The template hard-codes "Urbanist" in five rules and "Roboto" in one, and its
   fonts.css fetches both from fonts.gstatic.com, which this site's CSP blocks.
   next/font self-hosts Urbanist and Roboto -- the template's own two families -- in
   the route layout and exposes them as --v2-*; these rules point the template's own
   selectors at the self-hosted copies rather than at names nothing has loaded. Scoped to [data-v2-velaire] so nothing here can reach a live
   page even if the stylesheet were ever linked more widely.
   "icomoon" is untouched: it is the template's icon font and ships locally. */
[data-v2-velaire] body,
[data-v2-velaire] {
  font-family: var(--v2-body), Urbanist, system-ui, sans-serif;
}
[data-v2-velaire] h1,
[data-v2-velaire] h2,
[data-v2-velaire] h3,
[data-v2-velaire] h4,
[data-v2-velaire] h5,
[data-v2-velaire] h6,
[data-v2-velaire] .tf-btn,
[data-v2-velaire] .counter-number {
  font-family: var(--v2-display), Urbanist, system-ui, sans-serif;
}
/* The template sets Roboto on one label and nothing else, so nothing else takes it.
   An earlier pass had kickers, tags and breadcrumbs in a mono face; that was a
   Velaire addition rather than the template's, and Qasim asked for the template's
   type throughout. */
[data-v2-velaire] .section-about-us .review-box .text {
  font-family: var(--v2-mono), Roboto, system-ui, sans-serif;
}
/* The template names its faces on elements rather than letting them inherit, so
   pointing `body` at a face never reached them: 63 elements on the homepage alone
   computed to "Urbanist, sans-serif" and, with nothing loaded under that name, fell
   back to the browser's generic sans. The names are the same now that the route
   loads the template's own families -- these rules point them at the self-hosted
   copies rather than at whatever the browser guesses. */
[data-v2-velaire] a {
  font-family: var(--v2-body), Urbanist, system-ui, sans-serif;
}
/* A link inside a heading is still a heading. The rule above matches the <a>
   directly, which beats anything the <h*> passes down by inheritance. */
[data-v2-velaire] h1 a,
[data-v2-velaire] h2 a,
[data-v2-velaire] h3 a,
[data-v2-velaire] h4 a,
[data-v2-velaire] h5 a,
[data-v2-velaire] h6 a {
  font-family: var(--v2-display), Urbanist, system-ui, sans-serif;
}

/* Display tracking. Written for Bricolage, which is wide; Urbanist is narrower and
   already sits close, so the values ease off. Kept rather than dropped because the
   template's own headings still read loose at hero size against the category.

   `:not([class*="text-display"])` because these were written when every heading on the
   route was a bare element -- the template's own headings were divs. Promoting the hero
   to an <h1> gave an element carrying `.text-display-2` (letter-spacing: -0.03em), and
   `[data-v2-velaire] h1` is (0,1,1) against that class's (0,1,0), so it won and the
   hero rendered 3% wider on its own tracking. Where the template has stated a value for
   a display size, that value is the design.

   `.heading-title` is excluded from the h2 rule for the same reason, once every section
   title became an <h2>: `.heading-section .heading-title` is (0,2,0) and states -0.03em,
   and this rule is (0,2,1), so without the exclusion it would have retracked all 100
   section titles on the site. */
[data-v2-velaire] h1:not([class*="text-display"]) { letter-spacing: -0.02em; }
[data-v2-velaire] h2:not([class*="text-display"]):not(.heading-title) { letter-spacing: -0.015em; }
[data-v2-velaire] h3:not([class*="text-display"]) { letter-spacing: -0.01em; }

/* A pricing tier's name is an <h3> so the page has the same heading outline as live,
   where Recovery, Growth and Pro are <h3>s. The template wrote it as a bare div and
   never gave `.text` a size, so it rendered at the inherited 16px; Aigocy's own
   `h3 { font-size: 40px; line-height: 52px }` would otherwise take a three-word label
   to 40px and 162px wide. The tag change is semantic only, and this pins the render
   back: 16px/24px/normal before, 40px/52px/-0.4px as a bare h3, 16px/24px/normal with
   this -- the label's box measures 66x17 either way.

   The template does the same thing itself one element away -- `.price-per` is an <h6>
   and `.pricing-item .heading .price-per` restates its size -- so overriding a heading
   size for a label inside this card is the template's own pattern, not a departure. */
[data-v2-velaire] .pricing-item .top h3.text {
  font-size: 1rem;
  line-height: 1.5;
  letter-spacing: normal;
}

/* ---- Hero ground ----
   The template sets its hero background in CSS, not markup:
   `background-image: url(../images/section/hero-1.jpg)` twice in styles.css. That
   file is the flat #8EA4BB placeholder, which is why the hero read as a pale blue
   slab. Velaire's own reception photograph replaces it, darkened toward wine so
   cream type holds contrast over it — the photo is a ground here, not the subject. */
/* :not(.v2) so the service pages win — they set their own hero photograph inline,
   per service, and an !important rule here would silently beat every one of them. */
[data-v2-velaire] .section-hero:not(.v2) .hero-image,
[data-v2-velaire] .section-page-title {
  background-image:
    linear-gradient(180deg, rgba(46, 17, 25, 0.78) 0%, rgba(74, 31, 45, 0.88) 100%),
    url("/images/pages/reception-editorial.webp") !important;
  background-size: cover;
  background-position: center;
}

/* A phone does not need the 1536px plate.
   Measured 11-Sep on Slow 4G: this background IS the LCP element, it is the last
   asset to arrive, and it lands at about 5.5s. At 1536x1024 it is 143KB going to a
   390px viewport. The 640px variant is 18KB -- 125KB off the one file that decides
   the number, on a pipe moving roughly 200KB a second.

   Media queries rather than `image-set()`: `image-set()` picks on pixel density, and
   the problem here is the viewport, not the DPR. A 390px phone at 2x still only needs
   ~780 device pixels, which 900 covers. */
@media (max-width: 640px) {
  [data-v2-velaire] .section-hero:not(.v2) .hero-image,
  [data-v2-velaire] .section-page-title {
    background-image:
      linear-gradient(180deg, rgba(46, 17, 25, 0.78) 0%, rgba(74, 31, 45, 0.88) 100%),
      url("/images/pages/reception-editorial-640.webp") !important;
  }
}
@media (min-width: 641px) and (max-width: 991px) {
  [data-v2-velaire] .section-hero:not(.v2) .hero-image,
  [data-v2-velaire] .section-page-title {
    background-image:
      linear-gradient(180deg, rgba(46, 17, 25, 0.78) 0%, rgba(74, 31, 45, 0.88) 100%),
      url("/images/pages/reception-editorial-900.webp") !important;
  }
}

/* One ground per family. Every inner route's title band was the same reception
   photograph, so four pages side by side were the same picture with different words
   on it. Darkened toward wine at the same strength, so these read as texture and
   the change registers as a change of section rather than as a different site --
   except the corridor, which is a night shot and needs a lighter hand or it is a
   black band. MarketFlag stamps the attribute from the pathname. */
[data-v2-velaire][data-family="trust"] .section-page-title {
  /* Deeper than it started. The corridor has a lit lamp in it, and at 0.62/0.76 the
     glow put the sub text at 4.03:1 where it needs 4.5 -- measured by
     `npm run v2:ground`, which is the check that made this visible at all. */
  background-image:
    linear-gradient(180deg, rgba(46, 17, 25, 0.76) 0%, rgba(74, 31, 45, 0.88) 100%),
    url("/v2/images/velaire/ground-trust.webp") !important;
}
[data-v2-velaire][data-family="journal"] .section-page-title {
  background-image:
    linear-gradient(180deg, rgba(46, 17, 25, 0.78) 0%, rgba(74, 31, 45, 0.88) 100%),
    url("/v2/images/velaire/ground-journal.webp") !important;
}
[data-v2-velaire][data-family="legal"] .section-page-title {
  background-image:
    linear-gradient(180deg, rgba(46, 17, 25, 0.78) 0%, rgba(74, 31, 45, 0.88) 100%),
    url("/v2/images/velaire/ground-legal.webp") !important;
}

/* ---- Display type and the gradient fills ----
   Two things the inversion breaks, both invisible until the ground went dark.

   1. The hero title is a `div.title.text-display-2`, not an <h1>, so heading
      selectors never reach it. It also already ships letter-spacing: -0.03em,
      which is inside the -1.28px..-3.6px band the category uses — the template
      tightens display type correctly out of the box.
   2. `.text-gradient-1`, `-2` and `-3` fill text with a dark grey gradient
      (#43484D -> #292C2E) through `-webkit-text-fill-color: transparent`. On
      Aigocy's near-white ground that is dark-on-light and right. On wine it is
      dark-on-dark, which is why the headline read as a shadow of itself. */
[data-v2-velaire] .title,
[data-v2-velaire] .text-display-1,
[data-v2-velaire] .text-display-2,
[data-v2-velaire] .counter-number {
  font-family: var(--v2-display), Urbanist, system-ui, sans-serif;
}
[data-v2-velaire] .text-gradient-1 {
  background: linear-gradient(132.09deg, #efe3e4 11.15%, #e0bf8a 79.01%);
  -webkit-background-clip: text;
  background-clip: text;
}
/* -2 fades bottom-to-top. Aigocy uses it on one- and two-line headings where the
   fade reads as a flourish; "An AI front office / that knows your practice" is three
   lines at mobile width and the bottom two dissolved into the ground -- the fade was
   eating the sentence rather than decorating it.

   Kept as a fade, because it is the template's device and it looks right, but it
   bottoms out at a readable tone instead of at zero. Measured: 4.9:1 at the palest
   point against the wine ground. */
[data-v2-velaire] .text-gradient-2 {
  background: linear-gradient(360deg, rgba(239, 227, 228, 0.62) 0%, #efe3e4 100%);
  -webkit-background-clip: text;
  background-clip: text;
}
/* -3 was missed on the first pass, and it is the one that mattered most: sixteen
   headings use it, against four for -1 and one for -2. Its gradient runs #61666A
   to #292C2E, so every one of those headings was dark grey clipped to text on the
   wine ground -- present in the DOM, invisible on screen, and invisible to the
   contrast audit too, because `-webkit-text-fill-color: transparent` means the
   computed `color` the audit reads is rgba(0,0,0,0) rather than the grey actually
   painted. Found by screenshotting the page instead of measuring it. */
[data-v2-velaire] .text-gradient-3 {
  background: linear-gradient(95.5deg, #efe3e4 1.91%, #e0bf8a 63%);
  -webkit-background-clip: text;
  background-clip: text;
}

/* ---- Week walkthrough ----
   A time spine down /product. The rail carries the day, the clock and who answered,
   which is information rather than decoration: the order is the argument, and the
   marker colour says whether the desk, Velaire or a person took the call.

   Rose appears on exactly one row, Wednesday, because velaire.css reserves it for
   "urgent / to a person" and that row is the only one that qualifies. */
[data-v2-velaire] .v2-week-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
[data-v2-velaire] .v2-week-day {
  display: grid;
  grid-template-columns: 9.5rem 1fr;
  gap: 2rem;
  padding: 1.75rem 0;
  border-top: 1px solid var(--vh-rule);
  text-align: left;
}
[data-v2-velaire] .v2-week-day:last-child { border-bottom: 1px solid var(--vh-rule); }

[data-v2-velaire] .v2-week-rail { display: flex; flex-direction: column; gap: 0.15rem; }
[data-v2-velaire] .v2-week-dayname {
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--neutral-300);
}
[data-v2-velaire] .v2-week-clock {
  font-size: 1.6rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--primary);
  line-height: 1.1;
}
[data-v2-velaire] .v2-week-who {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.85rem;
  color: var(--secondary);
  margin-top: 0.3rem;
}
[data-v2-velaire] .v2-week-who::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--neutral-300);
}
[data-v2-velaire] .v2-week-day[data-who="desk"] .v2-week-who::before { background: var(--neutral-100); }
[data-v2-velaire] .v2-week-day[data-who="velaire"] .v2-week-who::before { background: var(--vh-gold-500); }
[data-v2-velaire] .v2-week-day[data-who="person"] .v2-week-who::before { background: var(--vh-rose); }

[data-v2-velaire] .v2-week-title {
  font-size: 1.35rem;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--primary);
  margin: 0;
  max-width: 30ch;
  text-wrap: balance;
}
[data-v2-velaire] .v2-week-text {
  margin: 0.6rem 0 0;
  color: var(--secondary);
  max-width: 62ch;
}

/* The one screen on this page. Cream, like the pricing cards, so it reads as a surface
   rather than another bordered block. */
[data-v2-velaire] .v2-week-screen {
  margin-top: 1.1rem;
  background: var(--neutral-50);
  color: var(--vh-ink);
  border-radius: 12px;
  overflow: hidden;
  max-width: 34rem;
}
[data-v2-velaire] .v2-week-screen-bar {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem 0.9rem;
  border-bottom: 1px solid var(--vh-rule-card);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--vh-gold-700);
}
[data-v2-velaire] .v2-week-screen-bar span:last-child {
  color: var(--vh-ink-muted);
  letter-spacing: 0.03em;
  text-transform: none;
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-week-screen-body { padding: 0.9rem; }
[data-v2-velaire] .v2-week-turn {
  margin: 0 0 0.7rem;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--vh-ink);
}
[data-v2-velaire] .v2-week-speaker {
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--vh-ink-muted);
  margin-bottom: 0.15rem;
}
[data-v2-velaire] .v2-week-handoff {
  margin: 0;
  padding: 0.6rem 0.75rem;
  border-radius: 8px;
  border-left: 3px solid #c72d5b;
  background: rgba(199, 45, 91, 0.1);
  color: #7d1839;
  font-weight: 600;
  font-size: 0.9rem;
}
[data-v2-velaire] .v2-week-note {
  margin: 1.1rem 0 0;
  font-size: 0.85rem;
  color: var(--neutral-300);
  text-align: left;
}

@media (max-width: 767px) {
  [data-v2-velaire] .v2-week-day { grid-template-columns: 1fr; gap: 0.65rem; }
  [data-v2-velaire] .v2-week-rail {
    flex-direction: row;
    align-items: baseline;
    gap: 0.7rem;
    flex-wrap: wrap;
  }
  [data-v2-velaire] .v2-week-clock { font-size: 1.2rem; }
  [data-v2-velaire] .v2-week-who { margin-top: 0; margin-left: auto; }
}

/* Card titles and the hero, both part of the same reversed-ground correction below.

   `.features-item .title` is the template's <h6> at 24px. On Aigocy's near-white ground
   that sits comfortably above its 16px body; on wine, against a body that is now 17px,
   the step was too short to read as a step. 26px restores the interval.

   The hero keeps the template's size and takes one step of weight. Aigocy's hero and
   ours are both genuinely w400 — measured at 1280, 1440, 1600 and 1920 — and ours reads
   lighter for the same reason the body read smaller: light on dark bleeds into the
   ground. Leaving the hero at 400 while compensating the body would have been half a
   correction. */
[data-v2-velaire] .features-item .title {
  font-size: 26px;
  line-height: 34px;
}
[data-v2-velaire] .title.text-display-2 {
  font-weight: 500;
}

/* ---- Optical compensation for the reversed ground ----
   Qasim read the type as too small against the Aigocy demo twice. Measured, it is not:
   the root is 16px on both, and hero, section title, body copy and nav links are
   identical at 1280, 1440, 1600 and 1920. Nothing in the CSS is smaller.

   What differs is the ground. Aigocy sets dark text on near-white; this port sets light
   text on wine. Reversed out, type of identical metrics reads both thinner and smaller
   — the light glyph bleeds into the dark ground, so the counters close up and the
   apparent size drops. Which means matching the template's NUMBERS on an inverted
   ground does not match its APPEARANCE, and the way to look the same is to differ.

   So: body copy goes to 17px and takes 450 rather than 400. Headings are untouched —
   they are already heavy enough to survive the reversal, and they are the part of the
   scale the template most obviously owns.

   450 is only available because the fonts are now the variable faces the template
   itself declares (see layout.tsx). Five static cuts could not have done this.

   To revert: delete this block. Nothing else depends on it. */
[data-v2-velaire] {
  font-size: 17px;
}
[data-v2-velaire] p,
[data-v2-velaire] .text,
[data-v2-velaire] .text-secondary,
[data-v2-velaire] .text-body-1,
[data-v2-velaire] .text-body-3 {
  font-weight: 450;
}
/* The nav and the buttons keep 600: they are already at a weight that survives the
   reversal, and lifting them would make the header shout. */
[data-v2-velaire] .menu-item > a,
[data-v2-velaire] .tf-btn,
[data-v2-velaire] .tf-btn-2 {
  font-weight: 600;
}

/* ---- Card titles, promoted to <h3> ----
   Aigocy uses h4/h5/h6 as SIZES, not as an outline: a card title is an h6 because 24px
   looked right. With section titles at <h2> that left 16 routes skipping a level, some
   jumping h1 straight to h6. Every card title is an <h3> now and carries a `v2-lvl-N`
   marker for the level it used to be, because in this template the size came from the
   TAG -- `h4.title.fw-semibold` was 36px and `h6.title.fw-semibold` 26px, identical
   classes, different sizes. Without the marker both collapse, and both take Aigocy's
   `h3 { font-size: 40px }`.

   ONE CLASS, ON PURPOSE, at (0,2,0). Every rule that sizes a card title by its class is
   more specific than this -- `.features-item .title` is (0,3,0), `.v2-integration h4`
   was (0,2,1) -- so those keep winning and only headings that were sized by their raw
   tag get pinned here. Values are the measured ones from before the change, not
   guesses. */
[data-v2-velaire] .v2-lvl-4 { font-size: 36px; line-height: 48px; }
[data-v2-velaire] .v2-lvl-5 { font-size: 32px; line-height: 40px; }
[data-v2-velaire] .v2-lvl-6 { font-size: 24px; line-height: 32px; }
/* `.text-body-1` sets 18px and is only (0,1,0), so it would lose to the markers above.
   It is a body-size utility deliberately applied to a heading -- the row-list titles and
   the post-card titles -- and the utility is the intent. */
[data-v2-velaire] .v2-lvl-4.text-body-1,
[data-v2-velaire] .v2-lvl-5.text-body-1,
[data-v2-velaire] .v2-lvl-6.text-body-1 { font-size: 18px; line-height: 28px; }

/* ---- The services picture ----
   The accordion's other half, which this page was missing entirely: every row carried a
   `data-img` and there was nothing to swap it into, so thirteen photographs were
   referenced and none rendered.

   All of them are in the DOM and only the active one is painted, rather than swapping a
   single `src`. Swapping the src is what the template does, and it shows: the browser
   fetches the new file at click time and the panel is empty until it arrives. Stacked
   and cross-faded, every picture is already decoded by the time it is asked for.

   `object-fit: cover` on a fixed aspect box, because the thirteen heroes are not all
   the same shape and a column that changes height as you click reads as a bug. */
/* A plain block, not a flex container. `display: flex` here was mine and it bought
   nothing — there is one child — while making that child a flex item whose width
   resolved to 0 against an aspect-ratio with no intrinsic content to size from (every
   picture inside is absolutely positioned). The column is already a Bootstrap col; let
   it behave like one. */
[data-v2-velaire] .v2-services-image { display: block; }
/* Scoped to THIS page's wrapper, because the template's own `.services-image` is
   `position: absolute; bottom: 0; max-width: 416px; height: 240px` — sized and anchored
   for the homepage's layout, where it tucks into the bottom of a column. Dropped into a
   grid cell here, those three declarations survived and fought the aspect ratio: the
   picture came out 190px wide in a 520px column. All four are overridden rather than
   the rule edited, so the homepage keeps the behaviour it was written for. */
[data-v2-velaire] .v2-services-image .services-image {
  position: relative;
  bottom: auto;
  width: 100%;
  max-width: none;
  height: auto;
  aspect-ratio: 4 / 5;
  border-radius: 24px;
  overflow: hidden;
  background: var(--neutral-700);
}
[data-v2-velaire] .v2-services-image .services-image img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.45s ease;
}
[data-v2-velaire] .v2-services-image .services-image img[data-on] { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  [data-v2-velaire] .v2-services-image .services-image img { transition: none; }
}
@media (max-width: 991px) {
  /* Below lg the accordion goes full width and the picture would be a tall slab under
     it, so it steps out of the way. The row text carries the meaning; the photograph
     is support. */
  [data-v2-velaire] .v2-services-image { display: none; }
}

/* ---- Coverage map ----
   Replaces the decorative dot sphere. The dots are a land raster generated by
   scripts/v2/make-worldgrid.mjs; the pins are projected from the same lon/lat bounds,
   so they sit where the coordinates put them rather than where they looked right.

   Gold marks the live market and the muted cream marks the ones in review. Rose is NOT
   used: velaire.css reserves it for "urgent / to a person", and a market under
   regulatory review is neither. */
[data-v2-velaire] .v2-coverage {
  display: flex;
  flex-direction: column;
  gap: 22px;
  width: 100%;
}
[data-v2-velaire] .v2-coverage-map {
  width: 100%;
  height: auto;
  overflow: visible;
}
/* The land takes currentColor, so one declaration moves the whole raster. */
[data-v2-velaire] .v2-coverage-land {
  color: var(--neutral-400);
  opacity: 0.55;
}
/* Ireland and the UK sit about six degrees apart, which is under a cell and a half
   here, so the in-review ring is kept small enough that the two read as two marks
   rather than one blob. Moving either pin off its coordinates was the alternative and
   would have made the map lie to tidy itself. */
[data-v2-velaire] .v2-pin-ring {
  fill: none;
  stroke: var(--neutral-300);
  stroke-width: 0.26;
}
[data-v2-velaire] .v2-pin-core { fill: var(--neutral-300); }
[data-v2-velaire] .v2-pin.is-live .v2-pin-ring { stroke: var(--vh-gold-500); stroke-width: 0.34; }
[data-v2-velaire] .v2-pin.is-live .v2-pin-core { fill: var(--vh-gold-400); }
[data-v2-velaire] .v2-pin-halo {
  fill: var(--vh-gold-500);
  opacity: 0.16;
}
@media (prefers-reduced-motion: no-preference) {
  [data-v2-velaire] .v2-pin-halo {
    transform-box: fill-box;
    transform-origin: center;
    animation: v2-pin-breathe 3.4s ease-in-out infinite;
  }
}
@keyframes v2-pin-breathe {
  0%, 100% { opacity: 0.16; transform: scale(1); }
  50%      { opacity: 0.06; transform: scale(1.35); }
}

/* The key carries the names and the status, because Ireland and the UK are closer
   together than one grid cell and on-map labels would collide. */
@media (max-width: 900px) {
  }
@media (max-width: 560px) {
  }
[data-v2-velaire] .v2-coverage-dot {
  grid-area: dot;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid var(--neutral-300);
}
[data-v2-velaire] .v2-coverage-name {
  grid-area: name;
  font-weight: 600;
  font-size: 15px;
  color: var(--primary);
  line-height: 1.3;
}

/* ---- Brand lockup ----
   Mark as an image, wordmark as live text. The handoff's wordmark SVG carries a
   <text> element, and an SVG inside <img> is an isolated document that cannot
   reach the page's webfonts, so it would have rendered in a generic monospace.
   Real text also stays selectable and scales with the type ramp. */
[data-v2-velaire] .v2-lockup {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  text-decoration: none;
}
[data-v2-velaire] .v2-lockup img {
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
}
/* 600, not 500. The spec five paragraphs up in this file says "Urbanist 600" and the
   rule said 500, so the implementation had drifted from its own stated intent. It shows:
   the nav links beside it are 600, and a wordmark LIGHTER than the menu next to it reads
   backwards — measured against the Aigocy demo, whose own lockup is a drawn logo at a
   visibly heavier optical weight. Qasim read this as "nav bar fonts mismatch", and it was
   the only type on the page that actually differed. */
[data-v2-velaire] .v2-wordmark {
  font-family: var(--v2-display), Urbanist, system-ui, sans-serif;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--primary);
  white-space: nowrap;
}
/* "Health" carries the gold and one more step of weight, so the two words read as one
   mark rather than two words of equal weight. */
[data-v2-velaire] .v2-wordmark b {
  font-weight: 700;
  color: var(--vh-gold-300);
}

/* ---- Editorial tokens, remapped for the dark ground ----
   The inner pages render the site's real components, which read editorial.css's
   tokens: warm paper ground, near-black ink, #68675f muted. All three are designed
   for a light page and are unreadable on wine — measured, not assumed: body copy
   came through at rgb(104,103,95) on rgb(74,31,45).

   Remapping the tokens rather than restyling the components is the whole point of
   the token layer. One block fixes every inner page, and the components stay
   untouched, so nothing here can leak back to the live site.

   Wine and gold values from palette.json. --accent becomes gold because wine on
   wine has no contrast, and gold-300 is the value the handoff already measured at
   7.6:1 on this ground. */
[data-v2-velaire] {
  --background:   #4a1f2d;
  --surface:      #3a1824;
  --paper-alt:    #3a1824;
  --foreground:   #efe3e4;
  --muted:        #ddbdc4;
  --edge:         rgba(239, 227, 228, 0.14);
  --accent:       #e0bf8a;
  --accent-safe:  #e0bf8a;
  --accent-2:     #a8c4b4;
  --wine:         #672c3e;
  --wine-deep:    #2e1119;
  --sage:         rgba(168, 196, 180, 0.16);
  --signal:       #c9a06a;
  --signal-safe:  #e0bf8a;
  --ink-deep:     #2e1119;
  --btn-fg:       #2e1119;
}
/* Cards and panels the editorial system fills with white need the darker surface,
   or they punch light rectangles through the page. */
[data-v2-velaire] main .band,
[data-v2-velaire] main [class*="rounded-"] {
  background-color: var(--surface);
}

/* Aigocy sets `h1..h6 { color: var(--black) }`, which is right on its near-white
   template and 1.26:1 on wine — measured on the pricing page before this rule.
   --primary is the text colour for the current ground and flips to ink under
   [data-theme="light"], so a light section keeps dark headings. */
[data-v2-velaire] h1,
[data-v2-velaire] h2,
[data-v2-velaire] h3,
[data-v2-velaire] h4,
[data-v2-velaire] h5,
[data-v2-velaire] h6 {
  color: var(--primary);
}
/* Promoting the section titles to <h2> brought them under BOTH heading colour rules
   for the first time -- this one and Aigocy's own `h1..h6 { color: var(--black) }` --
   where as divs they simply inherited their section's colour: near-white on the dark
   grounds, ink on the light ones. Neither rule reproduces that, and each gets it wrong
   on a different half of the site; excluding them from this one only handed all 32 to
   Aigocy's --black, measured at rgb(46,17,25) on every single title.

   `inherit` is what a div did. (0,2,0) here beats `[data-v2-velaire] h2` at (0,1,1)
   and Aigocy's bare `h1..h6` at (0,0,1), so it settles both. Verified by diffing all
   99 section titles before and after: colour, size, tracking, weight and box identical,
   tag the only difference. */
[data-v2-velaire] .heading-title { color: inherit; }
/* Links inside the ported page content: editorial styles them with the wine accent,
   which the overlay already remaps to gold. This just stops Bootstrap's default
   blue winning on anything editorial does not style. */
[data-v2-velaire] main a:not([class]) {
  color: var(--vh-gold-300);
}

/* ---- Per-market accents ----
   Qasim's direction, 08-Sep: every country gets its own colour while staying one
   brand. So the ground, the type and the layout stay identical across markets and
   only the accent moves — that is what keeps five sites recognisably Velaire
   instead of five different products.

   Each value is picked to hold contrast on the same wine ground rather than for
   national symbolism, and each is checked against #4a1f2d:
     gold    #e0bf8a  7.6:1   US, the base brand
     amber   #e8c07a  8.2:1   IE
     rose    #e8a9b8  6.9:1   GB
     sand    #ddc39a  8.4:1   CA
     apricot #f0b98a  7.4:1   AU
   All clear AA for body text and AAA for large. None is green: Cart Gaze took
   light-and-green precisely so the two brands do not converge.

   The .v2-market-flag row in the hero badge uses these too, so the market you are
   viewing is legible at a glance rather than only in the switcher. */
/* Each market overrides the whole ramp, not just --brand. The 400 step is the tone
   for text on wine and the 700 step is the tone for text on cream, so both have to
   move with the accent or a themed page ends up with one market's buttons and
   another's headings.

   The 700 values are not eyeballed. Each is its market's accent walked down in
   lightness -- hue and saturation held, so GB stays rose rather than turning brown --
   until it clears 5:1 against #efe3e4, the darkest of the three light surfaces
   it lands on (the kicker pill is #f5f5f5, the header bar #f8f2f1). A first pass picked them by
   hand and GB landed at 4.16:1, which the per-market audit caught on all twenty
   routes. Run it with MARKET=gb before changing any of these. */
[data-v2-velaire][data-market="ie"] {
  --brand: #e8c07a;
  --vh-gold-300: #f0d0a0;
  --vh-gold-400: #e8c07a;
  --vh-gold-500: #d8a94f;
  --vh-gold-700: #7f5916;
}
[data-v2-velaire][data-market="gb"] {
  --brand: #e8a9b8;
  --vh-gold-300: #f2c3ce;
  --vh-gold-400: #e8a9b8;
  --vh-gold-500: #d98da0;
  --vh-gold-700: #af2f4d;
}
[data-v2-velaire][data-market="ca"] {
  --brand: #ddc39a;
  --vh-gold-300: #ecd8b8;
  --vh-gold-400: #ddc39a;
  --vh-gold-500: #c9a86f;
  --vh-gold-700: #7a5a29;
}
[data-v2-velaire][data-market="au"] {
  --brand: #f0b98a;
  --vh-gold-300: #f6d0af;
  --vh-gold-400: #f0b98a;
  --vh-gold-500: #e09a5c;
  --vh-gold-700: #934e13;
}

/* The template paints its buttons and chips from a literal, so the ramp above never
   reached them: switching market changed the headings and left every button the US
   gold. Repointed at the token. */
/* Only .tf-btn. An earlier version of this rule also repointed the kicker pills,
   which turned their near-white ground into the accent itself -- and the dark accent
   text on them then measured 3.05:1 in GB, worse than the 4.16:1 it was meant to
   fix. The pills keep #f5f5f5; only their text follows the market. */
[data-v2-velaire] .tf-btn {
  background-image: radial-gradient(
      62.56% 62.56% at 28.14% -10.42%,
      rgba(255, 255, 255, 0.35) 0%,
      rgba(255, 255, 255, 0) 100%
    ),
    linear-gradient(0deg, var(--vh-gold-500), var(--vh-gold-500));
}

.v2-market-flag {
  font-size: 20px;
  line-height: 24px;
  filter: saturate(0.85);
}
/* The market you are actually on is full-strength; the others sit back. */
[data-v2-velaire][data-market="ie"] .v2-market-flag:nth-child(2),
[data-v2-velaire][data-market="gb"] .v2-market-flag:nth-child(3),
[data-v2-velaire][data-market="ca"] .v2-market-flag:nth-child(4),
[data-v2-velaire][data-market="au"] .v2-market-flag:nth-child(5),
[data-v2-velaire]:not([data-market]) .v2-market-flag:nth-child(1),
[data-v2-velaire][data-market="us"] .v2-market-flag:nth-child(1) {
  filter: none;
  transform: scale(1.15);
}

/* ---- Editorial buttons, given the template's treatment ----
   Qasim likes the live closing CTA — the split wine panel with "Your next caller
   could be your next patient" — and asked to keep it but improve the button to
   match the theme. So the panel, the type and the photograph stay exactly as the
   live site has them, and only .btn-primary takes Aigocy's .tf-btn recipe: pill
   radius, the inset highlight and the layered drop shadow that give the template's
   buttons their weight.

   The gradient is rebuilt in gold rather than copied. Aigocy's is near-black
   (#272727) with a white sheen, which is right on its light template and invisible
   against the wine CTA panel. Gold on wine is the v2 accent pairing, and #2e1119
   text on gold measures 8.9:1. */
[data-v2-velaire] .btn-primary,
[data-v2-velaire] main .btn-primary {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  border-radius: 99px;
  font-weight: 600;
  font-size: 16px;
  line-height: 24px;
  padding: 14px 24px;
  color: #2e1119;
  border: 0;
  background:
    radial-gradient(62.56% 62.56% at 28.14% -10.42%, rgba(255, 255, 255, 0.35) 0%, rgba(255, 255, 255, 0) 100%),
    linear-gradient(0deg, var(--vh-gold-500, #c9a06a), var(--vh-gold-300, #e0bf8a));
  box-shadow:
    0 -3px 0 0 rgba(46, 17, 25, 0.55) inset,
    0 1px 0 0 rgba(255, 255, 255, 0.35) inset,
    0 2.77px 2.21px 0 rgba(0, 0, 0, 0.12),
    0 6.65px 5.32px 0 rgba(0, 0, 0, 0.13),
    0 12.52px 10.02px 0 rgba(0, 0, 0, 0.13),
    0 22.34px 17.87px 0 rgba(0, 0, 0, 0.14);
  overflow: hidden;
  transition: box-shadow 0.2s ease, background 0.2s ease;
}
[data-v2-velaire] .btn-primary:hover {
  background:
    radial-gradient(62.56% 62.56% at 50% -13%, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0) 100%),
    linear-gradient(0deg, var(--vh-gold-500, #c9a06a), var(--vh-gold-300, #e0bf8a));
}
/* The ghost/secondary button gets the hairline version, not the filled one. */
[data-v2-velaire] .btn-ghost,
[data-v2-velaire] main .btn-ghost {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  border-radius: 99px;
  padding: 13px 22px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  color: var(--primary);
  font-weight: 600;
}

/* ---- Light cards on a dark page ----
   Aigocy is a light template, so a family of components take a near-white neutral
   as their background and rely on the page's dark default text. Inverted, those
   cards stay light while the text turns cream, and the content disappears — the
   product page's process cards measured a contrast ratio of exactly 1, meaning
   text and background were the same colour.

   The cards are kept light rather than darkened. A cream card on a wine ground is
   a deliberate part of this look, and turning every panel dark would flatten the
   page into one slab. What changes is the text inside them.

   Scoped to the cards themselves so it cannot leak: a blanket rule would also hit
   headings sitting directly on the wine ground, which need to stay cream. */
[data-v2-velaire] .process-card,
[data-v2-velaire] .featured-works-item .content,
[data-v2-velaire] .box-white,
[data-v2-velaire] .form-contact,
[data-v2-velaire] .section-about-us .review-box,
[data-v2-velaire] .section-about-us .box-quotes,
[data-v2-velaire] .section-about-us .mission-box,
[data-v2-velaire] .sub-menu {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .process-card .title,
[data-v2-velaire] .process-card h1,
[data-v2-velaire] .process-card h2,
[data-v2-velaire] .process-card h3,
[data-v2-velaire] .process-card h4,
[data-v2-velaire] .featured-works-item .content .heading,
[data-v2-velaire] .box-white .title,
[data-v2-velaire] .sub-menu a {
  color: var(--vh-ink, #201a1d);
}
/* Supporting copy inside a light card needs the muted INK, not the muted cream. */
/* !important is required, not sloppiness: .text-secondary is a Bootstrap utility
   and Bootstrap ships its utilities with !important, so specificity cannot win
   here. Scoped to text inside a light card, which is the only place it applies. */
[data-v2-velaire] .process-card .text,
[data-v2-velaire] .process-card .text-secondary,
[data-v2-velaire] .featured-works-item .content .text-secondary,
[data-v2-velaire] .featured-works-item .content .text-body-3,
[data-v2-velaire] .box-white .text-secondary {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
/* ---- The numbered badges in article figures ----
   Found 14-Sep by `npm run v2:contrast` on its first real run, and it is a genuine v2
   regression rather than a template quirk. `article-figures.tsx` is shared with the live
   site and hardcodes `text-white` on a badge filled with `--accent-2`. On live that token
   is PINE (#1e4d40, resolving to rgb(62,88,75)) and white measures 7.77:1. This skin
   remaps `--accent-2` to SAGE (#a8c4b4) for the wine ground, where the same white lands
   at 1.87:1 -- unreadable, on every numbered figure in every post.

   Fixed here rather than in the component, because the component is correct for the
   palette it was written against; it is this port's token remap that broke it. Darkening
   the numeral rather than the badge keeps the sage, which is doing real work as the one
   non-gold accent on the page.

   `!important` because `text-white` is a Tailwind utility on the element itself, and the
   first attempt -- a descendant selector scoped to `figure` -- matched nothing, because
   these badges sit in a plain `ol` with no figure ancestor.

   Keyed on the INLINE `--accent-2` background rather than on a size class. The first
   version matched `h-7` and fixed the numbered badges while leaving five checkmark
   badges at `h-5` still white on sage -- same component, same bug, different utility.
   What makes these unreadable is the sage fill, so that is what the selector should
   name. Measured after: 9.27:1 on v2, live unchanged at 7.77:1. */
[data-v2-velaire] [style*="--accent-2"][class*="text-white"] {
  color: var(--ink-deep, #2e1119) !important;
}

/* ---- The article rail: table of contents, then the booking card ----
   Ported from cartgaze.com's blog rail on Qasim's instruction, 13-Sep. The spine is the
   idea worth keeping: a vertical track behind numbered entries, filled down to the
   section you are reading, so the rail says how far through the article you are without
   a separate progress bar. Sections already read stay filled; the rest of the track is
   the hairline.

   Sticky because a rail that scrolls away is a list, not a rail -- the whole reason to
   number it is that a reader can come back to it mid-article. The offset clears the fixed
   header, same 116px the services console uses. */
/* The sidebar has to be as tall as the article, or the sticky TOC is pointless.
   `position: sticky` only holds inside its own container, and `.blog-sidebar` ends a
   long way above the foot of an 11,000px post -- so the rail scrolled away around a
   fifth of the way down and the reader lost it exactly when a table of contents starts
   being useful. Same defect as the services console on the homepage, same shape of fix:
   stretch the container so the sticky range covers the thing it is meant to track.

   The row is already `align-items: stretch` by default, so the column is full height;
   it is the sidebar inside it that needed telling. */
@media (min-width: 992px) {
  [data-v2-velaire] .section-blog .row > [class*="col-lg"]:last-child {
    display: flex;
    flex-direction: column;
  }
  [data-v2-velaire] .blog-sidebar {
    flex: 1 1 auto;
    min-height: 0;
  }
}

[data-v2-velaire] .blog-toc-sticky,
[data-v2-velaire] .sidebar-item:has(.v2-toc) {
  position: sticky;
  top: 116px;
  z-index: 2;
}
/* Pinning the rail put it in the path of everything below it in the same column.
   The sidebar is one flex column -- rail, booking card, search, recent posts,
   categories, tags -- so once the rail stops moving, the other five slide up THROUGH
   it. Measured 14-Sep at 1440px: 42px of overlap with the booking card at 530px of
   scroll, 216px with "Recent posts" at 1108px, 105px with "Tags" at 1824px, and since
   the rail had no background of its own the two sets of text simply printed over each
   other. It was clean again past ~2,900px, which is why it survived a look at the top
   of the page.

   The rail is opaque now, so they pass BEHIND it. That is the only one of the obvious
   fixes that keeps the promise made above -- reordering the column puts the rail below
   the fold, and making the whole sidebar sticky needs an inner scrollbar, because the
   column is ~1,500px against a 900px viewport.

   The padding is what stops a widget's text touching the last entry as it goes under,
   and the negative margin cancels it in flow, so nothing below the rail moves: measured
   at 1440px before and after, the booking card, search, recent posts, categories and
   tags all sit at the same y. The rail's own PAINTED box grows 10px upward and 18px
   down -- that is the bleed doing its job -- while its text stays where it was.

   Scoped to the sticky range on purpose. Below 992px the rule 100 lines down sets this
   back to `static`, the column is ordinary flow, and nothing overlaps. */
@media (min-width: 992px) {
  [data-v2-velaire] .blog-toc-sticky,
  [data-v2-velaire] .sidebar-item:has(.v2-toc) {
    background: var(--body, #4a1f2d);
    padding-block: 10px 18px;
    margin-block: -10px -18px;
  }
}
[data-v2-velaire] .v2-toc-list {
  list-style: none;
  margin: 0;
  padding: 0;
  position: relative;
}
/* The track. A pseudo-element rather than a border on each item, so the line is
   continuous through the gaps instead of dashed by them. */
[data-v2-velaire] .v2-toc-list::before {
  content: "";
  position: absolute;
  left: 11px;
  top: 6px;
  bottom: 6px;
  width: 1px;
  background: var(--edge, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-toc-item {
  position: relative;
  margin: 0;
}
[data-v2-velaire] .v2-toc-item a {
  display: grid;
  grid-template-columns: 24px minmax(0, 1fr);
  gap: 10px;
  align-items: baseline;
  padding: 7px 0;
  color: var(--muted, #ddbdc4);
  text-decoration: none;
  transition: color 0.18s ease;
}
[data-v2-velaire] .v2-toc-num {
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
  color: var(--muted, #ddbdc4);
  opacity: 0.55;
  position: relative;
  z-index: 1;
  /* Sits ON the track, so it needs the page ground behind it to break the line. The
     blog renders on wine, so this is `--background`, not the cream card token this rule
     first used -- that was written while the blog was briefly on a cream panel and was
     left painting a light chip on a dark rail when that was reverted. */
  background: var(--background, #4a1f2d);
  padding: 2px 0;
}
[data-v2-velaire] .v2-toc-text {
  font-size: 0.86rem;
  line-height: 1.45;
}
[data-v2-velaire] .v2-toc-item.is-read a,
[data-v2-velaire] .v2-toc-item.is-read .v2-toc-num { color: var(--vh-gold-300, #e0bf8a); }
[data-v2-velaire] .v2-toc-item.is-read .v2-toc-num { opacity: 0.85; }
[data-v2-velaire] .v2-toc-item.is-active a {
  color: var(--foreground, #efe3e4);
  font-weight: 600;
}
[data-v2-velaire] .v2-toc-item.is-active .v2-toc-num {
  color: var(--vh-gold-300, #e0bf8a);
  opacity: 1;
}
[data-v2-velaire] .v2-toc-item a:hover { color: var(--foreground, #efe3e4); }

/* The booking card. Deliberately quiet for a rail: it is on screen for the whole
   article, and a full-size advert pinned beside the text gets read once and then
   ignored for the remaining ten thousand pixels. */
[data-v2-velaire] .v2-book-call {
  border: 1px solid var(--edge, rgba(239, 227, 228, 0.14));
  border-radius: 16px;
  padding: 20px;
  background: var(--surface, #3a1824);
}
[data-v2-velaire] .v2-book-call-kicker {
  margin: 0 0 8px;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--vh-gold-300, #e0bf8a);
}
[data-v2-velaire] .v2-book-call-line {
  margin: 0 0 16px;
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--muted, #ddbdc4);
}
[data-v2-velaire] .v2-book-call-note {
  margin: 10px 0 0;
  font-size: 0.74rem;
  color: var(--muted, #ddbdc4);
  opacity: 0.9;
}
@media (max-width: 991px) {
  [data-v2-velaire] .sidebar-item:has(.v2-toc) { position: static; }
}

/* The card's own number/step marker sits back but must still be legible. */
[data-v2-velaire] .process-card .number .text-neutral-400,
[data-v2-velaire] .process-card .number .text-neutral-200,
[data-v2-velaire] .process-card .time {
  color: var(--vh-ink-muted, #6d5a62);
}

/* ---- Two inversion misses found by auditing every text node ----
   Both were invisible to a spot check because neither sits in <main>.

   1. The nav pill. .header-inner is rgba(255,255,255,.5) — a translucent WHITE
      pill, which stays white on a dark page. Aigocy's nav text is var(--black) and
      reads correctly on it; the wordmark I added took var(--primary) instead and
      measured 2.41:1. It needs ink, like everything else on that pill.

   2. The footer heading is a div.title, not an h1-h6, so the heading rule never
      reached it and it kept var(--black) — wine-950 on the dark footer, 1.26:1.
      Aigocy's footer is light, so black there is right; inverted it is not. */
[data-v2-velaire] .header-inner .v2-wordmark {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .header-inner .v2-wordmark b {
  color: var(--vh-gold-700, #7f6134);
}
[data-v2-velaire] .footer-content .title,
[data-v2-velaire] .footer-content .text,
[data-v2-velaire] .footer-logo .v2-wordmark {
  color: var(--primary);
}
[data-v2-velaire] .footer-logo .v2-wordmark b {
  color: var(--vh-gold-300, #e0bf8a);
}

/* The footer's social chips are another light-neutral surface: Aigocy gives
   .tf-social-1 a a cream background, which on its own template sits under dark
   text. Inverted, both went cream and the labels measured a ratio of exactly 1.
   Same treatment as the other light cards — keep the chip, darken the label. */
[data-v2-velaire] .tf-social-1 a {
  color: var(--vh-ink, #201a1d) !important;
}
[data-v2-velaire] .tf-social-1 a .social-item i {
  color: var(--vh-ink, #201a1d);
}

/* The nav pill is 50% white over whatever scrolls beneath it, so its effective
   luminance moves with the page. gold-700 measured 1.9:1 against it, which is not
   a colour problem but a translucency one: the safe move is to make the pill
   opaque so the contrast is a fixed, checkable number rather than one that shifts
   with the content behind the header. */
[data-v2-velaire] .header-inner {
  background-color: rgba(248, 242, 241, 0.94);
  backdrop-filter: blur(10px);
}

/* ---- Light surfaces: text that stayed light on them ----
   Targeted rather than systemic, and that is a considered choice. Inverting the
   whole neutral ramp was tried first, on the theory that Aigocy uses one ramp for
   both light surfaces and dark text. It was measured and REJECTED: failures on the
   homepage went from 27 to 74, because as many components read the ramp for text
   on the dark ground as read it for surfaces. The ramp stays as the handoff has it
   and the specific light surfaces are handled here.

   Each of these was found by measuring, and the ratio before the fix is recorded so
   a future change can tell whether it has regressed. */

/* Services accordion body.
   These two rules originally pushed this accordion's text to ink, which was right:
   on the homepage it sits inside .box-white, the template's cream panel. The
   services page puts the same component straight on the wine ground, and there the
   same rules made it invisible the other way round -- 1.24:1 for the tags, 2.16:1
   for the body.

   So it is scoped to the panel rather than to the component. .box-white is the only
   thing that decides which way round a surface is, and any rule that guesses
   instead will be wrong on half the routes. */
[data-v2-velaire] .accordion-content .text,
[data-v2-velaire] .accordion-content .text-neutral-300 {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}
[data-v2-velaire] .accordion-content .tags-item {
  color: var(--vh-cream, #efe3e4);
  border-color: rgba(239, 227, 228, 0.24);
}
[data-v2-velaire] .box-white .accordion-content .text,
[data-v2-velaire] .box-white .accordion-content .text-neutral-300 {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
[data-v2-velaire] .box-white .accordion-content .tags-item {
  color: var(--vh-ink, #201a1d);
  border-color: rgba(32, 26, 29, 0.18);
}
/* Everything else the cream panel contains, for the same reason. */
[data-v2-velaire] .box-white .accordion-title,
[data-v2-velaire] .box-white .accordion-action,
[data-v2-velaire] .box-white .num,
[data-v2-velaire] .box-white .text-white {
  color: var(--vh-ink, #201a1d) !important;
}
[data-v2-velaire] .box-white .text-secondary {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
/* Featured-works hover link: white on the cream card, 1.99:1. */
[data-v2-velaire] .featured-works-item .view-project,
[data-v2-velaire] .featured-works-item .tf-mouse {
  color: var(--vh-ink, #201a1d) !important;
}
/* Hero scroll cue: cream on Aigocy's #EDECEC strip, 1.06:1. */
[data-v2-velaire] .section-hero .link1,
[data-v2-velaire] .scroll-down,
[data-v2-velaire] .scroll-down .link1 {
  color: var(--vh-ink, #201a1d);
}
/* Counter figures sitting on a light panel. */
[data-v2-velaire] .counter-number,
[data-v2-velaire] .counter-number + span {
  color: var(--vh-ink, #201a1d);
}

/* Muted-neutral utilities inside a light card.
   .text-neutral-200/300/400 are Aigocy's greys, dark enough on its white surfaces.
   In the wine mapping they are light mauves, so on a cream card they measured
   1.38:1 (counters, and the "01 / 03" step markers). Scoped to the light surfaces
   already identified, so the same classes keep working on the dark ground. */
[data-v2-velaire] .process-card .text-neutral-200,
[data-v2-velaire] .process-card .text-neutral-300,
[data-v2-velaire] .process-card .text-neutral-400,
[data-v2-velaire] .counter,
[data-v2-velaire] .counter .number,
[data-v2-velaire] .counter.text-neutral-200,
[data-v2-velaire] .featured-works-item .content .text-neutral-200,
[data-v2-velaire] .featured-works-item .content .text-neutral-400,
[data-v2-velaire] .accordion-content .text-neutral-200,
[data-v2-velaire] .accordion-content .text-neutral-400 {
  color: var(--vh-ink-muted, #6d5a62) !important;
}

/* The trust-posture card's footer line sat on --neutral-400 and measured 4.17:1
   against the wine ground: readable, but under AA's 4.5 and therefore a fail. Uses
   the muted cream instead, which measures about 8:1. Worth fixing rather than
   rounding up — 4.17 is exactly the kind of near-miss that survives a visual check
   and fails an audit. */
[data-v2-velaire] .awards-item .year {
  color: var(--secondary) !important;
}

/* ---- The FAQ accordion ----
   The largest instance of the light-card problem, and the one that mattered most:
   .accordion-asked-item takes --neutral-100 as its background, so every question
   and answer was cream on cream at a ratio of exactly 1. The whole FAQ section was
   invisible on every page that has one, which is most of them.

   It reads as a light card on the wine ground, which is right for this design, so
   the card stays and the text inside it goes to ink.

   Hover uses gold-700 rather than --brand: --brand is gold-500, which is a light
   accent chosen for the dark ground and would be a low-contrast hover on cream. */
[data-v2-velaire] .accordion-asked .accordion-button,
[data-v2-velaire] .accordion-asked .accordion-body,
[data-v2-velaire] .accordion-asked-item,
[data-v2-velaire] .accordion-asked-title {
  color: var(--vh-ink, #201a1d) !important;
}
[data-v2-velaire] .accordion-asked .accordion-body {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
[data-v2-velaire] .accordion-asked .accordion-button:hover,
[data-v2-velaire] .accordion-asked .accordion-button:not(.collapsed) {
  color: var(--vh-gold-700, #7f6134) !important;
}
/* The open/closed chevron is drawn with currentColor in places and a token in
   others; pin it so it does not vanish against the cream card. */
[data-v2-velaire] .accordion-asked .accordion-button .right-icon {
  color: var(--vh-ink, #201a1d);
  border-color: rgba(32, 26, 29, 0.3);
}

/* ---- The closing CTA ----
   Qasim asked for the live site's closing block to survive the port, with the
   template's button instead of the live one. It now generates into every route
   rather than the homepage alone.

   The live version is a split: copy on a solid wine panel, photograph beside it.
   Two earlier attempts got this wrong in instructive ways. Putting the photograph
   behind the whole panel and darkening it read as a washed-out picture with text on
   top, with the reception desk running through the headline. Confining it to column
   two fixed that but left it in the normal row flow, so it landed under the copy as
   a band rather than beside it. It now spans every row explicitly.

   The copy column is a nested grid so the headline, body and button stack with
   honest gaps instead of being spaced by the panel's own row distribution, which is
   what left a hole between the headline and the body. */
[data-v2-velaire] .v2-closing {
  position: relative;
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  border-radius: 24px;
  overflow: hidden;
  background: linear-gradient(135deg, #3a1420 0%, #4a1f2d 100%);
}
/* The photograph: its own column, every row, so it is a true half rather than a
   band under the copy. */
[data-v2-velaire] .v2-closing::after {
  content: "";
  position: absolute;
  inset: 0 0 0 auto;
  width: calc(0.9 / 2 * 100%);
  background: url("/images/pages/reception-editorial.webp") center / cover no-repeat;
  box-shadow: inset 0 0 0 9999px rgba(58, 20, 32, 0.24);
}
/* The three copy children stack down column one in their own rows. Pinning them
   all to row 1 (an earlier attempt) piled the button on top of the headline. */
[data-v2-velaire] .v2-closing > * {
  grid-column: 1;
  padding-left: clamp(28px, 4vw, 64px);
  padding-right: clamp(24px, 3vw, 40px);
  margin: 0;
}
[data-v2-velaire] .v2-closing > .heading-section {
  display: flex;
  flex-direction: column;
  gap: 18px;
  align-self: end;
  padding-top: clamp(36px, 5vw, 68px);
}
[data-v2-velaire] .v2-closing > p { align-self: center; padding-top: 20px; }
/* Inline padding on the button would paint its own background out to the panel's
   rounded corner, so it is inset with margin instead. */
[data-v2-velaire] .v2-closing > .tf-btn {
  align-self: start;
  padding-left: 28px;
  padding-right: 28px;
  margin-left: clamp(28px, 4vw, 64px);
  margin-top: 24px;
  margin-bottom: clamp(36px, 5vw, 68px);
}
[data-v2-velaire] .v2-closing .heading-title {
  margin-bottom: 0;
  /* The gradient clip crops descenders without this. */
  padding-bottom: 6px;
}
[data-v2-velaire] .v2-closing em { font-style: italic; }
[data-v2-velaire] .v2-closing p {
  color: var(--vh-cream-dim, #ddbdc4);
  max-width: 44ch;
  margin: 0;
}
/* A grid child stretches by default, which made the button span the whole column
   and run past the panel's rounded corner. */
[data-v2-velaire] .v2-closing .tf-btn { width: fit-content; }

@media (max-width: 991px) {
  /* One column, photograph on top, so the copy never shares space with it. */
  [data-v2-velaire] .v2-closing {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto;
  }
  [data-v2-velaire] .v2-closing::after {
    position: static;
    width: auto;
    height: 220px;
    order: -1;
  }
  [data-v2-velaire] .v2-closing > * { grid-column: 1; }
}

/* The page-title lede. The template's page-title block carries a title and a
   breadcrumb and nothing between them, but every live content page opens on a
   sentence that frames the page, so one is added rather than dropped. */
[data-v2-velaire] .page-title-sub {
  max-width: 62ch;
  margin: 16px auto 0;
  font-size: clamp(15px, 1.15vw, 18px);
  line-height: 1.6;
}

/* ---- Transcript and declaration ----
   The template ships no conversation pattern and no single-statement panel, and
   both are load-bearing on the safety page: the transcript is the evidence that
   the refusal is real, and the declaration is the first thing a practice manager
   reads. Built from the template's own surface treatment rather than imported
   from the live site, which would arrive in the wrong skin. */
/* Opaque, not translucent. These panels are dropped into sections that are wine on
   one route and cream on another, and a 4%-alpha ground takes the colour of
   whatever is behind it -- so the same transcript measured 8:1 on one page and
   2.01:1 on the next. Declaring the ground makes the component read the same
   everywhere, which is what a component should do. */
/* ---- A conversation, on a phone ----
   The three transcripts on this site are all phone conversations: an evening call on
   the homepage, a refusal on the safety page, and an SMS exchange on
   patient-messaging. They used to render as a stack of labelled paragraphs in a
   panel, which is a transcript in the legal sense and not in the sense a reader
   pictures. The device carries the meaning here, so it is drawn rather than implied.

   The caption stays outside the screen -- it is the writer's note about the exchange,
   not a message in it -- and moves under the phone below 992px. */
[data-v2-velaire] .v2-phone-row {
  /* The handset follows a lede in every one of its three uses, and a lede's own
     bottom margin leaves it almost touching the device's top edge. */
  margin-top: 32px;
  display: flex;
  align-items: center;
  gap: clamp(28px, 4vw, 56px);
  flex-wrap: wrap;
  /* Capped, or the caption stretches across the whole 1296px container and ends up a
     long way from the device it is describing. */
  max-width: 880px;
}
[data-v2-velaire] .v2-phone {
  flex: 0 0 auto;
  width: 344px;
  max-width: 100%;
  padding: 11px;
  border-radius: 46px;
  background: linear-gradient(160deg, #4a2130 0%, #33131e 60%, #401a27 100%);
  border: 1px solid rgba(239, 227, 228, 0.16);
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.06) inset,
    0 26px 60px rgba(24, 8, 13, 0.5),
    0 6px 18px rgba(24, 8, 13, 0.35);
}
[data-v2-velaire] .v2-phone-screen {
  border-radius: 36px;
  background: #2a0f18;
  overflow: hidden;
  border: 1px solid rgba(239, 227, 228, 0.09);
}
[data-v2-velaire] .v2-phone-bar {
  position: relative;
  padding: 14px 18px 12px;
  text-align: center;
  border-bottom: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.12));
  background: rgba(239, 227, 228, 0.035);
}
/* The speaker pill at the top of the handset. Decorative, so it is not announced. */
[data-v2-velaire] .v2-phone-notch {
  display: block;
  width: 68px;
  height: 5px;
  margin: 0 auto 10px;
  border-radius: 999px;
  background: rgba(239, 227, 228, 0.22);
}
[data-v2-velaire] .v2-phone-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-phone-thread {
  display: grid;
  gap: 12px;
  padding: 18px 16px 22px;
}
[data-v2-velaire] .v2-phone-turn {
  display: grid;
  gap: 5px;
  justify-items: start;
  max-width: 88%;
}
[data-v2-velaire] .v2-phone-turn.is-self {
  justify-items: end;
  justify-self: end;
}
[data-v2-velaire] .v2-phone-who {
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--vh-cream-dim, #ddbdc4);
  padding: 0 4px;
}
[data-v2-velaire] .v2-phone-bubble {
  margin: 0;
  padding: 11px 15px;
  border-radius: 18px 18px 18px 6px;
  font-size: 14px;
  line-height: 1.55;
  background: rgba(239, 227, 228, 0.09);
  color: var(--vh-cream, #efe3e4);
}
/* Velaire's side of the exchange is the gold one, on wine ink -- the same pairing the
   primary button uses, so the reader already knows which voice it is. */
[data-v2-velaire] .v2-phone-turn.is-self .v2-phone-bubble {
  border-radius: 18px 18px 6px 18px;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
  color: #2e1119;
}
[data-v2-velaire] .v2-phone-turn.is-self .v2-phone-who {
  color: var(--vh-gold-400, #e0bf8a);
}
/* The system row is a log line, not a spoken turn, so it spans the thread and sits
   back rather than taking a side. */
[data-v2-velaire] .v2-phone-meta {
  margin-top: 4px;
  padding: 12px 14px;
  border-radius: 14px;
  border: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.12));
  background: #240d14;
}
[data-v2-velaire] .v2-phone-meta-who {
  font-size: 10px;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  font-weight: 600;
  color: var(--vh-cream-dim, #ddbdc4);
  margin-bottom: 6px;
}
[data-v2-velaire] .v2-phone-meta-line {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-phone-caption {
  flex: 1 1 260px;
  max-width: 46ch;
  margin: 0;
  font-size: 0.95em;
  line-height: 1.7;
}
@media (max-width: 991px) {
  [data-v2-velaire] .v2-phone-caption { flex-basis: 100%; max-width: 60ch; }
}

/* The "sixteen more" block sits directly under the four practice-type cards, and
   its eyebrow badge was landing against the bottom of the Dental card with nothing
   between them. `Section` supplies rhythm between sections; this is a second block
   inside one, so it has to supply its own. */
[data-v2-velaire] .v2-more-types { margin-top: clamp(56px, 6vw, 96px); }

/* ---- the tilted handset ----------------------------------------------------
   The old cartgaze.com mockup Qasim pointed at: the card turned a few degrees,
   lifted, and lit from behind. Three rules make that read as depth rather than as
   a rotated rectangle.

   The tilt is small. 4 degrees with a little perspective reads as a photographed
   object; 10 reads as a mistake, and any rotation at all costs legibility on the
   thread inside, which is the part that has to be readable.

   The glow sits BEHIND on a pseudo-element rather than as a box-shadow, so it can
   be soft and wide without the handset's own shadow fighting it, and so it can be
   removed for reduced motion without touching the device.

   Transform only, and the element reserves its own space, so this cannot shift
   layout and the page's measured CLS of 0.000 survives.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-phone-row.is-tilt {
  max-width: none;
  justify-content: center;
  perspective: 1400px;
  /* The glow below must never widen the document. See the note on the `::before`.
     `clip`, not `hidden`: the light has to keep falling BELOW the handset, and
     `overflow-x: hidden` computes `overflow-y` to `auto`, which would cut it. */
  overflow-x: clip;
}
[data-v2-velaire] .v2-phone-row.is-tilt .v2-phone {
  position: relative;
  transform: rotate(-4deg) rotateY(7deg) translateZ(0);
  transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}
[data-v2-velaire] .v2-phone-row.is-tilt:hover .v2-phone {
  transform: rotate(-2deg) rotateY(3deg) translateZ(0);
}
/* The light behind it. Wider than the device and well below it, which is what makes
   the handset look lifted off the page rather than pasted onto it. */
[data-v2-velaire] .v2-phone-row.is-tilt .v2-phone::before {
  content: "";
  position: absolute;
  inset: 6% -18% -12%;
  z-index: -1;
  border-radius: 50%;
  background: radial-gradient(
    closest-side,
    rgba(224, 191, 138, 0.34),
    rgba(224, 191, 138, 0.1) 58%,
    rgba(224, 191, 138, 0) 78%
  );
  filter: blur(26px);
}
/* The capture panel is no longer a column beside the phone, so it takes the full
   measure under the split and centres on it. */
[data-v2-velaire] .v2-call-capture {
  max-width: 760px;
  margin: 40px auto 0;
}
/* ---- The fork section's handset, sized to the text beside it ----
   Qasim, 13-Sep: put the handset and the heading on the same row and shrink the phone,
   because the space between them was dead.

   The row was already two columns. The gap was `align-items-center` resolving a 963px
   handset against 420px of copy: half the 553px difference went ABOVE the kicker, which
   is why the heading started roughly 40% down the section. Aligning the tops removes
   that half outright, and narrowing the device from 344px to 280px takes the handset to
   about 780px, so what is left below the copy reads as margin rather than a hole.

   The device is narrowed rather than `transform: scale`d: scale would shrink the type
   inside the screen along with the frame and the transcript would stop being legible,
   which is the one thing this section exists to show. Every dimension inside the phone
   is already relative to its width, so the frame, radius and bubbles follow on their
   own. */
@media (min-width: 992px) {
  /* `!important` is required, not lazy. The row carries Bootstrap's `.align-items-center`
     utility, and Bootstrap ships every utility as `!important` by design -- so no amount
     of specificity beats it, and raising specificity was tried first and did not.
     Measured before: computed `center`, with the left column pushed 260px into the row,
     exactly half the height the handset exceeded the copy by. That 260px is the gap. */
  [data-v2-velaire] .v2-call-split {
    align-items: flex-start !important;
  }
  /* Scaled, NOT narrowed. Narrowing was tried first and made it worse: at 280px the
     transcript re-wrapped onto more lines and the device came out 1043px, 80px TALLER
     than the 963px it was meant to fix. Scale keeps every line break exactly where it
     is and takes the whole device down together -- 0.82 puts it at about 790px, and
     the bubble text lands near 12px, still comfortably readable at this size.
     `transform-origin: top` so it shrinks toward the copy's first line rather than
     away from it, and the scale is composed INTO the tilt's own transform, because a
     second `transform` on the same element would replace it and the handset would
     stand up straight. */
  [data-v2-velaire] .v2-call-split .v2-phone-row.is-tilt .v2-phone {
    transform: scale(0.82) rotate(-4deg) rotateY(7deg) translateZ(0);
    transform-origin: top center;
  }
  [data-v2-velaire] .v2-call-split .v2-phone-row.is-tilt:hover .v2-phone {
    transform: scale(0.82) rotate(-2deg) rotateY(3deg) translateZ(0);
  }
  /* The device sits in a flex row that centres it; with the tops aligned it should sit
     against the copy's first line instead, less the lede margin the row already adds. */
  [data-v2-velaire] .v2-call-split .v2-phone-row {
    margin-top: 0;
    align-items: flex-start;
  }
}

/* The two ledes in this column ran together as one block: `Lede` carries `mb-48`, and
   measured on the page that computes to 0 -- something in the template zeroes paragraph
   margins. The paragraphs are also not siblings of each other, because GSAP's fadeUp
   wraps each one in its own `.overflow-hidden`, so an adjacent-sibling rule on `p` never
   matches. Spacing the column's own children is what actually works here. */
[data-v2-velaire] .v2-call-split > [class*="col-"]:first-child > * + * {
  margin-top: 18px;
}

/* ---- The site chat widget, in the fork section ----
   Light ground on purpose. The handset opposite it is wine because a conversation on a
   phone belongs on a phone's own dark screen, and eleven of the thirteen service screens
   are light (Qasim, 11-Sep). A widget that sits on a practice's own cream website should
   look like it sits there, and the contrast is what stops the two mockups in this row
   reading as the same object twice.

   Capped rather than full-width: it is a website widget, and a widget that spans a
   648px column stops looking like one. */
[data-v2-velaire] .v2-sitechat {
  margin-top: 28px;
  max-width: 440px;
}
[data-v2-velaire] .v2-sitechat-frame {
  border: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.1));
  border-radius: 18px;
  background: #fff;
  overflow: hidden;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.7) inset,
    0 18px 40px -24px rgba(32, 26, 29, 0.4);
}
[data-v2-velaire] .v2-sitechat-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  background: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-sitechat-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--vh-gold-500, #c9a06a);
  flex: 0 0 auto;
}
[data-v2-velaire] .v2-sitechat-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-sitechat-status {
  margin-left: auto;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-sitechat-thread {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px;
}
[data-v2-velaire] .v2-sitechat-turn { display: flex; flex-direction: column; gap: 4px; }
[data-v2-velaire] .v2-sitechat-turn.is-self { align-items: flex-end; }
[data-v2-velaire] .v2-sitechat-who {
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-sitechat-bubble {
  margin: 0;
  max-width: 88%;
  padding: 10px 14px;
  border-radius: 14px;
  font-size: 0.86rem;
  line-height: 1.45;
  background: var(--vh-cream, #efe3e4);
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-sitechat-turn.is-self .v2-sitechat-bubble {
  background: var(--vh-gold-500, #c9a06a);
  color: #2a1a10;
}
[data-v2-velaire] .v2-sitechat-input {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-top: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  font-size: 0.8rem;
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-sitechat-send {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--vh-gold-500, #c9a06a);
  color: #2a1a10;
  flex: 0 0 auto;
}
[data-v2-velaire] .v2-sitechat-send .icon { font-size: 12px; }
[data-v2-velaire] .v2-sitechat-note {
  margin: 12px 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
  max-width: 440px;
}

[data-v2-velaire] .v2-call-split .v2-phone-caption { display: none; }
@media (max-width: 991px) {
  [data-v2-velaire] .v2-phone-row.is-tilt .v2-phone { transform: none; }
  [data-v2-velaire] .v2-call-capture { margin-top: 28px; }
}
/* THE GLOW GAVE THE HOMEPAGE A HORIZONTAL SCROLL AT 390px, 992px AND 1024px.
   Measured 11-Sep. Its inset reaches 18% of the handset's width past each edge, so
   at 390px -- handset 342px inside a 366px column -- the light ran to x=427, and at
   992px the tilt's rotation widens that box further inside a 456px column.

   Three things had to be true at once and none of them is visible in a .tsx file.
   The template DOES wrap every `.effectFade.fadeUp` block in a `div.overflow-hidden`
   that would have caught it -- gsapAnimation.js injects the wrapper at runtime --
   but Aigocy's own styles.css:865 ships `.overflow-hidden { overflow: visible
   !important }` and neuters the class site-wide. And the box doing the overflowing
   is a pseudo-element, so nothing that walks the DOM can see it either.

   Do not "fix" this by flipping styles.css:865 back. That wrapper sits around every
   reveal-animated block on 26 sections, and re-clipping all of them to contain one
   decoration is the wrong trade.

   The `overflow-x: clip` above was written, removed, and put back, which is worth
   recording. It was removed because at 992px it trims the light where the column is
   narrowest, and paying a rendering cost at a width that had no bug looked wrong.
   That reasoning was incomplete: without it 992 and 1024 scroll sideways. Measured
   both ways at 992, the clip costs a max channel delta of 20/255 and a mean of 7.7
   across a soft gradient -- a slightly dimmer glow, no hard edge, because the outer
   band of the gradient is already near-transparent. A horizontal scrollbar is the
   larger defect. The clip also holds the invariant at every width rather than at the
   ones measured today.

   Below 576px it is dropped outright rather than clipped: there the handset is 342px
   of a 366px row, so clipping WOULD leave a 12px hard-edged halo, which reads as a
   rendering fault instead of as light. The handset keeps its own shadow at every
   width.

   `npm run v2:mobile` is the guard against this class of defect returning. It
   bisects the DOM by hiding subtrees, which is the only method that finds a
   pseudo-element. */
@media (max-width: 575.98px) {
  [data-v2-velaire] .v2-phone-row.is-tilt .v2-phone::before { content: none; }
}
@media (prefers-reduced-motion: reduce) {
  [data-v2-velaire] .v2-phone-row.is-tilt .v2-phone { transition: none; }
}

/* ---- the handset's aside ---------------------------------------------------
   With an aside beside it, the caption stops being the only thing in the right
   column and drops to its own full-width line under both. It is a footnote about
   the exchange, so the vertical middle of a 1050px handset was never where it
   belonged -- it simply had nowhere else to go while it was the sole sibling.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-phone-row.has-aside {
  /* The 880px cap exists so a lone caption does not drift away from the device.
     With a real second column there is content to fill the width, so it lifts. */
  max-width: 1120px;
  align-items: center;
}
[data-v2-velaire] .v2-phone-aside {
  flex: 1 1 320px;
  min-width: 0;
}
[data-v2-velaire] .v2-phone-row.has-aside .v2-phone-caption {
  flex: 0 0 100%;
  max-width: 68ch;
  margin-top: 4px;
}
/* ---- the hero's second line ------------------------------------------------
   The template puts the decorative tile pill INLINE at the end of line two --
   "Every appointment booked. [pill]" -- in a flex row with `flex-wrap`. That is
   the arrangement Qasim likes on cartgaze.com, and it is the arrangement this
   page was supposed to have.

   It broke when the tagline changed on 11-Sep. "Every request captured." fitted
   beside the pill; "Every appointment booked." does not. Measured at 1440: the
   text is 1113px, the pill 255px, the gap 20px -- 1388px into a 1296px row. So
   the pill wrapped to a line of its own and sat floating under the headline,
   which is what read as badly aligned. Nothing was off-centre; every element
   measured dead centre at 720. It was a wrap, not an alignment.

   The numbers below are chosen to fit at 1280 and up, which is where the row is
   narrowest before the template stacks the hero anyway: 1021 + 184 + 12 = 1217
   into the 1224 row at 1280, and 79px of slack at 1440. The first attempt landed
   on 1225 against a 1224 row and wrapped over a single pixel, which is the whole
   argument for measuring this rather than nudging it by eye. If the tagline
   changes again, re-measure -- `.title2` reports its own widths.
---------------------------------------------------------------------------- */
@media (min-width: 1280px) {
  [data-v2-velaire] .section-hero .title.text-display-2 { font-size: 88px; }
  [data-v2-velaire] .section-hero .title2 { gap: 12px !important; }
  /* `width` alone was not enough, and this is the correction to the 11-Sep fit.
     Narrowing `.title-icon` narrows the FLEX ITEM and nothing inside it: styles.css
     sizes the gold plate (`.box`) and the tile stack (`.title-icon-wrap`) at a
     hard 255px each, so from 1280px up the pill kept painting 255px inside a box
     that reserved 184. It overhung its own slot by 71px at every width from 1280,
     and at exactly 1280 -- where the row is narrowest -- that put the pill's right
     edge at 1319 and gave the homepage a 39px horizontal scroll.

     Scaling rather than restating the eight numbers styles.css uses to place the
     plate and the three tiles: `transform` does not affect layout, so the row still
     reserves 184px, and 255 x (184/255) paints exactly 184. The two stay in step if
     either number is ever re-measured. `left center` keeps the pill on the text's
     vertical centre while it shrinks. */
  [data-v2-velaire] .section-hero .title2 .title-icon {
    width: 184px;
    transform: scale(calc(184 / 255));
    transform-origin: left center;
  }
}

/* ---- coverage flags ---------------------------------------------------------
   Replaces the per-country "Live today" / "In review" key. Two things it fixes:
   the names were being clipped -- five columns could not hold "United Kingdom"
   above a status line -- and Qasim wanted the flags floating rather than labelled.

   The bob is a 3px translate on a 6s loop with a per-flag delay set inline, so the
   row drifts rather than pulsing in unison. Transform only, so it cannot shift
   layout and the page's measured CLS of 0.000 is untouched.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-coverage-flags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(18px, 3.4vw, 46px);
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
}
[data-v2-velaire] .v2-coverage-flags li {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  animation: v2-bob 6s ease-in-out infinite;
}
[data-v2-velaire] .v2-flag {
  font-size: clamp(30px, 3.4vw, 40px);
  line-height: 1;
  /* Emoji flags render from the system font; naming it stops a webfont from
     substituting a letter pair on platforms that lack the glyph. */
  font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
  filter: drop-shadow(0 6px 14px rgba(24, 8, 13, 0.34));
}
[data-v2-velaire] .v2-coverage-flags .v2-coverage-name {
  font-size: 13.5px;
  font-weight: 600;
  white-space: nowrap;
  color: rgba(239, 227, 228, 0.86);
}
[data-v2-velaire] .box-white .v2-coverage-flags .v2-coverage-name { color: var(--vh-ink, #201a1d); }
@keyframes v2-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-9px); }
}
@media (prefers-reduced-motion: reduce) {
  [data-v2-velaire] .v2-coverage-flags li { animation: none; }
}

/* ---- the platform band, on cream -------------------------------------------
   Qasim, 11-Sep: light. The band was wine-on-wine, which put a row of vendor marks
   on the darkest ground the page has -- the worst case for logos, since most brand
   marks are drawn for light backgrounds and every one of them had to be recoloured
   to survive it. On cream they sit the way their own brand pages show them.

   Scoped to `.v2-platforms` rather than applied to the section, so the page's own
   box-white rhythm is untouched.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-platforms {
  margin-top: 8px;
  padding: 34px clamp(20px, 3vw, 38px) 30px;
  border-radius: 26px;
  background: linear-gradient(168deg, #fbf7f6 0%, #f4ecec 100%);
  border: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-platforms .v2-float {
  margin-top: 0;
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}
[data-v2-velaire] .v2-platforms .v2-float-item { color: var(--vh-ink, #201a1d); }
[data-v2-velaire] .v2-platforms .v2-float-item .icon,
[data-v2-velaire] .v2-platforms .v2-float .v2-brand-svg { color: var(--vh-gold-700, #7b592d); }
[data-v2-velaire] .v2-platforms .v2-integration {
  background: rgba(32, 26, 29, 0.035);
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-platforms .v2-integration-name { color: var(--vh-ink, #201a1d); }
[data-v2-velaire] .v2-platforms .v2-integration-cat { color: var(--vh-ink-muted, #6d5a62); }
[data-v2-velaire] .v2-platforms .v2-integration-mark {
  color: var(--vh-gold-700, #7b592d);
  background: rgba(123, 89, 45, 0.09);
  border-color: rgba(123, 89, 45, 0.22);
}
[data-v2-velaire] .v2-platforms .v2-integration.is-live .v2-integration-chip {
  color: #2e1119;
  border-color: transparent;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
  font-weight: 600;
}
/* "Not built yet" must stay legible on cream rather than fading to nothing -- the
   row is only worth publishing if its status can be read. */
[data-v2-velaire] .v2-platforms .v2-integration.is-planned { opacity: 1; }
[data-v2-velaire] .v2-platforms .v2-integration.is-planned .v2-integration-chip,
[data-v2-velaire] .v2-platforms .v2-integration.is-sandbox .v2-integration-chip {
  color: var(--vh-ink-muted, #6d5a62);
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  background: rgba(32, 26, 29, 0.04);
}

/* ---- the floating platform strip -------------------------------------------
   The template's own `infiniteSlide` marquee, so the drift matches the "Runs on"
   row rather than introducing a second idea of a moving line. Marks are set in
   currentColor, which is how a wordmark should sit on a coloured ground: one tone,
   no vendor's brand colour competing with the page's.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-float {
  margin: 0 0 28px;
  padding: 18px 0;
  border-top: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  overflow: hidden;
}
[data-v2-velaire] .v2-float-item {
  display: inline-flex;
  align-items: center;
  flex-wrap: nowrap;
  gap: 10px;
  margin-right: 44px;
  white-space: nowrap;
  font-size: 15px;
  font-weight: 600;
  color: rgba(239, 227, 228, 0.82);
}
[data-v2-velaire] .v2-float-item .icon { font-size: 17px; color: var(--vh-gold-400, #e0bf8a); }
[data-v2-velaire] .v2-brand-svg {
  height: 22px;
  width: auto;
  max-width: 108px;
  fill: currentColor;
  flex: 0 0 auto;
}
[data-v2-velaire] .v2-float .v2-brand-svg { color: var(--vh-gold-400, #e0bf8a); }
[data-v2-velaire] .box-white .v2-float-item { color: var(--vh-ink, #201a1d); }
[data-v2-velaire] .box-white .v2-float-item .icon,
[data-v2-velaire] .box-white .v2-float .v2-brand-svg { color: var(--vh-gold-700, #7b592d); }
[data-v2-velaire] .box-white .v2-float { border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08)); }
/* Two marks in one tile (Zapier and Make) sit side by side rather than stacking. */
[data-v2-velaire] .v2-integration-mark.has-logo { gap: 5px; padding: 0 7px; width: auto; min-width: 34px; }
[data-v2-velaire] .v2-integration-mark .v2-brand-svg { height: 15px; width: auto; max-width: 58px; }
[data-v2-velaire] .v2-integration-cat {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
[data-v2-velaire] .v2-integration-top .v2-integration-chip { flex: 0 0 auto; }

/* ---- integration marks, and the compact grid --------------------------------
   Monograms rather than the vendors' logos, for the reason recorded on
   IntegrationMark: none of these vendors has endorsed Velaire, and this site's own
   /vendor-checklist tells buyers to distrust exactly that grid. Real brand files
   drop into .v2-integration-mark and nothing else changes.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-integration-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 34px;
  height: 34px;
  margin-right: 10px;
  border-radius: 10px;
  font-size: 15px;
  color: var(--vh-gold-400, #e0bf8a);
  background: rgba(224, 191, 138, 0.12);
  border: 1px solid rgba(224, 191, 138, 0.26);
}
[data-v2-velaire] .v2-integration-top { display: flex; align-items: center; gap: 8px; }
/* The category label takes the remaining room so the status chip stays right-aligned. */
[data-v2-velaire] .v2-integration-top .v2-integration-cat { flex: 1 1 auto; min-width: 0; }

/* Compact: mark, name and status, for the homepage where the bodies do not fit. */
[data-v2-velaire] .v2-integrations.is-compact {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
}
[data-v2-velaire] .v2-integrations.is-compact .v2-integration { padding: 16px 18px; }
[data-v2-velaire] .v2-integrations.is-compact .v2-integration-name {
  margin: 10px 0 0;
  font-size: 16px;
}
/* A "Not built yet" tile is dimmed but never hidden. The list is only worth
   publishing if the honest rows are in it. */
[data-v2-velaire] .v2-integrations.is-compact .v2-integration.is-planned { opacity: 0.62; }

/* ---- the app shell ---------------------------------------------------------
   A nav rail and a toolbar, so the panel reads as a cropped application rather
   than as a card with a title on it. Compared against Adminto's chat and table
   demos, which is what this closes: theirs look like software because they carry
   software's furniture.

   The rail is 40px and the panel's own padding moves onto .v2-app-main, so the
   rail runs the full height the way a real one does.
---------------------------------------------------------------------------- */
/* The screens are set in Roboto, not the page's Urbanist.
   Urbanist is a geometric display face and it is the right choice for the marketing
   page; it is the wrong choice for a picture of an admin tool, where every real
   reference (Adminto included) uses a UI face. Roboto is already loaded by the
   template for `--vh-font-mono`, so this is a different typeface at zero extra
   bytes -- which matters on a page whose measured problem is payload. */
[data-v2-velaire] .v2-console.v2-shot {
  padding: 0;
  overflow: hidden;
  font-family: Roboto, system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0;
}
[data-v2-velaire] .v2-console.v2-shot .v2-console-name,
[data-v2-velaire] .v2-console.v2-shot .v2-console-n,
[data-v2-velaire] .v2-console.v2-shot .v2-shot-kpi-n,
[data-v2-velaire] .v2-console.v2-shot .v2-shot-total { letter-spacing: -0.01em; }
[data-v2-velaire] .v2-app { display: flex; align-items: stretch; }
[data-v2-velaire] .v2-app-rail {
  flex: 0 0 44px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 16px 0;
  background: rgba(0, 0, 0, 0.2);
  border-right: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-app-logo {
  width: 18px;
  height: 18px;
  margin-bottom: 12px;
  border-radius: 6px;
  background: linear-gradient(160deg, var(--vh-gold-400, #e0bf8a), var(--vh-gold-500, #c9a06a));
}
[data-v2-velaire] .v2-app-ico {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  color: rgba(239, 227, 228, 0.56);
  font-size: 15px;
}
[data-v2-velaire] .v2-app-ico.is-on {
  color: var(--vh-gold-400, #e0bf8a);
  background: rgba(224, 191, 138, 0.14);
}
[data-v2-velaire] .v2-app-main { flex: 1 1 auto; min-width: 0; padding: 18px 18px 20px; }

/* Initials disc. */
[data-v2-velaire] .v2-av {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--vh-gold-400, #e0bf8a);
  background: rgba(224, 191, 138, 0.14);
  border: 1px solid rgba(224, 191, 138, 0.26);
  text-transform: uppercase;
}

/* Column header, and the row action every admin table carries. */
[data-v2-velaire] .v2-console-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 0 8px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.5);
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-console-head .v2-console-t { font-size: 10.5px; font-weight: 600; }
[data-v2-velaire] .v2-console-hd-s { flex: 0 0 auto; }
[data-v2-velaire] .v2-console-act {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  font-size: 12px;
  color: rgba(239, 227, 228, 0.34);
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-console-row:hover .v2-console-act {
  color: var(--vh-gold-400, #e0bf8a);
  border-color: rgba(224, 191, 138, 0.34);
}

/* The messaging view: a header with presence, and a composer under the thread. */
[data-v2-velaire] .v2-shot-msghead {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-bottom: 12px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-msghead-n { font-size: 13.5px; font-weight: 600; }
[data-v2-velaire] .v2-shot-live {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-left: auto;
  background: #6fbf8b;
}
[data-v2-velaire] .v2-shot-msghead-s { font-size: 11.5px; color: rgba(239, 227, 228, 0.6); }
[data-v2-velaire] .v2-shot-composer {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-composer-in {
  flex: 1 1 auto;
  padding: 9px 13px;
  border-radius: 10px;
  font-size: 12.5px;
  color: rgba(239, 227, 228, 0.4);
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-composer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  font-size: 13px;
  color: #2e1119;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a), var(--vh-gold-400, #e0bf8a));
}

/* The light ground restates every surface the shell introduced. */
[data-v2-velaire] .v2-console.is-light .v2-app-rail {
  background: rgba(32, 26, 29, 0.04);
  border-right-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}
[data-v2-velaire] .v2-console.is-light .v2-app-ico { color: rgba(32, 26, 29, 0.46); }
[data-v2-velaire] .v2-console.is-light .v2-app-ico.is-on {
  color: var(--vh-gold-700, #7b592d);
  background: rgba(123, 89, 45, 0.1);
}
[data-v2-velaire] .v2-console.is-light .v2-av {
  color: var(--vh-gold-700, #7b592d);
  background: rgba(123, 89, 45, 0.09);
  border-color: rgba(123, 89, 45, 0.22);
}
[data-v2-velaire] .v2-console.is-light .v2-console-head,
[data-v2-velaire] .v2-console.is-light .v2-shot-composer-in { color: var(--vh-ink-muted, #6d5a62); }
[data-v2-velaire] .v2-console.is-light .v2-console-head,
[data-v2-velaire] .v2-console.is-light .v2-shot-msghead,
[data-v2-velaire] .v2-console.is-light .v2-shot-composer,
[data-v2-velaire] .v2-console.is-light .v2-console-act,
[data-v2-velaire] .v2-console.is-light .v2-shot-composer-in { border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08)); }
[data-v2-velaire] .v2-console.is-light .v2-console-act { color: rgba(32, 26, 29, 0.3); }
[data-v2-velaire] .v2-console.is-light .v2-shot-composer-in { background: rgba(32, 26, 29, 0.035); }
[data-v2-velaire] .v2-console.is-light .v2-shot-msghead-s { color: var(--vh-ink-muted, #6d5a62); }

/* ---- the light screen -------------------------------------------------------
   The same frame on the homepage's cream instead of wine. Every child that assumed a
   dark ground is re-stated here rather than made conditional at each use, so a new
   screen kind inherits the light treatment without knowing about it.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-console.is-light {
  background: linear-gradient(165deg, #fbf7f6 0%, #f4ecec 100%);
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  box-shadow: 0 18px 44px rgba(24, 8, 13, 0.09);
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-console.is-light .v2-console-bar,
[data-v2-velaire] .v2-console.is-light .v2-console-row,
[data-v2-velaire] .v2-console.is-light .v2-shot-qual-row,
[data-v2-velaire] .v2-console.is-light .v2-shot-msg-to { border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08)); }
[data-v2-velaire] .v2-console.is-light .v2-console-mark,
[data-v2-velaire] .v2-console.is-light .v2-shot-kpi-n,
[data-v2-velaire] .v2-console.is-light .v2-console-n,
[data-v2-velaire] .v2-console.is-light .v2-shot-total,
[data-v2-velaire] .v2-console.is-light .v2-shot-msg-subject { color: var(--vh-gold-700, #7b592d); }
[data-v2-velaire] .v2-console.is-light .v2-console-mark { background: var(--vh-gold-700, #7b592d); }
[data-v2-velaire] .v2-console.is-light .v2-console-pill {
  color: var(--vh-gold-700, #7b592d);
  border-color: rgba(123, 89, 45, 0.32);
}
[data-v2-velaire] .v2-console.is-light .v2-console-pill.is-roadmap {
  color: var(--vh-ink-muted, #6d5a62);
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}
[data-v2-velaire] .v2-console.is-light .v2-console-when,
[data-v2-velaire] .v2-console.is-light .v2-console-l,
[data-v2-velaire] .v2-console.is-light .v2-console-reason,
[data-v2-velaire] .v2-console.is-light .v2-shot-note,
[data-v2-velaire] .v2-console.is-light .v2-shot-kpi-l,
[data-v2-velaire] .v2-console.is-light .v2-shot-kpi-s,
[data-v2-velaire] .v2-console.is-light .v2-shot-msg-k,
[data-v2-velaire] .v2-console.is-light .v2-console-t { color: var(--vh-ink-muted, #6d5a62); }
[data-v2-velaire] .v2-console.is-light .v2-console-stat,
[data-v2-velaire] .v2-console.is-light .v2-shot-kpi,
[data-v2-velaire] .v2-console.is-light .v2-shot-tag,
[data-v2-velaire] .v2-console.is-light .v2-shot-msg-card {
  background: rgba(32, 26, 29, 0.035);
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-console.is-light .v2-console-reason code {
  background: rgba(32, 26, 29, 0.06);
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-console.is-light .v2-shot-qual-bar,
[data-v2-velaire] .v2-console.is-light .v2-shot-chan-bar { background: rgba(32, 26, 29, 0.08); }
[data-v2-velaire] .v2-console.is-light .v2-console-chip.is-wait {
  color: var(--vh-ink-muted, #6d5a62);
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  background: rgba(32, 26, 29, 0.04);
}

/* ---- the KPI row, and capture quality -------------------------------------
   Both are the Dominion Aesthetics sheet's own blocks. The tiles wrap 3-up, and the
   quality bars are a percentage of a known denominator rather than a raw count --
   "44 of 62" is the honest reading and 71% is the one that is comparable month to
   month, so the bar shows the share and the note carries the caveat.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-shot-kpis {
  display: grid;
  /* Two-up, not three. The nav rail takes 44px off the column and at three across
     every label wrapped -- "Calls answered" over two lines is a worse tile than a
     wider one. Six tiles read as three rows of two just as well. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
}
[data-v2-velaire] .v2-shot-kpi {
  padding: 12px 11px;
  border-radius: 13px;
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-kpi-n {
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-shot-kpi-l { margin-top: 4px;
  font-weight: 500; font-size: 12.5px; line-height: 1.3; color: rgba(239, 227, 228, 0.82); }
[data-v2-velaire] .v2-shot-kpi-s { margin-top: 2px; font-size: 11px; line-height: 1.3; color: rgba(239, 227, 228, 0.64); }

[data-v2-velaire] .v2-shot-qual { margin: 0; padding: 0; list-style: none; }
[data-v2-velaire] .v2-shot-qual-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 0;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-qual-l { flex: 0 0 164px;
  font-weight: 500; font-size: 13.5px; }
[data-v2-velaire] .v2-shot-qual-bar {
  flex: 1 1 auto;
  height: 6px;
  border-radius: 999px;
  background: rgba(239, 227, 228, 0.08);
  overflow: hidden;
}
[data-v2-velaire] .v2-shot-qual-bar i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--vh-gold-500, #c9a06a);
}
[data-v2-velaire] .v2-shot-qual-bar i.is-full {
  background: linear-gradient(90deg, var(--vh-gold-500, #c9a06a), var(--vh-gold-400, #e0bf8a));
}
[data-v2-velaire] .v2-shot-qual-n {
  flex: 0 0 34px;
  text-align: right;
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* A thread shown on a handset, for the conversation screens. */
[data-v2-velaire] .v2-shot-phone {
  max-width: 250px;
  margin: 0 auto;
  padding: 14px 12px;
  border-radius: 26px;
  background: rgba(0, 0, 0, 0.22);
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
}
[data-v2-velaire] .v2-shot-phone::before {
  content: "";
  display: block;
  width: 42px;
  height: 4px;
  margin: 0 auto 12px;
  border-radius: 999px;
  background: rgba(239, 227, 228, 0.22);
}

/* ---- the front-desk frame --------------------------------------------------
   Sits under the console in the services column, which is the space that kept
   coming back as empty.

   Cropped to 4:3 rather than shown at its native 3:4. The source is portrait and
   at full height it would push the sticky block past the viewport, at which point
   the console stops behaving like a sticky panel and starts scrolling away -- the
   exact bug that took two attempts to find. The crop keeps the desk and both
   figures, which is the half of the frame that carries the story.

   `object-position` is centred: the frame puts the phone at the lower left and both
   women in the middle, so the band catches all three. The first generation of this
   image needed a left bias; it was replaced because its "phone" came out as an
   ambiguous black slab with a glowing orange strip and a cord attached to nothing --
   the weakest object in the frame and the one carrying the story. The prompt asked for
   "a soft light glowing at its base", and that is what produced it.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-desk {
  position: relative;
  margin: 16px 0 0;
  border-radius: 20px;
  overflow: hidden;
  line-height: 0;
}
[data-v2-velaire] .v2-desk img {
  width: 100%;
  height: clamp(150px, 18vh, 300px);
  object-fit: cover;
  object-position: 50% 42%;
  display: block;
}
/* The real brand asset over the photograph, never generated into it. */
[data-v2-velaire] .v2-desk-mark {
  position: absolute;
  left: 14px;
  bottom: 14px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 13px 7px 9px;
  border-radius: 999px;
  background: rgba(46, 17, 25, 0.82);
  backdrop-filter: blur(6px);
  border: 1px solid rgba(239, 227, 228, 0.18);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-desk-mark img { width: 22px; height: 22px; aspect-ratio: auto; }
/* Below ~880px of window there is no room for the frame AND a sticky console: the
   console and its caption alone are about 680px, and the 116px offset takes the rest.
   Shrinking the photo further would leave a 140px band doing nothing, so it comes out
   entirely and the console keeps the behaviour. */
@media (max-height: 879px) {
  [data-v2-velaire] .v2-desk { display: none; }
}

/* ---- per-service screens --------------------------------------------------
   Four kinds share the .v2-console frame: a thread, a slot grid, a step list and
   a stat block. Four rather than thirteen bespoke screens, so the column reads as
   one product rather than as thirteen unrelated graphics -- and so a fourteenth
   service needs data, not CSS.

   Height is held steady across kinds. Without a floor the column jumps every time
   the reader opens a different row, which on a sticky panel beside a twelve-item
   accordion is the most visible motion on the page.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-shot-body {
  min-height: 420px;
  padding-top: 14px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
[data-v2-velaire] .v2-shot { animation: v2-shot-in 0.42s cubic-bezier(0.2, 0.7, 0.3, 1); }
@keyframes v2-shot-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
/* The badge takes the status's own colour: a roadmap screen must not look shipped. */
[data-v2-velaire] .v2-console-pill.is-roadmap {
  color: rgba(239, 227, 228, 0.62);
  border-color: var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
}
[data-v2-velaire] .v2-console-pill.is-rolling-out {
  color: var(--vh-cream, #efe3e4);
  border-color: rgba(239, 227, 228, 0.4);
}

[data-v2-velaire] .v2-shot-thread { display: grid; gap: 12px; }
[data-v2-velaire] .v2-shot-turn { max-width: 88%; }
[data-v2-velaire] .v2-shot-turn.is-self { margin-left: auto; }
[data-v2-velaire] .v2-shot-who {
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.7);
  margin-bottom: 5px;
}
[data-v2-velaire] .v2-shot-turn.is-self .v2-shot-who {
  text-align: right;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-shot-bubble {
  margin: 0;
  padding: 10px 14px;
  border-radius: 16px 16px 16px 5px;
  font-size: 14.5px;
  line-height: 1.5;
  background: rgba(239, 227, 228, 0.09);
}
[data-v2-velaire] .v2-shot-turn.is-self .v2-shot-bubble {
  border-radius: 16px 16px 5px 16px;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
  color: #2e1119;
}

[data-v2-velaire] .v2-shot-slots {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
[data-v2-velaire] .v2-shot-slot {
  padding: 8px 14px;
  border-radius: 11px;
  font-size: 14px;
  font-weight: 600;
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
}
/* A taken slot has to look unavailable rather than merely quiet, or the grid reads
   as decoration. Struck through, and never offered. */
[data-v2-velaire] .v2-shot-slot.is-taken {
  color: rgba(239, 227, 228, 0.3);
  text-decoration: line-through;
  border-style: dashed;
}
[data-v2-velaire] .v2-shot-slot.is-picked {
  color: #2e1119;
  border-color: transparent;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
}

[data-v2-velaire] .v2-shot-steps { margin: 0; padding: 0; list-style: none; }
[data-v2-velaire] .v2-shot-step {
  position: relative;
  display: flex;
  gap: 14px;
  align-items: baseline;
  padding: 11px 0 11px 22px;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-step::before {
  content: "";
  position: absolute;
  left: 0;
  top: 17px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid rgba(239, 227, 228, 0.3);
}
[data-v2-velaire] .v2-shot-step.is-done::before {
  background: var(--vh-gold-400, #e0bf8a);
  border-color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-shot-step-t {
  flex: 0 0 82px;
  font-size: 13px;
  font-weight: 600;
  color: var(--vh-gold-400, #e0bf8a);
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-shot-step-l { font-size: 14.5px; line-height: 1.45; }

[data-v2-velaire] .v2-shot-rows { margin: 14px 0 0; }
[data-v2-velaire] .v2-shot-row {
  display: flex;
  gap: 14px;
  align-items: baseline;
  padding: 9px 0;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-row dt {
  flex: 0 0 122px;
  margin: 0;
  font-size: 12px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.72);
}
[data-v2-velaire] .v2-shot-row dd { margin: 0; font-size: 14.5px; font-weight: 500; }

/* Lead tags: where it came in, and how the caller sounded. Both are real fields on
   a Velaire lead, and both are what makes the list read as a CRM rather than a log.
   "Manual" is a real value and is shown -- a row a human took is not a failure. */
[data-v2-velaire] .v2-shot-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-top: 6px;
}
[data-v2-velaire] .v2-shot-tag {
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.84);
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
  white-space: nowrap;
}
[data-v2-velaire] .v2-shot-tag.is-mood.is-positive {
  color: var(--vh-gold-400, #e0bf8a);
  border-color: rgba(224, 191, 138, 0.38);
}
/* Unhappy takes the rose that is reserved for "to a person" -- an unhappy caller is
   the row a practice should look at, which is the same signal. */
[data-v2-velaire] .v2-shot-tag.is-mood.is-unhappy {
  color: var(--vh-rose, #e88faf);
  border-color: rgba(232, 143, 175, 0.4);
  background: rgba(232, 143, 175, 0.09);
}

/* The stored enum, shown as stored. Monospaced so it reads as a field value rather
   than as prose a copywriter chose. */
[data-v2-velaire] .v2-console-reason code {
  display: inline-block;
  margin-right: 6px;
  padding: 1px 6px;
  border-radius: 5px;
  font-family: var(--vh-font-mono, Roboto, monospace);
  font-size: 11.5px;
  color: rgba(239, 227, 228, 0.86);
  background: rgba(239, 227, 228, 0.07);
}
[data-v2-velaire] .v2-shot-tag.is-mood.is-hot {
  color: var(--vh-gold-400, #e0bf8a);
  border-color: rgba(224, 191, 138, 0.38);
}
[data-v2-velaire] .v2-shot-tag.is-mood.is-warm { color: rgba(239, 227, 228, 0.8); }
/* On cream every one of these was cream-on-cream and rendered as a blank pill. Each
   tint needs its light-ground twin or the tag is worse than absent -- an empty capsule
   reads as a rendering fault, not as a value. */
[data-v2-velaire] .v2-console.is-light .v2-shot-tag.is-mood.is-hot {
  color: var(--vh-gold-700, #7b592d);
  border-color: rgba(123, 89, 45, 0.34);
}
[data-v2-velaire] .v2-console.is-light .v2-shot-tag.is-mood.is-warm { color: var(--vh-ink, #201a1d); }
[data-v2-velaire] .v2-console.is-light .v2-shot-tag.is-mood.is-cold { color: var(--vh-ink-muted, #6d5a62); }
[data-v2-velaire] .v2-console.is-light .v2-shot-tag.is-sent { color: var(--vh-ink-muted, #6d5a62); }
[data-v2-velaire] .v2-console.is-light .v2-shot-tag.is-sent.is-negative {
  color: #a33a63;
  border-color: rgba(163, 58, 99, 0.34);
  background: rgba(163, 58, 99, 0.07);
}
[data-v2-velaire] .v2-console.is-light .v2-console-chip.is-clinical {
  color: #a33a63;
  border-color: rgba(163, 58, 99, 0.34);
  background: rgba(163, 58, 99, 0.07);
}
/* Sentiment is a separate axis from lead quality -- how the caller sounded, not how
   likely the booking is -- so it is tinted rather than gold, and never competes with
   the hot/warm/cold tag beside it. Negative takes the rose reserved for "to a person". */
[data-v2-velaire] .v2-shot-tag.is-sent { border-style: dashed; }
[data-v2-velaire] .v2-shot-tag.is-sent.is-negative {
  color: var(--vh-rose, #e88faf);
  border-color: rgba(232, 143, 175, 0.4);
}
[data-v2-velaire] .v2-shot-tag.is-sent.is-unknown { opacity: 0.6; }
[data-v2-velaire] .v2-shot-tag.is-mood.is-cold { color: rgba(239, 227, 228, 0.45); }

/* Admina's Top Revenue Channels, in Velaire's tokens: a total, then a ranked list of
   sources with a bar each. Its shape, not its markup -- Admina's stylesheet is not
   loaded here, and adding an admin CSS bundle to a page whose measured problem is
   payload would cost more than the layout is worth.

   It survives the empty state, which is the test that matters: six labels at zero
   still read as a list of the ways a booking can arrive. A donut at zero reads broken. */
[data-v2-velaire] .v2-shot-total {
  font-size: 38px;
  font-weight: 600;
  line-height: 1.05;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-shot-chan { margin: 14px 0 0; padding: 0; list-style: none; }
[data-v2-velaire] .v2-shot-chan-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
}
[data-v2-velaire] .v2-shot-chan-l { flex: 0 0 142px;
  font-weight: 500; font-size: 13.5px; }
[data-v2-velaire] .v2-shot-chan-bar {
  flex: 1 1 auto;
  height: 6px;
  border-radius: 999px;
  background: rgba(239, 227, 228, 0.08);
  overflow: hidden;
}
[data-v2-velaire] .v2-shot-chan-bar i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--vh-gold-500, #c9a06a), var(--vh-gold-400, #e0bf8a));
}
[data-v2-velaire] .v2-shot-chan-n {
  flex: 0 0 30px;
  text-align: right;
  font-size: 13.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
/* A channel the schema cannot record yet is dimmed and shows an em dash, never a 0 --
   zero is a measurement, and this is an absence of one. */
[data-v2-velaire] .v2-shot-chan-row.is-pending { opacity: 0.42; }
[data-v2-velaire] .v2-shot-chan-row.is-pending .v2-shot-chan-bar { background: none; border: 1px dashed var(--vh-glass-bd, rgba(239, 227, 228, 0.16)); height: 8px; }

/* A sent message, either the patient's text or the front desk's secure email. */
[data-v2-velaire] .v2-shot-msg-card {
  margin-top: 12px;
  padding: 16px 18px;
  border-radius: 16px;
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
}
[data-v2-velaire] .v2-shot-msg.is-sms .v2-shot-msg-card {
  border-radius: 18px 18px 18px 6px;
}
[data-v2-velaire] .v2-shot-msg-to {
  display: flex;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  padding-bottom: 9px;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-shot-msg-k {
  color: rgba(239, 227, 228, 0.7);
  font-weight: 500;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.08em;
  align-self: center;
}
[data-v2-velaire] .v2-shot-msg-subject {
  margin-top: 10px;
  font-size: 14px;
  font-weight: 600;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-shot-msg-body {
  margin: 9px 0 0;
  font-size: 14.5px;
  line-height: 1.55;
}
[data-v2-velaire] .v2-shot-msg-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 12px;
}

[data-v2-velaire] .v2-shot-note {
  margin: 14px 0 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: rgba(239, 227, 228, 0.76);
}

@media (prefers-reduced-motion: reduce) {
  [data-v2-velaire] .v2-shot { animation: none; }
}

/* ---- the capture panel, beside the handset ---------------------------------
   Dark, like the handset it sits with. The first version of this was a pale card
   on the cream ground and all but disappeared -- which is most of why Qasim read
   the section as "so simple". Two dark objects on cream is the pairing the page
   already uses everywhere else, and it makes the section read as one exhibit
   rather than as a phone with a note next to it.
---------------------------------------------------------------------------- */
[data-v2-velaire] .v2-capture {
  padding: clamp(22px, 2.6vw, 30px);
  border-radius: 22px;
  background: linear-gradient(165deg, #4a2130 0%, #33131e 62%, #401a27 100%);
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.06) inset,
    0 22px 50px rgba(24, 8, 13, 0.3);
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-capture-head {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-bottom: 15px;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-capture-dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--vh-gold-400, #e0bf8a);
}
/* A ring that breathes out of the dot. The panel describes a line being answered,
   so the one moving thing on it is the indicator that the line is open. Transform
   and opacity only, on a pseudo-element -- it cannot shift layout, which keeps the
   measured CLS of 0.000 on this page intact. */
[data-v2-velaire] .v2-capture-dot::after {
  content: "";
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1px solid var(--vh-gold-400, #e0bf8a);
  opacity: 0;
  animation: v2-ping 2.6s cubic-bezier(0.2, 0.7, 0.3, 1) infinite;
}
@keyframes v2-ping {
  0%   { transform: scale(0.6); opacity: 0.75; }
  70%  { transform: scale(1.5); opacity: 0; }
  100% { transform: scale(1.5); opacity: 0; }
}
[data-v2-velaire] .v2-capture-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-capture-rows { margin: 0; }
[data-v2-velaire] .v2-capture-row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 13px 0;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-capture-k {
  display: flex;
  align-items: center;
  gap: 9px;
  flex: 0 0 132px;
  margin: 0;
  font-size: 12.5px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.74);
}
[data-v2-velaire] .v2-capture-ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 1px solid rgba(224, 191, 138, 0.28);
  background: rgba(224, 191, 138, 0.09);
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.3, 1), background 0.35s;
}
[data-v2-velaire] .v2-capture-ico .icon {
  font-size: 12px;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-capture:hover .v2-capture-ico {
  transform: translateY(-2px);
  background: rgba(224, 191, 138, 0.16);
}
[data-v2-velaire] .v2-capture-v {
  flex: 1 1 auto;
  margin: 0;
  min-width: 0;
  font-size: clamp(15px, 1.4vw, 17px);
  font-weight: 500;
  line-height: 1.5;
}
/* Values that are a state rather than a fact are chipped, not weighted. `ok` is the
   settled one -- the appointment exists -- so it takes the gold fill the phone's own
   Velaire turns use; `wait` stays outlined. */
[data-v2-velaire] .v2-capture-v.is-ok {
  display: inline-block;
  flex: 0 0 auto;
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  color: #2e1119;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
}
[data-v2-velaire] .v2-capture-v.is-wait {
  display: inline-block;
  flex: 0 0 auto;
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(239, 227, 228, 0.88);
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
}
/* Three timestamps, evenly divided. The gold rule under each is the template's own
   underline idiom, so it reads as part of the page rather than as a new widget. */
[data-v2-velaire] .v2-capture-time {
  display: flex;
  gap: 10px;
  margin: 18px 0 0;
  padding: 0;
  list-style: none;
}
[data-v2-velaire] .v2-capture-time-item {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  padding-top: 11px;
  border-top: 2px solid rgba(239, 227, 228, 0.12);
}
/* A gold rule that draws itself across each step in turn -- the call moving along
   the timeline. scaleX on a pseudo-element, so no layout involvement.

   It runs ONCE and stays. An earlier version looped, and a rule that fills and then
   empties on a 5s cycle reads as a progress bar that never completes: land on the
   section mid-cycle and the panel looks like it is still loading. Played once, it is
   a reveal; looped, it was a spinner. */
[data-v2-velaire] .v2-capture-time-item::before {
  content: "";
  position: absolute;
  top: -2px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--vh-gold-400, #e0bf8a);
  transform: scaleX(0);
  transform-origin: left center;
  animation: v2-draw 0.9s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
[data-v2-velaire] .v2-capture-time-item:nth-child(2)::before { animation-delay: 0.32s; }
[data-v2-velaire] .v2-capture-time-item:nth-child(3)::before { animation-delay: 0.64s; }
@keyframes v2-draw {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Motion here is ornament, never information: every one of these states is legible
   with the animation removed, so honouring the preference costs the reader nothing. */
@media (prefers-reduced-motion: reduce) {
  [data-v2-velaire] .v2-capture-dot::after { animation: none; }
  [data-v2-velaire] .v2-capture-time-item::before { animation: none; transform: scaleX(1); }
  [data-v2-velaire] .v2-feature-card,
  [data-v2-velaire] .v2-feature-icon,
  [data-v2-velaire] .v2-capture-ico { transition: none; }
  [data-v2-velaire] .v2-feature-card:hover { transform: none; }
}
[data-v2-velaire] .v2-capture-time-t {
  display: block;
  font-size: 15px;
  font-weight: 600;
  color: var(--vh-gold-400, #e0bf8a);
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-capture-time-l {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.4;
  color: rgba(239, 227, 228, 0.76);
}
@media (max-width: 575px) {
  [data-v2-velaire] .v2-capture-time { flex-direction: column; gap: 0; }
  [data-v2-velaire] .v2-capture-time-item {
    display: flex;
    gap: 10px;
    align-items: baseline;
    padding: 9px 0;
    border-top: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  }
  [data-v2-velaire] .v2-capture-time-t { flex: 0 0 74px; }
}

[data-v2-velaire] .v2-capture-foot {
  margin: 15px 0 0;
  font-size: 14px;
  line-height: 1.6;
  color: rgba(239, 227, 228, 0.78);
}

/* ---- feature cards --------------------------------------------------------
   The template's own .process-card, with the icon and a meta line where its
   01/03 counter sits. Only the additions are styled; everything that makes it
   look like the rest of the page is inherited.
---------------------------------------------------------------------------- */
/* The card lifts and its icon fills. The template's own cards animate on hover, so
   a flat card beside them is what read as unfinished. */
[data-v2-velaire] .v2-feature-card {
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 0.4s;
}
[data-v2-velaire] .v2-feature-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 18px 40px rgba(24, 8, 13, 0.13);
}
[data-v2-velaire] .v2-feature-card:hover .v2-feature-icon {
  transform: scale(1.08) rotate(-6deg);
  border-color: rgba(224, 191, 138, 0.55);
  background: rgba(224, 191, 138, 0.16);
}
[data-v2-velaire] .v2-feature-icon {
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.3, 1), background 0.4s, border-color 0.4s;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  margin-bottom: 18px;
  border-radius: 50%;
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
}
[data-v2-velaire] .v2-feature-icon .icon {
  font-size: 21px;
  color: var(--vh-gold-400, #e0bf8a);
}
/* The card's meta line. 11.5px at 0.5 opacity, sitting under a 40px card title,
   was the smallest and faintest tier on the page -- "EXACTLY AS IT WORKS TODAY"
   read as a caption on a caption. Uppercase costs legibility on top of that,
   because it removes the ascenders and descenders a reader uses to shape a word,
   so this tier needs more size and more weight than a lowercase one at the same
   role would. */
[data-v2-velaire] .v2-feature-meta {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.72);
}
[data-v2-velaire] .box-white .v2-feature-icon {
  border-color: var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  background: rgba(32, 26, 29, 0.04);
}
[data-v2-velaire] .box-white .v2-feature-icon .icon { color: var(--vh-gold-700, #7b592d); }
[data-v2-velaire] .box-white .v2-feature-meta { color: var(--vh-ink-muted, #6d5a62); }

/* ---- Front Office console -------------------------------------------------
   An illustration of the product, in the homepage services column that was
   otherwise ~900px of empty cream beside a twelve-item accordion.

   Dark on the cream ground, the same move `.v2-phone` makes on the same page:
   a screen reads as a screen. It is sticky because the accordion beside it is
   far taller than the column, so without it the graphic scrolls away in the
   first second and the dead space comes straight back.
---------------------------------------------------------------------------- */
/* THE PANEL NEVER ACTUALLY STUCK, and two separate things stopped it.
   Measured 11-Sep after Qasim reported the empty column for a third time:

   1. The template wraps reveal-animated blocks in `.overflow-hidden`, and an
      `overflow: hidden` ancestor disables `position: sticky` entirely. So the
      declaration below was inert from the day it was written.
   2. `.col-left` is 1117px tall while the accordion beside it is 1908px. A sticky
      element can only travel inside its containing block, so even once it stuck it
      would have run out 791px early and left exactly the void being reported.

   Both are fixed here rather than in the generator, because the wrapper is generated
   markup and the column height belongs to the layout, not to the component. */
[data-v2-velaire] .section-services .col-left {
  height: 100%;
  display: flex;
  flex-direction: column;
}
/* ---- How far the console stays pinned ----
   A sticky element is only sticky inside its own container, and this one's container
   was sized to its content: `.col-left` ended 260px above the bottom of the
   thirteen-row accordion beside it, so the console unstuck early and left a tall
   empty half-column under it -- which is what Qasim flagged twice.

   `flex: 1 1 auto` was already on the wrapper and did nothing, because `.col-left` is
   a plain block and flex properties only apply to flex children. Making `.col-left`
   itself a full-height flex column is what lets the wrapper grow, and a grown wrapper
   is a longer sticky range: the console now travels beside the accordion to the end
   of it rather than stopping a third of the way down.

   `min-height: 0` is the usual flex-column escape hatch -- without it a flex item
   refuses to shrink below its content and the wrapper would overflow the column. */
/* `display: contents` is doing real work here, so it is worth saying why.

   A sticky element is only sticky within its own container, and this one's container --
   `.col-left` -- ended 260px above the bottom of the thirteen-row accordion beside it.
   So the console unstuck a third of the way down and left a tall empty half-column
   under it, which is what Qasim flagged twice.

   Neither `height: 100%` nor `flex-grow: 1` on `.col-left` moved it: measured at 1648px
   inside a 1908px parent even with `!important`. Making the bootstrap column a flex
   container did not help either. Rather than keep guessing, `display: contents` removes
   `.col-left`'s box entirely, so its children become flex items of the `d-flex
   flex-column h-100` wrapper -- which IS 1908px and definite -- and the console's own
   wrapper can then grow to fill it. Measured after: the sticky range ends at 4751px,
   exactly where the accordion ends. Gap 0.

   Safe because `.col-left` carries no box of its own in this section: the template
   styles `.col-left` only under `.section-about-us` and `.section-contact`.

   Scoped to the breakpoint where the console is actually sticky. Below 1200px it is
   `position: static` and the column is a normal stack, where flattening the wrapper
   would change the order of nothing but risks the template's own spacing rules. */
@media (min-width: 1200px) {
  [data-v2-velaire] .section-services .col-left {
    display: contents;
  }
  [data-v2-velaire] .section-services .col-left > .overflow-hidden:last-child {
    overflow: visible !important;
    flex: 1 1 auto;
    min-height: 0;
  }
}
[data-v2-velaire] .v2-console-wrap {
  position: sticky;
  /* Clears the fixed header, which is ~92px at this breakpoint. */
  top: 116px;
  margin-top: 40px;
}

/* ---- Where the pinned console sits in its own column ----
   The services column is one sticky console beside a thirteen-row accordion, so the
   console is pinned for most of the section and the space left over sits entirely
   BELOW it -- a tall empty half-column, which is what Qasim flagged on 12-Sep.

   Centring it splits that space above and below instead, so the column reads as
   composed rather than as a hole. `top` is the only lever sticky gives us, and the
   console's height changes with the service being shown, so this centres by viewport
   rather than by a height we cannot know: half the viewport, less half the element,
   with the header clearance as a floor so it can never slide under the fixed header.

   Kept to the breakpoint where the console is actually sticky -- below 1200px it is
   `position: static` (see the rule near the end of this file) and centring is
   meaningless. */
@media (min-width: 1200px) {
  [data-v2-velaire] .v2-console-wrap {
    top: max(116px, calc(50vh - 22rem));
  }
}

/* ---- The hero tiles, floating ----
   Qasim, 12-Sep: make them float. The template ships five float keyframe sets
   (`float1`..`float5`, styles.css:1642) that NOTHING uses, so this costs no new bytes
   on a page whose measured problem is payload.

   Why these are re-declared here rather than just `animation: float1`. The template's
   own keyframes animate the `transform` shorthand, and `gsapAnimation.js:476`
   (`animationGrow`) already scroll-scrubs `transform` on these same `.img-transform-3`
   tiles. A CSS animation outranks an inline style for the property it animates, so
   using float1 directly would silently kill that scrub and the two would fight.
   Animating the `translate` and `rotate` LONGHANDS instead composes with GSAP's matrix
   rather than replacing it, so the tiles drift AND keep their parallax.

   Three different durations, deliberately not multiples of one another, so the tiles
   never settle into a synchronised bob. Amplitudes stay small because
   `.title-icon-wrap` is `overflow: hidden` (styles.css:2954) and a larger movement
   clipped against its own frame. (The frame no longer clips: velaire-editorial.css,
   28-Sep, after the parallax cut the top tile.) */
@media (prefers-reduced-motion: no-preference) {
  [data-v2-velaire] .section-hero .content-wrap .title-icon .img-1,
  [data-v2-velaire] .section-hero .content-wrap .title-icon .img-2,
  [data-v2-velaire] .section-hero .content-wrap .title-icon .img-3 {
    animation-iteration-count: infinite;
    animation-timing-function: ease-in-out;
    will-change: translate, rotate;
  }
  [data-v2-velaire] .section-hero .content-wrap .title-icon .img-1 {
    animation-name: vhFloatA;
    animation-duration: 7s;
  }
  [data-v2-velaire] .section-hero .content-wrap .title-icon .img-2 {
    animation-name: vhFloatB;
    animation-duration: 9.5s;
  }
  [data-v2-velaire] .section-hero .content-wrap .title-icon .img-3 {
    animation-name: vhFloatC;
    animation-duration: 11s;
  }
}

@keyframes vhFloatA {
  0%, 100% { translate: 0 0; rotate: 0deg; }
  50%      { translate: 0 -5px; rotate: -3deg; }
}
@keyframes vhFloatB {
  0%, 100% { translate: 0 0; rotate: 0deg; }
  50%      { translate: 4px -2px; rotate: 4deg; }
}
@keyframes vhFloatC {
  0%, 100% { translate: 0 0; rotate: 0deg; }
  25%      { translate: -3px -4px; rotate: 3deg; }
  75%      { translate: 3px 2px; rotate: -3deg; }
}
[data-v2-velaire] .v2-console {
  padding: 18px 18px 20px;
  border-radius: 22px;
  background: linear-gradient(165deg, #4a2130 0%, #33131e 62%, #401a27 100%);
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.06) inset,
    0 22px 50px rgba(24, 8, 13, 0.34);
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-console-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-bottom: 13px;
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-console-mark {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: 0 0 auto;
  background: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-console-name {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: 0.01em;
}
[data-v2-velaire] .v2-console-pill {
  margin-left: auto;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
  border: 1px solid rgba(224, 191, 138, 0.4);
}
[data-v2-velaire] .v2-console-count {
  margin-left: auto;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: rgba(239, 227, 228, 0.42);
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-console.is-light .v2-console-count { color: var(--vh-ink-muted, #6d5a62); }
/* When a status pill is present the counter follows it rather than taking the
   auto-margin, or the two fight over the right edge. */
[data-v2-velaire] .v2-console-pill + .v2-console-count { margin-left: 10px; }

[data-v2-velaire] .v2-console-tabs {
  display: flex;
  gap: 16px;
  margin-top: 13px;
  font-size: 13px;
  color: rgba(239, 227, 228, 0.68);
}
[data-v2-velaire] .v2-console-tabs .is-on {
  color: var(--vh-cream, #efe3e4);
  font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--vh-gold-500, #c9a06a);
}
[data-v2-velaire] .v2-console-when {
  margin-top: 14px;
  font-size: 11.5px;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(239, 227, 228, 0.7);
}
[data-v2-velaire] .v2-console-stats {
  display: flex;
  gap: 8px;
  margin-top: 10px;
}
[data-v2-velaire] .v2-console-stat {
  flex: 1 1 0;
  min-width: 0;
  padding: 11px 10px;
  border-radius: 13px;
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-console-n {
  font-size: 27px;
  font-weight: 600;
  line-height: 1.1;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-console-l {
  margin-top: 3px;
  font-size: 12.5px;
  line-height: 1.35;
  color: rgba(239, 227, 228, 0.8);
}
[data-v2-velaire] .v2-console-list { margin-top: 14px; }
[data-v2-velaire] .v2-console-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 0;
  border-top: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-console-t {
  flex: 0 0 58px;
  font-weight: 500;
  font-size: 12.5px;
  color: rgba(239, 227, 228, 0.72);
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-console-mid { flex: 1 1 auto; min-width: 0; }
[data-v2-velaire] .v2-console-time-in {
  margin-right: 7px;
  font-size: 11.5px;
  font-weight: 600;
  color: rgba(239, 227, 228, 0.58);
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-console.is-light .v2-console-time-in { color: var(--vh-ink-muted, #6d5a62); }
[data-v2-velaire] .v2-shot-tags { flex-wrap: nowrap; overflow: hidden; }
[data-v2-velaire] .v2-console-who {
  font-size: 14.5px;
  font-weight: 600;
}
[data-v2-velaire] .v2-console-reason {
  font-size: 13px;
  line-height: 1.4;
  color: rgba(239, 227, 228, 0.8);
}
[data-v2-velaire] .v2-console-chip {
  flex: 0 0 auto;
  padding: 4px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  text-align: center;
}
[data-v2-velaire] .v2-console-chip.is-ok {
  color: #2e1119;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
}
[data-v2-velaire] .v2-console-chip.is-wait {
  color: rgba(239, 227, 228, 0.86);
  border: 1px solid var(--vh-glass-bd, rgba(239, 227, 228, 0.16));
  background: var(--vh-glass, rgba(239, 227, 228, 0.06));
}
/* --vh-rose is reserved for "urgent / to a person, never decorative". A clinical
   question routed straight to the team is exactly that, and the one row on this
   panel that must not read as a routine capture. */
[data-v2-velaire] .v2-console-chip.is-clinical {
  color: var(--vh-rose, #e88faf);
  border: 1px solid rgba(232, 143, 175, 0.42);
  background: rgba(232, 143, 175, 0.1);
}
[data-v2-velaire] .v2-console-caption {
  margin: 14px 0 0;
  font-size: 13.5px;
  line-height: 1.6;
  max-width: 42ch;
}
[data-v2-velaire] .box-white .v2-console-caption {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
/* Below the two-column breakpoint the accordion sits under the column, so there is
   nothing left to stick to and a sticky panel would trail the reader down the page. */
@media (max-width: 1199px) {
  [data-v2-velaire] .v2-console-wrap { position: static; }
}

[data-v2-velaire] .v2-declaration {
  max-width: 82ch;
  padding: clamp(28px, 3.5vw, 40px);
  border: 1px solid rgba(239, 227, 228, 0.16);
  border-radius: 20px;
  background: var(--neutral-900);
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-declaration-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #c9a06a);
  margin-bottom: 16px;
}
[data-v2-velaire] .v2-declaration p { line-height: 1.7; }
[data-v2-velaire] .v2-declaration p + p {
  margin-top: 16px;
  color: var(--vh-cream-dim, #ddbdc4);
}

/* ---- Feature matrix and footnotes ----
   The matrix is a table because it exists to be read across columns, which cards
   make impossible. It scrolls inside its own container so a narrow screen never
   makes the page itself scroll sideways. */
[data-v2-velaire] .v2-matrix-scroll {
  overflow-x: auto;
  border: 1px solid rgba(239, 227, 228, 0.16);
  border-radius: 20px;
  background: var(--neutral-900);
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-matrix {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  font-size: 0.95rem;
}
[data-v2-velaire] .v2-matrix th,
[data-v2-velaire] .v2-matrix td {
  padding: 14px 20px;
  text-align: left;
  border-bottom: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.1));
  vertical-align: top;
}
[data-v2-velaire] .v2-matrix thead th {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #c9a06a);
  white-space: nowrap;
}
/* Tier columns are compared against each other, so they are centred and narrow. */
[data-v2-velaire] .v2-matrix thead th:not(:first-child),
[data-v2-velaire] .v2-matrix td {
  text-align: center;
  width: 15%;
}
[data-v2-velaire] .v2-matrix-group th {
  background: rgba(239, 227, 228, 0.06);
  font-weight: 600;
  letter-spacing: 0.02em;
}
[data-v2-velaire] .v2-matrix tbody th {
  font-weight: 500;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-matrix-detail {
  display: block;
  margin-top: 4px;
  font-size: 0.85em;
  font-weight: 400;
  color: var(--vh-cream-dim, #ddbdc4);
}
/* A tick and an em dash rather than a tick and a cross: "not on this tier" is not
   a failure, and a red cross reads like one. */
[data-v2-velaire] .v2-matrix .v2-yes { color: var(--vh-gold-400, #c9a06a); font-size: 1.05em; }
[data-v2-velaire] .v2-matrix .v2-no { color: rgba(239, 227, 228, 0.35); }

/* Source notes. Small, but they are what makes a figure checkable rather than
   asserted, so they get real line-height instead of being squeezed. */
/* 0.94rem, up from 0.82. At 0.82 this tier rendered 13.12px, and it is not decoration:
   it carries the "Explore security" link, the pricing small print and the note that the
   week walkthrough is illustrative. On the wine ground, reversed, 13px is the size Qasim
   kept reading as too small — the headings he asked me to check were already right. */
/* 0.94rem is ~15px against a 17px body, and this port already documents why 16px
   body was raised to 17: light type on this ground reads thinner and smaller than
   the same metrics on cream. The footnote tier was carrying real sentences -- what
   happens without Velaire, and what is available today -- at the size the template
   uses for asides. Raised to sit just under body rather than well under it. */
[data-v2-velaire] .v2-footnote {
  max-width: 78ch;
  margin-top: 28px;
  font-size: 1.04rem;
  line-height: 1.72;
}
[data-v2-velaire] .v2-footnote a {
  color: var(--vh-gold-400, #c9a06a);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ---- The practice inbox ----
   The only place on the site that shows the product's surface rather than
   describing it. Drawn as a window on the wine ground so it reads as a screenshot
   of an application, which is what it is a still of. */
[data-v2-velaire] .v2-inbox {
  border: 1px solid rgba(239, 227, 228, 0.16);
  border-radius: 20px;
  overflow: hidden;
  background: var(--neutral-900);
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-inbox-chrome {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 14px 22px;
  border-bottom: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.12));
  background: var(--neutral-950);
}
[data-v2-velaire] .v2-inbox-body { padding: clamp(20px, 3vw, 32px); }
[data-v2-velaire] .v2-inbox-practice {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 24px;
}
/* Every "this is not real" label reads at body size, not as fine print. A
   screenshot of a healthcare inbox that does not say it is invented is a
   fabricated record, and these labels are the only thing separating the two. */
[data-v2-velaire] .v2-inbox-note {
  font-size: 0.82rem;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-inbox-eyebrow {
  display: block;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #c9a06a);
  margin-bottom: 10px;
}
[data-v2-velaire] .v2-inbox-greeting { margin-bottom: 28px; }
[data-v2-velaire] .v2-inbox-greeting h3 { margin: 0 0 6px; }
[data-v2-velaire] .v2-inbox-greeting p { margin: 0; }
[data-v2-velaire] .v2-inbox-caption {
  caption-side: top;
  padding: 12px 20px;
  text-align: left;
  font-size: 0.82rem;
  color: var(--vh-cream-dim, #ddbdc4);
}
/* This table is read row by row, unlike the feature matrix, so its cells stay
   left-aligned rather than centred. */
[data-v2-velaire] .v2-inbox-table thead th:not(:first-child),
[data-v2-velaire] .v2-inbox-table td {
  text-align: left;
  width: auto;
}
[data-v2-velaire] .v2-inbox-outcome {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 0.8rem;
  background: rgba(201, 160, 106, 0.16);
  color: var(--vh-gold-400, #c9a06a);
}

/* Hero buttons. Several live pages open on a pair, and the template's page-title
   block has a title and a breadcrumb and nothing else. */
[data-v2-velaire] .page-title-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: center;
  margin-top: 28px;
}

/* A quoted script line. Set as a quotation rather than body copy because that is
   what makes it evidence: this is the sentence the patient was read, reproduced,
   not a paraphrase of it. */
[data-v2-velaire] .v2-quote {
  margin: 0;
  padding-left: 20px;
  border-left: 3px solid var(--vh-gold-400, #c9a06a);
  font-size: clamp(1rem, 1.3vw, 1.15rem);
  line-height: 1.7;
}

/* Post-card excerpts. The template's horizontal blog card has a category, a title
   and a button and no room for a summary, but a list of nine-lesson guides is
   unusable without one. */
[data-v2-velaire] .infor_excerpt {
  margin: 10px 0 0;
  font-size: 0.92rem;
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Lane callouts above the blog card grid. They sit inside the grid column, so they
   need their own bottom margin rather than the grid's gap. */
[data-v2-velaire] .v2-lane-intro { margin-bottom: 40px; }

/* ---- Kicker pills ----
   `.heading-sub` is `width: max-content`, so a kicker longer than the viewport
   pushes the whole document sideways rather than wrapping. Aigocy's own kickers are
   one or two words, so the template never hit this; several of Velaire's are whole
   sentences ("Give every caller a thoughtful response. Give your team more room to
   care."), which overflowed the homepage by 246px at 390px wide.

   Capping the width rather than shortening the copy: the copy is the live site's
   and the constraint is the pill's. */
[data-v2-velaire] .heading-sub {
  max-width: 100%;
  width: fit-content;
  text-wrap: balance;
}

/* ---- Pricing card heading ----
   The template pairs the price and the button on one row. Aigocy's prices are two
   or three characters ("$49"); Velaire's run to "$1,999+", and at three cards
   across a 1024px viewport the button was squeezed to 48px and pushed 32px past the
   page edge. Wrapping it under the price is the fix; the row survives wherever it
   fits. */
[data-v2-velaire] .pricing-item .heading {
  flex-wrap: wrap;
  gap: 16px;
}
[data-v2-velaire] .pricing-item .heading .tf-btn {
  flex-shrink: 0;
}

/* A tier cell that carries a value rather than a yes/no. These are where the plans
   actually differ, so they read as text and not as a symbol. */
[data-v2-velaire] .v2-matrix .v2-val {
  font-size: 0.88rem;
  color: var(--vh-cream, #efe3e4);
}

/* ---- Integrations ----
   A grid of what Velaire connects to, each carrying its real status. The chip is
   the point: "Configured today" and "Sandbox only" are different claims, and the
   colours keep them different at a glance without a red/green scold. */
[data-v2-velaire] .v2-integrations {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: 16px;
}
[data-v2-velaire] .v2-integration {
  padding: 24px;
  border: 1px solid rgba(239, 227, 228, 0.16);
  border-radius: 18px;
  background: var(--neutral-900);
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-integration-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
[data-v2-velaire] .v2-integration-cat {
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-integration-chip {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}
[data-v2-velaire] .v2-integration.is-live .v2-integration-chip {
  background: rgba(201, 160, 106, 0.18);
  color: var(--vh-gold-400, #c9a06a);
}
/* Sandbox and not-built read as quieter rather than as failures. Neither is a
   problem; both are just not the same claim as "configured today". */
[data-v2-velaire] .v2-integration.is-sandbox .v2-integration-chip,
[data-v2-velaire] .v2-integration.is-planned .v2-integration-chip {
  background: rgba(239, 227, 228, 0.1);
  color: var(--vh-cream-dim, #ddbdc4);
}
/* Selects the heading by CLASS, not by tag. This was `.v2-integration h4` and stopped
   matching the moment card titles became <h3>, which handed these ten cards the 36px
   `.v2-lvl-4` fallback -- caught by diffing every card heading's computed size before
   and after, not by looking at the page. */
[data-v2-velaire] .v2-integration .v2-integration-name { margin: 0 0 8px; font-size: 1.05rem; line-height: 48px; }
/* SEPARATE OBSERVATION, deliberately not fixed here: 48px is a 2.86 ratio on 16.8px
   text, and it is only that because the template's h4 line-height leaked onto a card
   title that was never meant to be h4-sized. It is restored verbatim because this
   change's job is to move tags WITHOUT moving pixels, and quietly tightening it inside
   a refactor is how a visual regression gets attributed to the wrong commit. Worth
   revisiting on its own. */
[data-v2-velaire] .v2-integration p { margin: 0; font-size: 0.92rem; line-height: 1.6; }

/* ---- The infrastructure rail ----
   Real vendor names where the template had six invented logos under the line
   "Built on infrastructure with signed BAAs". Set as chips rather than marks: none
   of these companies has supplied a logo for this use, and the names are what the
   sentence above them is actually about. */
/* ---- Marks in the "Runs on" rail ----
   Qasim, 13-Sep: carry the real logos, the way cartgaze.com's rail does. Four of the
   seven vendors have one; the rest stay as wordmarks and the reasoning for each is in
   `vendor-marks.ts`. So this rail is deliberately MIXED, and the two chip kinds have
   to read as the same object at a glance: same pill, same height, same padding -- only
   the content differs.

   `currentColor` on the path is the whole trick. Simple Icons ship in the vendor's own
   brand colour, and a strip of four brand colours on a wine ground is noise; inheriting
   the chip's cream also means the marks follow any future theme without a second set.

   The height is set on the svg and the width left to `auto`, because these are not all
   square: Cal.com is a 4.81:1 wordmark and the rest are square glyphs. Fixing width
   instead would squash the wordmark to a fifth of its neighbours -- the exact bug the
   measured viewBoxes in vendor-marks.ts exist to prevent. */
/* ---- The HIPAA line under the benefits heading ----
   A claim plus where to check it, in one control. Sized down from the heading so it
   reads as a footnote to it rather than a second headline, and built as a link because
   this site's own /vendor-checklist tells buyers not to trust a bare compliance badge.
   US only -- the component returns null elsewhere, see hipaa-badge.tsx. */
[data-v2-velaire] .v2-hipaa-badge {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
  padding: 10px 20px 10px 16px;
  border: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.12));
  border-radius: 999px;
  background: rgba(32, 26, 29, 0.03);
  color: var(--vh-ink, #201a1d);
  text-decoration: none;
  text-align: left;
  transition: border-color 0.2s ease, background 0.2s ease;
}
[data-v2-velaire] .v2-hipaa-badge:hover,
[data-v2-velaire] .v2-hipaa-badge:focus-visible {
  border-color: var(--vh-gold-500, #c9a06a);
  background: rgba(201, 160, 106, 0.1);
}
[data-v2-velaire] .v2-hipaa-badge .icon {
  font-size: 18px;
  color: var(--vh-gold-700, #7b592d);
  flex: 0 0 auto;
}
[data-v2-velaire] .v2-hipaa-text {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
}
[data-v2-velaire] .v2-hipaa-text strong {
  font-size: 0.95rem;
  font-weight: 600;
}
[data-v2-velaire] .v2-hipaa-sub {
  font-size: 0.78rem;
  color: var(--vh-ink-muted, #6d5a62);
}

[data-v2-velaire] .v2-vendor-chip.has-logo {
  gap: 12px;
  padding: 8px 20px;
  /* Two marks in one chip (Tag Manager + Analytics) wrapped to a second line and made
     that chip 52px against the text chips' 42px. The rail is a row of pills and one
     taller pill reads as a mistake, so: never wrap, and pin every chip to one height
     regardless of what is inside it. */
  flex-wrap: nowrap;
}
/* infinityslide.js sets `display:block` inline on every child it manages, which beat the
   base rule's `inline-flex` -- so a chip holding two marks stacked them vertically and
   came out 52px against its neighbours' 42px. Scoped to the rail so the same class used
   elsewhere is untouched, and `!important` because the competing value is an inline
   style the vendor script writes at runtime. */
[data-v2-velaire] .partner-wrap .v2-vendor-chip {
  display: inline-flex !important;
  min-height: 42px;
}
[data-v2-velaire] .v2-vendor-chip .v2-brand-svg {
  height: 17px;
  width: auto;
  max-width: 120px;
  fill: currentColor;
  opacity: 0.92;
  flex: 0 0 auto;
}

[data-v2-velaire] .v2-vendor-chip {
  display: inline-flex;
  align-items: center;
  padding: 8px 18px;
  margin-right: 12px;
  border: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.18));
  border-radius: 999px;
  font-size: 0.9rem;
  white-space: nowrap;
  color: var(--vh-cream, #efe3e4);
  background: rgba(239, 227, 228, 0.05);
}

/* ---- Effects the template shipped as images ----
   Envato strips every raster, including the ones that are not photographs. A glow,
   a divider and two decorative shapes arrived as flat #8EA4BB plates, which on the
   wine ground read as grey-blue slabs across the page. Three of them are effects, so
   they are drawn here instead of being regenerated as pictures. */
[data-v2-velaire] .v2-light {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  width: min(670px, 80%);
  pointer-events: none;
}
[data-v2-velaire] .v2-light-top {
  top: 0;
  height: 59px;
  background: radial-gradient(
    ellipse at 50% 0%,
    rgba(224, 191, 138, 0.32) 0%,
    rgba(224, 191, 138, 0.08) 45%,
    transparent 72%
  );
}
[data-v2-velaire] .v2-light-bot {
  bottom: 0;
  height: 41px;
  background: radial-gradient(
    ellipse at 50% 100%,
    rgba(224, 191, 138, 0.22) 0%,
    rgba(224, 191, 138, 0.06) 45%,
    transparent 72%
  );
}
/* The rule beside a quotation's attribution. Four pixels of image for a line. */
[data-v2-velaire] .v2-cite-rule {
  display: inline-block;
  width: 2px;
  height: 36px;
  margin-right: 14px;
  border-radius: 2px;
  background: linear-gradient(
    180deg,
    rgba(224, 191, 138, 0.9),
    rgba(224, 191, 138, 0.15)
  );
}

/* ---- Gradient text on the light panels ----
   `.box-white` is the template's inverted section: a cream panel on the wine
   ground. The three text-gradient classes are remapped site-wide to cream-to-gold,
   which is right on wine and invisible on cream -- the process heading on every
   service page read as blank space with a working carousel beside it.

   Inside a light panel they go back to something like the template's original dark
   ramp, warmed toward the brand so it is not the borrowed grey. */
[data-v2-velaire] .box-white .text-gradient-1,
[data-v2-velaire] .box-white .text-gradient-2,
[data-v2-velaire] .box-white .text-gradient-3 {
  background: linear-gradient(95.5deg, #3a1420 1.91%, #7f6134 78%);
  -webkit-background-clip: text;
  background-clip: text;
}
/* Everything else inside the panel is ink on cream, not cream on wine. */
[data-v2-velaire] .box-white .heading-sub {
  color: var(--vh-gold-700, #7f6134);
}

/* ---- Light cards ----
   The template's card surfaces take --neutral-100, which is cream in this palette,
   while text on the wine ground is also cream. Where the two meet the ratio is
   exactly 1 and the card is blank: the type is there, it is the same colour as the
   paper.

   .features-item is the worst of them -- eight routes use it, including the whole
   "what it does now" grid on /product and the boundaries on /safety -- but the
   pattern repeats on every card that keeps a light surface, so they are handled
   together rather than one bug report at a time.

   The cards stay light. That is the template's design and it is right on wine; what
   changes is the ink. */
[data-v2-velaire] .features-item,
[data-v2-velaire] .features-item .title,
[data-v2-velaire] .features-item h6,
[data-v2-velaire] .process-card .title,
[data-v2-velaire] .process-card h4,
[data-v2-velaire] .pricing-item .text,
[data-v2-velaire] .pricing-item .title {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .features-item p,
[data-v2-velaire] .process-card .text,
[data-v2-velaire] .process-card p,
[data-v2-velaire] .pricing-item .list-text li,
[data-v2-velaire] .pricing-item .price-per {
  color: var(--vh-ink-muted, #6d5a62);
}
/* Every check-list on the site rendered as unmarked lines.
   The template's markup puts an <i class="icon icon-check-solid"> in each <li> and
   `.list-text.type-check li .icon` styles it into a 20px chip; the generated pages
   emitted the <li> without the <i>, so both pricing tables, the tier comparison,
   the "it does" panel and the not-a-fit list had no marker at all -- and plain
   `.list-text`, which the template gives no rule of any kind, had no gap either, so
   four separate claims ran together as one paragraph.

   The icons are back in the markup. These rules clean up two things they inherit:

   `.features-item .icon` is the template's 60px feature tile -- dark ground, 32px
   bottom margin, and the same eight-layer shadow its buttons use. A 20px tick inside
   a list in one of those cards keeps the shadow and the margin, which put a dark
   crescent under every check mark.

   Colour is left to the template (`--black` on the neutral chip, ~10:1). An earlier
   pass here pointed the glyph at the brand gold for consistency with the kickers and
   dropped it to 3.7:1 -- a 10px mark, legible but faint. Consistency is not worth
   that on the one element whose whole job is to be seen at a glance. */
[data-v2-velaire] .list-text .icon {
  box-shadow: none;
  margin-bottom: 0;
}

/* The 60px feature tile's own glyph, which inherited the cream it had on wine.
   Kept at two-class specificity on purpose: the template's own
   `.list-text.type-check li .icon` is more specific, so the ticks above stay the
   near-black the template gives them and only the tiles turn gold. */
[data-v2-velaire] .features-item .icon {
  color: var(--vh-gold-700, #7f6134);
}

/* Velaire has three tiers where the template has two, so the pricing cards run
   col-lg-4 and each is about half the width `.pricing-item` was drawn for. Its
   `.content` splits 50/50 regardless, which left the feature list roughly 110px
   wide: "Overflow coverage during business hours" broke over four lines beside a
   column of whitespace, and adding the tick took another 30px out of it. The
   template already stacks these two children once the card is narrow; a third of a
   row is narrow. Above 991px only -- below that the card is full width already and
   the template's own breakpoint has it. */
@media (min-width: 992px) {
  [data-v2-velaire] .col-lg-4 .pricing-item .content {
    flex-direction: column;
  }
  [data-v2-velaire] .col-lg-4 .pricing-item .content > * {
    width: 100%;
  }
}

/* The negative variant. The template ships one list style and its glyph is a tick,
   which beside "Velaire is the wrong tool for these" says the opposite of the
   sentence above it. Same chip and the same ink; the cross and the muted ground do
   the work. */
[data-v2-velaire] .list-text.type-check.type-cross li .icon {
  background-color: rgba(109, 90, 98, 0.14);
}

/* ---- The header bar ----
   `.header-inner` is a cream pill on the wine ground and the nav links inherit the
   cream used for text ON wine, so Product / Services / Pricing / Trust / Journal
   were rendering at a contrast ratio of 1.01:1 — present, clickable, and completely
   invisible. Only the logo and the gold button could be seen.

   This is the single most visible defect the port had, it shipped on all twenty
   routes, and no check caught it until contrast measurement went into the audit:
   parity saw the words in the DOM, and the layout audit saw a header of the right
   size in the right place. */
[data-v2-velaire] .header-inner .item-link,
[data-v2-velaire] .header-inner .nav-menu-main a,
[data-v2-velaire] .header-inner .menu-item > a {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .header-inner .item-link:hover,
[data-v2-velaire] .header-inner .menu-item:hover > a {
  color: var(--vh-gold-700, #7f6134);
}
/* The submenu is its own cream card; same reasoning, and it was already ink. */
[data-v2-velaire] .header-inner .sub-menu .item-link {
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .header-inner .sub-menu .item-link:hover {
  color: var(--vh-gold-700, #7f6134);
}
/* The mobile menu trigger sits in the same bar. */
[data-v2-velaire] .header-inner .open-mb-menu {
  color: var(--vh-ink, #201a1d);
}

/* ---- .text-muted: a collision, not a colour choice ----
   The live app uses Tailwind's `text-muted`, mapped to its own --muted token.
   Bootstrap also defines `.text-muted`, as rgba(33,37,41,.75), and Bootstrap's
   stylesheet loads inside the v2 routes. So every live component reused here --
   the ROI calculator, the vendor checklist, article bodies, sources -- rendered its
   secondary text as near-black on the wine ground at 1.01:1. Present, and unreadable.

   That is 47 elements on /vendor-checklist alone and the largest single contrast
   defect in the port. It is a class collision rather than a design decision, so the
   fix is to give the class the value the live app meant, adjusted for this ground. */
[data-v2-velaire] .text-muted {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}
/* Inside a light panel the same class needs ink, not cream. */
[data-v2-velaire] .box-white .text-muted,
[data-v2-velaire] .features-item .text-muted,
[data-v2-velaire] .process-card .text-muted,
[data-v2-velaire] .pricing-item .text-muted {
  color: var(--vh-ink-muted, #6d5a62) !important;
}

/* The hero standfirst is not on the wine ground: `.sub` carries its own
   near-white pill (linear-gradient(#f5f5f5, #f5f5f5)), so it is a light chip and
   needs dark ink. Reading the section it sits in rather than the element's own
   background was the mistake -- it made the ratio worse, 2.21:1 down to 1.61:1,
   before the audit caught it. Gold-700 is the tone for gold text on cream. */
[data-v2-velaire] .section-hero .sub,
[data-v2-velaire] .heading-sub:not(.style-1) {
  color: var(--vh-gold-700, #7f6134);
}
/* `.style-1` is the same pill inverted -- neutral-900 rather than #f5f5f5 -- so it
   is a dark chip and takes the light gold. Two variants of one class, opposite
   grounds; treating them alike broke nine routes in the direction the plain variant
   had just been fixed in. */
[data-v2-velaire] .heading-sub.style-1 {
  color: var(--vh-gold-400, #e0bf8a);
}
/* ---- Aigocy's brand red ----
   #EA2B16 -> #FF3B26 is Aigocy's accent, and styles.css paints four things with it:
   the hero's icon plate, the process-card icon, the features-center panel, and the
   pricing toggle. All four were still bright red-orange on a wine-and-gold site --
   the loudest colour on the page, and the only one that belongs to another brand.

   Repointed at the accent ramp, so they follow the market theme like everything
   else rather than being a fifth colour that never moves. */
[data-v2-velaire] .section-hero .content-wrap .title-icon .box,
[data-v2-velaire] .process-card .icon,
[data-v2-velaire] .tf-switch-check:checked {
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%) !important;
}
/* The plate's glow is four stacked shadows in rgba(255, 59, 0), which left an
   orange halo around a gold plate once the fill was changed. */
[data-v2-velaire] .section-hero .content-wrap .title-icon .box {
  box-shadow:
    0 3px 6px rgba(0, 0, 0, 0.09),
    0 23px 37px rgba(201, 160, 106, 0.18),
    0 76px 123px rgba(201, 160, 106, 0.06) !important;
}
/* This used to run `filter: sepia(1) ... ` over the three tiles, because at the time
   they were Envato's stripped placeholders -- flat #8EA4BB rectangles that had to be
   tinted out of grey-blue. They are generated art now, cream cards carrying a wine
   glyph, so the filter was pushing brand colour towards brown for no reason. Only
   the slight transparency is kept, which is what sits them into the plate. */
[data-v2-velaire] .section-hero .content-wrap .title-icon-wrap img,
[data-v2-velaire] .img-transform-3 {
  opacity: 0.92;
}
[data-v2-velaire] .features-center {
  background-image: linear-gradient(180deg, #3a1420 0%, #4a1f2d 100%) !important;
}
/* The panel's fill was repointed above, but its glow is a separate declaration: seven
   stacked shadows in rgba(255, 59, 0), Aigocy's brand orange. So a wine card sat in
   the middle of the diagram wearing a bright orange halo -- the loudest thing on the
   page, and the last of the four places that red survived. Same shadow geometry, in
   the gold the rest of the page glows with. */
[data-v2-velaire] .features-center {
  box-shadow:
    0 -3px 0 rgba(0, 0, 0, 0.06) inset,
    0 1px 0 rgba(255, 255, 255, 0.14) inset,
    0 3.44px 5.57px rgba(0, 0, 0, 0.09),
    0 22.91px 37.08px rgba(201, 160, 106, 0.16),
    0 76px 123px rgba(201, 160, 106, 0.05),
    0 5.05px 8.18px rgba(201, 160, 106, 0.1),
    0 16.98px 27.47px rgba(201, 160, 106, 0.05),
    0 56px 83px rgba(201, 160, 106, 0.22),
    0 14px 34px rgba(201, 160, 106, 0.22),
    0 1px 2px rgba(201, 160, 106, 0.35) !important;
}

/* The workflow preview's active tab is a light chip, and it takes its text colour
   from --accent, which v2 remaps to the light gold used on the wine ground. Light
   on light, 1.52:1. The chip is the template's; the ink has to follow it. */
[data-v2-velaire] .workflow-tabs button[aria-selected="true"],
[data-v2-velaire] .workflow-tabs button.is-active,
[data-v2-velaire] .workflow-tabs > button {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .workflow-tabs > button:not([aria-selected="true"]):not(.is-active) {
  color: var(--vh-cream-dim, #ddbdc4);
}

/* `.text-secondary` is cream-dim, for the wine ground. Inside a light card it is
   cream on cream. The card-scoped `p` rule earlier in this file loses to it on
   specificity, which is why this needs its own rule rather than another selector in
   that list. Found at 390px only, because the two-column features grid puts
   different items in the measured column at different widths -- an argument for
   auditing every breakpoint rather than the widest one. */
[data-v2-velaire] .features-item .text-secondary,
[data-v2-velaire] .process-card .text-secondary,
[data-v2-velaire] .pricing-item .text-secondary,
[data-v2-velaire] .v2-inbox .text-secondary {
  color: var(--vh-ink-muted, #6d5a62) !important;
}

/* `.text-brand` is gold-500 at 22px, which is under the 24px large-text threshold,
   so it needs 4.5:1 rather than 3:1 and gold-500 gives 3.48:1 on wine. */
[data-v2-velaire] .text-brand {
  /* styles.css declares this one !important, so specificity alone does not win. */
  color: var(--vh-gold-400, #e0bf8a) !important;
}

/* ---- Footer ----
   `.footer-image` is a decorative slot the template fills with a wide, faint
   lockup (Envato's placeholder is 1280x399). Ours held the square brand mark,
   stretched by object-fit: cover to 1296x1296 at full opacity, which put a giant
   opaque glyph over the copyright line and the company description. It reads as a
   cartoon face rather than a watermark.

   Constrained to the slot's real proportions and taken down to a watermark, behind
   the content rather than over it. */
[data-v2-velaire] footer .footer-image {
  height: 360px;
  overflow: hidden;
  opacity: 0.06;
  pointer-events: none;
  z-index: 0;
}
[data-v2-velaire] footer .footer-image img {
  height: 100%;
  object-fit: contain;
}
[data-v2-velaire] footer .container {
  position: relative;
  z-index: 1;
}

/* The market chips that replaced the template's social row. */
[data-v2-velaire] .v2-market-code {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
}

/* Text inside the self-contained panels, now that their ground is fixed. The
   light-card rules earlier in this file must not reach into them. */
[data-v2-velaire] .v2-inbox .text-secondary,
[data-v2-velaire] .v2-phone-row .text-secondary,
[data-v2-velaire] .v2-integration .text-secondary,
[data-v2-velaire] .v2-declaration .text-secondary {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}
[data-v2-velaire] .v2-inbox h3,
[data-v2-velaire] .v2-inbox-brand,
[data-v2-velaire] .v2-integration h4,
[data-v2-velaire] .v2-matrix tbody th {
  color: var(--vh-cream, #efe3e4);
}

/* ---- The services accordion, now that it renders as the template intends ----
   Adding the missing `.accordion-faq_item` wrapper turned these from unstyled
   blocks into the template's full-width cream cards -- and cream cards inherit the
   cream text used on wine. 1:1 for the status tag, 1.25:1 for the 48px title.

   Every rule here is scoped to the item, so the accordions that sit on the wine
   ground (the FAQ ones) are untouched. */
[data-v2-velaire] .accordion-faq_item .accordion-title,
[data-v2-velaire] .accordion-faq_item .accordion-title .num,
[data-v2-velaire] .accordion-faq_item .accordion-action {
  color: var(--vh-ink, #201a1d) !important;
}
[data-v2-velaire] .accordion-faq_item .accordion-content .text,
[data-v2-velaire] .accordion-faq_item .accordion-content .text-neutral-300,
[data-v2-velaire] .accordion-faq_item .accordion-content .text-body-3 {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
[data-v2-velaire] .accordion-faq_item .tags-item:not(.is-link) {
  color: var(--vh-ink, #201a1d) !important;
  border-color: rgba(32, 26, 29, 0.2);
}
/* The open item DOES invert to a dark panel, and finding that took three attempts.
   The card element itself is cream whether open or closed -- which is what
   `getComputedStyle` on the item reports, and why the second attempt "corrected"
   the first one away. The dark panel is `.accordion-action::before`, a full-size
   pseudo-element that `.collapsed::before` parks at `bottom: 110%` and the open one
   slides into place. Reading elements and not their pseudo-elements is a blind spot
   the contrast audit shares, so this one had to be caught by looking.

   The panel's own fill is hardcoded #18181B, Aigocy's near-black. Repointed at the
   wine so the open item is on-palette rather than a black slab in a cream list. */
[data-v2-velaire] .accordion-faq_item .accordion-action::before {
  background: radial-gradient(
      62.56% 62.56% at 28.14% -10.42%,
      rgba(255, 255, 255, 0.14) 0%,
      rgba(255, 255, 255, 0) 100%
    ),
    #3a1824 !important;
  box-shadow:
    0 -8px 0 0 rgba(239, 227, 228, 0.08) inset,
    0 4px 0 0 rgba(255, 255, 255, 0.14) inset,
    0 8px 2px 0 rgba(0, 0, 0, 0.06),
    0 3px 3px 0 rgba(0, 0, 0, 0.1) !important;
}
/* On that panel the text goes back to cream. */
[data-v2-velaire] .accordion-faq_item .accordion-action:not(.collapsed) .accordion-title,
[data-v2-velaire] .accordion-faq_item .accordion-action:not(.collapsed) .accordion-title .num,
[data-v2-velaire] .accordion-faq_item .accordion-action:not(.collapsed) ~ .collapse .accordion-content .text,
[data-v2-velaire] .accordion-faq_item .accordion-action:not(.collapsed) ~ .collapse .accordion-content .text-neutral-300,
[data-v2-velaire] .accordion-faq_item .accordion-action:not(.collapsed) ~ .collapse .tags-item:not(.is-link) {
  color: var(--vh-cream, #efe3e4) !important;
  /* The chip's edge inverts with its text. The closed-item rule above paints it
     dark ink at 20%, which is invisible once the panel behind it goes dark. */
  border-color: rgba(239, 227, 228, 0.28);
}

/* The pricing figure and the contact block's heading, both cream on the cream
   card the template gives them. */
[data-v2-velaire] .pricing-item .price-number,
[data-v2-velaire] .section-contact .col-right .heading,
[data-v2-velaire] .form-contact .heading {
  color: var(--vh-ink, #201a1d);
}
/* --neutral-400 is a mid mauve that lands at 4.17:1 on wine -- a near miss rather
   than a failure, but it is body copy and it reads as washed out. */
[data-v2-velaire] .text-neutral-400 {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}

/* `.tags-item` is a class the template's markup uses and its stylesheet never
   defines -- a chip with no chip. It shows on the service and practice pages, where
   the row is three or four of them and they ran together as one sentence
   ("No new scheduling software Right service, right slot Confirmation built in").
   On the services accordion it is a single short status label and reads as one
   either way. The colour rules around here were already written as though a border
   existed, and they still win over this one. */
[data-v2-velaire] .list-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
[data-v2-velaire] .tags-item:not(.is-link) {
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border: 1px solid;
  border-color: color-mix(in srgb, currentColor 32%, transparent);
  border-radius: 99px;
  font-size: 13px;
  line-height: 18px;
}

/* The filled tag variant. It paints its own wine chip, so it inverts back to cream
   while its plain siblings stay ink on the cream card. */
[data-v2-velaire] .tags-item.is-link {
  color: var(--vh-cream, #efe3e4) !important;
  border-color: transparent;
}

/* A plain `.tags-item` on the wine ground (article bodies use them for prerequisite
   chips) inherits --neutral-200, a mid mauve, at 4.25:1. Just under AA for text at
   16px, and it is a label rather than decoration. */
[data-v2-velaire] .blog-prose .tags-item,
[data-v2-velaire] .blog-sidebar .tags-item,
[data-v2-velaire] .blog-single-wrap .tags-item:not(.is-link) {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}

/* The working search field and its results. The template's input is styled by
   .form-search, which is kept; the results list is new because the template has
   nowhere to put one. */
[data-v2-velaire] .v2-search-results {
  margin-top: 14px;
  border-top: 1px solid rgba(239, 227, 228, 0.14);
  padding-top: 12px;
}
[data-v2-velaire] .v2-search-results ul {
  display: grid;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}
[data-v2-velaire] .v2-search-results a {
  display: block;
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.4;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-search-results a:hover { color: var(--vh-gold-400, #e0bf8a); }
[data-v2-velaire] .v2-search-cat,
[data-v2-velaire] .v2-search-empty {
  display: block;
  margin-top: 3px;
  font-size: 0.8rem;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-search-empty { margin: 0; }

/* The price figure is 72px in the template, sized for "$49". Velaire's longest is
   "$1,999" and at three cards across a 1024px viewport the card is 309px wide while
   the figure plus its "/mo" needs 316. Scaled to the card rather than fixed, so it
   fits whatever the longest tier name turns out to be. */
[data-v2-velaire] .pricing-item .price-number {
  font-size: clamp(40px, 5.2vw, 72px);
  /* The template is 72px/72px at desktop and 40px/48px below 768 — ratio 1.0 at the top
     of its scale and 1.2 at the bottom. A single ratio cannot be both, and the 1.05 that
     used to sit here measured 75.6px against the demo's 72px. Clamping the line-height
     on the same curve as the size hits BOTH of the template's anchors exactly: at full
     size the two maxima meet at 72/72, and at the floor the 48px minimum sits over a
     40px figure. */
  line-height: clamp(48px, 5.2vw, 72px);
}
[data-v2-velaire] .pricing-item .heading > .d-flex {
  flex-wrap: wrap;
  min-width: 0;
}

/* ---- .collapse: the second class collision, and the costlier one ----
   Tailwind ships a `.collapse` utility that sets `visibility: collapse`, and the
   live app's global bundle loads on these routes. Bootstrap uses `.collapse` for
   something else entirely -- an element it shows and hides with `display` -- and 39
   elements here carry it.

   So every Bootstrap collapse on the v2 routes was invisible: the services
   accordion's content on the homepage and on /services, and every FAQ answer. The
   markup was right, the `.show` class was toggling correctly, and nothing rendered.

   Three checks missed it and each for a structural reason: parity saw the text in
   the DOM, the leftover-copy check saw nothing foreign, and the contrast audit skips
   anything with `visibility` other than visible -- which is exactly what this was.
   It took opening the accordion and looking at the hole where the answer should be.

   Nothing in this app wants Tailwind's version: the only related class in the live
   components is `border-collapse`, which is unaffected. Bootstrap's semantics win
   inside the preview. */
[data-v2-velaire] .collapse {
  visibility: visible;
}

/* ---- The marquee's edge fade ----
   `.infiniteslide_wrap::after` fades the rail's two ends so chips slide in and out
   rather than being clipped. Aigocy fades to #EDECEC, its near-white ground. On
   wine that painted a pale band straight across the rail and swallowed the first
   chip -- "Vercel" was behind a white haze on the homepage.

   The contrast audit found this, which is worth noting: it flagged the chips at
   1.06:1 because it reads the mask's most-opaque stop as their ground, and that
   turned out to be exactly right. */
[data-v2-velaire] .infiniteslide_wrap::after {
  background: linear-gradient(
    90deg,
    #4a1f2d 0%,
    rgba(74, 31, 45, 0) 12%,
    rgba(74, 31, 45, 0) 88%,
    #4a1f2d 100%
  );
}

/* ---- The template's decorative SVGs ----
   Twenty of them, every one filled with #8EA4BB. Envato strips the artwork from
   these the same way it strips the photographs, so they arrive as flat grey-blue
   shapes -- a blue shield sitting in a benefit card on a healthcare page.

   They are abstract composition pieces with nothing to preserve, so the fill is
   rewritten in the files themselves rather than replaced with generated artwork.
   An attempt to do it from CSS via `fill="currentColor"` does not work and is worth
   recording: an SVG loaded through <img> is an isolated document and cannot see the
   page's `color`, so all twenty rendered black. Same reason the wordmark SVG could
   not reach the page's webfonts, noted elsewhere in this file.

   Three tones, by where each file renders: warm muted on the cream cards, light warm
   on the gold hero plate, the accent on the wine ground. */

/* ---- The contact section's photograph ----
   Aigocy's version sits on a dark image; Velaire's is a bright, warm desk shot, and
   the cream-to-gold heading over it was close to invisible. A scrim on the image
   itself rather than a change to the type, because the type is right and the
   template's composition is right -- what is missing is the contrast the original
   image supplied for free.

   The contrast audit could not find this: the ground here is a photograph, and it
   samples CSS colours rather than pixels. Everything on a photographic ground is
   still an eyes-only check. */
[data-v2-velaire] .section-contact .contact-image::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    100deg,
    rgba(46, 17, 25, 0.86) 0%,
    rgba(46, 17, 25, 0.62) 42%,
    rgba(46, 17, 25, 0.28) 72%,
    rgba(46, 17, 25, 0.2) 100%
  );
  pointer-events: none;
}
/* `.contact-image` is already `position: absolute; inset: 0 16px` -- it IS the
   section's background. Declaring `position: relative` here took it out of that and
   dropped it into the flow as a block above the copy, which is a good illustration
   of why the scrim goes on the existing element rather than a new wrapper. */
[data-v2-velaire] .section-contact .contact-image {
  border-radius: 40px;
  overflow: hidden;
}
/* The two contact rows sit on the darkened half and go back to cream. */
[data-v2-velaire] .section-contact .contact-item .title,
[data-v2-velaire] .section-contact .contact-item .text {
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .section-contact .contact-item .text {
  color: var(--vh-cream-dim, #ddbdc4);
}

/* The wrapper that keeps switchPrice() away from the period label. It must not
   change the layout it sits in. */
[data-v2-velaire] .price-per-wrap {
  display: contents;
}

/* The benefits card's progress rows.
   The bars carried invented percentages (Accuracy 60, Latency 100, Safety 40, Cost
   80) inside a card headed "It does not assess symptoms" -- so on this site
   "Accuracy 60%" read as a measured claim about a healthcare product, and a low one.
   The rows keep their rhythm and now name the four clinical acts the agent will not
   perform; the bar itself is gone rather than given a made-up length. */
[data-v2-velaire] .benefits-progress-item {
  display: flex;
  align-items: center;
}
[data-v2-velaire] .benefits-progress-item .progress-line {
  display: none;
}
[data-v2-velaire] .benefits-progress-item .progress-text {
  width: 100%;
  justify-content: flex-start;
  gap: 10px;
}
[data-v2-velaire] .benefits-progress-item .progress-text .icon {
  color: var(--vh-gold-700, #7b592d);
  font-size: 12px;
}


/* ---- The country control ----
   Velaire sells in one market and has route structure for five. The header needs
   to say which one you are looking at and offer the others without pretending they
   are open, so the four closed markets carry a status word rather than a link into
   a themed copy of pages that claim what the product does.

   Built from the template's own `menu-item has-child` so it inherits the nav's
   hover dropdown with no script. Two things the template's own menus do not do are
   added: `:focus-within`, so it opens from the keyboard, and a right-hand edge, so
   a 260px panel under the last item before the CTA does not hang off the pill. */
[data-v2-velaire] .tf-header.header2 .header-inner {
  /* The template ships 807px for this variant. The country control adds an item, and
     Qasim's note that our bar does not look like the theme's is mostly room: the
     demo's nav sits in a much wider pill with more air between the logo, the links
     and the button. */
  max-width: 980px;
  padding-left: 28px;
}
[data-v2-velaire] .header-inner .nav-menu-main {
  gap: 28px;
}

/* The current page, marked. The template styles `.item-link.active` with the accent
   and a rule that grows under the label, and its own demo shows it under "Home";
   the port never set the class, so the header never said where you were. The label
   takes gold-700 rather than --brand: --brand is the fill gold and lands at 2.2:1 on
   the cream pill, which is a colour you cannot read. */
[data-v2-velaire] .header-inner .item-link.active {
  color: var(--vh-gold-700, #7b592d);
}
[data-v2-velaire] .v2-country {
  margin-left: 4px;
  padding-left: 16px;
  border-left: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}
[data-v2-velaire] .v2-country-trigger {
  align-items: center;
  gap: 6px;
}
[data-v2-velaire] .v2-country-trigger .icon {
  font-size: 14px;
}
[data-v2-velaire] .v2-country-code {
  font-family: var(--vh-font-mono, monospace);
  font-size: 13px;
  letter-spacing: 0.04em;
}
[data-v2-velaire] .v2-country-menu {
  left: auto;
  right: -8px;
  min-width: 264px;
  padding: 8px 0;
}
/* A market that is not open is a <span>, not a link, and needs the same box: without it
   the name sat on the panel's rounded edge. Australia's row did from 11-Sep; on 27-Sep,
   when Ireland, the UK and Canada became "coming soon" too, Ireland's "I" was clipped. */
[data-v2-velaire] .v2-country-menu li > a,
[data-v2-velaire] .v2-country-menu li > span {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding: 8px 16px;
  white-space: nowrap;
}
[data-v2-velaire] .v2-country-menu a[aria-current] .v2-country-name {
  color: var(--vh-gold-700, #7b592d);
}
[data-v2-velaire] .v2-country-soon {
  font-family: var(--vh-font-mono, monospace);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--vh-ink-muted, #6d5a62);
  background-color: rgba(32, 26, 29, 0.06);
  border-radius: 999px;
  padding: 2px 8px;
}
/* The template opens its dropdowns on hover only. A keyboard user tabbing to the
   trigger would otherwise focus five links inside a panel with visibility: hidden. */
[data-v2-velaire] .v2-country .menu-item:focus-within .sub-menu {
  pointer-events: all;
  transform: translateY(0);
  opacity: 1;
  visibility: visible;
}


/* ---- Bootstrap's `.border` utility ----
   `.border { border: var(--bs-border-width) var(--bs-border-style)
   var(--bs-border-color) !important; }`. The live components embedded in this route
   write Tailwind's `border border-edge`, and `!important` beats a token class
   whatever the load order, so 54 elements across four routes drew Bootstrap's
   #dee2e6 instead of the intended rgba(239,227,228,.14). The vendor checklist is
   where it shows: 30 of the 54, and its entire card is drawn in hairlines, so the
   page read as an unstyled table on the wine ground.

   Third collision of this kind, after `.text-muted` and `.collapse`. Scoped to the
   pair, so an element that means a different border colour keeps it. */
[data-v2-velaire] .border.border-edge {
  border-color: var(--edge) !important;
}

/* ---- The template's decorative art ----
   Aigocy's hero cluster is three tilted tiles carrying tool logos, and the two large
   shapes further down are illustrated benefit cards. Envato strips all of it, so the
   port inherited a gold pill with three blank cream squares on it and, above "It does
   not touch medication", a plain beige shield two hundred pixels tall. Generated art
   replaces both -- flat graphics for the tiles, which render at about 100px where a
   photograph is mush, and photographs for the benefit shapes, which do not.

   The tiles were SVGs whose intrinsic size positioned them, so the replacements have
   to be sized here -- and the first attempt guessed 92px square for all three, which
   Qasim twice said was too big. It was: measuring the opaque card inside each of
   Envato's own SVGs gives three tilted cards of 64.6, 57.6 and 50.3px, sitting in
   bounding boxes of 91x53, 74x74 and 65x65 at (66,0), (162,38) and (83,90) inside the
   255x177 wrap. So the template's tiles are barely half the area ours were, and they
   are rotated about twenty degrees, which is most of why the cluster read as a flat
   stack rather than scattered tiles.

   Sized and tilted to that measurement. `left`/`top` place each box so its rotated
   bounding box lands on the template's, since rotation is about the centre and does
   not move the layout box. */
[data-v2-velaire] .section-hero .content-wrap .title-icon .img-1,
[data-v2-velaire] .section-hero .content-wrap .title-icon .img-2,
[data-v2-velaire] .section-hero .content-wrap .title-icon .img-3 {
  border-radius: 14px;
  box-shadow: 0 6px 18px rgba(46, 17, 25, 0.22);
}
[data-v2-velaire] .section-hero .content-wrap .title-icon .img-1 {
  width: 58px;
  height: 58px;
  left: 83px;
  top: 0;
  transform: rotate(-13deg);
}
[data-v2-velaire] .section-hero .content-wrap .title-icon .img-2 {
  width: 58px;
  height: 58px;
  left: 170px;
  top: 46px;
  transform: rotate(20deg);
}
[data-v2-velaire] .section-hero .content-wrap .title-icon .img-3 {
  width: 50px;
  height: 50px;
  left: 91px;
  top: 98px;
  transform: rotate(-21deg);
}
/* The tools section's six floaters, same treatment. The template sizes each one by
   width, so a square keeps its own height. */
[data-v2-velaire] .section-tools [class^="img-"],
[data-v2-velaire] .section-tools [class*=" img-"] {
  height: auto;
  border-radius: 18px;
  box-shadow: 0 6px 18px rgba(46, 17, 25, 0.22);
}
/* ---- The two benefit cards ----
   Aigocy fills these slots with a 231x278 shield and a 239x361 panel: flat shapes with
   transparency, which the card composition tucks its heading underneath
   (`margin-top: -112px` on one, `margin-bottom: -26px` on the other). Neither move
   survives contact with a photograph -- the overlap puts body text over the picture,
   and the sizes leave a small portrait floating in a card three times its width. That
   is the "not getting good fit" Qasim flagged on 10-Sep, and the two cards did not
   match each other either: 278px of picture in one, 361px in the other.

   So each card gets a full-width band across the top, both the same height, with the
   text below it. Both photographs were regenerated landscape for this -- see the
   BENEFITS set in scripts/v2/generate-art.mjs -- because cropping the old portraits
   into a band would have kept a quarter of the frame.

   `text-align` cannot place these: the reset makes `img` a block, so the template's
   own `center` and `end` were doing nothing and both pictures sat hard left. Width
   settles it instead. */
[data-v2-velaire] .benefits-secure-inner img,
[data-v2-velaire] .benefits-design-inner .item-img-1 {
  display: block;
  width: 100%;
  height: 260px;
  border-radius: 20px;
  object-fit: cover;
}
[data-v2-velaire] .benefits-design .content,
[data-v2-velaire] .benefits-secure .content {
  width: 100%;
  margin-top: 24px;
}
[data-v2-velaire] .benefits-secure-inner {
  margin-top: 0;
  margin-bottom: 0;
}
[data-v2-velaire] .benefits-design .benefits-design-inner {
  margin-right: 0;
  margin-top: 0;
}
/* The flip on hover was a shape turning over. A photograph turning over shows its
   own mirror image, which reads as a glitch. */
[data-v2-velaire] .benefits-box.benefits-secure:hover img {
  transform: none;
}

/* The two-column feature grid centres its columns against each other, which is
   invisible when the item count is even and obvious when it is not: three cards give
   two on the left and one on the right, and the single one floated to the middle with
   a gap above it and a gap below the pair. Both columns start at the top.

   Scoped to `.v2-features-plain`, because `.features-wrap` is also the template's own
   features DIAGRAM, where the same `align-items: center` is what sits the brand
   lockup on the connector line between the columns. Written unscoped first, which
   moved that lockup to the top of the diagram and left both lines running to empty
   space -- the class name is shared, the two uses are not. */
[data-v2-velaire] .features-wrap.v2-features-plain {
  align-items: flex-start;
}

/* The row list's outline. `.awards-item` takes `--neutral-800`, which in this palette
   is the wine ground itself, so every row was outlined in the colour behind it and
   the lists read as bare columns of text -- on the homepage's "what does not change"
   rows, the security page's pillars and the market pages. A hairline in the cream
   instead, the same one the transcript and the panels use. */
[data-v2-velaire] .awards-item {
  border-color: var(--vh-rule, rgba(239, 227, 228, 0.14));
}

/* The leading cell that Aigocy fills with a partner logo. `.awards-item > div` is
   `width: 100%` inside a flex row, so cells divide by how many there are — an icon cell
   without an explicit basis would take a full quarter of the row and push the text off
   balance. Fixed basis, and it does not grow. */
[data-v2-velaire] .v2-row-icon {
  flex: 0 0 64px !important;
  width: 64px !important;
  display: flex;
  align-items: center;
  justify-content: flex-start;
}
[data-v2-velaire] .v2-row-icon .icon {
  font-size: 26px;
  line-height: 1;
  color: var(--vh-gold-700);
  transition: color 0.3s ease;
}
[data-v2-velaire] .awards-item:hover .v2-row-icon .icon { color: var(--vh-gold-400); }
@media (max-width: 767px) {
  [data-v2-velaire] .v2-row-icon { flex-basis: 40px !important; width: 40px !important; }
}

/* The row's column widths, made explicit.

   Aigocy sizes these cells with `.awards-item > div { width: 100% }`, so they divide the
   row evenly BY COUNT and everything depends on how many there are. Two things broke
   that here. The title is a heading rather than a div -- an <h4> in the port, an <h3>
   since the heading-level pass -- so the selector never matched it and it sized to its
   own content; and the icon cell added a fourth child, squeezing the unmatched title
   further. Measured at 1440: icon 64px, title 141px, body 508px, meta 508px, which is
   why "Tuesday, 11am. Your desk is staffed and free." broke across three lines beside a
   single line of body copy.

   Explicit bases, which is what the port's own notes say any cell in this row needs.
   `align-items: center` because the cells are different heights by nature -- a two-line
   title beside a one-line note read as misaligned rather than as a row.

   lg and up only: below 992 the template stacks these cells and starts the meta column
   left, and that behaviour is correct. */
@media (min-width: 992px) {
  [data-v2-velaire] .awards-item { align-items: center; }
  [data-v2-velaire] .awards-item > .title { flex: 0 0 23%; width: auto; }
  [data-v2-velaire] .awards-item > .text { flex: 1 1 auto; width: auto; min-width: 0; }
  [data-v2-velaire] .awards-item > .year { flex: 0 0 25%; width: auto; }
}

/* THE HOVER STATE, which was making every row list on the site unreadable.
   Reported by Qasim: "all rows are such, when we hover over it its text disappears."

   `.awards-item:hover` in the template paints `#18181B`, a hardcoded near-black, and
   Aigocy can do that because on its light template these rows carry genuinely white
   text that stays readable when the fill goes dark. This port puts the same rows on
   the cream `box-white` sections, where the text is overridden to ink so it reads on
   cream — and nothing was overriding it BACK on hover. Fill goes near-black, text
   stays ink, row goes blank. It hit the homepage's "what does not change" rows, the
   /security pillars, the market pages and the vendor checklist alike.

   Two fixes in one: the fill takes the wine well rather than Aigocy's neutral, so a
   hovered row belongs to this palette; and every cell inside gets its light colour
   back, including `.year`, whose own rule further up carries `!important`. */
[data-v2-velaire] .awards-item:hover {
  background: var(--neutral-950);
  box-shadow:
    0 -4px 0 0 var(--well, #2e1119) inset,
    0 2px 0 0 rgba(239, 227, 228, 0.1) inset,
    0 8px 2px 0 rgba(20, 8, 13, 0.06),
    0 3px 3px 0 rgba(20, 8, 13, 0.1);
  border-color: var(--neutral-950);
}
/* `!important` here is required rather than lazy, and it is worth saying which rule
   forces it. The rows carry `.text-white`, and `[data-v2-velaire] .box-white
   .text-white` sets ink with `!important` so the cream panels read correctly. An
   `!important` declaration can only be beaten by another one, so the hover state has
   to answer in kind. Scoped to a hovered row, which is the only place it applies. */
[data-v2-velaire] .awards-item:hover .title,
[data-v2-velaire] .awards-item:hover .text,
[data-v2-velaire] .awards-item:hover .text-white,
[data-v2-velaire] .awards-item:hover .text * {
  color: var(--primary) !important;
}
[data-v2-velaire] .awards-item:hover .text-secondary,
[data-v2-velaire] .awards-item:hover .year {
  color: var(--secondary) !important;
}

/* A statistic that wraps is not a statistic. "&lt; 48" and its "hrs" suffix are two
   inline spans, and at 140px the box is too narrow to hold them: the number broke
   after the "<" and the page read "< hrs" over "48". Nothing here is long enough to
   need two lines, so none of them get to take one. */
[data-v2-velaire] .statistic-number {
  white-space: nowrap;
  align-items: baseline;
}
/* Aigocy's `.prefix` is sized at 232px against a 240px number, which is right for what
   the template puts there: one glyph, a `+` or a `%`. Ours is a unit word, and "hrs"
   at 232px is wider than the slide -- with `white-space: nowrap` above stopping it
   wrapping, it simply overflowed and painted across the next slide's numeral instead.
   (Without the nowrap it wrapped to "< hrs" over "48", which is how this started.)

   Sized as the unit it is rather than as a second numeral, and set on the baseline so
   it sits under the number's shoulder. A one-character prefix keeps the template's own
   proportion, since nothing here overrides that case. */
[data-v2-velaire] .section-statistic .swiper .statistic-number .prefix {
  font-size: 0.3em;
  line-height: 1;
  letter-spacing: 0;
}
/* And the figure itself comes down from 240px to 212px, because the slide is 526px
   wide and Velaire's longest figure does not fit it at the template's size: "< 48"
   alone measures 459px, so with any suffix at all it overflowed into the next slide's
   numeral. Aigocy's own figures are three or four glyphs ("230K", "98%"); ours is
   eight including the comparator and the unit.

   Scaled rather than truncated, and scaled on the parent so the 0.3em suffix follows
   it and the proportion the template drew is kept. Longest slide now measures 489px
   against 526 available. The three narrower breakpoints already step this down and
   have room, so they are left alone. */
@media (min-width: 1200px) {
  [data-v2-velaire] .section-statistic .swiper .statistic-number {
    font-size: 212px;
    line-height: 212px;
  }
}

/* ---- The secondary button's edge ----
   Aigocy's `.tf-btn-2` is a near-white pill on a near-white page: its edge comes
   from an inset shadow, not from the fill. The palette swap turned it into a
   near-black wine pill and kept the shadow, which is darker still -- so on the wine
   ground the button had no edge at all. "See pricing" beside the hero's gold button
   read as floating text until you hovered it and the label turned gold, which is
   how Qasim found it.

   An outline rather than a lighter fill, so the primary/secondary hierarchy stays:
   gold is the thing to click, this is the thing beside it. On a cream card, where
   the same class is a dark pill and already obvious, the ring is faint and does no
   harm. `npm run v2:audit` measures it now -- CIE76 dE against the page behind the
   control, because WCAG's 3:1 is a luminance test and the gold pill on the cream
   header is 1.4:1 by luminance while being unmistakably a button. */
[data-v2-velaire] .tf-btn-2 {
  border: 1px solid rgba(239, 227, 228, 0.42);
}

/* ---- The mobile menu's scrim ----
   The template dims the page behind the menu with 56% black. That reads as a clear
   separation over Aigocy's near-white page; over this one it is dark on dark, and
   the hero photograph and the gold button stayed visible through the menu at close
   to half strength. The menu text still clears AA against it -- the problem is that
   the panel does not read as a panel. Deeper scrim, same treatment. */
[data-v2-velaire] .offcanvas-menu.show {
  background: rgba(20, 7, 11, 0.88);
}

/* ---- Tailwind's layout utilities, inside the live components ----
   Tailwind emits its utilities in `@layer utilities` and the template's stylesheets
   are unlayered, and unlayered CSS beats layered CSS whatever the specificity says.
   So on this route `label { display: inline-block }` beat `.flex`, `* { padding: 0 }`
   beat `.p-6`, and the three embedded live components arrived with no padding, no
   flex rows and no grids: 125 elements measured different from the same component on
   the live route. None of it was visible to the contrast, overflow or copy checks --
   it is layout, and the pages still rendered.

   Everything below is scoped to `.v2-live`, so nothing outside an embedded component
   can change, and restricted to layout properties, so colour, hover and focus stay
   exactly as the components ship them. See scripts/v2/collisions.mjs for why the
   obvious fix -- moving the template into a lower layer -- was tried and reverted.
   Regenerate with `npm run v2:collisions`; do not hand-edit. */

/* The marker itself takes part in no layout. A real wrapper would break any live
   component that expects to be a grid or flex child of the section around it. */
[data-v2-velaire] .v2-live {
  display: contents;
}

/* == generated by scripts/v2/collisions.mjs -- do not hand-edit == */
[data-v2-velaire] .v2-live .absolute {
  position: absolute !important;
}
[data-v2-velaire] .v2-live .border {
  border-style: var(--tw-border-style) !important;
  border-width: 1px !important;
}
[data-v2-velaire] .v2-live .border-b {
  border-bottom-style: var(--tw-border-style) !important;
  border-bottom-width: 1px !important;
}
[data-v2-velaire] .v2-live .border-t {
  border-top-style: var(--tw-border-style) !important;
  border-top-width: 1px !important;
}
[data-v2-velaire] .v2-live .bottom-24 {
  bottom: calc(var(--spacing) * 24) !important;
}
[data-v2-velaire] .v2-live .bottom-5 {
  bottom: calc(var(--spacing) * 5) !important;
}
[data-v2-velaire] .v2-live .fixed {
  position: fixed !important;
}
[data-v2-velaire] .v2-live .flex {
  display: flex !important;
}
[data-v2-velaire] .v2-live .flex-1 {
  flex: 1 1 0% !important;
}
[data-v2-velaire] .v2-live .flex-col {
  flex-direction: column !important;
}
[data-v2-velaire] .v2-live .font-bold {
  --tw-font-weight: var(--font-weight-bold) !important;
  font-weight: var(--font-weight-bold) !important;
}
[data-v2-velaire] .v2-live .gap-2 {
  gap: calc(var(--spacing) * 2) !important;
}
[data-v2-velaire] .v2-live .gap-3 {
  gap: calc(var(--spacing) * 3) !important;
}
[data-v2-velaire] .v2-live .grid {
  display: grid !important;
}
[data-v2-velaire] .v2-live .h-14 {
  height: calc(var(--spacing) * 14) !important;
}
[data-v2-velaire] .v2-live .h-3\.5 {
  height: calc(var(--spacing) * 3.5) !important;
}
[data-v2-velaire] .v2-live .h-4 {
  height: calc(var(--spacing) * 4) !important;
}
[data-v2-velaire] .v2-live .h-6 {
  height: calc(var(--spacing) * 6) !important;
}
[data-v2-velaire] .v2-live .h-px {
  height: 1px !important;
}
[data-v2-velaire] .v2-live .items-center {
  align-items: center !important;
}
[data-v2-velaire] .v2-live .justify-between {
  justify-content: space-between !important;
}
[data-v2-velaire] .v2-live .justify-center {
  justify-content: center !important;
}
[data-v2-velaire] .v2-live .left-\[-9999px\] {
  left: -9999px !important;
}
[data-v2-velaire] .v2-live .max-h-\[min\(34rem\,calc\(100dvh-8rem\)\)\] {
  max-height: min(34rem, 100dvh - 8rem) !important;
}
[data-v2-velaire] .v2-live .max-w-\[92\%\] {
  max-width: 92% !important;
}
[data-v2-velaire] .v2-live .ml-auto {
  margin-left: auto !important;
}
[data-v2-velaire] .v2-live .opacity-0 {
  opacity: 0 !important;
}
[data-v2-velaire] .v2-live .overflow-hidden {
  overflow: hidden !important;
}
[data-v2-velaire] .v2-live .overflow-y-auto {
  overflow-y: auto !important;
}
[data-v2-velaire] .v2-live .p-1\.5 {
  padding: calc(var(--spacing) * 1.5) !important;
}
[data-v2-velaire] .v2-live .p-3 {
  padding: calc(var(--spacing) * 3) !important;
}
[data-v2-velaire] .v2-live .px-3 {
  padding-inline: calc(var(--spacing) * 3) !important;
}
[data-v2-velaire] .v2-live .px-3\.5 {
  padding-inline: calc(var(--spacing) * 3.5) !important;
}
[data-v2-velaire] .v2-live .px-4 {
  padding-inline: calc(var(--spacing) * 4) !important;
}
[data-v2-velaire] .v2-live .py-2 {
  padding-block: calc(var(--spacing) * 2) !important;
}
[data-v2-velaire] .v2-live .py-2\.5 {
  padding-block: calc(var(--spacing) * 2.5) !important;
}
[data-v2-velaire] .v2-live .py-3 {
  padding-block: calc(var(--spacing) * 3) !important;
}
[data-v2-velaire] .v2-live .py-4 {
  padding-block: calc(var(--spacing) * 4) !important;
}
[data-v2-velaire] .v2-live .right-5 {
  right: calc(var(--spacing) * 5) !important;
}
[data-v2-velaire] .v2-live .rounded-2xl {
  border-radius: var(--radius-2xl) !important;
}
[data-v2-velaire] .v2-live .rounded-full {
  border-radius: 3.40282e+38px !important;
}
[data-v2-velaire] .v2-live .rounded-lg {
  border-radius: var(--radius-lg) !important;
}
[data-v2-velaire] .v2-live .rounded-tl-sm {
  border-top-left-radius: var(--radius-sm) !important;
}
[data-v2-velaire] .v2-live .rounded-tr-sm {
  border-top-right-radius: var(--radius-sm) !important;
}
[data-v2-velaire] .v2-live .rounded-xl {
  border-radius: var(--radius-xl) !important;
}
[data-v2-velaire] .v2-live .text-left {
  text-align: left !important;
}
[data-v2-velaire] .v2-live .text-muted {
  color: var(--muted) !important;
}
[data-v2-velaire] .v2-live .text-sm {
  font-size: var(--text-sm) !important;
  line-height: var(--tw-leading,var(--text-sm--line-height)) !important;
}
[data-v2-velaire] .v2-live .text-xs {
  font-size: var(--text-xs) !important;
  line-height: var(--tw-leading,var(--text-xs--line-height)) !important;
}
[data-v2-velaire] .v2-live .w-\[min\(23rem\,calc\(100vw-2\.5rem\)\)\] {
  width: min(23rem, -2.5rem + 100vw) !important;
}
[data-v2-velaire] .v2-live .w-14 {
  width: calc(var(--spacing) * 14) !important;
}
[data-v2-velaire] .v2-live .w-3\.5 {
  width: calc(var(--spacing) * 3.5) !important;
}
[data-v2-velaire] .v2-live .w-4 {
  width: calc(var(--spacing) * 4) !important;
}
[data-v2-velaire] .v2-live .w-6 {
  width: calc(var(--spacing) * 6) !important;
}
[data-v2-velaire] .v2-live .w-full {
  width: 100% !important;
}
[data-v2-velaire] .v2-live .w-px {
  width: 1px !important;
}
/* == end generated == */

/* ---- The vendor checklist, in the template's shapes ----
   See src/app/v2-velaire-health-website/vendor-checklist/checklist.tsx. The rows are
   Aigocy's `.awards-item`; what is left is the two cells' internal layout, the tick
   control, and the sticky progress bar. */
[data-v2-velaire] .v2-checklist { display: grid; gap: 56px; }
[data-v2-velaire] .v2-checklist-bar {
  position: sticky;
  top: 96px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 24px;
  border-radius: 999px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  /* Opaque enough to read over a row scrolling under it; the blur does the rest. */
  background: rgba(46, 17, 25, 0.92);
  backdrop-filter: blur(10px);
}
[data-v2-velaire] .v2-checklist-count {
  font-size: 20px;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-checklist-reset {
  border: 0;
  background: none;
  padding: 0;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-cream-dim, #ddbdc4);
  text-decoration: underline;
  text-underline-offset: 4px;
}
[data-v2-velaire] .v2-checklist-reset:hover { color: var(--vh-cream, #efe3e4); }
/* `.awards-item` pads 0 39px and gives each cell `min-height: 98px`, which is right
   for a one-line award and wrong for a question with a rationale under it. */
[data-v2-velaire] .v2-checklist-item { padding: 8px 32px; align-items: stretch; }
[data-v2-velaire] .v2-checklist-item > div { min-height: 0; padding: 22px 0; }
[data-v2-velaire] .v2-checklist-ask { display: flex; gap: 16px; padding-right: 32px !important; }
[data-v2-velaire] .v2-checklist-answer {
  border-left: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  padding-left: 32px !important;
}
@media (max-width: 767px) {
  [data-v2-velaire] .v2-checklist-item { flex-wrap: wrap; padding: 8px 22px; }
  [data-v2-velaire] .v2-checklist-ask { padding-right: 0 !important; }
  [data-v2-velaire] .v2-checklist-answer {
    border-left: 0;
    border-top: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
    padding-left: 0 !important;
  }
}
[data-v2-velaire] .v2-checklist-tick {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin-top: 2px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 7px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.24));
  background: rgba(239, 227, 228, 0.05);
  color: transparent;
  transition: all 0.2s ease;
}
[data-v2-velaire] .v2-checklist-tick .icon { font-size: 13px; }
[data-v2-velaire] .v2-checklist-tick:hover { border-color: var(--vh-gold-400, #e0bf8a); }
[data-v2-velaire] .v2-checklist-item.is-asked .v2-checklist-tick {
  border-color: var(--vh-gold-500, #c9a06a);
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
  color: #2e1119;
}
/* A question already asked steps back rather than disappearing: the list is a record
   of the conversation, not a queue that empties. */
[data-v2-velaire] .v2-checklist-item.is-asked .v2-checklist-ask > div { opacity: 0.55; }
[data-v2-velaire] .v2-checklist-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
  margin-bottom: 8px;
}

/* ---- Article sources ----
   See src/app/v2-velaire-health-website/sources.tsx. */
[data-v2-velaire] .v2-sources {
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-sources-title {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
  margin-bottom: 14px;
}
[data-v2-velaire] .v2-sources-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
  font-size: 15px;
}
[data-v2-velaire] .v2-sources-list li { display: flex; gap: 10px; }
[data-v2-velaire] .v2-sources-n {
  flex: 0 0 auto;
  color: var(--vh-gold-400, #e0bf8a);
  font-variant-numeric: tabular-nums;
}
[data-v2-velaire] .v2-sources-list a {
  color: var(--vh-cream-dim, #ddbdc4);
  text-decoration: underline;
  text-underline-offset: 3px;
}
[data-v2-velaire] .v2-sources-list a:hover { color: var(--vh-cream, #efe3e4); }

/* ---- Article prose ----
   See src/app/v2-velaire-health-website/post-body.tsx. The live map was Tailwind
   against tokens the template does not define; this is the same structure in the
   template's type and the --vh-* ramp. */
[data-v2-velaire] .v2-prose {
  max-width: 68ch;
  font-size: 1.05rem;
  line-height: 1.75;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-prose p { margin: 0 0 22px; }
/* The opening paragraph carries the article in a scan, so it is set larger and in
   full cream rather than the body's dimmed tone. */
[data-v2-velaire] .v2-prose > *:first-child p:first-of-type,
[data-v2-velaire] .v2-prose p:first-of-type {
  font-size: 1.16rem;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-prose-h2 {
  margin: 52px 0 18px;
  scroll-margin-top: 112px;
  font-size: clamp(26px, 2.4vw, 32px);
  line-height: 1.22;
  font-weight: 600;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-prose > *:first-child .v2-prose-h2 { margin-top: 0; }
[data-v2-velaire] .v2-prose-anchor {
  display: flex;
  align-items: baseline;
  gap: 12px;
  color: inherit;
  text-decoration: none;
}
[data-v2-velaire] .v2-prose-anchor:hover { color: var(--vh-gold-400, #e0bf8a); }
/* Lesson numbers on the guide lane. Tabular so a nine-lesson guide's numerals line up
   down the left edge rather than drifting. */
[data-v2-velaire] .v2-prose-num {
  flex: 0 0 auto;
  font-size: 0.62em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-prose-h3 {
  margin: 34px 0 12px;
  font-size: 20px;
  line-height: 1.35;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-prose-link {
  color: var(--vh-gold-400, #e0bf8a);
  text-decoration: underline;
  text-underline-offset: 3px;
}
[data-v2-velaire] .v2-prose-link:hover { color: var(--vh-cream, #efe3e4); }
[data-v2-velaire] .v2-prose-ul,
[data-v2-velaire] .v2-prose-ol {
  margin: 0 0 22px;
  padding-left: 22px;
  display: grid;
  gap: 9px;
}
[data-v2-velaire] .v2-prose-ul { list-style: disc; }
[data-v2-velaire] .v2-prose-ol { list-style: decimal; }
[data-v2-velaire] .v2-prose-ul::marker,
[data-v2-velaire] .v2-prose-ul li::marker,
[data-v2-velaire] .v2-prose-ol li::marker { color: var(--vh-gold-500, #c9a06a); }
[data-v2-velaire] .v2-prose-quote {
  margin: 34px 0;
  padding: 4px 0 4px 24px;
  border-left: 3px solid var(--vh-gold-500, #c9a06a);
  font-size: 1.2rem;
  line-height: 1.45;
  font-style: italic;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-prose-code {
  padding: 2px 7px;
  border-radius: 6px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: rgba(239, 227, 228, 0.06);
  font-family: var(--vh-font-mono, monospace);
  font-size: 0.85em;
  color: var(--vh-cream, #efe3e4);
}

/* ---- Tables ----
   Qasim asked for these specifically on 10-Sep. The live map drew a hairline grid in
   `border-edge`, which in this palette is close enough to the ground that a table
   read as loose text in columns. Three things fix that and none of them is more rule:
   a header band that is actually a band, zebra rows so the eye tracks across a wide
   row, and a first column that behaves like a row label rather than another cell.

   The article column is 680px, so a wide table scrolls inside its own box. The page
   body never scrolls sideways. */
[data-v2-velaire] .v2-prose-tablewrap {
  margin: 0 0 28px;
  overflow-x: auto;
  border-radius: 18px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: rgba(239, 227, 228, 0.03);
}
[data-v2-velaire] .v2-prose-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14.5px;
  line-height: 1.5;
}
[data-v2-velaire] .v2-prose-table thead {
  background: rgba(239, 227, 228, 0.07);
}
[data-v2-velaire] .v2-prose-table th {
  padding: 14px 18px;
  text-align: left;
  white-space: nowrap;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
  border-bottom: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
}
[data-v2-velaire] .v2-prose-table td {
  padding: 14px 18px;
  vertical-align: top;
  color: var(--vh-cream-dim, #ddbdc4);
  border-bottom: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.08));
}
[data-v2-velaire] .v2-prose-table tbody tr:nth-child(even) td {
  background: rgba(239, 227, 228, 0.025);
}
[data-v2-velaire] .v2-prose-table tbody tr:last-child td { border-bottom: 0; }
/* The first cell of a row names what the row is about, so it reads as a label. */
[data-v2-velaire] .v2-prose-table td:first-child {
  color: var(--vh-cream, #efe3e4);
  font-weight: 600;
}
[data-v2-velaire] .v2-prose-hook { margin-bottom: 40px; }
[data-v2-velaire] .v2-prose-hook-line {
  margin: 0;
  font-size: 1.35rem;
  line-height: 1.4;
  color: var(--vh-cream, #efe3e4);
}

/* ---- The guide lesson rail ----
   See src/app/v2-velaire-health-website/lesson-rail.tsx. `.sidebar-item` supplies the
   panel; this is the numbered list inside it. */
[data-v2-velaire] .v2-lesson-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 11px;
  font-size: 14.5px;
}
[data-v2-velaire] .v2-lesson-list a {
  display: flex;
  align-items: baseline;
  gap: 10px;
  color: var(--vh-cream-dim, #ddbdc4);
  line-height: 1.35;
}
[data-v2-velaire] .v2-lesson-list a:hover { color: var(--vh-cream, #efe3e4); }
[data-v2-velaire] .v2-lesson-n {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--vh-gold-400, #e0bf8a);
  opacity: 0.75;
}
[data-v2-velaire] .v2-lesson-list a:hover .v2-lesson-n { opacity: 1; }
/* An aside hangs off the lesson above it, so it lines up with that lesson's label
   rather than with its number. */
[data-v2-velaire] .v2-lesson-list li.is-aside { padding-left: 31px; }

/* ---- The pillar-page callout ----
   See src/app/v2-velaire-health-website/related.tsx. */
[data-v2-velaire] .v2-callout {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  padding: 30px 34px;
  border-radius: 24px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: var(--neutral-900);
}
@media (max-width: 767px) {
  [data-v2-velaire] .v2-callout { flex-direction: column; align-items: flex-start; }
}
[data-v2-velaire] .v2-callout-eyebrow {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-callout-title {
  margin: 8px 0 8px;
  font-size: 22px;
  color: var(--vh-cream, #efe3e4);
}

/* ---- Card titles ----
   Aigocy clamps `.infor_name` to one line, which suits its demo titles ("AI Trends
   2025") and truncates every one of Velaire's: five of six on /blog were cut, and in
   the article column's narrow card "AI Receptionist vs Answering Service: What
   Actually Differs" rendered as "AI...". A title a reader cannot read is not a card.

   Two lines, which fits every title in the two lanes today at the index width and
   still keeps the cards to a common height. */
[data-v2-velaire] .article-blog .infor_name {
  -webkit-line-clamp: 2;
}
/* The stacked variant, for the article column's two-card row. `.style-horizontal` is
   the template's own side-by-side; without it the card is already a column, so this
   only has to give the picture a shape.

   The clamps go wider again here, because this card is 309px rather than the index's
   520: at that width the same titles need three lines and the excerpts five. Measured
   rather than guessed -- `scrollHeight / lineHeight` against `clientHeight`. */
[data-v2-velaire] .article-blog:not(.style-horizontal) .blog-image img {
  width: 100%;
  height: 190px;
  object-fit: cover;
}
[data-v2-velaire] .article-blog:not(.style-horizontal) .infor_name {
  -webkit-line-clamp: 3;
}
[data-v2-velaire] .article-blog:not(.style-horizontal) .infor_excerpt {
  -webkit-line-clamp: 5;
}
/* Two cards side by side should be the same height whatever their titles do. */
[data-v2-velaire] .article-blog:not(.style-horizontal) {
  height: 100%;
  display: flex;
  flex-direction: column;
}
[data-v2-velaire] .article-blog:not(.style-horizontal) .blog-content { flex: 1 1 auto; }

/* ---- The trust row's mark ----
   Aigocy's `.awards-item` opens with an `.image` cell holding a partner logo. Velaire
   has none, so the cell holds the pillar's icon instead -- the template's own icomoon
   face, at the size the features cards use it. `.awards-item > div` is `width: 100%`,
   so without a narrower basis the mark would claim a full quarter of the row. */
[data-v2-velaire] .v2-award-mark {
  flex: 0 0 auto;
  width: 64px !important;
  display: flex;
  align-items: center;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-award-mark .icon { font-size: 26px; }
@media (max-width: 767px) {
  [data-v2-velaire] .v2-award-mark { width: 100% !important; min-height: 0 !important; padding-bottom: 0 !important; }
}

/* ---- A slot that leads into the template block below it ----
   `beforeProcess` and `beforePricing` are a heading and a line introducing the block
   directly underneath. With `flat-spacing`'s own bottom padding they sat in a box of
   their own with a hundred and forty pixels of nothing between them and the thing
   they introduce, which is what made them read as two sections rather than one.

   Named rather than called `pb-0`: Tailwind also defines that, and this route loads
   both stylesheets. */
/* ---- section rhythm --------------------------------------------------------
   Qasim reported empty space on the homepage repeatedly, and measuring all 26
   sections settled what it was. Every one of them fills 100% of its container
   horizontally, so nothing is stranded sideways, and every one carries the
   template's `flat-spacing`: 120px top and 120px bottom. That is 240px of white
   between every pair of sections, twenty-five times down the page.

   It is the template's designed rhythm rather than a defect, which is why no
   single section looked wrong when checked on its own -- and why it kept coming
   back after each individual fix.

   88px here is a judgement, not a measurement: it keeps the sections clearly
   separated while removing roughly 1,600px from the page. `section-tools` is the
   one outlier at 192, brought into line with the rest.

   Scoped to the v2 tree. `flat-spacing` is the vendor class and the live routes
   use it too; this must not reach them before the redesign does.
---------------------------------------------------------------------------- */
@media (min-width: 1440px) {
  [data-v2-velaire] .flat-spacing { padding-top: 88px; padding-bottom: 88px; }
  [data-v2-velaire] .section-tools.flat-spacing { padding-top: 88px; padding-bottom: 88px; }
}

[data-v2-velaire] .flat-spacing.v2-lead-in { padding-bottom: 0; }
/* The heading's own 64px is enough once the section's padding is gone. */
[data-v2-velaire] .v2-lead-in .heading-section { margin-bottom: 32px; }

/* ---- Slot content that landed on the cream panel ----
   The homepage's live sections used to sit in one block before the footer, all of it
   on the wine ground. Interleaving them (10-Sep) put four of the six inside
   `.box-white`, the template's cream panel, and two kinds of ink came with them that
   are only right on wine.

   `.box-white .text-secondary` already exists further up this file and would have
   handled the phone's caption, but the `.v2-phone-row .text-secondary` rule added
   with the phone view has equal specificity and comes later, so it won. The panel has
   to win here: it is the thing that decides which way round a surface is.

   `.year` is the row list's meta column, which is `text-neutral-400` -- cream-dim,
   and 1.56:1 on cream. */
[data-v2-velaire] .box-white .v2-phone-row .text-secondary,
[data-v2-velaire] .box-white .v2-phone-caption {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
[data-v2-velaire] .box-white .awards-item .year,
[data-v2-velaire] .box-white .awards-item .year .text-neutral-400 {
  color: var(--vh-ink-muted, #6d5a62) !important;
}
/* And the row's own hairline, which is set for the wine ground. */
[data-v2-velaire] .box-white .awards-item {
  border-color: rgba(32, 26, 29, 0.14);
}

/* ---- The Growth Leak calculator ----
   See src/app/v2-velaire-health-website/roi-calculator/calculator.tsx. Inputs left,
   readout right and sticky, which is the live component's own shape and the reason it
   reads as an instrument rather than a form. */
[data-v2-velaire] .v2-calc {
  display: grid;
  gap: 24px;
  grid-template-columns: 1.05fr 0.95fr;
  align-items: start;
}
@media (max-width: 991px) {
  [data-v2-velaire] .v2-calc { grid-template-columns: 1fr; }
}
[data-v2-velaire] .v2-calc-panel {
  padding: 28px;
  border-radius: 24px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: var(--neutral-900);
}
[data-v2-velaire] .v2-calc-inputs { display: grid; gap: 20px; }
[data-v2-velaire] .v2-calc-h {
  margin: 0;
  font-size: 19px;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-calc-sub { margin: 8px 0 0; font-size: 14.5px; line-height: 1.6; }
[data-v2-velaire] .v2-calc-note { margin: 12px 0 0; font-size: 12.5px; line-height: 1.6; }
[data-v2-velaire] .v2-calc-assumption {
  padding: 18px;
  border-radius: 16px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: rgba(0, 0, 0, 0.16);
}
[data-v2-velaire] .v2-calc-readout {
  display: grid;
  gap: 16px;
  position: sticky;
  top: 112px;
}
@media (max-width: 991px) {
  [data-v2-velaire] .v2-calc-readout { position: static; }
}
/* The figure's own surface: the deepest on the page, so the number reads as a
   measurement rather than as another card. */
[data-v2-velaire] .v2-calc-figure {
  padding: 28px;
  border-radius: 24px;
  border: 1px solid rgba(239, 227, 228, 0.12);
  background: #200b12;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-calc-label {
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-calc-money {
  margin: 10px 0 0;
  font-size: clamp(2.2rem, 5vw, 3rem);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-calc-range {
  margin: 10px 0 0;
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-calc-breakeven { margin-top: 20px; font-size: 12.5px; }
[data-v2-velaire] .v2-calc-stats {
  margin-top: 24px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}
[data-v2-velaire] .v2-calc-stat {
  padding: 10px 12px;
  border-radius: 14px;
  background: rgba(239, 227, 228, 0.06);
}
[data-v2-velaire] .v2-calc-stat-label {
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-calc-stat-value {
  margin-top: 3px;
  font-size: 18px;
  font-variant-numeric: tabular-nums;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-calc-strong { color: var(--vh-gold-400, #e0bf8a); }
[data-v2-velaire] .v2-calc-fields {
  margin-top: 14px;
  display: grid;
  gap: 12px;
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 575px) {
  [data-v2-velaire] .v2-calc-fields { grid-template-columns: 1fr; }
}
[data-v2-velaire] .v2-calc-fields input {
  width: 100%;
  padding: 11px 14px;
  border-radius: 12px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: rgba(0, 0, 0, 0.18);
  color: var(--vh-cream, #efe3e4);
  font-size: 14px;
}
[data-v2-velaire] .v2-calc-fields input::placeholder { color: rgba(221, 189, 196, 0.6); }
[data-v2-velaire] .v2-calc-cta { margin-top: 16px; gap: 8px; }
[data-v2-velaire] .v2-calc-cta:disabled { opacity: 0.6; }
[data-v2-velaire] .v2-calc-error { margin: 8px 0 0; font-size: 12.5px; color: #ff9b8a; }
/* Off-screen rather than hidden: a display:none field is not submitted, and a bot
   that reads the DOM would see it is not meant to be filled. */
[data-v2-velaire] .v2-honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

/* The sliders. `accent-color` styles the native control in every browser this site
   supports, which is one declaration against forty for a hand-built track. */
[data-v2-velaire] .v2-slider { display: grid; gap: 7px; }
[data-v2-velaire] .v2-slider label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font-size: 14px;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-slider-value {
  font-variant-numeric: tabular-nums;
  color: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-slider input[type="range"] {
  width: 100%;
  accent-color: var(--vh-gold-500, #c9a06a);
}
[data-v2-velaire] .v2-slider-hint {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--vh-cream-dim, #ddbdc4);
}

/* ---- The demo request form ----
   See src/app/v2-velaire-health-website/demo/demo-form.tsx. The fieldset/label/input
   rhythm is the template's own contact form; these rules cover what that form does not
   have -- a two-up row, a <select>, the consent line and the error. */
[data-v2-velaire] .v2-demo-form {
  padding: 32px;
  border-radius: 24px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: var(--neutral-900);
}
[data-v2-velaire] .v2-demo-pair {
  display: grid;
  gap: 0 20px;
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 575px) {
  [data-v2-velaire] .v2-demo-pair { grid-template-columns: 1fr; }
}
[data-v2-velaire] .v2-demo-form fieldset { display: block; }
[data-v2-velaire] .v2-demo-form label { display: block; color: var(--vh-cream, #efe3e4); }
[data-v2-velaire] .v2-demo-form input,
[data-v2-velaire] .v2-demo-form select {
  width: 100%;
  padding: 12px 15px;
  border-radius: 12px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: rgba(0, 0, 0, 0.18);
  color: var(--vh-cream, #efe3e4);
  font-size: 14.5px;
}
[data-v2-velaire] .v2-demo-form input::placeholder { color: rgba(221, 189, 196, 0.6); }
/* The native menu is painted by the OS, so its options need a colour scheme that
   matches rather than a background rule -- a dark <option> list on a light panel is a
   platform decision, not ours. */
[data-v2-velaire] .v2-demo-form select { color-scheme: dark; }
[data-v2-velaire] .v2-demo-consent {
  margin: 4px 0 16px;
  font-size: 12.5px;
  line-height: 1.6;
}
[data-v2-velaire] .v2-demo-error { margin: 0 0 12px; font-size: 12.5px; color: #ff9b8a; }
[data-v2-velaire] .v2-demo-form .tf-btn:disabled { opacity: 0.6; }
[data-v2-velaire] .v2-demo-done { text-align: center; }

/* ---- The Cal.com card ----
   `.cal-shell` is the live app's wrapper around the booking iframe -- a plain class in
   editorial.css, not Tailwind, which is why it never needed the collision wrapper. The
   iframe's contents are Cal's and not ours to restyle; the card around it is, and it
   should match the panel beside it rather than the live app's surface. */
[data-v2-velaire] .cal-shell {
  border-radius: 24px;
  border-color: var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: var(--neutral-900);
}

/* ---- The call-journey preview ----
   See src/app/v2-velaire-health-website/product/workflow.tsx. The handset itself is
   the `.v2-phone` block earlier in this file; this is the frame around it. */
[data-v2-velaire] .v2-workflow {
  padding: 26px;
  border-radius: 28px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: var(--neutral-900);
}
[data-v2-velaire] .v2-workflow-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--vh-hairline, rgba(239, 227, 228, 0.1));
  font-size: 12.5px;
}
[data-v2-velaire] .v2-workflow-live {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-workflow-dot {
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--vh-gold-400, #e0bf8a);
}
[data-v2-velaire] .v2-workflow-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 22px;
}
[data-v2-velaire] .v2-workflow-tabs button {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px;
  border-radius: 999px;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: rgba(239, 227, 228, 0.05);
  font-size: 13.5px;
  font-weight: 600;
  transition: all 0.2s ease;
}
[data-v2-velaire] .v2-workflow-tabs button[aria-selected="true"] {
  border-color: transparent;
  background: linear-gradient(180deg, var(--vh-gold-500, #c9a06a) 0%, var(--vh-gold-400, #e0bf8a) 100%);
  color: #2e1119;
}
[data-v2-velaire] .v2-workflow-n { font-variant-numeric: tabular-nums; opacity: 0.7; }
[data-v2-velaire] .v2-workflow-body {
  display: flex;
  gap: clamp(24px, 3.5vw, 44px);
  align-items: center;
  flex-wrap: wrap;
}
[data-v2-velaire] .v2-workflow-body .v2-phone { margin-top: 0; }
[data-v2-velaire] .v2-workflow-side { flex: 1 1 280px; }
[data-v2-velaire] .v2-workflow-title {
  margin: 0 0 12px;
  font-size: clamp(20px, 2vw, 26px);
  line-height: 1.28;
  /* The scene titles carry their own line break. */
  white-space: pre-line;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire] .v2-workflow-detail { margin: 0 0 22px; font-size: 14.5px; line-height: 1.65; }
[data-v2-velaire] .v2-workflow-foot {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
}
[data-v2-velaire] .v2-workflow-status {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--vh-gold-400, #e0bf8a);
}

/* The row list's first cell is an `<h4>` now, because it names what the row is about
   and the live routes mark the same thing as a heading. Aigocy sizes the cells with
   `.awards-item > div { width: 100% }`, which an `h4` does not match -- so the title
   column collapsed to its content width and every row's text wrapped into a ragged
   two-word stack. Same declarations, extended to the heading. */
[data-v2-velaire] .awards-item > h4 {
  width: 100%;
  padding: 8px 0;
  min-height: 98px;
  align-content: center;
}
/* The checklist's rows override that min-height for a question with a rationale under
   it; its cells are all divs, so this keeps them in step. */
[data-v2-velaire] .v2-checklist-item > h4 { min-height: 0; padding: 22px 0; }

/* The hero heading is an <h1> now (see the h1 pass in scripts/v2/html-to-jsx.mjs), so
   the elements nested inside it are spans -- an <h1> takes phrasing content and a div
   inside one is invalid. Every rule that positions them is written against the class
   and does not care, but a span is inline by default, so the ones that are not already
   `d-flex` need their box back. */
[data-v2-velaire] h1.title .title-icon,
[data-v2-velaire] h1.title .title-icon .box,
[data-v2-velaire] h1.title .title-icon-wrap {
  display: block;
}
[data-v2-velaire] h1.title .title-icon .box { display: flex; }

/* ============ The article reads on paper ============
   Ported from cartgaze.com's `.blog-white` (globals.css, "blog: white paper"), on
   Qasim's instruction 14-Sep. The reasoning there applies here unchanged: a long read
   wants paper, and the rest of the site keeps its ground.

   The 13-Sep attempt at this was reverted because flipping the ground left the figure
   cards, callouts and checklists INSIDE the article dark -- 20 AA failures, ratios to
   1.09:1. What makes it work now is that those components turn out to be fully
   token-driven: `article-figures.tsx` paints with `bg-surface`, `border-edge`,
   `text-muted` and `text-foreground`, and the v2 skin only supplies the values. So this
   is a token scope, not a rewrite, and it cannot miss a component the way a per-class
   sweep would. article-figures.tsx is SHARED with the live site, which is the other
   reason this is CSS here rather than an edit there.

   Scoped to .section-blog, which is the article row and the related band below it. The
   hero keeps its own dark image treatment -- same split cartgaze uses. */
[data-v2-velaire] .blog-white {
  background: #f8f2f1;

  /* the tokens article-figures.tsx and the post body actually read */
  --background: #f8f2f1;
  --surface:    #ffffff;
  --foreground: #201a1d;
  --muted:      #6d5a62;
  /* The wine --edge is cream at 14%; on paper every hairline would vanish. Warm
     neutral at 11%, matching the note cartgaze leaves about its own slate --edge. */
  --edge:       rgba(32, 26, 29, 0.11);
  /* Gold on cream, not gold on wine: --vh-gold-400 is 3.0:1 on paper. The ramp already
     carries the cream-safe step and documents it as 5.0:1. */
  --accent-safe: #7b592d;

  /* the template's own text tokens */
  --primary:   #201a1d;
  --secondary: #6d5a62;
  /* The sticky TOC rail paints `var(--body)` so it can occlude the widgets passing
     behind it (see the pinned-rail block above). --body is the WINE page ground, so
     without this the rail stayed wine while the article around it went to paper, and
     the gold inside it -- correctly remapped to the cream-safe step below -- landed on
     wine at 2.17:1. The rail's ground has to follow the scope it sits in. */
  --body:      #f8f2f1;

  /* --vh-cream is used 81 times across this file and is NEVER declared, so every use
     resolves to its literal #efe3e4 fallback and no scope can reach it. Declaring it
     here is what lets cream body text become ink inside the article. Same latent bug
     this file already documents for --vh-gold-400. */
  --vh-cream:     #201a1d;
  --vh-cream-dim: #6d5a62;
  --vh-rule:      rgba(32, 26, 29, 0.11);
  --vh-rule-card: rgba(32, 26, 29, 0.08);

  /* The gold ramp is two-sided and the wine side is the default. --vh-gold-300/400 are
     the tone for text on the dark ground and measure 1.58:1 on paper; --vh-gold-700 is
     the step the ramp already documents as "gold text on the cream pill, 5.0:1". Every
     kicker, lesson number and source numeral reads through these, so remapping the two
     tokens fixes all of them at once. */
  --vh-gold-300: #7b592d;
  --vh-gold-400: #7b592d;

  /* raised surfaces inside the article (callout, inbox, workflow, calc panel) */
  --neutral-900: #ffffff;
  --neutral-950: #f8f2f1;
}
/* A numeral that was white because it sat in a filled badge on wine. On paper the fill
   is gone and white on #f8f2f1 is 1.11:1. Scoped to the numeral rather than to
   `.text-white`, which is still correct wherever a dark fill survives. */
[data-v2-velaire] .blog-white .number.text-white {
  color: #201a1d !important;
}
/* .v2-callout hardcodes its ground rather than reading the ramp. */
[data-v2-velaire] .blog-white .v2-callout {
  background: #ffffff;
  border: 1px solid rgba(32, 26, 29, 0.11);
}
/* The template's cream-text utilities. These carry literal cream rather than reading a
   token, so the scope above cannot reach them -- the sidebar's post dates, category
   links and counts stayed #efe3e4 on paper at 1.13:1. `.box-white` (the skin's existing
   cream panel) solves the same problem the same way at line 1268; this mirrors that
   treatment for the article row rather than inventing a second vocabulary. */
[data-v2-velaire] .blog-white .text-white,
[data-v2-velaire] .blog-white .text-white-64,
[data-v2-velaire] .blog-white .link1,
[data-v2-velaire] .blog-white .time,
[data-v2-velaire] .blog-white .title,
[data-v2-velaire] .blog-white .sidebar-title,
[data-v2-velaire] .blog-white .num {
  color: #201a1d !important;
}
[data-v2-velaire] .blog-white .text-secondary,
[data-v2-velaire] .blog-white .text-muted,
[data-v2-velaire] .blog-white .text-white-64 {
  color: #6d5a62 !important;
}

/* ---- Article typography: the cartgaze.com setting ----
   Ported 14-Sep on Qasim's instruction, from the "Drop cap" and "Justified with
   hyphenation" blocks in that site's globals.css. */

/* Justified with hyphenation, which is the pair that holds. The note in the source
   file is worth keeping: justify ALONE opens rivers at this measure, and hyphens:auto
   supplies the breaks that prevent them. `hyphens` needs <html lang="en">, which is
   present, or the property silently does nothing.

   Scoped to the article's own paragraphs and list items. Callouts, captions, tables and
   the sidebar keep ragged-right on purpose -- they are short measures, and justifying a
   short measure is exactly how you get rivers. */
/* Justify ONLY where the measure is wide enough to carry it. Measured 14-Sep across
   eight widths: at 390px the text is 366px, ~45 characters, and the worst lines stretch
   their word spaces to 1.69x the median -- visible rivers. 480px is worse still, 1.88x.
   From ~700px the text reaches its 42rem clamp, ~74 characters, and settles at 1.38x.

   A media query would get this wrong, and the number that proves it is 992px: the text
   drops to 555px and 65 characters THERE, because that is exactly where the sidebar
   returns and squeezes the column. Width of the viewport is not the thing that matters;
   width of the COLUMN is. So the condition is a container query on the column itself,
   and it is correct at every width including the ones nobody thought to test.

   Degrades the right way: an engine without container queries never matches, so the text
   stays ragged-right, which is the safe setting rather than the broken one.

   cartgaze.com justifies at all widths and has the same rivers on a phone. This is a
   deliberate divergence, and one rule to revert if parity matters more. */
[data-v2-velaire] .blog-white .blog-single-wrap {
  container-type: inline-size;
  container-name: article;
}
@container article (min-width: 672px) {
  [data-v2-velaire] .blog-white .v2-prose > p,
  [data-v2-velaire] .blog-white .v2-prose .v2-prose-ul > li,
  [data-v2-velaire] .blog-white .v2-prose .v2-prose-ol > li {
    text-align: justify;
    hyphens: auto;
    -webkit-hyphens: auto;
    /* keeps a last line from ending on a lone word, which justification makes louder */
    text-wrap: pretty;
  }
}
/* A justified last line has nothing to stretch to, so it is ragged by definition -- but
   a lone short line that HAS been stretched reads as a mistake. */
[data-v2-velaire] .blog-white .v2-prose > p:last-child {
  text-align: left;
}

/* The drop cap, on the opening paragraph only. `initial-letter` does the sinking
   properly: it aligns the cap-height to the first line and the baseline to the third,
   which float-plus-font-size never quite manages. The float branch is the fallback for
   engines without it. */
[data-v2-velaire] .blog-white .v2-prose > *:first-child p:first-of-type::first-letter,
[data-v2-velaire] .blog-white .v2-prose > p:first-of-type::first-letter {
  font-family: var(--vh-font-display, Urbanist, sans-serif);
  font-weight: 700;
  color: #4a1f2d;
  -webkit-initial-letter: 3;
  initial-letter: 3;
  margin-right: 0.6rem;
}
@supports not (initial-letter: 3) {
  [data-v2-velaire] .blog-white .v2-prose > *:first-child p:first-of-type::first-letter,
  [data-v2-velaire] .blog-white .v2-prose > p:first-of-type::first-letter {
    float: left;
    font-size: 3.45em;
    line-height: 0.85;
    padding-top: 0.06em;
    margin-right: 0.5rem;
  }
}
/* The opening paragraph carries the cap and drops the margin that would fight the rule
   above it. It does NOT keep its own larger size: cartgaze.com set it a step larger
   once and removed it, because with a standfirst already in the hero the first
   paragraph read as a different setting from the article beneath it. The cap is the
   signal now, so the size bump is redundant and was doing that same damage here. */
[data-v2-velaire] .blog-white .v2-prose > *:first-child p:first-of-type,
[data-v2-velaire] .blog-white .v2-prose p:first-of-type {
  font-size: 1.05rem;
  margin-top: 0;
}

/* Body copy is INK, not the muted tone.
   Measured 14-Sep on the rendered article: the opening paragraph was #201a1d and every
   paragraph after it #6d5a62, because .v2-prose sets its colour to --vh-cream-dim and
   only the first paragraph overrides it. On wine that reads as a deliberate dimming; on
   paper it reads as the article going grey after its first breath, which is what Qasim
   was seeing. cartgaze.com sets the whole body at full foreground and keeps the muted
   tone for captions, labels and meta, which is what these two rules restore. */
[data-v2-velaire] .blog-white .v2-prose {
  color: var(--vh-ink, #201a1d);
}
/* The muted tone keeps the jobs it is actually for. */
[data-v2-velaire] .blog-white .v2-prose figcaption,
[data-v2-velaire] .blog-white .v2-prose .v2-prose-caption,
[data-v2-velaire] .blog-white .v2-prose small {
  color: var(--vh-ink-muted, #6d5a62);
}

/* The count at the foot of the rail, mirroring .post-toc-count. */
[data-v2-velaire] .v2-toc-count {
  margin: 14px 0 0;
  padding-left: 14px;
  font-family: var(--vh-font-mono, Roboto, sans-serif);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  color: var(--vh-cream-dim, #ddbdc4);
}
[data-v2-velaire] .v2-toc-count span {
  color: var(--vh-cream, #efe3e4);
  font-weight: 600;
}

/* ---- The share row that closes an article ----
   cartgaze.com's `entry-footer`. Its version carries the tags too; here those already
   sit in the sidebar as "Topics in this post", so repeating them below the article
   would be the same list twice on one screen. Label and marks only. */
[data-v2-velaire] .v2-entry-footer {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 44px;
  padding-top: 22px;
  border-top: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}
[data-v2-velaire] .v2-entry-footer-label {
  margin: 0;
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-entry-footer .tf-social-1 {
  display: flex;
  gap: 10px;
  margin: 0;
}
[data-v2-velaire] .v2-entry-footer .v2-brand-svg {
  height: 16px;
  width: 16px;
}

/* ---- The author block ---- */
[data-v2-velaire] .v2-author {
  margin-top: 34px;
  padding: 26px 28px;
  border: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
  border-radius: 18px;
  background: var(--surface, #ffffff);
}
[data-v2-velaire] .v2-author-eyebrow {
  margin: 0 0 16px;
  font-family: var(--vh-font-mono, Roboto, sans-serif);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-author-row { display: flex; align-items: flex-start; gap: 16px; }
/* The monogram stands in until a real headshot exists. Wine fill, cream letters --
   the one place on a paper article where the brand colour carries text, so it is
   checked: #efe3e4 on #4a1f2d is 9.4:1. */
[data-v2-velaire] .v2-author-monogram {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #4a1f2d;
  color: var(--vh-cream, #efe3e4);
  font-family: var(--vh-font-display, Urbanist, sans-serif);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: 0.02em;
}
[data-v2-velaire] .v2-author-avatar {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}
[data-v2-velaire] .v2-author-copy { min-width: 0; }
[data-v2-velaire] .v2-author-name {
  margin: 0;
  font-family: var(--vh-font-display, Urbanist, sans-serif);
  font-size: 1rem;
  font-weight: 700;
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire] .v2-author-role {
  margin: 2px 0 0;
  font-size: 0.85rem;
  color: var(--vh-ink-muted, #6d5a62);
}
[data-v2-velaire] .v2-author-bio {
  margin: 12px 0 0;
  font-size: 0.93rem;
  line-height: 1.65;
  color: var(--vh-ink, #201a1d);
  /* a short measure, so it stays ragged rather than justified */
  text-align: left;
}

/* ---- Measure: text clamped, media not ----
   cartgaze.com's rule, which is NOT what its comment says. The comment claims a ~75ch
   measure; the rule is `max-width: 42rem` on the TEXT CHILDREN only --
   `.post-prose > :is(p, h2, h3, h4, ul, ol, blockquote, hr)` -- with nothing on the
   container. Measured 14-Sep, that works out at ~672px against v2's 680px, so the
   reading measure here already matched and widening it would only push the line length
   past comfortable.

   What did NOT match: .v2-prose clamped the whole CONTAINER, so figures, tables and
   callouts were squeezed to text width too. cartgaze's own note is the principle worth
   taking -- "media wider than text, never the reverse" -- so the container is released
   to the full column (746px at 1440) and only the text keeps the measure.

   The clamp is 42rem, not the 68ch the container used, and that is not a style
   preference. `ch` resolves against the ELEMENT'S OWN font size, so 68ch on a 2rem h2
   is 1308px -- measured, after a first attempt at this left every heading running the
   full width while the paragraphs clamped correctly. An absolute unit is the only way a
   heading and a paragraph land on the same measure, which is presumably why cartgaze
   uses one. 42rem is 672px against the 680px these paragraphs had; an 8px reflow. */
[data-v2-velaire] .blog-white .v2-prose {
  max-width: none;
}
[data-v2-velaire] .blog-white .v2-prose > :is(p, h2, h3, h4, ul, ol, blockquote, hr) {
  max-width: 42rem;
}

/* ---- The pinned rail is a GROUP: contents + booking card ----
   Qasim, 14-Sep: the card was scrolling away and vanishing behind the pinned contents,
   which is what the opaque rail above does to anything travelling past it. The fix is
   not to pin the card separately -- two independently pinned boxes at the same offset
   would sit on top of each other -- but to pin the wrapper that holds both, so they
   move as one object and the card stays on screen for the whole article.

   The item-level rule higher up is what used to do the pinning, and it has to be turned
   OFF inside the wrapper: a sticky element nested in a sticky element resolves against
   the inner containing block and offsets the contents against its own group. The
   occlusion (ground + bleed) moves up to the wrapper with it, so the whole group is
   what content passes behind. */
[data-v2-velaire] .blog-toc-sticky .sidebar-item:has(.v2-toc) {
  position: static;
  background: none;
  padding-block: 0;
  margin-block: 0;
}
@media (min-width: 992px) {
  [data-v2-velaire] .blog-toc-sticky {
    background: var(--body, #4a1f2d);
    padding-block: 10px 18px;
    margin-block: -10px -18px;
    /* The group is taller than one item, so on a short viewport it would be cut off at
       the fold with its button below the cut -- the one part that has to stay reachable.
       Scroll inside the rail rather than losing the end of it. */
    max-height: calc(100vh - 150px);
    overflow-y: auto;
    scrollbar-width: thin;
  }
}
@media (max-width: 991px) {
  [data-v2-velaire] .blog-toc-sticky {
    position: static;
    max-height: none;
    overflow: visible;
    background: none;
    padding-block: 0;
    margin-block: 0;
  }
}

/* The face on the booking card, as cartgaze.com's sidebar CTA carries. */
[data-v2-velaire] .v2-book-call-avatar {
  display: block;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  object-fit: cover;
  margin: 0 0 14px;
  border: 1px solid var(--vh-rule-card, rgba(32, 26, 29, 0.08));
}

/* ============================================================================
   LIGHT THEME — the whole site, not one surface
   ============================================================================
   The article proved the method: a surface goes light when its GROUND and every
   token its components read move together. `.blog-white` does that for one section
   and measures zero AA failures. This is the same token set lifted to the root, so
   it applies to every surface at once.

   It is declared but NOT switched on: nothing sets data-theme on the v2 root yet.
   The toggle goes in only when `npm run v2:contrast -- --light` is green, because a
   button that flips the site into a failing state is worse than no button. The
   baseline when this was written was 688 of 4,081 elements below AA with the old
   per-section tokens; this block plus the component work below is what closes it.

   Why root and not a class per section: a toggle has to reach everything, and 17
   hand-placed wrappers is 17 chances to miss one. The article keeps its own
   `.blog-white` because it is light in BOTH themes -- a printed page does not go
   dark when the chrome does. */
[data-v2-velaire][data-theme="light"] {
  --background: #f8f2f1;
  --surface:    #ffffff;
  --foreground: #201a1d;
  --muted:      #6d5a62;
  --edge:       rgba(32, 26, 29, 0.11);
  --accent-safe: #7b592d;

  --primary:   #201a1d;
  --secondary: #6d5a62;
  --body:      #f8f2f1;

  --vh-cream:     #201a1d;
  --vh-cream-dim: #6d5a62;
  --vh-rule:      rgba(32, 26, 29, 0.11);
  --vh-rule-card: rgba(32, 26, 29, 0.08);
  --vh-gold-300: #7b592d;
  --vh-gold-400: #7b592d;

  /* The DARK end of the ramp inverts; the light end does NOT. That asymmetry is not a
     style choice, it is what the components force. --neutral-100 is documented as
     "text / cream fill" and is doing both jobs: .process-card and .accordion-asked-item
     take it as a card BACKGROUND, and their titles hardcode ink on top. Invert it and
     the card goes wine while its text stays ink -- 1.24:1, measured. The light steps
     already read as light surfaces in either theme, so they stay put.

     They have to be RE-DECLARED, not omitted. The per-section [data-theme="light"]
     block near the top of this file is unscoped, so putting data-theme on the ROOT makes
     it apply sitewide as well -- and it sets --neutral-100 to wine, for the section case
     where that token is carrying text rather than a fill. Dropping the line here just
     handed the value back to it. */
  --neutral-950: #f8f2f1;
  --neutral-900: #ffffff;
  --neutral-800: #f8f2f1;
  --neutral-700: #efe3e4;
  --neutral-100: #efe3e4;
  --neutral-50:  #f8f2f1;
}
/* `body` is OUTSIDE the [data-v2-velaire] wrapper, so the palette above cannot reach it
   -- custom properties inherit down, not up. Its background was handled here from the
   start; its COLOR was not, and that is the bug that made the service and solution detail
   pages render in cream on paper. Most pages hide it because their sections set a colour
   of their own; those two templates let text inherit straight from body. */
[data-v2-velaire][data-theme="light"] body,
body:has([data-v2-velaire][data-theme="light"]) {
  background: #f8f2f1;
  color: #201a1d;
}

/* The template's own text utilities under the light theme. These carry literal cream or
   white rather than reading a token, so the palette above cannot reach them -- exactly
   the problem `.blog-white` hit, solved the same way at the same specificity. `.box-white`
   has done this since the skin was written; this is the sitewide version of it. */
[data-v2-velaire][data-theme="light"] .text-white,
[data-v2-velaire][data-theme="light"] .link1,
[data-v2-velaire][data-theme="light"] .time,
[data-v2-velaire][data-theme="light"] .num,
[data-v2-velaire][data-theme="light"] .sidebar-title {
  color: #201a1d !important;
}
[data-v2-velaire][data-theme="light"] .text-white-64,
[data-v2-velaire][data-theme="light"] .text-secondary,
[data-v2-velaire][data-theme="light"] .text-muted {
  color: #6d5a62 !important;
}

/* ---- Dark islands inside the light theme ----
   The mirror of `.blog-white`, which stays light in the dark theme because a printed
   page does not go dark when the chrome does. These go the other way: a phone handset,
   an app console, a chart panel and the off-canvas overlay are OBJECTS with their own
   ground, not surfaces of the page. They stay dark in both themes for the same reason a
   photograph does -- inverting them would not be a light theme, it would be a different
   illustration.

   They were failing because the ROOT tokens flipped underneath them: the ground stayed
   dark by its own rule while the text inside went to ink. Re-declaring the wine values
   inside each island is what keeps the pair together.

   `.v2-console.is-light` is EXCLUDED, and the exclusion is the whole point of the class:
   it is the console drawn as a pale card rather than a dark one, and 11 of the homepage
   showcase's 13 screens use it. Treating it as a dark island painted cream text on its
   own near-white gradient -- 1.03:1, invisible -- which is the same defect this block
   exists to fix, arrived at from the other direction. An island is dark because of what
   it IS, so the test has to be the object's own ground, not its tag. */
[data-v2-velaire][data-theme="light"] .v2-phone,
[data-v2-velaire][data-theme="light"] .v2-console:not(.is-light),
[data-v2-velaire][data-theme="light"] .v2-capture,
[data-v2-velaire][data-theme="light"] .v2-calc-figure,
[data-v2-velaire][data-theme="light"] .v2-desk-mark,
[data-v2-velaire][data-theme="light"] .v2-checklist-bar,
[data-v2-velaire][data-theme="light"] .pagination-item.active {
  --primary:      #efe3e4;
  --secondary:    #ddbdc4;
  --foreground:   #efe3e4;
  --muted:        #ddbdc4;
  --vh-cream:     #efe3e4;
  --vh-cream-dim: #ddbdc4;
  --vh-gold-300:  #e0bf8a;
  --vh-gold-400:  #e0bf8a;
  --vh-rule:      rgba(239, 227, 228, 0.14);
  --edge:         rgba(239, 227, 228, 0.14);
  color: var(--vh-cream, #efe3e4);
}

/* Components that carry cream text from the template's own rules rather than from a
   token this theme can reach. Scoped to the light theme, so wine cannot regress from
   any of them by construction. */
[data-v2-velaire][data-theme="light"] .tf-social-1 span,
[data-v2-velaire][data-theme="light"] .v2-week-dayname,
[data-v2-velaire][data-theme="light"] .v2-no,
[data-v2-velaire][data-theme="light"] .v2-sitechat-status,
[data-v2-velaire][data-theme="light"] .v2-prose-ul > li,
[data-v2-velaire][data-theme="light"] .v2-prose-ol > li {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire][data-theme="light"] .v2-footnote {
  color: var(--vh-ink-muted, #6d5a62);
}
/* The last of the template's cream text, on pages whose lists and leads sit directly on
   the page ground. Scoped to the light theme; wine is untouched. */
[data-v2-velaire][data-theme="light"] li.fw-semibold,
[data-v2-velaire][data-theme="light"] p.text {
  color: var(--vh-ink, #201a1d);
}
[data-v2-velaire][data-theme="light"] .v2-week-note,
[data-v2-velaire][data-theme="light"] .v2-calc-note {
  color: var(--vh-ink-muted, #6d5a62);
}
/* The island has to beat the sitewide utility overrides above, which carry !important
   because the template's own rules do. Text inside a dark object follows the object. */
[data-v2-velaire][data-theme="light"] .v2-phone *,
[data-v2-velaire][data-theme="light"] .v2-console:not(.is-light) *,
[data-v2-velaire][data-theme="light"] .v2-capture *,
[data-v2-velaire][data-theme="light"] .v2-calc-figure *,
[data-v2-velaire][data-theme="light"] .v2-checklist-bar * {
  color: var(--vh-cream, #efe3e4) !important;
}
[data-v2-velaire][data-theme="light"] .v2-checklist-bar .text-secondary,
[data-v2-velaire][data-theme="light"] .v2-phone .text-secondary,
[data-v2-velaire][data-theme="light"] .v2-console:not(.is-light) .text-secondary {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}
/* The slider track is a light grey the theme does not own, so the value and its note
   need a tone chosen against THAT rather than against the page. gold-700 is 4.37:1 on
   it and ink-muted 4.39:1 -- both just under. Ink clears it at 12.6:1. */
[data-v2-velaire][data-theme="light"] .v2-slider-value,
[data-v2-velaire][data-theme="light"] .v2-calc-note {
  color: var(--vh-ink, #201a1d);
}
/* The off-canvas menu is page chrome and follows the theme: light panel, ink text. It
   was briefly in the dark-island set above, which was wrong -- its panel went light with
   the rest of the chrome while its text was being held cream, 1.15:1. */
[data-v2-velaire][data-theme="light"] .offcanvas-menu,
[data-v2-velaire][data-theme="light"] .offcanvas-menu a,
[data-v2-velaire][data-theme="light"] .offcanvas-menu p,
[data-v2-velaire][data-theme="light"] .offcanvas-menu span,
[data-v2-velaire][data-theme="light"] .offcanvas-menu li {
  color: var(--vh-ink, #201a1d) !important;
}
/* Beats the sitewide .text-secondary override, which carries !important. */
[data-v2-velaire][data-theme="light"] .v2-calc-note.text-secondary {
  color: var(--vh-ink, #201a1d) !important;
}
/* An OPAQUE panel under the light theme. The menu's own ground is a translucent scrim,
   and the gate's ground walk skips anything below 0.85 alpha -- correctly, since a
   reader sees whatever is behind it too. Over a paper page that composite is close
   enough to paper that cream text on it is 1.15:1 and ink text sits on an unpredictable
   mid-tone. Making the panel opaque removes the ambiguity rather than arguing with it,
   which is also what a light-theme menu should look like. */
[data-v2-velaire][data-theme="light"] .offcanvas-menu,
[data-v2-velaire][data-theme="light"] .offcanvas-menu.show {
  background: #f8f2f1 !important;
}

/* ---- The light/dark control ---- */
[data-v2-velaire] .v2-theme-toggle {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  margin-left: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  border: 1px solid var(--vh-rule, rgba(239, 227, 228, 0.14));
  background: transparent;
  color: var(--vh-cream, #efe3e4);
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease;
}
[data-v2-velaire] .v2-theme-toggle svg { width: 17px; height: 17px; }
[data-v2-velaire] .v2-theme-toggle:hover { border-color: var(--vh-gold-400, #e0bf8a); color: var(--vh-gold-400, #e0bf8a); }
/* The header is a near-opaque cream pill, so the control reads ink on it in BOTH
   themes — the one place the button does not follow the page ground. */
[data-v2-velaire] .header-inner .v2-theme-toggle {
  color: var(--vh-ink, #201a1d);
  border-color: rgba(32, 26, 29, 0.18);
}
[data-v2-velaire] .header-inner .v2-theme-toggle:hover {
  color: var(--vh-gold-700, #7b592d);
  border-color: var(--vh-gold-700, #7b592d);
}

/* ---- The heroes stay dark in both themes ----
   A hero here is a PHOTOGRAPH under a wine wash, not a surface of the page. Flipping its
   text to ink put dark type on a dark picture: measured 1:1, 1.14:1 and 1.41:1 on the
   page title, the breadcrumb and the lede, on 16 of 16 routes.

   Found by LOOKING at the light theme, not by the gate -- v2:contrast measures text on
   cards and grounds, and text over a photograph is v2:ground's job, which had never been
   told about the light theme. Both gates carry --light now. A green run on one of two
   gates is not a green site, and that is the second time today that shape of mistake has
   cost something. */
[data-v2-velaire][data-theme="light"] .section-hero,
[data-v2-velaire][data-theme="light"] .section-page-title,
[data-v2-velaire][data-theme="light"] .section-contact .contact-image {
  --primary:      #efe3e4;
  --secondary:    #ddbdc4;
  --foreground:   #efe3e4;
  --muted:        #ddbdc4;
  --vh-cream:     #efe3e4;
  --vh-cream-dim: #ddbdc4;
  --vh-gold-300:  #e0bf8a;
  --vh-gold-400:  #e0bf8a;
  --vh-ink:       #efe3e4;
  --vh-ink-muted: #ddbdc4;
  --neutral-100:  #efe3e4;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire][data-theme="light"] .section-hero .text-white,
[data-v2-velaire][data-theme="light"] .section-hero .link1,
[data-v2-velaire][data-theme="light"] .section-hero p.text,
[data-v2-velaire][data-theme="light"] .section-page-title .text-white,
[data-v2-velaire][data-theme="light"] .section-page-title .link1,
[data-v2-velaire][data-theme="light"] .section-page-title p.text {
  color: var(--vh-cream, #efe3e4) !important;
}
[data-v2-velaire][data-theme="light"] .section-hero .text-secondary,
[data-v2-velaire][data-theme="light"] .section-page-title .text-secondary {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}

/* ============================================================================
   LIGHT THEME, ROUND TWO — what the contrast gate could not see
   ============================================================================
   Qasim reviewed the light theme on the real site and found it broken in places the
   gate called green. Every one of these defeats a colour-vs-background check in the
   same way: the pixels you can see are not the element's computed `color`.
   scripts/v2/legible.mjs now measures the rendered pixels instead. */

/* 1. GRADIENT-FILLED HEADINGS. `background-clip: text` paints the glyphs from a
      gradient while `color` still reports the first stop, so the gate read cream and
      the screen showed a cream wash on paper. The skin already has the ink version of
      this gradient for its cream panels (`.box-white .text-gradient-*`); this is that
      same treatment at root. NOT inside the heroes, which stay dark. */
[data-v2-velaire][data-theme="light"] .text-gradient-1,
[data-v2-velaire][data-theme="light"] .text-gradient-2,
[data-v2-velaire][data-theme="light"] .text-gradient-3 {
  background: linear-gradient(95.5deg, #3a1420 1.91%, #7f6134 78%);
  -webkit-background-clip: text;
  background-clip: text;
}
[data-v2-velaire][data-theme="light"] .section-hero .text-gradient-1,
[data-v2-velaire][data-theme="light"] .section-hero .text-gradient-2,
[data-v2-velaire][data-theme="light"] .section-hero .text-gradient-3,
[data-v2-velaire][data-theme="light"] .section-page-title .text-gradient-1,
[data-v2-velaire][data-theme="light"] .section-page-title .text-gradient-2,
[data-v2-velaire][data-theme="light"] .section-page-title .text-gradient-3,
/* The dark bands too. Applying the ink gradient at root put ink glyphs on the closing
   band and the inverted feature panel -- 13 headings across the site, invisible. A
   gradient-filled heading has to follow the ground it sits on, and these grounds stay
   dark on purpose. */
[data-v2-velaire][data-theme="light"] .v2-closing .text-gradient-1,
[data-v2-velaire][data-theme="light"] .v2-closing .text-gradient-2,
[data-v2-velaire][data-theme="light"] .v2-closing .text-gradient-3,
[data-v2-velaire][data-theme="light"] .features-center .text-gradient-1,
[data-v2-velaire][data-theme="light"] .features-center .text-gradient-2,
[data-v2-velaire][data-theme="light"] .features-center .text-gradient-3,
/* And the contact band, which is the same case as the heroes and was missed for the
   same reason the address block was: its ground is a photograph under a scrim whose
   wine is written in literal rgba, so the band stays dark in this theme while the root
   rule above handed its heading the ink gradient. 2.20:1 on "Book a demo, see it on
   your own numbers" -- the headline over the only form on the site. */
[data-v2-velaire][data-theme="light"] .section-contact .text-gradient-1,
[data-v2-velaire][data-theme="light"] .section-contact .text-gradient-2,
[data-v2-velaire][data-theme="light"] .section-contact .text-gradient-3 {
  background: linear-gradient(132.09deg, #efe3e4 11.15%, #e0bf8a 100%);
  -webkit-background-clip: text;
  background-clip: text;
}

/* 2. DARK ISLANDS PIN THE RAMP TOO. Pinning only the text tokens was half the job: the
      phone's own bubbles fill from --neutral-900, which the light theme turns white, so
      a white bubble carried cream text at 2.49:1. An island keeps its whole palette. */
[data-v2-velaire][data-theme="light"] .v2-phone,
[data-v2-velaire][data-theme="light"] .v2-console:not(.is-light),
[data-v2-velaire][data-theme="light"] .v2-capture,
[data-v2-velaire][data-theme="light"] .v2-calc-figure,
[data-v2-velaire][data-theme="light"] .v2-checklist-bar {
  /* The heroes are NOT in this list. Pinning the ramp inside .section-page-title took
     every inner route up by exactly 2 -- measured, 16 routes, +2 each -- because the band
     holds ordinary page furniture that then sat on a surface token turned dark under it.
     The heroes keep their TEXT tokens from the rule above, which is what they needed;
     their ground is a photograph and was never reading the ramp. */
  --neutral-950: #2e1119;
  --neutral-900: #3a1824;
  --neutral-800: #4a1f2d;
  --neutral-700: #672c3e;
  --neutral-300: #c49aa6;
  --neutral-100: #efe3e4;
  --neutral-50:  #f8f2f1;
  --surface: #3a1824;
  --background: #4a1f2d;
}

/* 3. THE MARQUEE'S FADE MASK. It fades the rail's two ends so chips slide in and out
      rather than being clipped, and it fades TO THE GROUND -- which the file already
      documents as having been wrong once, when Aigocy's near-white mask painted a pale
      band across the wine rail. Same bug mirrored: a wine mask on a paper rail is the
      dark blob over "Google Workspace". */
[data-v2-velaire][data-theme="light"] .infiniteslide_wrap::after {
  background: linear-gradient(
    90deg, #f8f2f1 0%, rgba(248, 242, 241, 0) 12%,
    rgba(248, 242, 241, 0) 88%, #f8f2f1 100%
  );
}

/* 4. THE PINK STEP. --neutral-300 is a wine-ramp mid-tone, readable on the dark ground
      and not on white: it is the pink body copy in the "How it works" cards. */
[data-v2-velaire][data-theme="light"] .text-neutral-300,
[data-v2-velaire][data-theme="light"] .text-neutral-200,
[data-v2-velaire][data-theme="light"] .text-neutral-400 {
  color: var(--vh-ink-muted, #6d5a62) !important;
}

/* 5. THE SECONDARY PILL. .tf-btn-2 is a wine pill with cream text; on paper it reads as
      a grey metallic slab, which is the odd chip on "The working day" and "How it works".
      Wine fill with cream text is right in both themes -- it is a button, not a surface. */
[data-v2-velaire][data-theme="light"] .tf-btn-2 {
  color: #efe3e4;
}

/* 6. GRADIENT GROUNDS. Everything left was an element whose background is a GRADIENT,
      which is why colour-vs-background compositing never saw any of it: it walks up
      looking for a background COLOUR and a gradient is not one. Two answers, depending
      on what the thing is.

      Dark bands that should stay dark -- the closing CTA, the inverted feature panel and
      the open FAQ row are deliberate dark areas, and a dark band on a light page is a
      design, not a bug. They keep the wine palette. */
[data-v2-velaire][data-theme="light"] .v2-closing,
[data-v2-velaire][data-theme="light"] .features-center,
[data-v2-velaire][data-theme="light"] .accordion-faq_item .accordion-action:not(.collapsed),
[data-v2-velaire][data-theme="light"] .accordion-faq_item .accordion-action:not(.collapsed) ~ * {
  --primary:      #efe3e4;
  --secondary:    #ddbdc4;
  --foreground:   #efe3e4;
  --muted:        #ddbdc4;
  --vh-cream:     #efe3e4;
  --vh-cream-dim: #ddbdc4;
  --vh-ink:       #efe3e4;
  --vh-ink-muted: #ddbdc4;
  --vh-gold-300:  #e0bf8a;
  --vh-gold-400:  #e0bf8a;
  --neutral-300:  #c49aa6;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire][data-theme="light"] .v2-closing .text-secondary,
[data-v2-velaire][data-theme="light"] .v2-closing .text-white,
[data-v2-velaire][data-theme="light"] .v2-closing p,
[data-v2-velaire][data-theme="light"] .features-center .text-secondary,
[data-v2-velaire][data-theme="light"] .features-center .text-white,
[data-v2-velaire][data-theme="light"] .accordion-faq_item .accordion-action:not(.collapsed),
[data-v2-velaire][data-theme="light"] .accordion-faq_item .accordion-action:not(.collapsed) * {
  color: var(--vh-cream, #efe3e4) !important;
}

/*    Small chips, on the other hand, are furniture and should follow the page. The
      country switcher's `.social-item` carries a wine gradient with ink text on it. */
[data-v2-velaire][data-theme="light"] .tf-social-1 .social-item,
[data-v2-velaire][data-theme="light"] .tf-social-1 a {
  background: #ffffff;
  background-image: none;
  border: 1px solid rgba(32, 26, 29, 0.12);
}

/* 7. --neutral-100 READ AS TEXT. The token is "text / cream fill" and this theme keeps it
      CREAM so the card fills stay light (see the ramp note above). Anything using it for
      TEXT therefore lands cream on paper: the messaging timeline's timestamps and the
      coverage map's country names. Named rather than solved at the token, because the
      fill job and the text job genuinely want opposite values and the fill job is the
      one with more callers. */
[data-v2-velaire][data-theme="light"] .v2-coverage-name,
[data-v2-velaire][data-theme="light"] .v2-msg-when,
[data-v2-velaire][data-theme="light"] .v2-thread-when {
  color: var(--vh-ink, #201a1d) !important;
}

/* 8. THE DIMMED MARKET. Australia is a span rather than a link because it has its own
      site now, and it is dimmed to say so -- at 0.4, which was chosen against wine. Ink
      at 40% over paper is 2.3:1, so on the light theme the fifth market simply vanished
      rather than reading as unavailable. Dimmer than its neighbours, still legible. */
[data-v2-velaire][data-theme="light"] .tf-social-1 span.text-body-1 {
  opacity: 0.72;
}

/* 9. THE KICKER CHIP. The odd metallic chip on "The working day", "How it works" and
      "Trust posture" -- a dark slab carrying gold-700 text on a light page.

      It took four rounds of probing to find because a pseudo-element is invisible to
      getComputedStyle on the element, to the compositing walk in v2:contrast, and to any
      inspection of ancestors. The pixel gate saw the text was wrong; only elementFromPoint
      plus getComputedStyle(el, "::before") said why.

      CORRECTED 14-Sep. The ::before is NOT the pill -- it is the 6px DOT at the chip's
      left, which is why the chip carries `padding-left: 30px`. The slab is the element's
      own second background layer, handled by the rule below. Painting the ::before white
      to "fix the pill" therefore did nothing to the slab and deleted the dot from every
      chip on a light band, which is what a desktop pass found. The dot takes the same
      gold as the chip's text, the way the template pairs it with --brand. */
[data-v2-velaire][data-theme="light"] .heading-sub.style-1::before {
  background-color: var(--vh-gold-700, #7b592d);
}
/* The slab is the element's OWN second background layer, not the ::before I chased
   first: `linear-gradient(0deg, #fff, rgb(24,24,27))` -- white to near-black, Aigocy's
   metallic pill. Keep the sheen layer, replace the base. This is the rule that actually
   fixed the chip. */
[data-v2-velaire][data-theme="light"] .heading-sub.style-1 {
  background-image:
    radial-gradient(62.56% 62.56% at 28.14% -10.42%, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 100%),
    linear-gradient(0deg, #ffffff, #f3ece9);
}
[data-v2-velaire][data-theme="light"] .heading-sub.style-1 {
  color: var(--vh-gold-700, #7b592d);
}
/* Inside the dark bands and heroes the chip keeps its wine slab and cream text, so its
   dot goes cream rather than gold-700, which would disappear into the wine. */
[data-v2-velaire][data-theme="light"] .v2-closing .heading-sub.style-1::before,
[data-v2-velaire][data-theme="light"] .features-center .heading-sub.style-1::before,
[data-v2-velaire][data-theme="light"] .section-hero .heading-sub.style-1::before,
[data-v2-velaire][data-theme="light"] .section-page-title .heading-sub.style-1::before {
  background-color: #f8f2f1;
}

/* ============================================================================
   LIGHT THEME, ROUND THREE — what the PIXEL gate found that the colour gate could not
   ============================================================================
   v2:contrast is green on every route in the light theme, and the homepage still had
   four sections nobody could read. The pixel gate (scripts/v2/legible.mjs) found them by
   cropping each text box out of a real screenshot, and the wine theme was run as a
   control: 21 boxes fail there too, so only the 23 that fail in light ALONE were treated
   as findings, and each of those was looked at side by side before it was touched.

   Five of the 23 were artifacts -- marquee clones measured off-screen, and text inside a
   closed dropdown. The rest are these four, and they are four different mistakes. */

/* 1. THE CHAT WIDGET IS A LIGHT CARD, AND THE THEME PAINTED IT BLACK.
      `.v2-sitechat-frame` is literally `background: #fff`: it is a picture of a chat
      window on a website, and a white one in both themes. But its bar and its bubbles
      fill from --vh-cream, and the light theme sets --vh-cream to #201a1d, because on
      this site "cream" is the TEXT tone on wine. One token, two jobs -- text on the dark
      ground and a surface on the light one -- and the theme can only flip it for one of
      them. So the bar went near-black under ink text (1.01:1) and the visitor's bubble
      rendered as a solid black brick.

      Pinning the tone tokens inside the widget is the fix rather than rewriting the two
      fills, because the widget is a light island the way .blog-white is: it does not
      follow the page, so it should not read the page's tokens. */
[data-v2-velaire][data-theme="light"] .v2-sitechat {
  --vh-cream:     #efe3e4;
  --vh-cream-dim: #ddbdc4;
}

/* 2. THE FOUR STEP CARDS. `.team-item` fills from a gradient that the light theme takes
      to white, while the step's name is a hardcoded #fff from the template. White on
      white, 1.00:1, on all four -- "Call comes in", "Velaire answers", "The appointment
      is booked", "Follow-up runs itself", which is the whole explanation of what the
      product does. The card is a real surface and should be light, so the text moves. */
[data-v2-velaire][data-theme="light"] .section-team .team-item .name,
[data-v2-velaire][data-theme="light"] .section-team .team-item .name a {
  color: var(--vh-ink, #201a1d);
}

/* 3. THE STATISTICS STRIP. Same shape, opposite element: `.box-black` is named for a
      dark band and the light theme turns it to #f8f2f1, but its three labels keep the
      template's white. The band's own heading had already gone to ink, so the band is
      meant to be light here and only the labels were left behind. */
[data-v2-velaire][data-theme="light"] .box-black .title,
[data-v2-velaire][data-theme="light"] .box-black .text-body-1 {
  color: var(--vh-ink, #201a1d);
}

/* 4. THE CONTACT BAND IS A PHOTOGRAPH, AND ITS LEFT COLUMN SITS ON IT.
      The existing hero rule lists `.section-contact .contact-image`, but this section's
      photo is a bare <img> and the copy over it lives in `.col-left`, so nothing pinned
      it: the address block went to ink-muted over a wine photograph, 1.19:1. Only the
      left column is pinned -- the form in the right column is a light panel and reads
      correctly already. */
[data-v2-velaire][data-theme="light"] .section-contact .col-left {
  --vh-cream:     #efe3e4;
  --vh-cream-dim: #ddbdc4;
  --vh-ink:       #efe3e4;
  --vh-ink-muted: #ddbdc4;
  --primary:      #efe3e4;
  --secondary:    #ddbdc4;
  --foreground:   #efe3e4;
  --muted:        #ddbdc4;
  --vh-gold-300:  #e0bf8a;
  --vh-gold-400:  #e0bf8a;
  color: var(--vh-cream, #efe3e4);
}
[data-v2-velaire][data-theme="light"] .section-contact .col-left .text,
[data-v2-velaire][data-theme="light"] .section-contact .col-left .text-secondary,
[data-v2-velaire][data-theme="light"] .section-contact .col-left p {
  color: var(--vh-cream-dim, #ddbdc4) !important;
}

/* 5. THE GOLD CHIPS INSIDE THE DARK ISLANDS. The island rule paints every descendant
      cream with !important, which is right for text on the island's own dark ground and
      wrong for the two things inside it that sit on a GOLD fill: Velaire's side of the
      phone transcript, and the settled value in the capture panel. Both pair gold with
      wine ink on purpose -- the same pairing the primary button uses, so the reader knows
      whose voice it is -- and both came out cream-on-gold at 2.49:1.

      Restated after the blanket rule rather than carved out of it, because the blanket is
      the safe default: a new element inside an island should inherit cream and only leave
      it by saying so. */
[data-v2-velaire][data-theme="light"] .v2-phone-turn.is-self .v2-phone-bubble,
[data-v2-velaire][data-theme="light"] .v2-capture-v.is-ok {
  color: #2e1119 !important;
}

/* A `.content` with ONE child should use the whole card.
   `.pricing-item .content` is a two-column construct -- `> * { width: 50% }` -- and that
   is right when it holds a list beside a block of text. The compare cards give it a list
   and nothing else, so the list was laid out in half the card with the other half empty,
   which is why "Covers the hours that person is rostered to work" broke over three lines
   in a 600px-wide card. `:only-child` keeps the two-column case exactly as it was. */
[data-v2-velaire] .pricing-item .content > *:only-child {
  width: 100%;
}

/* The hero's "Scroll for more" tab, 26-Sep-2026. It sits on Aigocy's #EDECEC strip in
   BOTH themes, and two older rules each coloured its label for the wrong ground: the
   light theme's hero rule forces every `.section-hero .link1` to cream (1.06:1 on the
   strip), and the template's hover turns it gold (2.04:1). Found by
   `v2:interactive --natural`, which measures the hovered state; the resting-state
   contrast gate cannot see a hover. Ink in every state, on the strip it actually has. */
[data-v2-velaire] .section-hero .scroll-more .link1,
[data-v2-velaire] .section-hero .scroll-more:hover .link1,
[data-v2-velaire] .section-hero .scroll-more:focus-visible .link1,
[data-v2-velaire][data-theme="light"] .section-hero .scroll-more .link1 {
  color: #201a1d !important;   /* literal: the strip does not change with the theme, so neither does its ink */
}

/* The product page's workflow tab numbers. `opacity: 0.7` on an inactive tab was set
   for the wine theme; in the light theme the tab is white and the number measured
   3.27:1. The selected tab keeps its gold-on-ink pairing. */
[data-v2-velaire][data-theme="light"] .v2-workflow-tabs button:not([aria-selected="true"]) .v2-workflow-n {
  opacity: 1;
  color: #6d5a62;            /* 6.3:1 on white */
}

/* The SELECTED workflow tab's number, both themes. `.v2-workflow-n` sets
   `opacity: 0.7` for every tab; over the selected tab's gold gradient that blends
   the ink to rgb(84,52,42) on rgb(171,133,83) = 3.27:1 (found by
   `v2:interactive --natural`, 62-route run, 26-Sep-2026). The 26-Sep fix above
   covered the inactive tabs in the light theme; this covers the selected one. */
[data-v2-velaire] .v2-workflow-tabs button[aria-selected="true"] .v2-workflow-n {
  opacity: 1;                /* #2e1119 at full ink on the gold gradient: ~6.5:1 */
}

/* The pros/cons figure's "pros" title, on the white figure card inside the paper
   article. It is coloured `var(--accent-2)`, which the wine ground remaps to sage
   #a8c4b4: 1.87:1 on white ("Worth costing a fix", /guides/size-your-missed-call-gap;
   found by `v2:audit` 26-Sep-2026). NOT fixed by remapping the token in .blog-white:
   that was tried first and failed the numbered badges that use --accent-2 as a
   BACKGROUND under ink (2.81:1). Only a text colour matches `style="color:…"`
   at the start of the attribute, which a background-color never does. */
[data-v2-velaire] .blog-white [style^="color:var(--accent-2)"] { color: #3f6b55 !important; }   /* 6.2:1 on white */
