/**
 * VATECH India — redesign layer.
 * Sits on top of Medilab (main.css) + Bootstrap. Two full themes flip off
 * Bootstrap's `data-bs-theme` attribute on <html> (set by a no-flash script in
 * header.php, toggled from site.js):
 *   LIGHT = original VATECH white & red   ·   DARK = green / mint / charcoal
 * Components are styled through the tokens below, so one attribute themes it all.
 * Only additions/overrides live here so the vendor theme stays untouched.
 */

/* ---- Brand typeface: Vatech (self-hosted, Light 300 + Regular 400) ----- */
/* Only Regular + Light exist. Claiming the Regular cut across 400–800 makes the
   browser render it as-is for every heading weight instead of faux-bolding it. */
@font-face {
  font-family: "Vatech";
  src: url("../fonts/Vatech-Regular.woff2") format("woff2");
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Vatech";
  src: url("../fonts/Vatech-Light.woff2") format("woff2");
  font-weight: 100 300;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   Design tokens — light (:root) + dark ([data-bs-theme="dark"])
   Components are styled THROUGH these tokens so one attribute
   themes the whole site.
   ============================================================ */
:root {
  color-scheme: light;

  /* Surfaces & text — original VATECH white & navy */
  --bg:        #ffffff;   /* page ground */
  --surface:   #ffffff;   /* cards / panels */
  --mist:      #f3f7fb;   /* cool, hue-biased neutral (insets, section tint) */
  --line:      #e3ebf3;   /* borders */
  --ink:       #17304d;   /* deep navy — headings/body */
  --ink-soft:  #47607d;   /* muted text */
  --paper:     var(--surface);   /* legacy alias */

  /* Accent — VATECH red; used as TEXT / icons / links / fills */
  --accent:       #d81f3f;
  --accent-hover: #b3162f;
  --accent-soft:  #fdecef;   /* pale pink chip / focus glow */
  --accent-vivid: #d81f3f;   /* glows, progress, pops */
  --accent-fill:  #d81f3f;   /* solid FILL that carries white text */
  --on-fill:      #ffffff;   /* text on --accent-fill */
  --on-vivid:     #ffffff;   /* icon/text on --accent-vivid */
  --accent-rgb:   216, 31, 63;   /* accent as RGB for glows/pulse */

  /* CTA / accent-element button */
  --cta-bg:       #d81f3f;
  --cta-fg:       #ffffff;
  --cta-fg-2:     rgba(255, 255, 255, .9);   /* secondary text on a CTA/coloured surface */
  --cta-hover-bg: #b3162f;
  --cta-hover-fg: #ffffff;

  /* Decorative dark band (stat strip, newsletter) */
  --deep:         #17304d;   /* navy */
  --deep-2:       #0f2135;   /* gradient end */
  --on-deep:      #ffffff;
  --on-deep-soft: rgba(255, 255, 255, .75);

  /* Semantic (kept separate from the brand) */
  --ok:   #1a9d6a;
  --err:  #c4362e;
  --warn: #b4791a;

  /* Legacy brand aliases → the accent, so any untouched selector still themes. */
  --brand:      var(--accent);
  --brand-dark: var(--accent-hover);
  --brand-soft: var(--accent-soft);

  /* Brand gradient — a fill that carries white text (CTA bands, active pills). */
  --grad-brand: linear-gradient(120deg, var(--accent-fill) 0%, var(--accent-hover) 100%);

  /* Medilab (main.css) bridge variables */
  --background-color: var(--bg);
  --surface-color: var(--surface);
  --accent-color: var(--accent);
  --heading-color: var(--ink);
  --heading-font: "Vatech", "Poppins", sans-serif;   /* brand headings; Poppins fallback for any missing glyph (e.g. ₹) */
  --nav-font: "Vatech", "Raleway", sans-serif;        /* brand nav + buttons; Raleway fallback */
  --default-color: #45566a;
  --nav-color: var(--ink);
  --nav-hover-color: var(--accent);
  --nav-dropdown-hover-color: var(--accent);
  /* Nav dropdown + mobile panels: theme-aware surfaces (were hardcoded #fff) */
  --nav-mobile-background-color: var(--surface);
  --nav-dropdown-background-color: var(--surface);
  --nav-dropdown-color: var(--ink);

  --radius: 14px;
  --shadow-sm: 0 2px 10px rgba(23, 48, 77, .06);
  --shadow-md: 0 12px 30px rgba(23, 48, 77, .10);

  --hero-veil: rgba(255, 255, 255, .86);   /* readability veil over the hero bg video (light) */
  --glass-bg: rgba(255, 255, 255, .72);    /* frosted header once scrolled (light) */
  --glass-brd: rgba(255, 255, 255, .5);
  --map-land: #c4d5e4;                      /* network world-map land fill (light) */
  --mist-0:   rgba(243, 247, 251, 0);       /* --mist at 0 alpha — map top/bottom fade */

  /* Free-demo modal sheet */
  --dm-scrim:  rgba(23, 48, 77, .46);        /* navy-biased dim, not a flat black */
  --dm-glass:  rgba(255, 255, 255, .88);     /* frosted sheet ground */
  --dm-brd:    rgba(255, 255, 255, .68);
  --dm-field:  #ffffff;                      /* input ground on the glass */
  --dm-bleed:  rgba(216, 31, 63, .12);       /* accent wash behind the sheet head */
  --dm-shadow: 0 32px 80px rgba(23, 48, 77, .26);
  /* Bright in the middle so the rail reads as an edge light, not a flat bar. */
  --dm-rail:   linear-gradient(90deg, var(--accent-hover) 0%, var(--accent-vivid) 50%, var(--accent-hover) 100%);
}

[data-bs-theme="dark"] {
  color-scheme: dark;

  /* Surfaces & text — green-black + mint */
  --bg:        #091413;   /* green-black page ground */
  --surface:   #0f1d18;   /* elevated cards */
  --mist:      #16261f;   /* insets / section tint */
  --line:      rgba(176, 228, 204, .14);
  --ink:       #e7f1ec;   /* near-white green tint */
  --ink-soft:  #a9c3b8;

  /* Accent — mint / emerald */
  --accent:       #04e59e;   /* bright mint reads on dark */
  --accent-hover: #5bf0be;
  --accent-soft:  rgba(4, 229, 158, .14);
  --accent-vivid: #04e59e;
  --accent-fill:  #285a48;   /* emerald FILL that carries white text */
  --on-fill:      #ffffff;
  --on-vivid:     #37383a;   /* charcoal icon/text on mint */
  --accent-rgb:   4, 229, 158;

  /* CTA — charcoal + mint (client seed spec) */
  --cta-bg:       #37383a;
  --cta-fg:       #04e59e;
  --cta-fg-2:     #e7f1ec;
  --cta-hover-bg: #04e59e;
  --cta-hover-fg: #37383a;

  /* Decorative dark band */
  --deep:         #16261f;   /* nudge bands off the page ground */
  --deep-2:       #0c1a15;
  --on-deep:      #e7f1ec;
  --on-deep-soft: rgba(231, 241, 236, .72);

  --ok:   #3fbf87;
  --err:  #f1707a;
  --warn: #e0a32e;

  /* Ends on --deep, not the mint --accent-hover: white text has to survive it. */
  --grad-brand: linear-gradient(120deg, var(--accent-fill) 0%, var(--deep) 100%);

  --background-color: var(--bg);
  --surface-color: var(--surface);
  --heading-color: var(--ink);
  --default-color: #c7d8cf;
  --nav-color: var(--ink);

  --shadow-sm: 0 2px 10px rgba(0, 0, 0, .5);
  --shadow-md: 0 16px 34px rgba(0, 0, 0, .6);

  --hero-veil: rgba(9, 20, 19, .82);   /* readability veil over the hero bg video (dark) */
  --glass-bg: rgba(9, 20, 19, .6);     /* frosted header once scrolled (dark) */
  --glass-brd: rgba(176, 228, 204, .14);
  --map-land: #2c4234;                  /* network world-map land fill (dark) */
  --mist-0:   rgba(22, 38, 31, 0);      /* --mist at 0 alpha — map top/bottom fade */

  /* Free-demo modal sheet. The rail starts/ends on the emerald --accent-fill
     rather than the light theme's dark red, which would vanish on this ground. */
  --dm-scrim:  rgba(4, 12, 10, .62);
  --dm-glass:  rgba(15, 29, 24, .86);
  --dm-brd:    rgba(176, 228, 204, .16);
  --dm-field:  #0b1714;
  --dm-bleed:  rgba(4, 229, 158, .10);
  --dm-shadow: 0 32px 80px rgba(0, 0, 0, .66);
  --dm-rail:   linear-gradient(90deg, var(--accent-fill) 0%, var(--accent-vivid) 50%, var(--accent-fill) 100%);
}

/* Medilab's light-section preset (footer + some sections) → theme-aware greens,
   so those areas tint correctly in BOTH modes instead of Medilab's fixed blue/white. */
.light-background { --background-color: var(--mist); --surface-color: var(--surface); }

body { -webkit-font-smoothing: antialiased; transition: background-color .35s ease, color .35s ease; }

.container { max-width: 1200px; }

/* Tabular numerals wherever figures line up (specs, phone, stats) */
.specs-table td, .stat-num, .topbar { font-variant-numeric: tabular-nums; }

/* Accent-coloured inline icons/text — themes red (light) → mint (dark).
   Use in place of Bootstrap's fixed .text-danger for decorative accents. */
.text-accent { color: var(--accent) !important; }

/* ---- Header / nav polish ---------------------------------------------- */
.header .branding { padding: 10px 0; }
.header.sticky-top { box-shadow: var(--shadow-sm); }

/* Glassmorphic header once the page is scrolled (Medilab adds body.scrolled > 100px).
   The branding row frosts over the page beneath.

   The blur lives on a ::before layer rather than on #header itself, and that is
   load-bearing: backdrop-filter (like transform/filter) makes an element the
   CONTAINING BLOCK for its position:fixed descendants. .navmenu is a child of
   #header and uses `position: fixed; inset: 0` for the mobile overlay, so putting
   the filter on #header collapsed the whole menu into the 60px header box — the
   menu silently failed to open on mobile once scrolled past 100px. It also trapped
   .navmenu's z-index inside #header's, letting .scroll-top paint over the menu.
   Keep backdrop-filter OFF #header. */
body.scrolled #header {
  box-shadow: var(--shadow-md);
  border-bottom: 1px solid var(--glass-brd);
}
body.scrolled #header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: var(--glass-bg);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
}
body.scrolled #header .branding { background-color: transparent; }
@supports not ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  body.scrolled #header::before { background-color: var(--hero-veil); }   /* opaque fallback */
}
.navmenu ul li > a { letter-spacing: .1px; }

/* Medilab colours these off --accent-color, which flips to bright mint in dark
   (white text on mint fails). Pin the FILLS to stable/CTA tokens so they stay
   readable in both themes. */
.header .topbar { background-color: var(--accent-fill); }                 /* emerald bar + white text */
.header .cta-btn:not(.cta-outline) { background: var(--accent-vivid); color: var(--on-vivid); }
.header .cta-btn:not(.cta-outline):hover { background: var(--accent-hover); color: var(--on-vivid); }
.scroll-top { background-color: var(--accent-fill); }                     /* white arrow reads on emerald */
@media (max-width: 1199px) {
  [data-bs-theme="dark"] .navmenu a i:hover,
  [data-bs-theme="dark"] .navmenu .active i,
  [data-bs-theme="dark"] .navmenu .active:focus i { color: var(--cta-bg); }
}

/* ---- Theme toggle ------------------------------------------------------ */
.theme-toggle {
  display: inline-grid; place-items: center; flex-shrink: 0;
  width: 40px; height: 40px; margin-left: 10px;
  border-radius: 999px; border: 1.5px solid var(--line);
  background: var(--surface); color: var(--ink); cursor: pointer; font-size: 1.05rem;
  transition: color .2s ease, border-color .2s ease, background-color .2s ease, transform .2s ease;
}
.theme-toggle:hover { color: var(--accent); border-color: var(--accent); transform: translateY(-1px); }
.theme-toggle .th-sun { display: none; }
[data-bs-theme="dark"] .theme-toggle .th-moon { display: none; }
[data-bs-theme="dark"] .theme-toggle .th-sun { display: inline-block; }

/* ---- Logo variants ------------------------------------------------------
   The light master paints "india" in #404141 grey, which all but vanishes on
   the dark theme's near-black surfaces; the dark file is the same wordmark with
   "india" in white. Same swap mechanism as the toggle icons above. Scoped to
   .logo so the Valt sub-brand images are untouched. */
.logo .logo-dark { display: none; }
[data-bs-theme="dark"] .logo .logo-light { display: none; }
[data-bs-theme="dark"] .logo .logo-dark { display: block; }

.cta-btn {
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  padding: 9px 20px;
  font-family: var(--nav-font);
  font-weight: 600;
  font-size: 14px;
  background: var(--accent-vivid);
  color: var(--on-vivid);
  margin-left: 10px;
  transition: transform .2s ease, background-color .2s ease, color .2s ease, box-shadow .2s ease;
  white-space: nowrap;
}
.cta-btn:hover { background: var(--accent-hover); color: var(--on-vivid); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.cta-btn.cta-outline {
  background: transparent;
  color: var(--ink);
  border: 1.5px solid var(--line);
}
.cta-btn.cta-outline:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* ---- Buttons ----------------------------------------------------------- */
.btn-brand {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-vivid); color: var(--on-vivid); border-radius: 999px;
  padding: 11px 26px; font-weight: 600; border: none; transition: .2s; text-decoration: none;
}
.btn-brand:hover { background: var(--accent-hover); color: var(--on-vivid); transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn-ghost {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); color: var(--ink); border: 1.5px solid var(--line);
  border-radius: 999px; padding: 11px 26px; font-weight: 600; transition: .2s; text-decoration: none;
}
.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }

/* ---- Section headings -------------------------------------------------- */
.section-title h2 { font-weight: 700; text-wrap: balance; }
.eyebrow {
  display: inline-block; text-transform: uppercase; letter-spacing: 2px;
  font-size: 12px; font-weight: 700; color: var(--accent);
  background: var(--accent-soft); padding: 5px 12px; border-radius: 999px; margin-bottom: 14px;
}

/* ---- HERO -------------------------------------------------------------- */
.hero-redesign {
  position: relative;
  background:
    radial-gradient(1200px 500px at 85% -10%, rgba(var(--accent-rgb), .10), transparent 60%),
    linear-gradient(180deg, var(--bg) 0%, var(--mist) 100%);
  padding: 60px 0 40px;
  overflow: hidden;
}
/* Hero with a background video: solid fallback ground + video behind; the existing
   accent gradient moves to an overlay above the video, and a theme-adaptive veil
   keeps all hero content readable (dark ink in light theme, light ink in dark). */
.hero-redesign.has-video { background: var(--bg); isolation: isolate; }
.hero-redesign.has-video .hero-bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.hero-redesign.has-video .hero-bg-video { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-redesign.has-video::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(1200px 500px at 85% -10%, rgba(var(--accent-rgb), .16), transparent 60%),
    var(--hero-veil);
}
.hero-redesign.has-video .hero-head,
.hero-redesign.has-video .hero-stage,
.hero-redesign.has-video .hero-foot { position: relative; z-index: 2; }
.hero-head { max-width: 780px; margin: 0 auto; text-align: center; }
.hero-head h1 {
  font-size: clamp(2rem, 4.4vw, 3.25rem);
  line-height: 1.08; font-weight: 800; color: var(--ink); text-wrap: balance;
  margin-bottom: 18px;
}
.hero-head h1 .accent { color: var(--accent); }
.hero-head h1 .hl-1, .hero-head h1 .hl-2 { display: block; }
/* Reserve the widest word's width (hidden sizer) with the animated text overlaid,
   so line 2 keeps a constant width and the hero height never shifts as words swap. */
.hero-typewriter { position: relative; display: inline-block; white-space: nowrap; text-align: left; vertical-align: baseline; }
.hero-typewriter .tw-reserve { visibility: hidden; }
.hero-typewriter .tw-mask { position: absolute; left: 0; top: 0; }
.tw-caret {
  display: inline-block; width: 2px; height: 1em; margin-left: 4px; border-radius: 1px;
  background: currentColor; vertical-align: -0.12em;
  animation: twBlink 1s steps(1) infinite;
}
@keyframes twBlink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .tw-caret { animation: none; opacity: 1; } }
.hero-head .lead {
  font-size: 1.12rem; color: var(--ink-soft); max-width: 60ch; margin: 0 auto 26px;
}
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 22px; }
.hero-foot { margin-top: 36px; text-align: center; }
.hero-foot .hero-actions, .hero-foot .hero-trust { justify-content: center; }

/* Slider dots (replace the hero CTAs): muted round dots, active one is an accent pill */
.hero-dots { display: flex; justify-content: center; align-items: center; gap: 9px; }
.hero-dots:not(:empty) { margin-bottom: 26px; }
.hero-dot {
  width: 9px; height: 9px; padding: 0; border: 0; cursor: pointer;
  border-radius: 999px; background: var(--ink-soft); opacity: .4;
  transition: width .35s cubic-bezier(.4, .1, .2, 1), opacity .3s ease, background .3s ease;
}
.hero-dot:hover { opacity: .7; }
.hero-dot.is-active { width: 30px; opacity: 1; background: var(--accent); }
.hero-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) { .hero-dot { transition: opacity .3s ease; } }
.hero-trust { display: flex; flex-wrap: wrap; gap: 22px; color: var(--ink-soft); font-size: .92rem; }
.hero-trust span { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; }
.hero-trust i { color: var(--accent); font-size: 1.15rem; }

/* ---- Rotating flagship coverflow (full-width, frameless) --------------- */
.hero-stage { margin-top: 40px; }
.heroCoverflow { max-width: none; margin: 0; padding: 30px 0 34px; overflow: hidden; }
.heroCoverflow .swiper-slide { height: 440px; }       /* equal height keeps every card level */

.hero-card {
  height: 100%; overflow: visible;
  display: flex; flex-direction: column;
  padding: 8px 24px 16px;
  background: transparent; border: 0; box-shadow: none;
  opacity: .42; transform-origin: center center;
  transform: translateY(var(--arch, 0px));   /* JS sets --arch per distance -> arched deck */
  transition: opacity .45s ease, transform .45s ease;
}
.hero-card-media {
  position: relative; height: 210px; padding: 6px;
  display: flex; align-items: center; justify-content: center;
}
.hero-card-media img {
  max-width: 100%; max-height: 100%; object-fit: contain; transition: filter .45s ease;
}
.hero-card-body {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  text-align: center; padding-top: 14px;
}
.hero-card-cat {
  text-transform: uppercase; letter-spacing: 2px;
  font-size: 11px; font-weight: 700; color: var(--accent);
  background: var(--accent-soft); padding: 4px 11px; border-radius: 999px; margin-bottom: 12px;
}
.hero-card-name {
  font-family: var(--heading-font); font-weight: 800; color: var(--ink); text-wrap: balance;
  font-size: 1.35rem; line-height: 1.15; margin-bottom: 8px;
}
.hero-card-tag { color: var(--ink-soft); font-size: .95rem; margin-bottom: 14px; max-width: 34ch; }
.hero-card-high {
  display: inline-flex; align-items: center; gap: 9px; font-weight: 600; color: var(--ink);
  font-size: .9rem; margin-bottom: 18px;
}
.hero-card-high i { color: var(--accent); }
.hero-card-cta { margin-top: auto; }

/* centre card "pop": fully opaque, scaled up, product lifted with a soft glow */
.heroCoverflow .swiper-slide-active .hero-card {
  opacity: 1; transform: translateY(var(--arch, 0px)) scale(1.05); animation: heroPop .5s ease;
}
.heroCoverflow .swiper-slide-active .hero-card-media::before {
  content: ""; position: absolute; inset: 6% 8%; z-index: -1;
  background: radial-gradient(closest-side, rgba(var(--accent-rgb), .16), transparent 72%);
}
.heroCoverflow .swiper-slide-active .hero-card-media img {
  filter: drop-shadow(0 16px 22px rgba(0, 0, 0, .25));
}
@keyframes heroPop {
  0%   { transform: scale(.94); }
  60%  { transform: scale(1.08); }
  100% { transform: scale(1.05); }
}

@media (max-width: 575.98px) {
  .heroCoverflow { padding: 20px 0 24px; }
  .heroCoverflow .swiper-slide { height: 396px; }
  .hero-card-media { height: 160px; }
  .hero-card-name { font-size: 1.2rem; }
}
@media (prefers-reduced-motion: reduce) {
  .heroCoverflow .swiper-slide-active .hero-card { animation: none; }
}

/* ---- FAQ collage (masonry) --------------------------------------------- */
.faq-collage { position: relative; }
.faq-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); overflow: hidden; color: var(--ink); margin-bottom: 20px;
}
.is-masonry .faq-card {                       /* JS-enhanced: absolute + glide */
  position: absolute; top: 0; left: 0; margin: 0; will-change: transform;
  transition: transform .5s cubic-bezier(.22, .61, .36, 1),
              background-color .35s ease, color .35s ease, border-color .35s ease, box-shadow .35s ease;
}
.faq-card.open {
  background: var(--accent-fill); color: var(--on-fill);
  border-color: transparent; box-shadow: var(--shadow-md);
}
.faq-q {
  width: 100%; text-align: left; background: none; border: 0; cursor: pointer; color: inherit;
  display: flex; align-items: flex-start; gap: 14px; padding: 20px 22px;
  font-family: var(--heading-font); font-weight: 700; font-size: 1.04rem; line-height: 1.35;
}
.faq-q span { flex: 1; }
.faq-q:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
.faq-ic {
  flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent); font-size: .95rem;
  transition: transform .35s ease, background-color .35s ease, color .35s ease;
}
.faq-card.open .faq-ic { transform: rotate(45deg); background: rgba(255, 255, 255, .2); color: var(--on-fill); }
.faq-a { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .4s ease; }
.is-masonry .faq-a { grid-template-rows: 0fr; }
.is-masonry .faq-card.open .faq-a { grid-template-rows: 1fr; }
.faq-a > div { overflow: hidden; min-height: 0; }
.faq-a p { margin: 0; padding: 0 22px 22px; font-size: .95rem; line-height: 1.62; color: inherit; }
.faq-no-anim .faq-card, .faq-no-anim .faq-a { transition: none !important; }
@media (prefers-reduced-motion: reduce) {
  .is-masonry .faq-card, .faq-a { transition: none; }
}

/* ---- FAQ category filter (glass segmented control) --------------------- */
.faq-filters-wrap { display: flex; justify-content: center; }
.faq-filters {
  position: relative;                 /* the sliding thumb is absolute against this */
  display: inline-flex; gap: 2px; padding: 5px;
  max-width: 100%; margin-bottom: clamp(22px, 3vw, 34px);
  border-radius: 999px;
  background: var(--glass-bg); border: 1px solid var(--glass-brd);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  box-shadow: var(--shadow-md);
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
}
.faq-filters::-webkit-scrollbar { display: none; }
@supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
  .faq-filters { background: var(--surface); }
}
.faq-pill-thumb {
  position: absolute; top: 5px; left: 0; height: 40px;
  border-radius: 999px; background: var(--grad-brand);
  pointer-events: none; z-index: 0;
}
/* Only once JS has measured — otherwise the thumb slides in from x=0 on load. */
.faq-filters.is-ready .faq-pill-thumb {
  transition: transform .38s cubic-bezier(.22, 1, .36, 1),
              width     .38s cubic-bezier(.22, 1, .36, 1);
}
.faq-pill {
  position: relative; z-index: 1;     /* above the sliding thumb */
  flex: 0 0 auto; height: 40px; padding-inline: 18px;
  border: 0; border-radius: 999px; background: transparent;
  color: var(--ink);
  font-family: var(--nav-font); font-size: .85rem; font-weight: 600; line-height: 1;
  white-space: nowrap; cursor: pointer;
  display: inline-flex; align-items: center;
  transition: color .28s linear, background-color .2s ease;
}
.faq-pill:not(.is-active):hover { background: rgba(var(--accent-rgb), .12); }
.faq-pill.is-active { background: transparent; color: #fff; }   /* the thumb is the ground now */
.faq-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Filtered-out cards leave the flow entirely so the masonry can re-pack. */
.faq-card.is-filtered { display: none; }
/* Opacity only — .is-masonry .faq-card owns transform for positioning. */
.faq-card.is-swapping {
  animation: faq-card-in .42s cubic-bezier(.22, 1, .36, 1) both;
  animation-delay: var(--faq-d, 0ms);
}
@keyframes faq-card-in { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .faq-card.is-swapping { animation: none; }
  .faq-filters.is-ready .faq-pill-thumb { transition: none; }
}
@media (max-width: 575.98px) {
  .faq-pill { padding-inline: 14px; font-size: .8rem; }
}

/* ---- Stat strip -------------------------------------------------------- */
.stat-strip { background: var(--deep); color: var(--on-deep); }
.stat-strip .stat-num { font-size: 2rem; font-weight: 800; color: var(--on-deep); font-family: var(--heading-font); }
.stat-strip .stat-lbl { color: var(--on-deep-soft); font-size: .9rem; }
.stat-strip .stat-item { padding: 26px 0; }
.stat-strip .stat-item + .stat-item { border-left: 1px solid rgba(231, 241, 236, .14); }

/* ---- Feature / why cards ---------------------------------------------- */
.feature-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px 26px; height: 100%; transition: .25s; box-shadow: var(--shadow-sm);
}
.feature-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: transparent; }
.feature-card .fc-icon {
  width: 56px; height: 56px; border-radius: 14px; display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent); font-size: 1.5rem; margin-bottom: 18px;
}
.feature-card h3 { font-size: 1.15rem; font-weight: 700; margin-bottom: 10px; }
.feature-card p { margin: 0; color: var(--ink-soft); font-size: .96rem; }

/* ---- Product cards (homepage swiper + listing) ------------------------ */
.product-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 24px; height: 100%; display: flex; flex-direction: column; transition: .25s;
  box-shadow: var(--shadow-sm);
}
.product-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.product-card .pc-media {
  background: var(--mist); border-radius: 10px; height: 200px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 18px; padding: 16px; overflow: hidden;
}
/* min-height:0 defeats the flex/grid auto-min-size that otherwise lets a tall
   PNG ignore max-height:100% and overflow onto the card text. */
/* The bottom fade dissolves the cut-out into the --mist well: product_card_shot()
   art is framed hard against the bottom edge on the A9, Green X and PaX-i, which
   without it reads as a chopped photo. A mask rather than an ::after veil, so the
   image itself goes transparent and dark theme needs no override — and it goes on
   the <img>, never on .pc-media, which would cut a notch out of the well instead.
   Measured down the image here (width/height are auto) rather than down the well,
   so the fade hugs the product. */
.product-card .pc-media img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  min-width: 0; min-height: 0; object-fit: contain; display: block;
  -webkit-mask-image: linear-gradient(to bottom, #000 75%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 75%, transparent 100%);
}
.product-card .pc-cat { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--accent); }
.product-card h3 { font-size: 1.2rem; font-weight: 700; margin: 6px 0 10px; }
.product-card p { color: var(--ink-soft); font-size: .93rem; flex-grow: 1; }
.product-card .pc-link { font-weight: 600; color: var(--accent); display: inline-flex; align-items: center; gap: 6px; }
.product-card .pc-link i { transition: transform .2s; }
.product-card:hover .pc-link i { transform: translateX(4px); }

/* Featured-products carousel: equal-height cards, no pagination gap */
.productsSwiper .swiper-wrapper { align-items: stretch; }
.productsSwiper .swiper-slide { height: auto; display: flex; }
.productsSwiper .swiper-slide .product-card { width: 100%; }
.productsSwiper .swiper-pagination { display: none; }

/* ---- Homepage video tiles (full-bleed, hover-reveal, themed gradient) --- */
/* Both sections run edge-to-edge (no .container), so tiles are square-cornered
   cinematic bands with a viewport-relative height cap. */
.video-duo, .video-banner { padding: 0; }
.video-duo .vrow { display: grid; grid-template-columns: 1fr 1fr; gap: 0; }
.vtile {
  position: relative; display: block; overflow: hidden; border-radius: 0;
  background: var(--mist); isolation: isolate;
}
.video-duo .vtile { height: clamp(300px, 32vw, 520px); }
.vtile--wide { height: clamp(340px, 44vw, 640px); }
.vtile-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .7s cubic-bezier(.28, .11, .32, 1);
}
/* invisible cover link carries the tile's main navigation (below the caption) */
.vtile-cover { position: absolute; inset: 0; z-index: 1; }
.vtile-cap {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-start;
  padding: clamp(20px, 3vw, 44px); color: var(--on-fill);
  opacity: 0; transition: opacity .4s ease;
}
/* themed bottom-half gradient — red in light, emerald in dark */
.vtile-cap::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(to top,
    var(--accent-fill) 0%, rgba(var(--accent-rgb), .55) 28%, transparent 60%);
}
.vtile-cat {
  text-transform: uppercase; letter-spacing: 2px; font-size: 12px; font-weight: 700;
  opacity: .92; margin-bottom: 6px;
  transform: translateY(12px); transition: transform .45s ease;
}
.vtile-title {
  font-family: var(--heading-font); font-weight: 800;
  font-size: clamp(1.3rem, 2.4vw, 1.9rem); line-height: 1.15;
  transform: translateY(12px); transition: transform .45s ease .04s;
}
/* split caption: text left, CTA right — both on the caption's bottom row so the
   button lines up with the left-hand content (not the middle of the video) */
.vtile-cap--split .vtile-cap-row {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; gap: 24px;
}
.vtile-cap-text { display: flex; flex-direction: column; align-items: flex-start; min-width: 0; }
/* hover CTA — reuses the hero .btn-ghost design */
.vtile-cta {
  pointer-events: none;   /* re-enabled once revealed (below) */
  flex: none; white-space: nowrap;
  transform: translateX(16px); opacity: 0;
  transition: transform .45s ease .1s, opacity .35s ease .1s, color .2s ease, border-color .2s ease;
}
.vtile:hover .vtile-cap, .vtile:focus-within .vtile-cap { opacity: 1; }
.vtile:hover .vtile-cat, .vtile:focus-within .vtile-cat,
.vtile:hover .vtile-title, .vtile:focus-within .vtile-title { transform: translateY(0); }
.vtile:hover .vtile-cta, .vtile:focus-within .vtile-cta {
  transform: translateX(0); opacity: 1; pointer-events: auto;
}
.vtile:hover .vtile-video, .vtile:focus-within .vtile-video { transform: scale(1.04); }
.vtile-cover:focus-visible, .vtile:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }

@media (hover: none) {
  .vtile-cap { opacity: 1; }
  .vtile-cat, .vtile-title { transform: none; }
  .vtile-cta { opacity: 1; transform: none; pointer-events: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .vtile-video, .vtile-cat, .vtile-title, .vtile-cta {
    transition: opacity .3s ease; transform: none;
  }
  .vtile:hover .vtile-video, .vtile:focus-within .vtile-video { transform: none; }
}
@media (max-width: 575px) {
  .vtile-cap--split .vtile-cap-row { flex-direction: column; align-items: flex-start; gap: 14px; }
}
@media (max-width: 734px) {
  .video-duo .vrow { grid-template-columns: 1fr; }
  .video-duo .vtile { height: clamp(240px, 56vw, 360px); }
  .vtile--wide { height: clamp(260px, 62vw, 420px); }
}

/* ---- Software screenshot frame ----------------------------------------
   Shared by software.php's listing rows and index.php's software band, so it
   lives here rather than in assets/css/software.css, which only the software
   pages load. A UI capture wants the opposite of .pc-media's cover-crop: a
   fixed box it sits inside, on a surface ground with a hairline, so nothing is
   trimmed and the frame reads as a screen. */
.sw-hub-shot {
  margin: 0; padding: clamp(12px, 1.6vw, 20px);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
  aspect-ratio: 4 / 3; display: grid; place-items: center; overflow: hidden;
}
.sw-hub-shot img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  object-fit: contain; border-radius: 8px;
}

/* ---- CTA band ---------------------------------------------------------- */
.cta-band {
  background: linear-gradient(120deg, var(--accent-fill) 0%, var(--accent-hover) 100%);
  color: var(--on-fill); border-radius: 20px; padding: 46px; box-shadow: var(--shadow-md);
}
.cta-band h2 { color: var(--on-fill); font-weight: 800; }
.cta-band p { color: var(--cta-fg-2); margin-bottom: 0; }
.cta-band .btn-brand { background: var(--surface); color: var(--accent); }
.cta-band .btn-brand:hover { background: var(--ink); color: var(--on-fill); }
[data-bs-theme="dark"] .cta-band { background: linear-gradient(120deg, var(--accent-fill) 0%, var(--deep) 100%); }
[data-bs-theme="dark"] .cta-band .btn-brand { background: var(--accent-vivid); color: var(--on-vivid); }
[data-bs-theme="dark"] .cta-band .btn-brand:hover { background: var(--cta-bg); color: var(--cta-fg); }

/* ---- Page hero (inner pages) ------------------------------------------ */
.page-hero {
  background:
    radial-gradient(800px 300px at 90% -20%, rgba(var(--accent-rgb), .10), transparent 60%),
    linear-gradient(180deg, var(--bg), var(--mist));
  padding: 46px 0 34px; border-bottom: 1px solid var(--line);
}
.page-hero h1 { font-weight: 800; color: var(--ink); margin-bottom: 8px; }
.page-hero .breadcrumbs { font-size: .9rem; color: var(--ink-soft); }
.page-hero .breadcrumbs a { color: var(--ink-soft); }
.page-hero .breadcrumbs a:hover { color: var(--accent); }

/* ---- Product detail ---------------------------------------------------- */
.product-detail .pd-media {
  background: var(--mist); border-radius: var(--radius); border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  min-height: 340px; overflow: hidden;
}
.product-detail .pd-media img {
  max-width: 100%; width: auto; height: auto;
  min-width: 0; min-height: 0; object-fit: contain; display: block;
}
.specs-table { width: 100%; border-collapse: collapse; }
.specs-table td { padding: 13px 16px; border-bottom: 1px solid var(--line); }
.specs-table tr td:first-child { font-weight: 600; color: var(--ink); width: 45%; }
.specs-table tr:nth-child(odd) { background: var(--mist); }

/* ---- Valt by Vatech sub-brand ----------------------------------------- */
.valt-brand-logo { height: 46px; width: auto; display: block; }
.valt-section-logo { height: 84px; width: auto; margin: 0 auto 10px; display: block; }
.valt-section-tagline { max-width: 60ch; margin: 0 auto; color: var(--ink-soft); }
.valt-section-tagline strong { color: var(--accent); }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- Network page: group companies ------------------------------------ */
.net-company {
  height: 100%; display: flex; flex-direction: column; gap: 12px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 24px; box-shadow: var(--shadow-sm);
  transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.net-company:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.net-company-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.net-ic {
  width: 52px; height: 52px; border-radius: 13px; flex-shrink: 0;
  display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent); font-size: 1.5rem;
}
.net-tag {
  font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .5px;
  color: var(--ink-soft); background: var(--mist); border: 1px solid var(--line);
  padding: 5px 10px; border-radius: 999px; white-space: nowrap;
}
.net-company h3 { font-size: 1.14rem; margin: 4px 0 0; text-wrap: balance; }
.net-web {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  font-size: .82rem; font-weight: 600; color: var(--accent); text-decoration: none; letter-spacing: .2px;
}
.net-web:hover { color: var(--accent-hover); text-decoration: underline; }
.net-company p { margin: 0; color: var(--ink-soft); font-size: .95rem; line-height: 1.62; }

/* ---- Network page: worldwide map band --------------------------------- */
.net-map { padding: 60px 0; background: var(--mist); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.net-band {
  display: inline-block; margin-top: 6px;
  background: var(--accent-fill); color: var(--on-fill); font-family: var(--heading-font);
  font-weight: 600; letter-spacing: .5px; padding: 9px 26px; border-radius: 999px;
  box-shadow: var(--shadow-sm);
}
.net-map-wrap { width: 100%; max-width: 1500px; margin: 26px auto 0; padding: 0 16px; }
.net-map-img {
  display: block; width: 100%; height: auto;
  border-radius: var(--radius); border: 1px solid var(--line); box-shadow: var(--shadow-md);
}

/* ---- Network page: subsidiary office cards ---------------------------- */
.net-office {
  height: 100%; position: relative; overflow: hidden;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 22px 22px 20px; box-shadow: var(--shadow-sm);
  transition: transform .25s ease, box-shadow .25s ease;
}
.net-office::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
  background: var(--accent); transform: scaleX(0); transform-origin: left; transition: transform .3s ease;
}
.net-office:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.net-office:hover::before { transform: scaleX(1); }
.net-office-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
.net-country { display: inline-flex; align-items: center; gap: 8px; font-family: var(--heading-font); font-weight: 600; color: var(--ink); font-size: 1.08rem; }
.net-country i { color: var(--accent); font-size: 1rem; }
.net-hq {
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .5px;
  color: var(--accent); background: var(--accent-soft); padding: 4px 9px; border-radius: 999px; white-space: nowrap;
}
.net-legal { color: var(--ink); font-size: .92rem; font-weight: 600; margin: 0 0 14px; }
.net-contact { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
.net-contact li { display: flex; gap: 10px; font-size: .9rem; color: var(--ink-soft); line-height: 1.5; }
.net-contact li i { color: var(--accent); flex-shrink: 0; margin-top: 3px; width: 15px; text-align: center; }
.net-contact a { color: var(--ink-soft); text-decoration: none; word-break: break-word; }
.net-contact a:hover { color: var(--accent); text-decoration: underline; }
.net-contact .net-tel, .net-contact .net-fax { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
  .net-company, .net-office { transition: none; }
  .net-company:hover, .net-office:hover { transform: none; }
  .net-office::before { transition: none; }
}

.feature-media {
  background: var(--mist); border: 1px solid var(--line); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.feature-media img { max-width: 100%; max-height: 100%; width: auto; height: auto; min-width: 0; min-height: 0; object-fit: contain; }

.pd-feature { display: flex; gap: 16px; padding: 18px 0; border-bottom: 1px solid var(--line); }
.pd-feature .pf-ic { color: var(--accent); font-size: 1.4rem; flex-shrink: 0; margin-top: 2px; }
.pd-feature h4 { font-size: 1.05rem; font-weight: 700; margin-bottom: 4px; }
.pd-feature p { margin: 0; color: var(--ink-soft); font-size: .95rem; }

/* ---- Forms ------------------------------------------------------------- */
.form-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px; box-shadow: var(--shadow-sm);
}
.php-email-form .form-control,
.php-email-form .form-select {
  border-radius: 10px; border: 1.5px solid var(--line); padding: 12px 14px; font-size: .96rem;
  background: var(--surface); color: var(--ink);
}
.php-email-form .form-control:focus,
.php-email-form .form-select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.php-email-form label.req::after { content: " *"; color: var(--accent); }
.form-note { font-size: .85rem; color: var(--ink-soft); }

/* ---- The one enquiry form (includes/enquiry-form.php) ------------------
   Promoted out of the demo modal, which was the only one of the four form
   designs worth keeping, and now shared by every lead form on the site.
   .vf-card is the solid inline card; .dm-card below adds the glass for the
   modal. Everything is themed through the tokens, so dark mode comes free. */
.vf-card {
  position: relative;
  overflow: hidden;                          /* clips the rail + head wash to the radius */
  padding: clamp(24px, 3vw, 34px);
  border: 1px solid var(--line);
  border-radius: 20px;                       /* matches .cta-band */
  background: var(--surface);
  box-shadow: var(--shadow-md);
  color: var(--ink);
}
/* Accent wash behind the heading — the same radial recipe as .page-hero. */
.vf-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 210px; z-index: 0;
  pointer-events: none;
  background: radial-gradient(460px 200px at 82% -30%, var(--dm-bleed), transparent 68%);
}
.vf-card > * { position: relative; z-index: 1; }
.vf-rail { position: absolute; inset: 0 0 auto 0; height: 4px; z-index: 2; background: var(--dm-rail); }

.vf-head { margin-bottom: 22px; }
.vf-title {
  margin: 8px 0 6px;
  font-family: var(--heading-font); font-weight: 800;
  font-size: clamp(1.3rem, 3.4vw, 1.45rem); line-height: 1.15; letter-spacing: -.01em;
  color: var(--ink);
}
.vf-sub { margin: 0; color: var(--ink-soft); font-size: .95rem; }

.vf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
.vf-full { grid-column: 1 / -1; }
.vf-field { display: flex; flex-direction: column; min-width: 0; }
.vf-form .form-label { margin-bottom: 5px; font-family: var(--nav-font); font-weight: 600; font-size: .85rem; }
.vf-opt { color: var(--ink-soft); font-weight: 400; font-size: .8rem; }

/* Full field styling, owned here rather than inherited from .php-email-form —
   see the note in includes/enquiry-form.php. Medilab styles .contact inputs by
   type and its list predates input[type=tel], so a form wearing that class got
   a Mobile field 6px taller than the one beside it. This one doesn't wear it. */
.vf-form .form-control,
.vf-form .form-select {
  border-radius: 10px; border: 1.5px solid var(--line); padding: 12px 14px;
  background: var(--surface); color: var(--ink); line-height: 1.5; height: auto;
}
.vf-form .form-control:focus,
.vf-form .form-select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.vf-form label.req::after { content: " *"; color: var(--accent); }

/* 16px is a threshold, not a preference: below it iOS Safari zooms the viewport
   on focus — on the highest-intent screen the site has. Desktop drops back to
   the site's .96rem once there is no touch keyboard to trigger it. */
.vf-form .form-control,
.vf-form .form-select,
.php-email-form .form-control,
.php-email-form .form-select { font-size: 16px; }
/* The width test alone was wrong: a tablet is >=768px AND has a touch keyboard,
   so iPads were still being zoomed on focus. Gate on a fine pointer instead —
   .96rem lands on mouse-driven screens only, every touch device keeps 16px.
   (career.php uses .php-email-form rather than the .vf-* sheet, so it was
   missing the 16px floor at every width; it is covered above now.) */
@media (min-width: 768px) and (hover: hover) and (pointer: fine) {
  .vf-form .form-control,
  .vf-form .form-select,
  .php-email-form .form-control,
  .php-email-form .form-select { font-size: .96rem; }
}

/* Per-field errors. The handler returns errors{field: message}; site.js marks
   the wrapper and fills the span. */
.vf-error { display: none; margin-top: 5px; font-size: .82rem; color: var(--err); }
.vf-field.is-invalid .form-control,
.vf-field.is-invalid .form-select { border-color: var(--err); }
.vf-field.is-invalid .form-control:focus,
.vf-field.is-invalid .form-select:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--err) 22%, transparent);
}
.vf-field.is-invalid .vf-error { display: block; }

.vf-check { display: flex; align-items: center; gap: 9px; margin-top: 16px; }
.vf-check .form-check-input { margin: 0; flex: 0 0 auto; }
.vf-check .form-check-label { font-size: .9rem; color: var(--ink); }

.vf-submit {
  width: 100%; margin-top: 20px; padding: 13px 26px;
  border: 0; border-radius: 999px;
  background: var(--grad-brand); color: var(--on-fill);
  font-family: var(--nav-font); font-weight: 600; font-size: 1rem; cursor: pointer;
  transition: filter .2s ease, box-shadow .2s ease, transform .12s ease-out;
}
.vf-submit:hover { filter: brightness(1.08); box-shadow: var(--shadow-md); }
.vf-submit:active { transform: scale(.985); }     /* feedback on press, not release */
.vf-submit:disabled { filter: saturate(.55); cursor: default; }
.vf-submit:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.vf-note { margin: 12px 0 0; text-align: center; }
.vf-note a { color: var(--accent); font-weight: 600; }

/* Success state — the fields step aside so the confirmation is the whole sheet. */
.vf-card.is-sent .vf-form { display: none; }
.vf-sent { text-align: center; padding: 14px 0 6px; }
.vf-sent-mark {
  display: grid; place-items: center; width: 58px; height: 58px; margin: 0 auto 16px;
  border-radius: 999px; background: var(--grad-brand); color: var(--on-fill); font-size: 1.5rem;
}
.vf-sent h3 { font-family: var(--heading-font); font-weight: 800; font-size: 1.3rem; margin-bottom: 8px; }
.vf-sent p { color: var(--ink-soft); font-size: .95rem; margin-bottom: 20px; }
.vf-sent a { color: var(--accent); font-weight: 600; }
.vf-sent-close {
  border: 1.5px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--ink);
  padding: 10px 30px; font-family: var(--nav-font); font-weight: 600; cursor: pointer;
  transition: border-color .2s ease, color .2s ease;
}
.vf-sent-close:hover { border-color: var(--accent); color: var(--accent); }
.vf-sent-close:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Reassurance rail beside the form — replaces the .feature-card that was
   stretched to 537px to match the form while holding ~110px of content. */
.vf-aside {
  background: var(--mist); border: 1px solid var(--line); border-radius: 20px;
  padding: 28px; display: flex; flex-direction: column; gap: 22px;
}
.vf-point { display: flex; gap: 13px; align-items: flex-start; }
.vf-point .vf-ico {
  width: 38px; height: 38px; flex: 0 0 38px; border-radius: 11px; display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent); font-size: 1.05rem;
}
.vf-point h4 { margin: 0 0 3px; font-size: .94rem; font-weight: 700; color: var(--ink); }
.vf-point p { margin: 0; font-size: .85rem; color: var(--ink-soft); line-height: 1.5; }
.vf-point a { color: var(--accent); font-weight: 600; }

/* ---- Enquiry section background — the CBCT sweep ----------------------
   The form section used to be flat --bg while the hero directly above carried a
   gradient, so the card floated in nothing. This starts the section at --mist
   where the hero ends and fades to --bg, then lays concentric arcs over it:
   a scan sweep abstracted, not illustrated. currentColor picks up --accent, so
   it flips red -> mint with the theme like everything else. */
/* A background only — no padding of its own, so it composes with whatever
   section class a page already uses (.section, .contact section, …) instead of
   fighting it. Deliberately NOT overflow:hidden: the arcs are absolutely
   positioned to inset:0 and cannot escape the padding box anyway, and clipping
   the section would catch AOS's fade-up transforms mid-flight. */
.vf-bg { position: relative; }
.vf-bg > * { position: relative; z-index: 1; }
.vf-bg::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(720px 420px at 78% 8%, var(--dm-bleed), transparent 66%),
    linear-gradient(180deg, var(--mist) 0%, var(--bg) 62%);
}
.vf-arcs {
  position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%;
  pointer-events: none; color: var(--accent);
}
/* Mirrored variant — the sweep enters from the top-LEFT instead.
   Used on the product/software FAQ band so it and the enquiry band directly
   below it read as a matched pair rather than the same background twice.

   scaleX(-1) rather than a second copy of includes/enquiry-arcs.php: it flips
   the circle centre AND the mask's fade direction in one move, and the SVG is
   aria-hidden with no text, so there is nothing to un-mirror. Duplicating the
   partial would also restart its `static $arc_n` mask counter at 1, and a page
   carrying both files would then emit vf-arcmask-1 twice.

   Must stay after `.vf-bg > *` above: same specificity, so source order is what
   keeps the SVG at position:absolute; z-index:0 instead of being lifted with
   the content. */
.vf-bg.is-flipped::before {
  background:
    radial-gradient(720px 420px at 22% 8%, var(--dm-bleed), transparent 66%),
    linear-gradient(180deg, var(--mist) 0%, var(--bg) 62%);
}
.vf-bg.is-flipped .vf-arcs { transform: scaleX(-1); }
/* Texture at phone scale is just noise — the wash alone carries it there. */
@media (max-width: 575.98px) { .vf-arcs { display: none; } }

/* ---- Phones -----------------------------------------------------------
   Was: a 12px outer gutter against 30px of card padding — cramped at the edges
   and loose in the middle at once. */
@media (max-width: 575.98px) {
  .vf-bg > .container { --bs-gutter-x: 36px; }           /* 18px each side */
  .vf-card { padding: 24px 20px; border-radius: 18px; }  /* 20px is too round at this width */
  .vf-grid { grid-template-columns: minmax(0, 1fr); gap: 15px; }
  .vf-aside { padding: 20px; border-radius: 18px; gap: 18px; }
}

.form-alert { border-radius: 10px; padding: 12px 16px; margin-bottom: 16px; font-size: .95rem; display: none; }
.form-alert.show { display: block; }
.form-alert.ok  { background: color-mix(in srgb, var(--ok) 12%, var(--surface)); color: var(--ok); border: 1px solid color-mix(in srgb, var(--ok) 34%, var(--surface)); }
.form-alert.err { background: color-mix(in srgb, var(--err) 12%, var(--surface)); color: var(--err); border: 1px solid color-mix(in srgb, var(--err) 34%, var(--surface)); }

/* ---- Newsletter band (between page content and footer) ---------------- */
.newsletter-band {
  background: linear-gradient(120deg, var(--deep) 0%, var(--deep-2) 100%);
  color: var(--on-deep); padding: 34px 0;
}
.newsletter-band-copy h3 { color: var(--on-deep); font-weight: 700; margin-bottom: 4px; }
.newsletter-band-copy p { color: var(--on-deep-soft); margin: 0; }
.newsletter-band .form-alert { max-width: 480px; }

/* Newsletter input group */
.newsletter-field { display: flex; background: var(--surface); border-radius: 999px; padding: 4px; border: 1px solid var(--line); max-width: 320px; }
.newsletter-field input { border: none; background: transparent; padding: 8px 14px; flex: 1; outline: none; font-size: .9rem; color: var(--ink); }
.newsletter-field button { border: none; background: var(--accent-vivid); color: var(--on-vivid); width: 40px; border-radius: 999px; cursor: pointer; }
.newsletter-field button:hover { background: var(--accent-hover); color: var(--on-vivid); }
/* Larger variant used in the band */
.newsletter-field-lg { max-width: 480px; margin-left: auto; padding: 5px; }
.newsletter-field-lg input { padding: 12px 18px; font-size: 1rem; }
.newsletter-field-lg button { width: auto; padding: 0 22px; font-weight: 600; display: inline-flex; align-items: center; }
@media (max-width: 991px) { .newsletter-field-lg { margin-left: 0; } }

/* ---- Floating WhatsApp ------------------------------------------------- */
.whatsapp-float {
  position: fixed; right: 22px; bottom: 22px; z-index: 996;
  width: 54px; height: 54px; border-radius: 50%; background: #25d366; color: #fff;
  display: grid; place-items: center; font-size: 1.6rem; box-shadow: 0 8px 24px rgba(37,211,102,.45);
  transition: transform .2s;
}
.whatsapp-float:hover { color: #fff; transform: scale(1.08); }

/* Stack the back-to-top button ABOVE the WhatsApp float so they don't overlap.
   WhatsApp: 54px tall at bottom:22px → its top edge is at 76px; place this above it. */
#scroll-top.scroll-top { right: 29px; bottom: 88px; width: 40px; height: 40px; }

/* Medilab ships .scroll-top at z-index 99999, which outranks the mobile nav
   overlay (9997/9998) and paints the arrow on top of the open menu. Nothing on
   this site sits between 995 and 99999, so lowering it changes no paint order
   anywhere — it just stops the float winning against the menu. */
#scroll-top.scroll-top { z-index: 995; }

/* Both floats step aside for the mobile menu, matching what they already do for
   the demo modal and the video lightbox further down this file. */
body.mobile-nav-active .scroll-top,
body.mobile-nav-active .whatsapp-float { opacity: 0; visibility: hidden; pointer-events: none; }

/* ---- Mobile: floats must clear the home indicator and cover less content ----
   These are fixed FABs, so they inherently sit over the page. Shrinking them and
   tucking them into the safe area keeps them off text and controls on phones. */
@media (max-width: 767.98px) {
  .whatsapp-float {
    right: 14px;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    width: 46px; height: 46px; font-size: 1.35rem;
  }
  #scroll-top.scroll-top {
    right: 18px;
    bottom: calc(70px + env(safe-area-inset-bottom, 0px));
    width: 38px; height: 38px;
  }
}

/* ---- Labeled dimension placeholders ----------------------------------- */
.ph {
  width: 100%;
  aspect-ratio: var(--ph-w) / var(--ph-h);
  background: linear-gradient(135deg, var(--mist) 0%, var(--accent-soft) 100%);
  border: 1.5px dashed color-mix(in srgb, var(--accent) 32%, var(--line));
  border-radius: var(--radius);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 12px; text-align: center; color: var(--ink); overflow: hidden;
}
.ph-ic { font-size: clamp(1.1rem, 3vw, 1.6rem); line-height: 1; color: color-mix(in srgb, var(--accent) 55%, var(--ink)); opacity: .55; }
.ph-dim { font-family: var(--heading-font); font-weight: 700; font-variant-numeric: tabular-nums; font-size: clamp(.95rem, 2.4vw, 1.35rem); letter-spacing: .3px; }
.ph-label { text-transform: uppercase; letter-spacing: 1.5px; font-size: .68rem; font-weight: 600; color: var(--ink-soft); max-width: 90%; }
.ph-mob { font-size: .66rem; color: var(--ink-soft); opacity: .8; font-variant-numeric: tabular-nums; }
/* Very small placeholders (e.g. gallery thumbs): drop the icon to avoid crowding */
.ph.ph-sm .ph-ic { display: none; }
/* Fill-mode: placeholder fills its parent box (e.g. fixed-height product-card media)
   instead of imposing its own aspect ratio. */
.ph.ph-fill { height: 100%; aspect-ratio: auto; }
.pc-media .ph { border-radius: 8px; }

/* ---- 360° spin viewer -------------------------------------------------- */
.media-tabs { display: inline-flex; gap: 4px; background: var(--mist); border: 1px solid var(--line); border-radius: 999px; padding: 4px; margin-bottom: 14px; }
.media-tab { border: none; background: transparent; color: var(--ink-soft); font-weight: 600; font-size: .85rem; padding: 6px 16px; border-radius: 999px; cursor: pointer; transition: .2s; display: inline-flex; align-items: center; }
.media-tab.active { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-sm); }

.spin-viewer {
  position: relative; width: 100%; aspect-ratio: 1 / 1;
  background: var(--mist); border: 1px solid var(--line); border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center;
  cursor: grab; user-select: none; overflow: hidden; touch-action: pan-y;
}
.spin-viewer.is-dragging { cursor: grabbing; }
.spin-viewer .spin-img { max-width: 88%; max-height: 88%; object-fit: contain; pointer-events: none; -webkit-user-drag: none; }
.spin-viewer .spin-progress { position: absolute; left: 22%; right: 22%; bottom: 26px; height: 4px; background: var(--line); border-radius: 99px; overflow: hidden; }
.spin-viewer .spin-progress span { display: block; height: 100%; width: 0; background: var(--accent-vivid); transition: width .12s ease; }
.spin-viewer .spin-hint {
  position: absolute; bottom: 14px; left: 50%; transform: translateX(-50%);
  background: var(--deep); color: var(--on-deep); font-size: .75rem; font-weight: 600;
  padding: 6px 12px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px;
  opacity: 0; transition: opacity .3s; pointer-events: none; white-space: nowrap;
}
.spin-viewer.is-ready .spin-hint { opacity: 1; }
.spin-viewer .spin-hint.is-hidden { opacity: 0; }
.spin-viewer:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* ---- Motion / a11y ----------------------------------------------------- */
a:focus-visible, button:focus-visible, .cta-btn:focus-visible, input:focus-visible, .theme-toggle:focus-visible {
  outline: 3px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* ---- Responsive -------------------------------------------------------- */
@media (max-width: 991px) {
  .hero-redesign { padding: 40px 0 30px; }
  .hero-redesign .hero-copy { text-align: center; margin-bottom: 32px; }
  .hero-redesign .hero-copy .lead { margin-left: auto; margin-right: auto; }
  .hero-actions, .hero-trust { justify-content: center; }
  .stat-strip .stat-item + .stat-item { border-left: none; }
  .cta-band { padding: 32px 24px; text-align: center; }
}

/* ======================================================================
   Our Philosophy — long-form editorial essay
   ====================================================================== */
.philo-hero {
  text-align: center; padding: 74px 0 46px;
  background: linear-gradient(180deg, var(--accent-soft) 0%, var(--bg) 72%);
  border-bottom: 1px solid var(--line);
}
.philo-hero .eyebrow { display: inline-block; }
.philo-hero h1 { font-size: clamp(2rem, 4.6vw, 3.15rem); margin: 12px auto 14px; max-width: 18ch; text-wrap: balance; }
.philo-sub { color: var(--ink-soft); font-size: 1.16rem; max-width: 44ch; margin: 0 auto 34px; }
.video-frame {
  position: relative; max-width: 900px; margin: 0 auto; cursor: pointer; aspect-ratio: 16 / 9;
  border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); border: 1px solid var(--line);
}
.video-embed { display: block; width: 100%; height: 100%; }
.video-embed img { width: 100%; height: 100%; object-fit: cover; display: block; }
.video-iframe { width: 100%; height: 100%; border: 0; display: block; }
.video-play { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; }
.video-play span {
  width: 78px; height: 78px; border-radius: 50%; background: var(--accent-vivid); color: var(--on-vivid);
  display: grid; place-items: center; font-size: 2rem; box-shadow: 0 10px 28px rgba(9, 20, 19, .3);
  transition: transform .25s ease;
}
.video-frame:hover .video-play span { transform: scale(1.09); }
.video-cap { color: var(--ink-soft); font-size: .95rem; max-width: 60ch; margin: 16px auto 0; }

/* Reading column + chapter rhythm */
.essay-col { max-width: 720px; margin-inline: auto; }
.essay-col p { color: var(--ink-soft); font-size: 1.075rem; line-height: 1.85; margin: 0 0 1.2em; }
.essay-col p:last-child { margin-bottom: 0; }
.essay-col .subhead {
  font-family: var(--heading-font); font-weight: 700; color: var(--ink);
  font-size: 1.2rem; margin: 1.6em 0 .6em;
}
.chapter-head { text-align: center; max-width: 760px; margin: 0 auto 36px; }
.chapter-index { font-family: var(--heading-font); font-weight: 800; font-size: clamp(2.6rem, 5.5vw, 3.6rem); line-height: 1; color: var(--accent); opacity: .17; }
.chapter-title { font-size: clamp(1.6rem, 3.1vw, 2.2rem); margin: 4px 0 0; text-wrap: balance; }

/* Calligraphy set as real type */
.calligraphy {
  max-width: 560px; margin: 0 auto 6px; text-align: center;
  background: var(--mist); border: 1px solid var(--line); border-radius: var(--radius); padding: 42px 24px;
}
.calligraphy .cjk {
  font-family: "Nanum Myeongjo", "Noto Serif KR", "Batang", "SimSun", serif;
  font-size: clamp(2.8rem, 8vw, 4.6rem); letter-spacing: .28em; color: var(--ink); line-height: 1.1;
  padding-left: .28em; /* balance the trailing letter-spacing */
}
.calligraphy figcaption { margin-top: 18px; color: var(--ink-soft); font-size: .96rem; }

/* Figures */
.essay-fig { margin: 0; }
.essay-fig .ph { border-radius: var(--radius); }
.essay-fig img { width: 100%; height: auto; display: block; border-radius: var(--radius); border: 1px solid var(--line); }
.essay-pair .essay-fig img { aspect-ratio: 4 / 3; object-fit: cover; }
.essay-fig figcaption { margin-top: 10px; text-align: center; color: var(--ink-soft); font-size: .86rem; }
.essay-media { max-width: 1000px; margin: 34px auto 0; }
.essay-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; max-width: 1000px; margin: 34px auto 0; }
.essay-portrait { max-width: 460px; margin: 40px auto 0; }
.ox-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; max-width: 860px; margin: 30px auto 0; }
@media (max-width: 640px) { .essay-pair { grid-template-columns: 1fr; } .ox-grid { grid-template-columns: repeat(2, 1fr); } }

/* Pull-quote band */
.pullband { background: var(--mist); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.pullquote { text-align: center; max-width: 840px; margin: 0 auto; }
.pullquote .intro { color: var(--ink-soft); font-size: 1.06rem; line-height: 1.8; margin-bottom: 24px; }
.pullquote .pq {
  display: block; font-family: var(--heading-font); font-weight: 700; text-wrap: balance;
  font-size: clamp(1.5rem, 3.4vw, 2.25rem); color: var(--ink); line-height: 1.3;
}
.pullquote .pq::before { content: "\201C"; color: var(--accent); margin-right: .04em; }
.pullquote .pq::after { content: "\201D"; color: var(--accent); margin-left: .04em; }
.pullquote .pq.cjk {
  font-family: "Nanum Myeongjo", "Noto Serif KR", "Batang", "SimSun", var(--heading-font), serif;
  letter-spacing: .06em;
}
.pullquote .attrib { color: var(--ink-soft); font-size: .98rem; font-style: italic; margin-top: 18px; }

@media (prefers-reduced-motion: reduce) {
  .video-frame:hover .video-play span { transform: none; }
}

/* ======================================================================
   Register Concern — dual audience forms + contact
   ====================================================================== */
/* ======================================================================
   Testimonials — video grid + cross-scrolling review wall
   ====================================================================== */
.tw-videos { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 20px; }
.tw-video { border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); box-shadow: var(--shadow-sm); background: var(--surface); transition: transform .25s ease, box-shadow .25s ease; }
.tw-video:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.tw-video-thumb { position: relative; aspect-ratio: 16 / 9; display: block; overflow: hidden; }
.tw-video-thumb[data-yt] { cursor: pointer; }
.tw-video-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.tw-video-thumb .ph { position: absolute; inset: 0; border-radius: 0; }
.tw-video-thumb .video-play span { width: 54px; height: 54px; font-size: 1.4rem; }
.tw-video-iframe { width: 100%; aspect-ratio: 16 / 9; border: 0; display: block; }
.tw-video-meta { padding: 12px 15px; }
.tw-video-name { font-weight: 600; color: var(--ink); font-size: .95rem; margin: 0; }
.tw-video-city { color: var(--ink-soft); font-size: .82rem; margin: 2px 0 0; }

.tw-wall { display: flex; flex-direction: column; gap: 26px; }
.tw-row { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent); }
.tw-track { display: flex; width: max-content; will-change: transform; }
.tw-dup { display: contents; }
.tw-left  { animation: twLeft 68s linear infinite; }
.tw-right { animation: twRight 68s linear infinite; }
.tw-row:hover .tw-track { animation-play-state: paused; }
@keyframes twLeft  { from { transform: translateX(0); }    to { transform: translateX(-50%); } }
@keyframes twRight { from { transform: translateX(-50%); } to { transform: translateX(0); } }

.tw-card {
  flex: 0 0 340px; width: 340px; margin-right: 28px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 22px 22px 20px; box-shadow: var(--shadow-sm);
  transition: background-color .3s ease, transform .3s ease, box-shadow .3s ease;
}
.tw-card:hover { background: var(--cta-bg); transform: translateY(-5px); box-shadow: var(--shadow-md); }
.tw-stars { color: #f5b301; font-size: .82rem; letter-spacing: 1px; margin-bottom: 10px; }
.tw-quote { color: var(--ink-soft); font-size: .95rem; line-height: 1.6; margin: 0 0 16px; }
.tw-foot { display: flex; align-items: center; gap: 12px; }
.tw-avatar { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent); font-weight: 700; font-size: .9rem; flex-shrink: 0; }
.tw-name { display: block; font-weight: 600; color: var(--ink); font-size: .92rem; margin: 0; line-height: 1.2; }
.tw-city { display: block; color: var(--ink-soft); font-size: .8rem; margin-top: 3px; line-height: 1.2; }
.tw-card:hover .tw-quote { color: var(--cta-fg-2); }
.tw-card:hover .tw-name  { color: var(--cta-fg); }
.tw-card:hover .tw-city  { color: var(--cta-fg-2); }
.tw-card:hover .tw-avatar { background: var(--surface); color: var(--accent); }
[data-bs-theme="dark"] .tw-card:hover .tw-avatar { background: var(--accent-vivid); color: var(--on-vivid); }

/* Video testimonial cards inside the marquee (homepage first row) */
.twv-card {
  flex: 0 0 300px; width: 300px; margin-right: 28px; display: block;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  overflow: hidden; box-shadow: var(--shadow-sm); text-decoration: none;
  transition: transform .3s ease, box-shadow .3s ease, border-color .3s ease;
}
a.twv-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-md); border-color: rgba(var(--accent-rgb), .4); }
.twv-thumb { position: relative; display: block; aspect-ratio: 16 / 9; overflow: hidden; background: var(--mist); }
.twv-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .4s ease; }
a.twv-card:hover .twv-thumb img { transform: scale(1.05); }
.twv-thumb .video-play span { width: 52px; height: 52px; font-size: 1.35rem; transition: transform .25s ease; }
a.twv-card:hover .video-play span { transform: scale(1.08); }
.twv-name { display: block; padding: 13px 16px; font-family: var(--heading-font); font-weight: 700; color: var(--ink); font-size: .95rem; line-height: 1.25; }

/* ---- Free-demo modal (includes/demo-modal.php + site.js initDemoModal) ---
   Bootstrap's own modal supplies the focus trap, Escape, scroll lock and focus
   restore; everything visual below overrides its --bs-modal-* defaults, which
   have never been used on this site and would otherwise paint a plain white box.

   Stacking: Medilab parks .scroll-top at z-index 99999 and the mobile .navmenu
   at 9997/9998, both far above Bootstrap's 1055 — so the sheet is lifted clear
   and the back-to-top button steps aside, exactly as the yt-modal does below. */
.demo-modal { z-index: 100000; }
.demo-modal + .modal-backdrop,
.modal-backdrop { z-index: 99998; background: var(--dm-scrim); }
.modal-backdrop.show { opacity: 1; }        /* the tint already carries its own alpha */
/* Bootstrap compensates for the vanished scrollbar on .sticky-top (the header),
   but these two are plain position:fixed and would jog sideways on open. */
body.modal-open .scroll-top,
body.modal-open .whatsapp-float { opacity: 0; visibility: hidden; pointer-events: none; }

.demo-modal .modal-dialog { max-width: 560px; margin-inline: auto; padding: 16px; }

/* Materialise from the CTA that opened it: JS writes the trigger's viewport
   centre into --dm-ox/--dm-oy, and since the dialog is itself centred, its own
   box coordinate for that point is 50% + (point − viewport centre). */
.demo-modal { --dm-ox: 50vw; --dm-oy: 50vh; }
.demo-modal.fade .modal-dialog {
  transform: scale(.96) translateY(10px);
  transform-origin: calc(var(--dm-ox) - 50vw + 50%) calc(var(--dm-oy) - 50vh + 50%);
  transition: transform .38s cubic-bezier(.22, 1, .36, 1), opacity .38s cubic-bezier(.22, 1, .36, 1);
  opacity: 0;
}
.demo-modal.show .modal-dialog { transform: scale(1) translateY(0); opacity: 1; }

/* The sheet is a .vf-card wearing glass. Everything else it needs — rail, head,
   grid, controls, submit, note, success panel — now comes from the shared .vf-*
   block above, so the modal and the inline forms cannot drift apart again. */
.dm-card {
  padding: clamp(26px, 4vw, 38px);
  border-color: var(--dm-brd);
  background: var(--dm-glass);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
  backdrop-filter: blur(18px) saturate(140%);
  box-shadow: var(--dm-shadow);
}
@supports not ((backdrop-filter: blur(18px)) or (-webkit-backdrop-filter: blur(18px))) {
  .dm-card { background: var(--surface); }
}
/* Inputs sit on their own solid ground: a translucent field over translucent
   glass is the one material stack that never stays legible. */
.dm-card .form-control,
.dm-card .form-select { background: var(--dm-field); }
.dm-card .vf-head { padding-right: 44px; }   /* clear the close button */

.dm-close {
  position: absolute; top: 16px; right: 16px; z-index: 3;
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); color: var(--ink-soft); cursor: pointer; font-size: .8rem;
  transition: color .2s ease, border-color .2s ease, transform .2s ease;
}
.dm-close:hover { color: var(--accent); border-color: var(--accent); transform: rotate(90deg); }
.dm-close:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; }

/* Phones: a floating sheet — still bottom-anchored so the submit button stays
   thumb-reachable, but inset from the bezel on all four sides rather than run
   edge to edge, which read as cramped against the screen. */
@media (max-width: 575.98px) {
  .demo-modal .modal-dialog {
    margin: 0; max-width: none; min-height: 100%; align-items: flex-end;
    /* Padding, not margin, matching the desktop rule above — the dialog stays
       the scroll container. The bottom inset also clears the home indicator, so
       the card no longer carries its own safe-area padding. */
    padding: 14px 14px calc(14px + env(safe-area-inset-bottom, 0px));
  }
  .dm-card {
    /* All four corners now it floats: a squared-off bottom edge with no border
       under it reads as a rendering fault once there is backdrop below. */
    border-radius: 22px;
    max-height: calc(92vh - 28px);   /* less the dialog's own inset */
    overflow-y: auto;
    /* Wider than the inline card's 20px — a modal has no page gutter helping it. */
    padding: 26px 22px 24px;
  }
  /* Grab handle — tells you the sheet is a sheet before you try to drag it. */
  .dm-card .vf-head::before {
    content: ""; display: block; width: 38px; height: 4px; margin: 0 auto 18px;
    border-radius: 999px; background: var(--line);
  }
  .demo-modal.fade .modal-dialog { transform: translateY(24px); }
  .demo-modal.show .modal-dialog { transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .demo-modal.fade .modal-dialog { transform: none; transition: opacity .2s ease; }
  .demo-modal.show .modal-dialog { transform: none; }
  .dm-close:hover { transform: none; }
  .vf-submit:active { transform: none; }
}

/* Dependency-free YouTube modal player (site.js initVideoModal) */
.yt-modal { position: fixed; inset: 0; z-index: 2000; opacity: 0; visibility: hidden; transition: opacity .25s ease, visibility .25s ease; }
.yt-modal.is-open { opacity: 1; visibility: visible; }
.yt-modal-backdrop { position: absolute; inset: 0; background: rgba(9, 20, 19, .82); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.yt-modal-box { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: min(94vw, 960px); }
.yt-modal-frame { position: relative; aspect-ratio: 16 / 9; background: #000; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-md); }
.yt-modal-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.yt-modal-close { position: absolute; z-index: 2; top: clamp(12px, 2.5vh, 26px); right: clamp(12px, 2.5vw, 30px); width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(255, 255, 255, .35); background: rgba(255, 255, 255, .14); color: #fff; -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); cursor: pointer; display: grid; place-items: center; font-size: 1.05rem; transition: background .2s ease, transform .2s ease, border-color .2s ease; }
.yt-modal-close:hover { background: var(--accent-vivid); border-color: var(--accent-vivid); color: var(--on-vivid); transform: scale(1.06); }
.yt-modal-close:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
body.yt-modal-lock { overflow: hidden; }
/* The Medilab "back to top" button sits at z-index 99999 — keep it out of the
   modal (pointless while scroll is locked anyway). */
body.yt-modal-lock .scroll-top { opacity: 0; visibility: hidden; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .yt-modal { transition: none; } }

@media (prefers-reduced-motion: reduce) {
  .tw-track { animation: none; }
  .tw-row { overflow-x: auto; }
  .tw-card, .tw-video, .twv-card, .twv-thumb img { transition: none; }
}

/* Google review button */
.btn-review {
  display: inline-flex; align-items: center; gap: 9px;
  background: var(--surface); color: var(--ink); border: 1.5px solid var(--line); border-radius: 999px;
  padding: 10px 20px; font-family: var(--nav-font); font-weight: 600; text-decoration: none;
  transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
}
.btn-review:hover { color: var(--ink); border-color: var(--accent); box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.btn-review .star { color: #f5b301; font-size: 1.05rem; }

/* .reg-switch / .reg-switch-btn had their own plain-pill styling here. They now
   share the glass segmented control defined with the events filter at the end
   of this file — one component instead of two that looked almost alike. */

.reg-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden; }
.reg-formhead {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--accent-fill); color: var(--on-fill); padding: 15px 22px;
  font-family: var(--heading-font); font-weight: 700; font-size: 1.05rem; letter-spacing: .3px; text-transform: uppercase;
}
.reg-formhead i { font-size: 1.5rem; opacity: .92; }
.reg-body { padding: 26px; }
.reg-subtitle { font-size: 1.16rem; color: var(--ink); margin: 0 0 18px; }

/* The .reg-map / .reg-contact / .reg-address / .reg-contact-list block that
   used to sit here was dead: it styled the standalone Register Concern page,
   which merged into direct.php (now a 301 stub). Verified unreferenced across
   every .php and .html file before removal. direct.php's contact section uses
   .feature-card and .sv-map instead. */

/* Header CTA pulse (Request Free Demo) */
@keyframes ctaPulse {
  0%   { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), .55); }
  70%  { box-shadow: 0 0 0 12px rgba(var(--accent-rgb), 0); }
  100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
}
.cta-btn.cta-pulse { animation: ctaPulse 1.9s ease-out infinite; }
.cta-btn.cta-pulse:hover { animation: none; }
@media (prefers-reduced-motion: reduce) { .cta-btn.cta-pulse { animation: none; } }

/* ======================================================================
   "On this page" section rail  (includes/section-nav.php + site.js)
   A fixed right-edge spine inside a frosted panel: closed it is a slim pill of
   dots on a hairline track, and on hover / keyboard focus it grows leftward to
   let the labels out. Everything is token-driven, so it themes with the rest of
   the site. Desktop only.
   ====================================================================== */
.section-rail {
  --rail-pad:   13px;   /* panel inset; also sets the dot column's x-offset */
  --rail-dot:   10px;   /* dot diameter — the track aligns to its centre */
  --rail-row:   32px;   /* row height; the track insets by half a row */
  --rail-label: 200px;  /* how far a label may unfurl before it wraps */
  --rail-panel: rgba(255, 255, 255, .93);

  position: fixed;
  top: 50%;
  right: clamp(14px, 2.2vw, 34px);
  transform: translateY(-50%);
  z-index: 1010;                 /* above content, under the sticky header (1020) */
  display: flex;
  justify-content: flex-end;
  opacity: 1;
  transition: opacity .35s ease, transform .35s ease;
}
[data-bs-theme="dark"] .section-rail { --rail-panel: rgba(15, 29, 24, .90); }

/* Rendered hidden; site.js reveals it once the scroll-spy is wired, so the
   rail never shows up as a row of inert dots when JS is unavailable. */
.section-rail[hidden] { display: none; }

/* Parked out of the way once the reader reaches the footer. */
.section-rail.is-away { opacity: 0; pointer-events: none; transform: translate(14px, -50%); }

/* The frosted panel is always on — closed it reads as a slim pill of dots,
   open it simply grows. Only the shadow lifts, so opening feels like the panel
   coming forward rather than materialising out of nothing. */
.section-rail-panel {
  display: flex;
  flex-direction: column;
  padding: var(--rail-pad);
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--rail-panel);
  box-shadow: var(--shadow-sm);
  backdrop-filter: blur(14px) saturate(120%);
  -webkit-backdrop-filter: blur(14px) saturate(120%);
  transition: box-shadow .3s ease, border-color .3s ease;
}
.section-rail:hover .section-rail-panel,
.section-rail:focus-within .section-rail-panel,
.section-rail.is-open .section-rail-panel { box-shadow: var(--shadow-md); }

.section-rail-list { list-style: none; margin: 0; padding: 0; }
/* Positioned wrapper so the track measures against the spy rows only — never
   stretching down past the hairline into the action below. */
.section-rail-group { position: relative; }

/* The track spans dot-centre to dot-centre, so it never overshoots the ends. */
.section-rail-track {
  position: absolute;
  top: calc(var(--rail-row) / 2);
  bottom: calc(var(--rail-row) / 2);
  left: calc(var(--rail-dot) / 2 - 1px);
  width: 2px;
  border-radius: 2px;
  background: var(--line);
  overflow: hidden;
}
.section-rail-fill {
  position: absolute;
  inset: 0 0 auto 0;
  height: var(--rail-progress, 0%);
  background: linear-gradient(180deg, var(--accent-vivid), var(--accent));
  border-radius: 2px;
  transition: height .3s cubic-bezier(.4, 0, .2, 1);
}

/* Dot leads, label unfurls to its right. The panel is pinned to the screen
   edge, so opening carries the dot column leftward — one motion, one easing. */
.section-rail-link {
  display: flex;
  align-items: center;
  /* Collapses with the label, so the dot column hugs the screen edge when
     closed instead of floating 12px in from it. */
  gap: 0;
  height: var(--rail-row);
  text-decoration: none;
  color: var(--ink-soft);
  font-family: var(--nav-font);
  font-size: .82rem;
  font-weight: 500;
  line-height: 1.25;
  white-space: nowrap;
  transition: gap .38s cubic-bezier(.4, 0, .2, 1), color .25s ease;
}
.section-rail:hover .section-rail-link,
.section-rail:focus-within .section-rail-link,
.section-rail.is-open .section-rail-link { gap: 12px; }
.section-rail-link:hover { color: var(--ink); }

.section-rail-dot {
  position: relative;
  flex: 0 0 var(--rail-dot);
  width: var(--rail-dot);
  height: var(--rail-dot);
  border-radius: 50%;
  background: var(--bg);
  border: 2px solid var(--line);
  transition: background .3s ease, border-color .3s ease, transform .3s cubic-bezier(.34, 1.4, .64, 1), box-shadow .3s ease;
}
/* Passed: the fill has swept over this dot. Active: the reader is here. */
.section-rail-link.is-passed .section-rail-dot { background: var(--accent-vivid); border-color: var(--accent-vivid); }
.section-rail-link:hover .section-rail-dot { border-color: var(--accent); transform: scale(1.2); }
.section-rail-link.is-active .section-rail-dot {
  background: var(--accent-vivid);
  border-color: var(--accent-vivid);
  transform: scale(1.3);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .16), 0 0 14px 2px rgba(var(--accent-rgb), .5);
}
.section-rail-link.is-active { color: var(--ink); font-weight: 600; }

/* Labels stay in the DOM (screen readers read them); they are clipped, not
   removed. max-width drives the panel's own width, so the panel grows with them. */
.section-rail-text {
  max-width: 0;
  overflow: hidden;
  opacity: 0;
  text-align: left;
  transform: translateX(-6px);
  transition: max-width .38s cubic-bezier(.4, 0, .2, 1), opacity .28s ease, transform .38s cubic-bezier(.4, 0, .2, 1);
}
.section-rail:hover .section-rail-text,
.section-rail:focus-within .section-rail-text,
.section-rail.is-open .section-rail-text {
  max-width: var(--rail-label);
  opacity: 1;
  transform: none;
}

/* The action sits below a hairline: a destination, not a section of reading. */
.section-rail-actions { margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--line); }
.section-rail-link.is-cta { color: var(--accent); font-weight: 600; }
.section-rail-link.is-cta .section-rail-dot {
  background: var(--accent-vivid);
  border-color: var(--accent-vivid);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .14);
}
.section-rail-link.is-cta:hover { color: var(--accent-hover); }
.section-rail-link.is-cta:hover .section-rail-dot { transform: scale(1.25); box-shadow: 0 0 0 5px rgba(var(--accent-rgb), .2); }

.section-rail-link:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 10px; }

/* Anchors that aren't <section> still need to clear the sticky header. */
#enquire { scroll-margin-top: 90px; }

@media (max-width: 991px) { .section-rail { display: none; } }
@media (prefers-reduced-motion: reduce) {
  .section-rail-fill, .section-rail-text, .section-rail-dot, .section-rail-link { transition: none; }
}

/* ======================================================================
   About VATECH India — anniversary story
   ====================================================================== */
/* .story-hero shares this treatment so every About-cluster page (Network,
   Business Field) opens the same way as About VATECH India / Our Philosophy. */
.about-hero, .story-hero {
  position: relative; text-align: center; overflow: clip;
  padding: clamp(52px, 8vw, 84px) 0 clamp(38px, 6vw, 56px);
  background: linear-gradient(180deg, var(--accent-soft) 0%, var(--bg) 74%);
  border-bottom: 1px solid var(--line);
}
.about-hero::before, .story-hero::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(520px 320px at 82% 8%, rgba(var(--accent-rgb), .10), transparent 60%),
    radial-gradient(420px 260px at 14% 20%, rgba(var(--accent-rgb), .07), transparent 60%);
}
.about-hero .container, .story-hero .container { position: relative; z-index: 1; }
.about-hero .eyebrow, .story-hero .eyebrow { display: inline-block; }
.about-hero h1, .story-hero h1 { font-size: clamp(1.9rem, 4.4vw, 3rem); margin: 16px auto 14px; max-width: 20ch; text-wrap: balance; }
.about-sub, .story-hero-sub { color: var(--ink-soft); font-size: 1.16rem; max-width: 52ch; margin: 0 auto 30px; }
.story-hero .breadcrumbs { justify-content: center; }
.story-hero .breadcrumbs a { color: var(--ink-soft); }
.story-hero .breadcrumbs a:hover { color: var(--accent); }

/* Anniversary lockup: giant accent "10" beside a Years / place label */
.anniv { display: inline-flex; align-items: center; gap: clamp(12px, 2.4vw, 22px); margin: 6px 0 4px; }
.anniv-num {
  font-family: var(--heading-font); font-weight: 800; line-height: .82; letter-spacing: -.02em;
  font-size: clamp(5rem, 15vw, 9.5rem);
  color: var(--accent);   /* fallback where background-clip:text is unsupported */
  background: linear-gradient(158deg, var(--accent-vivid) 0%, var(--accent) 72%);
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
}
.anniv-side { display: flex; flex-direction: column; align-items: flex-start; text-align: left; }
.anniv-yrs { font-family: var(--heading-font); font-weight: 800; font-size: clamp(1.5rem, 3.8vw, 2.5rem); color: var(--ink); line-height: 1; }
.anniv-sub { color: var(--ink-soft); font-weight: 700; font-size: clamp(.72rem, 1.5vw, .86rem); text-transform: uppercase; letter-spacing: 2px; margin-top: 8px; }

/* Stat chips */
.about-stats { list-style: none; margin: 32px 0 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: clamp(18px, 4vw, 44px); }
.about-stats li { display: flex; flex-direction: column; align-items: center; }
.about-stats b { font-family: var(--heading-font); font-weight: 800; font-variant-numeric: tabular-nums; font-size: clamp(1.6rem, 3.2vw, 2.2rem); color: var(--ink); line-height: 1; }
.about-stats span { color: var(--ink-soft); font-size: .9rem; margin-top: 7px; max-width: 16ch; text-align: center; }

/* ---- "Our Stories" milestone timeline (scroll-spy) --------------------- */
/* section, .section set overflow:clip in main.css, which kills position:sticky
   on descendants — release it here so the year-rail can stick. */
.story-section { overflow: visible; }
.story { display: grid; grid-template-columns: minmax(96px, 190px) 1fr; gap: clamp(22px, 5vw, 72px); align-items: start; }

.story-rail { position: sticky; top: 96px; align-self: start; --story-row: 40px; }
.story-railgroup { position: relative; }
.story-years { list-style: none; margin: 0; padding: 0; }
.story-track {
  position: absolute; top: calc(var(--story-row) / 2); bottom: calc(var(--story-row) / 2);
  left: 5px; width: 2px; border-radius: 2px; background: var(--line); overflow: hidden;
}
.story-fill {
  position: absolute; inset: 0 0 auto 0; height: var(--story-progress, 0%);
  background: linear-gradient(180deg, var(--accent-vivid), var(--accent));
  border-radius: 2px; transition: height .3s cubic-bezier(.4, 0, .2, 1);
}
.story-year {
  display: flex; align-items: center; gap: 14px; height: var(--story-row);
  text-decoration: none; color: var(--ink-soft);
  font-family: var(--heading-font); font-weight: 700; font-variant-numeric: tabular-nums;
  font-size: 1.02rem; transition: color .25s ease;
}
.story-dot {
  position: relative; z-index: 1; flex: 0 0 12px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--bg); border: 2px solid var(--line);
  transition: background .3s ease, border-color .3s ease, transform .3s cubic-bezier(.34, 1.4, .64, 1), box-shadow .3s ease;
}
.story-year:hover { color: var(--ink); }
.story-year:hover .story-dot { border-color: var(--accent); transform: scale(1.15); }
.story-year.is-passed .story-dot { background: var(--accent-vivid); border-color: var(--accent-vivid); }
.story-year.is-active { color: var(--ink); font-weight: 800; }
.story-year.is-active .story-dot {
  background: var(--accent-vivid); border-color: var(--accent-vivid); transform: scale(1.35);
  box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .16), 0 0 14px 2px rgba(var(--accent-rgb), .5);
}
.story-year:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; border-radius: 8px; }

.story-list { list-style: none; margin: 0; padding: 0; }
.story-item { scroll-margin-top: 96px; max-width: 64ch; }
.story-item + .story-item { margin-top: clamp(30px, 5vw, 54px); }
.story-item-yr { font-family: var(--heading-font); font-weight: 800; font-variant-numeric: tabular-nums; font-size: clamp(1.9rem, 3.6vw, 2.6rem); line-height: 1; color: var(--accent); }
.story-head { font-size: clamp(1.2rem, 2.4vw, 1.5rem); margin: 8px 0 8px; text-wrap: balance; }
.story-item p { color: var(--ink-soft); font-size: 1.06rem; line-height: 1.8; margin: 0; }

@media (max-width: 991px) {
  .story { grid-template-columns: 1fr; gap: 0; }
  .story-rail { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .story-fill, .story-dot { transition: none; }
}

/* ---- "Proud Milestones" — scroll-driven card stack --------------------
   Progressive enhancement. Default (mobile / no-JS / reduced-motion) is a
   plain readable list of glass cards. On wide screens with motion allowed,
   site.js adds .is-live: the section pins and scroll drives a right-aligned
   card stack, a big vertical year index (left) and a vertical progress /
   "stat" bar overlaid on the stack (right). */
.milestones { overflow: visible; }               /* release overflow:clip so the stack can pin (story-section also sets this) */
.mstack { position: relative; --m-step: 52vh; }  /* --m-step = scroll distance per year */

/* --- shared card look (glassmorphic) --- */
.mcard {
  background: var(--glass-bg); border: 1px solid var(--glass-brd);
  border-radius: calc(var(--radius) + 6px); padding: clamp(22px, 3vw, 38px);
  box-shadow: var(--shadow-md);
  -webkit-backdrop-filter: blur(14px) saturate(140%); backdrop-filter: blur(14px) saturate(140%);
  transition: background .4s ease, color .4s ease, border-color .4s ease, box-shadow .4s ease;
}
.mcard-yr { display: block; font-family: var(--heading-font); font-weight: 800; font-variant-numeric: tabular-nums; letter-spacing: .05em; color: var(--accent); font-size: 1rem; }
.mcard-head { margin: .4em 0 .5em; font-size: clamp(1.3rem, 2.5vw, 2rem); text-wrap: balance; }
.mcard-body { margin: 0; color: var(--ink-soft); font-size: clamp(1.03rem, 1.15vw, 1.13rem); line-height: 1.78; }

/* --- static fallback: a centred vertical list; index-only chrome hidden --- */
.mstack:not(.is-live) .mstack-grid { display: block; }
.mstack:not(.is-live) .mstack-wheel,
.mstack:not(.is-live) .mstack-nav,
.mstack:not(.is-live) .mstack-bar { display: none; }
.mstack:not(.is-live) .mstack-cards { max-width: 720px; margin-inline: auto; }
.mstack:not(.is-live) .mcard + .mcard { margin-top: 18px; }

/* --- live (interactive carousel) mode --- */
.mstack.is-live { --card-g: clamp(48px, 4.5vw, 84px); --card-w: clamp(440px, 46vw, 820px); }
.mstack.is-live .mstack-sticky {
  position: relative; min-height: clamp(600px, 88vh, 800px);
  display: flex; align-items: center;
}
/* Year stays at the normal body margin (default container); only the card
   column breaks out to the right screen edge (full-bleed negative-margin). */
.mstack.is-live .mstack-grid {
  display: grid; grid-template-columns: minmax(64px, auto) 1fr;
  gap: clamp(24px, 4vw, 56px); align-items: center; width: 100%;
  margin-right: calc(50% - 50vw);
}

/* Left — year dial: a vertical stack that mirrors the card deck. JS moves the
   years up/down on scroll; the active year is enlarged, bold and gradient-filled,
   the others are dimmed and smaller (same manner as the background cards). */
.mstack { --year-grad: linear-gradient(180deg, #f0324c 0%, #a3122a 100%); }
.mstack-wheel {
  position: relative; justify-self: start;
  width: clamp(130px, 13vw, 190px); height: clamp(340px, 60vh, 520px);
  user-select: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 78%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 22%, #000 78%, transparent 100%);
}
.mstack-wheel-list { position: absolute; inset: 0; margin: 0; padding: 0; list-style: none; }
.mstack-wheelyr {
  position: absolute; top: 50%; left: 0; width: 100%;
  font-family: var(--heading-font); font-weight: 800; font-variant-numeric: tabular-nums;
  font-size: clamp(1.8rem, 2.8vw, 2.15rem); line-height: 1; letter-spacing: .01em;
  color: var(--ink-soft); will-change: transform, opacity;
  transition: color .3s ease, font-size .3s ease;   /* transform/opacity are JS-driven per frame */
}
.mstack-wheelyr.is-active {
  font-weight: 900;
  font-size: clamp(2.8rem, 4.7vw, 3.8rem);
  color: var(--accent);   /* fallback where background-clip:text is unsupported */
  background: var(--year-grad);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}

/* Center/right — the stack area */
.mstack-cards { position: relative; }
.mstack.is-live .mstack-cards { height: clamp(380px, 60vh, 520px); }
.mstack.is-live .mcard {
  position: absolute; top: 50%; right: var(--card-g); left: auto;
  width: var(--card-w); margin: 0;
  padding: clamp(28px, 2.6vw, 46px);
  transform-origin: 50% 50%; will-change: transform, opacity;
  /* JS sets transform/opacity/z-index (translate keeps the -50% vertical centering) */
}
.mstack.is-live .mcard.is-front {
  background: linear-gradient(150deg, var(--accent-vivid) 0%, var(--accent-hover) 100%);
  color: var(--on-fill); border-color: transparent;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  box-shadow: 0 24px 54px rgba(var(--accent-rgb), .30);
}
[data-bs-theme="dark"] .mstack.is-live .mcard.is-front {
  background: linear-gradient(150deg, #37765f 0%, var(--accent-fill) 100%);
  box-shadow: 0 24px 54px rgba(0, 0, 0, .5);
}
[data-bs-theme="dark"] .mstack { --year-grad: linear-gradient(180deg, #6ef2c6 0%, #04b981 100%); }
.mstack.is-live .mcard.is-front .mcard-yr,
.mstack.is-live .mcard.is-front .mcard-head { color: var(--on-fill); }
.mstack.is-live .mcard.is-front .mcard-yr { opacity: .85; }
.mstack.is-live .mcard.is-front .mcard-body { color: var(--on-fill); opacity: .92; }

/* Right — vertical progress / stat bar, overlaid on the stack's right edge */
.mstack-bar {
  position: absolute; top: 50%; right: clamp(10px, 1.6vw, 20px); transform: translateY(-50%);
  height: min(56vh, 440px); z-index: 30; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.mstack-bar-count { font-family: var(--heading-font); font-weight: 800; font-variant-numeric: tabular-nums; font-size: .8rem; color: var(--ink-soft); letter-spacing: .04em; white-space: nowrap; }
.mstack-bar-count b { color: var(--accent); font-size: 1.02rem; }
.mstack-bar-track { position: relative; flex: 1 1 auto; width: 5px; border-radius: 5px; background: var(--line); overflow: hidden; }
.mstack-bar-fill { position: absolute; inset: 0 0 auto 0; height: 0%; background: linear-gradient(180deg, var(--accent-vivid), var(--accent)); border-radius: 5px; transition: height .18s linear; }
.mstack-bar-label { writing-mode: vertical-rl; text-transform: uppercase; letter-spacing: 3px; font-size: .62rem; color: var(--ink-soft); }

/* Prev / next carousel controls — centred just below the card stack */
.mstack-nav {
  position: absolute; bottom: calc(-1 * clamp(50px, 7vh, 78px));
  right: var(--card-g);   /* right edge aligns with the card stack's right edge */
  display: flex; gap: 16px; z-index: 40;
}
.mstack-arrow {
  width: clamp(46px, 3.4vw, 56px); height: clamp(46px, 3.4vw, 56px); border-radius: 50%;
  border: 0; background: var(--mist); color: var(--ink); cursor: pointer;
  display: grid; place-items: center; font-size: 1.25rem; line-height: 1;
  box-shadow: var(--shadow-sm);
  transition: background .2s ease, color .2s ease, transform .2s ease, opacity .2s ease;
}
.mstack-arrow:hover { background: var(--accent-vivid); color: var(--on-vivid); transform: translateY(-2px); }
.mstack-arrow:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.mstack-arrow:disabled { opacity: .3; cursor: default; box-shadow: none; }
.mstack-arrow:disabled:hover { background: var(--mist); color: var(--ink); transform: none; }

/* ---- The VATECH family chips ------------------------------------------ */
.brand-chip {
  display: flex; flex-direction: column; height: 100%; text-align: center;
  padding: 22px 18px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-sm);
  transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
}
.brand-chip:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); border-color: rgba(var(--accent-rgb), .4); }
.brand-chip b { font-family: var(--heading-font); font-weight: 800; color: var(--ink); font-size: 1.06rem; line-height: 1.2; }
.brand-chip span { color: var(--ink-soft); font-size: .9rem; margin-top: 7px; }

/* ======================================================================
   Network — themed interactive world map (recolours with dark/light)
   ====================================================================== */
.wmap-frame { position: relative; width: 100%; }   /* holds map + overlay legend (legend never scrolls with the map) */
.wmap-stage { width: 100%; }                        /* full-bleed band — map spans the screen width */
.wmap {
  position: relative; width: 100%; aspect-ratio: 2000 / 790; overflow: hidden;
  background: var(--mist);                           /* same colour as the section → seamless, uniform */
}
/* stable top + bottom fade into the section colour */
.wmap::after {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(to bottom, var(--mist) 0%, var(--mist-0) 13%, var(--mist-0) 80%, var(--mist) 100%);
}
.wmap-svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.wmap-land { fill: var(--map-land); transition: fill .3s ease; }

.wmap-pins { position: absolute; inset: 0; z-index: 3; }
.wmap-pin { position: absolute; transform: translate(-50%, -50%); width: 18px; height: 18px; display: grid; place-items: center; text-decoration: none; z-index: 1; transition: opacity .3s ease, transform .3s ease; }
.wmap-pin:hover, .wmap-pin:focus-visible { z-index: 6; outline: none; }
.wmap-dot {
  width: 13px; height: 13px; border-radius: 50%; background: var(--accent-vivid);
  border: 2px solid var(--surface); box-shadow: 0 0 0 2px rgba(var(--accent-rgb), .32), 0 2px 6px rgba(9, 20, 19, .3);
  transition: transform .2s ease, box-shadow .2s ease;
}
/* pulsing halo behind each dot */
.wmap-pin::before {
  content: ""; position: absolute; width: 13px; height: 13px; border-radius: 50%;
  background: rgba(var(--accent-rgb), .5); z-index: -1; animation: wmapPulse 2.6s ease-out infinite;
}
.wmap-pin--hq .wmap-dot { width: 15px; height: 15px; }
.wmap-pin--global { width: 22px; height: 22px; }
.wmap-pin--global .wmap-dot { width: 17px; height: 17px; box-shadow: 0 0 0 3px rgba(var(--accent-rgb), .4), 0 0 16px 2px rgba(var(--accent-rgb), .55); }
.wmap-pin--global::before { width: 17px; height: 17px; }
.wmap-pin:hover .wmap-dot, .wmap-pin:focus-visible .wmap-dot { transform: scale(1.28); }
.wmap-pin:focus-visible .wmap-dot { box-shadow: 0 0 0 3px var(--accent), 0 0 0 6px rgba(var(--accent-rgb), .3); }
@keyframes wmapPulse { 0% { transform: scale(1); opacity: .55; } 70%, 100% { transform: scale(3.4); opacity: 0; } }

/* tooltip — themed surface, appears above the pin */
.wmap-tip {
  position: absolute; bottom: calc(100% + 11px); left: 50%; transform: translateX(-50%) translateY(4px);
  min-width: 150px; max-width: 240px; padding: 9px 12px; border-radius: 10px; text-align: left;
  background: var(--surface); color: var(--ink); border: 1px solid var(--line); box-shadow: var(--shadow-md);
  opacity: 0; pointer-events: none; transition: opacity .2s ease, transform .2s ease; z-index: 7;
}
.wmap-tip b { display: block; font-family: var(--heading-font); font-size: .9rem; line-height: 1.15; }
.wmap-tip span { display: block; color: var(--ink-soft); font-size: .78rem; line-height: 1.3; margin-top: 3px; }
.wmap-tip::after { content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%); border: 6px solid transparent; border-top-color: var(--surface); }
.wmap-pin:hover .wmap-tip, .wmap-pin:focus-visible .wmap-tip { opacity: 1; transform: translateX(-50%) translateY(0); }

/* legend = glassmorphic per-type toggles, overlaid at the map's bottom-centre */
.wmap-legend {
  position: absolute; left: 50%; bottom: clamp(12px, 3.4%, 28px); transform: translateX(-50%); z-index: 4;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 4px; padding: 6px;
  border-radius: 999px; background: var(--glass-bg); border: 1px solid var(--glass-brd);
  -webkit-backdrop-filter: blur(12px) saturate(140%); backdrop-filter: blur(12px) saturate(140%);
  box-shadow: var(--shadow-md);
}
.wmap-lg {
  display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
  padding: 7px 14px; border: 0; border-radius: 999px; background: transparent;
  color: var(--ink); font-family: var(--nav-font); font-size: .82rem; font-weight: 600; line-height: 1;
  transition: background .2s ease, color .2s ease, opacity .2s ease;
}
.wmap-lg:hover { background: rgba(var(--accent-rgb), .12); }
.wmap-lg > span {
  width: 11px; height: 11px; border-radius: 50%; background: var(--accent-vivid);
  border: 2px solid var(--surface); box-shadow: 0 0 0 2px rgba(var(--accent-rgb), .3);
  transition: background .2s ease, box-shadow .2s ease;
}
.wmap-lg--global > span { width: 13px; height: 13px; }
.wmap-lg[aria-pressed="false"] { opacity: .55; color: var(--ink-soft); }
.wmap-lg[aria-pressed="false"] > span { background: transparent; box-shadow: inset 0 0 0 2px var(--ink-soft); }
.wmap-lg:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* type filters — hide a whole category when its toggle is switched off */
.wmap--hide-global .wmap-pin--global,
.wmap--hide-hq .wmap-pin--hq,
.wmap--hide-sub .wmap-pin--sub { opacity: 0; pointer-events: none; transform: translate(-50%, -50%) scale(.1); }

/* office card / section flash when a pin scrolls to it */
.net-office, #network-group { scroll-margin-top: 96px; }
.is-flash { animation: netFlash 1.5s ease-out 1; }
@keyframes netFlash {
  0%   { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
  14%  { box-shadow: 0 0 0 4px rgba(var(--accent-rgb), .55); }
  100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb), 0); }
}

/* Small phones: keep the map tall enough to read — give it a min width and let
   the band scroll sideways rather than squashing to a sliver. */
@media (max-width: 640px) {
  .wmap-stage { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .wmap { min-width: 680px; }
}
@media (max-width: 575px) {
  .wmap-dot { width: 11px; height: 11px; }
  .wmap-pin--global .wmap-dot { width: 14px; height: 14px; }
  .wmap-tip { min-width: 120px; font-size: .95em; }
}
@media (prefers-reduced-motion: reduce) {
  .wmap-pin::before { animation: none; }
  .wmap-land, .wmap-dot, .wmap-tip { transition: none; }
  .is-flash { animation: none; outline: 3px solid var(--accent); outline-offset: 3px; }
}

/* ============================================================
   Nested nav dropdown (Products › category › product)
   main.css already ships the second-level flyout — these are
   fixes/polish on top of it, kept here so the vendor theme
   stays untouched.
   ============================================================ */

/* --- Desktop flyout ---------------------------------------- */
@media (min-width: 1200px) {
  /* header.php appends two .navmenu-cta <li>s for the mobile panel. They are
     display:none up here, but display:none does NOT stop an element matching
     :last-child — and main.css:369 uses .navmenu > ul > li:last-child to strip
     the final item's 14px of right padding. With the CTAs appended, the last
     real nav item stopped matching and regained that padding, shoving the
     header CTAs 14px right on every page. Re-target the last VISIBLE item.
     The 3 tracks the two .navmenu-cta items in header.php — keep them in sync. */
  .navmenu > ul > li:nth-last-child(3) { padding-right: 0; }
  /* main.css opens the flyout at left:103%, leaving a 3% dead zone between the
     parent <li> and the submenu. The pointer crossing it exits the li and the
     menu snaps shut before you arrive. Butt them together instead. */
  .navmenu .dropdown .dropdown:hover > ul { left: 100%; }

  /* Second level sits alongside its parent, so give it a hairline of separation
     and a touch more room — product names are longer than category names. */
  .navmenu .dropdown .dropdown > ul {
    border: 1px solid var(--line);
    margin-left: 2px;
  }
  .navmenu .dropdown .dropdown > ul li { min-width: 230px; }

  /* Category row: label left, chevron pinned right (screenshot layout). */
  .navmenu .dropdown .dropdown > a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  /* That flyout opens sideways, so point the chevron sideways too. */
  .navmenu .dropdown .dropdown > a .toggle-dropdown {
    transform: rotate(-90deg);
    transition: transform .3s, color .3s;
  }
  /* Whole category row highlights on hover, so it reads as the open branch. */
  .navmenu .dropdown .dropdown:hover > a {
    background: var(--mist);
    color: var(--nav-dropdown-hover-color);
  }
}

/* --- Mobile accordion -------------------------------------- */
@media (max-width: 1199px) {
  /* main.css tints the nested level with a hardcoded rgba(33,37,41,.1) — a dark
     wash that vanishes against the dark theme's surfaces. Token it. And since
     every level adds margin:10px 20px, level 2 would land 40px in: tighten it
     and reclaim the width for the longer product names. */
  .navmenu .dropdown ul ul {
    background-color: var(--mist);
    border-left: 2px solid var(--line);
    margin: 6px 0 6px 12px;
    padding: 6px 0;
  }
  /* .navmenu a is nowrap on mobile, so a long name ("AS 200E Wireless Scanner")
     would push out of the drawer. Let the deepest level wrap and step it down. */
  .navmenu .dropdown ul ul a {
    white-space: normal;
    font-size: 15px;
    padding: 8px 16px;
  }

  /* --- CTAs inside the mobile panel (header.php .navmenu-cta) --------------
     The real header CTAs are display:none at this width, so these stand in.
     Full-width and equal, sitting below the link list with a rule above. */
  .navmenu .navmenu-cta { margin: 0 10px; }
  .navmenu .navmenu-cta:first-of-type {
    margin-top: 14px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
  }
  .navmenu .navmenu-cta + .navmenu-cta { margin-top: 10px; }
  .navmenu .navmenu-cta a {
    display: block;
    width: 100%;
    padding: 13px 18px;
    border-radius: 999px;
    font-size: 16px;
    font-weight: 600;
    text-align: center;
    white-space: normal;
    min-height: 48px;
  }
  .navmenu .navmenu-cta a.navmenu-cta-primary {
    background: var(--accent-vivid);
    color: var(--on-vivid);
  }
  .navmenu .navmenu-cta a.navmenu-cta-secondary {
    border: 1px solid var(--line);
    color: var(--heading);
  }

  /* --- "View all X" (header.php $render_nav) -------------------------------
     The dropdown row is the accordion toggle at this width, so a parent that
     has a page of its own — Software, and the six product categories — needs
     this to stay reachable. Set apart from its siblings: accent ink and a rule
     under it, so it reads as the way INTO the section rather than as one more
     item in it. */
  .navmenu .nav-viewall > a {
    color: var(--accent);
    font-weight: 600;
    border-bottom: 1px solid var(--line);
  }

  /* --- Theme toggle inside the panel (header.php .navmenu-theme) -----------
     The header's own .theme-toggle is a sibling of <nav>, so the open panel
     (position:fixed; inset:0) covers it completely. Hide it here rather than
     leave a control that looks live and cannot be tapped. */
  .header .theme-toggle { display: none; }

  .navmenu .navmenu-theme { margin: 10px 10px 0; }
  .navmenu .navmenu-theme-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    min-height: 48px;
    padding: 13px 18px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: none;
    color: var(--heading);
    font-family: var(--nav-font);
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
  }
  .navmenu .navmenu-theme-ic {
    display: inline-flex;
    font-size: 1.05rem;
    line-height: 0;
    color: var(--accent);
  }
  /* .th-sun / .th-moon swap is declared globally on .theme-toggle; repeat it
     for this button so the icon tracks the theme here too. */
  .navmenu .navmenu-theme-btn .th-sun { display: none; }
  [data-bs-theme="dark"] .navmenu .navmenu-theme-btn .th-moon { display: none; }
  [data-bs-theme="dark"] .navmenu .navmenu-theme-btn .th-sun { display: inline-block; }
}

/* ======================================================================
   Why Digital cluster — bespoke sections
   (why-digital · vatech-is-the-best · films-are-expensive ·
    film-cost-calculator · convenience · digital-system)

   These six pages were previously rendered by templates/content-page.php
   and had no vocabulary of their own. They now use the About-cluster
   grammar (.story-hero / .chapter-head / .essay-col / .pullband / .cta-band)
   plus the components below, each page carrying one signature artifact.

   Every colour goes through a token. The only literals are inside
   color-mix() against --on-fill, which is #ffffff in BOTH themes.
   ====================================================================== */

/* ---- Shared: anchor offsets -------------------------------------------
   main.css gives `section` scroll-margin-top:72px, but the sticky header is
   taller than that once .branding is counted — the same 96px correction the
   network + milestone sections already make. Grouped for every new anchor. */
#switch, #compare, #practice, #gains, #vatech, #explore,
#proof, #installs, #support, #value, #voices, #advantage,
#hidden, #ledger, #roi, #wages, #archive,
#calc, #assumptions, #beyond, #after,
#day, #pillars, #software, #chairside, #learn,
#sensor, #latitude, #dose, #clarity, #build,
#suite, #layers, #connects, #cleverdent, #ezdent, #ez3d,
#promise { scroll-margin-top: 96px; }
/* #cleverdent/#ezdent/#ez3d are software.php's three title anchors, read from
   `legacy_id` in data/software.php — r2.html still links #ezdent and #ez3d. The
   same declaration also sits at software.css:287, which is where it used to
   live; the hub no longer loads that file, and the rule is inert on the detail
   pages (their ids are #top/#overview/#features/…), so the duplicate is
   harmless. Delete that one when software.css is next opened for other reasons.

   Note what is NOT in the list above: #top. A bare `#top` here would carry id
   specificity and beat `body.pstore-page [id]` in product-store.css, silently
   re-breaking the product store's own anchor offset under its taller sticky
   header — the same trap that caught #form. Every About-cluster page lets
   main.css's 72px stand on its hero instead. */

/* ---- Shared: ghost button on a CTA band -------------------------------
   .btn-ghost's default `background: var(--surface)` is a white/near-black
   pill that fights the gradient band. On the band it becomes an outline.
   --on-fill is #ffffff in both themes, so the color-mix is theme-safe. */
.cta-band .btn-ghost {
  background: transparent; color: var(--on-fill);
  border-color: color-mix(in srgb, var(--on-fill) 45%, transparent);
}
.cta-band .btn-ghost:hover {
  background: color-mix(in srgb, var(--on-fill) 12%, transparent);
  border-color: var(--on-fill); color: var(--on-fill);
}

/* ---- Shared: the bar primitive ----------------------------------------
   One bar, reused by the value ladder (.vb-), the cost ledger (.cl-), the
   dose chart (.ds-dose-) and the calculator's 5-year projection (.calc-year-).
   Width is STATIC — set --w (0…1) on the fill. The grow animation is layered
   on only once AOS has initialised, so with JS off (or AOS blocked) every bar
   renders at its correct full width instead of collapsing to nothing. */
.wd-bar {
  position: relative; height: 10px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--ink) 9%, transparent);
}
.wd-bar-fill {
  display: block; height: 100%; border-radius: inherit;
  width: calc(var(--w, 1) * 100%);
  background: var(--accent-vivid);
  transform-origin: left;
}
[data-aos].aos-init .wd-bar-fill {
  transform: scaleX(0);
  transition: transform .8s cubic-bezier(.22, .61, .36, 1);
}
[data-aos].aos-animate .wd-bar-fill { transform: scaleX(1); }

/* ---- Shared: numbered step list ---------------------------------------
   why-digital #compare (two columns, film vs digital) and convenience #learn
   (one column). The connector is drawn on the <li>, not the marker, so the
   last step can suppress it without a :last-child rule on a pseudo-element. */
.wd-steps { list-style: none; margin: 0; padding: 0; counter-reset: wdstep; }
.wd-step {
  position: relative; display: flex; gap: 14px; align-items: flex-start;
  padding: 0 0 18px 0;
}
.wd-step:last-child { padding-bottom: 0; }
/* vertical connector between markers */
.wd-step:not(:last-child)::before {
  content: ""; position: absolute; left: 13px; top: 28px; bottom: 0; width: 2px;
  background: var(--line);
}
.wd-step-n {
  flex: 0 0 28px; width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center; position: relative; z-index: 1;
  background: var(--accent-soft); color: var(--accent);
  font-family: var(--heading-font); font-weight: 700; font-size: .82rem;
  font-variant-numeric: tabular-nums;
}
.wd-step-t { color: var(--ink); font-size: .96rem; line-height: 1.5; padding-top: 3px; }
.wd-step-t b { font-weight: 600; }
.wd-step-t small { display: block; color: var(--ink-soft); font-size: .85rem; margin-top: 2px; }

/* ---- why-digital.php #compare — two-track workflow comparison ---------- */
.wd-tracks { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; align-items: start; }
/* Flex column + margin-top:auto on the foot: the two tracks have different step
   counts, so the shorter one would otherwise stretch to match and leave a void
   under its last step. This drops each closing line to the card's baseline. */
.wd-track {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 26px 26px 22px; box-shadow: var(--shadow-sm); height: 100%;
  display: flex; flex-direction: column;
}
/* The digital column is the answer, so it carries the accent edge. */
.wd-track.is-digital { border-color: color-mix(in srgb, var(--accent) 42%, var(--line)); }
.wd-track-head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 8px; margin-bottom: 20px; padding-bottom: 14px; border-bottom: 1px solid var(--line);
}
.wd-track-head h3 { margin: 0; font-size: 1.16rem; color: var(--ink); }
.wd-track-time {
  font-size: .82rem; font-weight: 700; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.wd-track.is-digital .wd-track-time { color: var(--accent); }
.wd-track-foot {
  margin: auto 0 0; padding-top: 14px; border-top: 1px solid var(--line);
  color: var(--ink-soft); font-size: .9rem; font-style: italic;
}
/* Film's last step is the retake loop — mark it as the failure branch. */
.wd-track.is-film .wd-step:last-child .wd-step-n {
  background: color-mix(in srgb, var(--err) 14%, transparent); color: var(--err);
}
.wd-track.is-digital .wd-step-n { background: var(--accent-soft); color: var(--accent); }

@media (max-width: 767px) { .wd-tracks { grid-template-columns: 1fr; } }

/* ---- why-digital.php #explore — hub cards to the rest of the cluster --- */
.wd-hub { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; }
.wd-hub-card {
  display: flex; align-items: flex-start; gap: 14px; text-decoration: none;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 22px; box-shadow: var(--shadow-sm);
  transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease;
}
.wd-hub-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: color-mix(in srgb, var(--accent) 50%, var(--line)); }
.wd-hub-ic {
  flex: 0 0 44px; width: 44px; height: 44px; border-radius: 12px;
  display: grid; place-items: center; background: var(--accent-soft); color: var(--accent);
  font-size: 1.16rem;
}
.wd-hub-body { flex: 1 1 auto; min-width: 0; }
.wd-hub-body b { display: block; font-family: var(--heading-font); font-weight: 700; color: var(--ink); font-size: 1.02rem; margin-bottom: 5px; }
.wd-hub-body span { display: block; color: var(--ink-soft); font-size: .9rem; line-height: 1.55; }
.wd-hub-go { flex: 0 0 auto; color: var(--accent); font-size: 1rem; padding-top: 11px; transition: transform .25s ease; }
.wd-hub-card:hover .wd-hub-go { transform: translateX(4px); }

@media (max-width: 991px) { .wd-hub { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 575px) { .wd-hub { grid-template-columns: 1fr; } }

/* ---- films-are-expensive.php #ledger — the accumulating cost ledger ----
   The bar on row n is the RUNNING TOTAL (n/6), not that line's magnitude —
   the page deliberately asserts no rupee figure, and hands the real number
   to film-cost-calculator.php via .cl-total. */
.cl-list { list-style: none; margin: 0; padding: 0; }
.cl-row {
  display: grid; grid-template-columns: 48px minmax(0, 1fr) minmax(180px, 280px);
  gap: 18px; align-items: center;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px 22px; margin-bottom: 12px; box-shadow: var(--shadow-sm);
}
.cl-ic {
  width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;
  background: var(--accent-soft); color: var(--accent); font-size: 1.2rem;
}
.cl-body { min-width: 0; }
.cl-name { display: block; font-family: var(--heading-font); font-weight: 700; color: var(--ink); font-size: 1.02rem; }
.cl-desc { display: block; color: var(--ink-soft); font-size: .9rem; line-height: 1.5; margin-top: 3px; }
.cl-bar-wrap { display: flex; flex-direction: column; gap: 7px; }
.cl-bar-lbl { color: var(--ink-soft); font-size: .78rem; font-variant-numeric: tabular-nums; }

/* The closing row: visually the sum of everything above it. */
.cl-total {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px;
  margin-top: 22px; padding: 26px 30px; border-radius: var(--radius);
  background: var(--grad-brand); color: var(--on-fill); box-shadow: var(--shadow-md);
}
.cl-total-copy { flex: 1 1 340px; min-width: 0; }
.cl-total-copy b { display: block; font-family: var(--heading-font); font-weight: 800; font-size: 1.2rem; margin-bottom: 6px; }
.cl-total-copy span { display: block; color: var(--cta-fg-2); font-size: .94rem; line-height: 1.6; }
/* Same inversion .cta-band applies, so the button reads on the gradient. */
.cl-total .btn-brand { background: var(--surface); color: var(--accent); flex: 0 0 auto; }
.cl-total .btn-brand:hover { background: var(--ink); color: var(--on-fill); }
[data-bs-theme="dark"] .cl-total .btn-brand { background: var(--accent-vivid); color: var(--on-vivid); }
[data-bs-theme="dark"] .cl-total .btn-brand:hover { background: var(--cta-bg); color: var(--cta-fg); }

@media (max-width: 767px) {
  .cl-row { grid-template-columns: 48px minmax(0, 1fr); row-gap: 14px; }
  .cl-bar-wrap { grid-column: 1 / -1; }
  .cl-total { padding: 22px; }
}

/* ---- film-cost-calculator.php #calc — the console ----------------------
   NO position:sticky on .calc-out, and therefore no `overflow: visible`
   release on the section. Once .calc-fields went two-up the form shrank to
   ~330px against the result panel's ~489px, so the panel is the taller pane at
   every desktop width — it fills the grid row and sticky could never engage.
   Both panes are visible together anyway, which is what sticky was for.
   Releasing `overflow: clip` here would only reintroduce the page-level
   horizontal-scroll risk for nothing. */
.calc-shell { display: grid; grid-template-columns: minmax(0, 1fr) minmax(340px, 420px); gap: 26px; align-items: start; }
.calc-form-head { font-size: 1.16rem; margin: 0 0 20px; color: var(--ink); }
/* Four short numeric fields stacked full-width in a ~700px column read as a
   very tall, very empty form. Two-up keeps the console compact and roughly
   level with the result panel beside it. */
.calc-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 20px; }
.calc-field { margin-bottom: 0; }
.calc-field .form-label { font-weight: 600; color: var(--ink); margin-bottom: 6px; }
.calc-hint { display: block; color: var(--ink-soft); font-size: .82rem; margin-top: 5px; }

/* These inputs are NOT inside a .php-email-form, so the themed field rules
   above never reach them and they would render as raw Bootstrap (white box,
   grey border) in dark mode. Mirror those rules here rather than reusing the
   .php-email-form hook, which site.js's form handling also keys off. */
.calc-form .form-control {
  border-radius: 10px; border: 1.5px solid var(--line); padding: 12px 14px; font-size: 1rem;
  background: var(--surface); color: var(--ink); font-variant-numeric: tabular-nums;
}
.calc-form .form-control:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

/* Result panel. Every colour is a token — --grad-brand, --on-fill and
   --cta-fg-2 are all defined in BOTH themes, so this is correct by
   construction rather than by the luck the old inline whites relied on. */
.calc-out {
  background: var(--grad-brand); color: var(--on-fill);
  border-radius: 20px; padding: 30px; box-shadow: var(--shadow-md);
}
.calc-out-head {
  margin: 0 0 10px; color: var(--cta-fg-2);
  text-transform: uppercase; letter-spacing: 2px; font-size: 12px; font-weight: 700;
}
.calc-fig {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; margin: 0 0 4px;
  font-family: var(--heading-font); font-weight: 800; font-size: clamp(1.9rem, 4vw, 2.5rem);
  line-height: 1.15; font-variant-numeric: tabular-nums; color: var(--on-fill);
}
.calc-fig.is-sub { font-size: 1.25rem; font-weight: 700; color: var(--cta-fg-2); margin-bottom: 0; }
.calc-fig-unit { font-size: .88rem; font-weight: 500; color: var(--cta-fg-2); letter-spacing: .2px; }

.calc-years { margin-top: 24px; padding-top: 20px; border-top: 1px solid color-mix(in srgb, var(--on-fill) 22%, transparent); }
.calc-years-head { margin: 0 0 14px; color: var(--cta-fg-2); font-size: .84rem; font-weight: 600; }
.calc-year { display: grid; grid-template-columns: 54px minmax(0, 1fr) auto; gap: 12px; align-items: center; margin-bottom: 10px; }
.calc-year:last-child { margin-bottom: 0; }
.calc-year-lbl { color: var(--cta-fg-2); font-size: .84rem; }
.calc-year-bar {
  height: 8px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--on-fill) 18%, transparent);
}
.calc-year-fill {
  display: block; height: 100%; border-radius: inherit;
  width: calc(var(--w, 1) * 100%); background: var(--on-fill); transform-origin: left;
}
[data-aos].aos-init .calc-year-fill { transform: scaleX(0); transition: transform .8s cubic-bezier(.22, .61, .36, 1); }
[data-aos].aos-animate .calc-year-fill { transform: scaleX(1); }
.calc-year-val { color: var(--on-fill); font-size: .88rem; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }

.calc-note { margin: 20px 0 0; color: var(--cta-fg-2); font-size: .88rem; line-height: 1.55; }
.calc-cta { margin-top: 16px; background: var(--surface); color: var(--accent); }
.calc-cta:hover { background: var(--ink); color: var(--on-fill); }
[data-bs-theme="dark"] .calc-cta { background: var(--accent-vivid); color: var(--on-vivid); }
[data-bs-theme="dark"] .calc-cta:hover { background: var(--cta-bg); color: var(--cta-fg); }

/* #assumptions — the counted / not-counted lists inside a .feature-card */
.calc-ul { margin: 0; padding-left: 1.1em; color: var(--ink-soft); font-size: .93rem; line-height: 1.7; }
.calc-ul li { margin-bottom: 4px; }
.calc-ul li::marker { color: var(--accent); }

@media (max-width: 991px) { .calc-shell { grid-template-columns: 1fr; } }
@media (max-width: 479px) { .calc-fields { grid-template-columns: 1fr; } }

/* ---- digital-system.php #latitude — the exposure-latitude visual -------
   Two bands on a shared exposure axis. --from/--to are inline percentages of
   the track. The bands are ILLUSTRATIVE of relative behaviour, not measured
   values — the section carries a visible .form-note saying so. */
.ds-lat {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px; box-shadow: var(--shadow-sm);
}
.ds-lat-row { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 18px; align-items: center; margin-bottom: 16px; }
.ds-lat-name { color: var(--ink); font-weight: 600; font-size: .95rem; }
/* Gridlines every 25% so they land under the -20% / set point / +20% ticks,
   which .ds-lat-scale's space-between places at 0 / 25 / 50 / 75 / 100. */
.ds-lat-track {
  position: relative; height: 42px; border-radius: 10px;
  background: repeating-linear-gradient(90deg, transparent 0 calc(25% - 1px), color-mix(in srgb, var(--ink) 10%, transparent) calc(25% - 1px) 25%),
              color-mix(in srgb, var(--ink) 4%, transparent);
}
.ds-lat-band {
  position: absolute; top: 6px; bottom: 6px;
  left: var(--from, 30%); right: calc(100% - var(--to, 70%));
  border-radius: 8px; display: grid; place-items: center; overflow: hidden;
}
.ds-lat-band-t {
  font-size: .74rem; font-weight: 700; letter-spacing: .3px; white-space: nowrap;
  padding: 0 8px; text-overflow: ellipsis; overflow: hidden; max-width: 100%;
}
.ds-lat-band.is-other  { background: color-mix(in srgb, var(--ink) 16%, transparent); }
.ds-lat-band.is-other .ds-lat-band-t  { color: var(--ink-soft); }
.ds-lat-band.is-vatech { background: var(--accent-vivid); }
.ds-lat-band.is-vatech .ds-lat-band-t { color: var(--on-vivid); }

.ds-lat-row.is-scale { margin-bottom: 0; }
.ds-lat-scale { display: flex; justify-content: space-between; height: auto; background: none; padding-top: 6px; }
.ds-lat-tick { color: var(--ink-soft); font-size: .76rem; font-variant-numeric: tabular-nums; }
.ds-lat-tick.is-mid { color: var(--ink); font-weight: 700; }
.ds-lat-say { margin: 22px 0 0; padding-top: 18px; border-top: 1px solid var(--line); color: var(--ink-soft); font-size: .98rem; line-height: 1.75; }
.ds-lat-say b { color: var(--ink); font-weight: 600; }

@media (max-width: 767px) {
  .ds-lat-row { grid-template-columns: 1fr; gap: 8px; }
  .ds-lat-row.is-scale .ds-lat-name { display: none; }
  .ds-lat { padding: 22px; }
}

/* ---- digital-system.php #sensor — the cut-through ----------------------
   The drawing deliberately carries no text. At phone width the SVG is about
   350px across, so a label set in the viewBox's own units would land near 7px.
   The words are the <ol> beside it, which stays at page text size, wraps,
   translates and reads in document order. The swatches are what tie the two
   together, so the list reads as a key rather than a second list. */
.ds-cut {
  margin: 0;
  display: grid; grid-template-columns: minmax(0, 290px) minmax(0, 1fr);
  gap: 32px; align-items: center;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px; box-shadow: var(--shadow-sm);
}
.ds-cut-draw svg { display: block; width: 100%; height: auto; }

.ds-cut-ray   { stroke: var(--accent); stroke-width: 3; stroke-linecap: round; }
.ds-cut-light { stroke: var(--accent); stroke-width: 2.5; stroke-linecap: round; opacity: .65; }
.ds-cut-tip-a { fill: var(--accent); }
.ds-cut-tip-i { fill: var(--ink); }
.ds-cut-shell { fill: color-mix(in srgb, var(--ink) 5%, transparent); stroke: var(--ink); stroke-width: 2.5; }
.ds-cut-scint { fill: var(--accent-soft); stroke: var(--accent); stroke-width: 1.5; }
.ds-cut-cmos  { fill: var(--accent-vivid); }
.ds-cut-cable { fill: none; stroke: var(--ink); stroke-width: 3; stroke-linecap: round; }

.ds-cut-key { list-style: none; margin: 0; padding: 0; }
.ds-cut-key li { position: relative; padding-left: 28px; margin-bottom: 18px; }
.ds-cut-key li:last-child { margin-bottom: 0; }
.ds-cut-key li::before {
  content: ""; position: absolute; left: 0; top: .34em;
  width: 14px; height: 14px; border-radius: 4px; border: 1.5px solid var(--accent);
}
.ds-cut-key .is-ray::before   { background: var(--accent); border-radius: 50%; }
.ds-cut-key .is-scint::before { background: var(--accent-soft); }
.ds-cut-key .is-cmos::before  { background: var(--accent-vivid); }
.ds-cut-key .is-out::before   { background: transparent; border-color: var(--ink); }
.ds-cut-key b    { display: block; color: var(--ink); font-weight: 600; font-size: .97rem; }
.ds-cut-key span { display: block; color: var(--ink-soft); font-size: .92rem; line-height: 1.6; }

@media (max-width: 767px) {
  .ds-cut { grid-template-columns: 1fr; gap: 24px; padding: 22px; }
  .ds-cut-draw { max-width: 250px; margin: 0 auto; }
}

/* ---- digital-system.php #dose — relative dose comparison --------------- */
.ds-dose {
  max-width: 900px; margin: 34px auto 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 26px 30px; box-shadow: var(--shadow-sm);
}
.ds-dose-row { display: grid; grid-template-columns: 150px minmax(0, 1fr) minmax(0, 230px); gap: 18px; align-items: center; margin-bottom: 18px; }
.ds-dose-row:last-child { margin-bottom: 0; }
.ds-dose-lbl { color: var(--ink); font-weight: 600; font-size: .95rem; }
.ds-dose-bar { height: 14px; }
/* The last row is the VATECH one — accent it, mute the comparators. */
.ds-dose-row:not(:last-child) .wd-bar-fill { background: color-mix(in srgb, var(--ink) 26%, transparent); }
.ds-dose-note { color: var(--ink-soft); font-size: .85rem; line-height: 1.5; }

@media (max-width: 767px) {
  .ds-dose-row { grid-template-columns: 1fr; gap: 7px; }
  .ds-dose { padding: 22px; }
}

/* ---- convenience.php #day — a day in the clinic ------------------------
   Six stops on a shared rule. The horizontal scroller lives on .cv-day
   itself, NOT on the section: main.css keeps `overflow: clip` on sections,
   and moving the scroll to the section would let a wide track scroll the
   whole page sideways. Zero JS — native scroll-snap on mobile. */
/* No padding-top: .cv-rule is positioned against .cv-day's padding box while
   .cv-dot is positioned against .cv-stop (a grid item, which starts at the
   CONTENT box). Any padding here offsets the dots off the rule by that amount. */
.cv-day {
  position: relative;
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 18px;
}
.cv-day:focus-visible { outline: 3px solid var(--accent); outline-offset: 6px; border-radius: var(--radius); }
/* The rule the dots sit on. Top offset matches .cv-dot's own offset. */
.cv-rule { position: absolute; left: 0; right: 0; top: 40px; height: 2px; background: var(--line); }
/* Deliberately unboxed — the rule and the dots carry the structure, so cards
   here would compete with the .feature-card grid in the next section. */
.cv-stop { position: relative; display: flex; flex-direction: column; padding: 0 0 18px; }
.cv-time {
  font-family: var(--heading-font); font-weight: 800; font-size: 1.02rem; color: var(--accent);
  font-variant-numeric: tabular-nums; line-height: 1; margin-bottom: 22px;
}
.cv-dot {
  position: absolute; top: 35px; left: 0; width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent-vivid); box-shadow: 0 0 0 4px var(--bg);
}
.cv-what { font-size: 1rem; margin: 14px 0 6px; color: var(--ink); text-wrap: balance; }
.cv-desc { color: var(--ink-soft); font-size: .89rem; line-height: 1.6; margin: 0 0 12px; }
.cv-saved {
  margin-top: auto; align-self: flex-start; display: inline-flex; align-items: center; gap: 6px;
  background: var(--accent-soft); color: var(--accent);
  border-radius: 999px; padding: 5px 12px; font-size: .76rem; font-weight: 700; line-height: 1.3;
}
.cv-total {
  max-width: 720px; margin: 34px auto 0; text-align: center;
  color: var(--ink); font-size: 1.06rem; font-weight: 600; text-wrap: balance;
}

@media (max-width: 991px) {
  /* Horizontal scroller below the 6-up grid's comfortable width. */
  .cv-day {
    grid-template-columns: repeat(6, minmax(210px, 1fr));
    overflow-x: auto; scroll-snap-type: x mandatory;
    padding-bottom: 12px; -webkit-overflow-scrolling: touch;
  }
  .cv-stop { scroll-snap-align: start; }
  /* The rule would only span the visible box, so drop it and let the dots read
     as markers on their own rather than as a broken line. */
  .cv-rule { display: none; }
}

/* #learn — the shared .wd-steps primitive in a single centred column */
.cv-learn {
  max-width: 620px; margin-inline: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px; box-shadow: var(--shadow-sm);
}

/* ---- vatech-is-the-best.php #value — the value ladder ------------------
   Each row's bar is split into a price segment and a workflow segment, sized
   by an inline --s (0…1). Proportions are ILLUSTRATIVE OF POSITIONING ONLY —
   no price is stated or implied, and the section says so in a .form-note. */
.vb-ladder {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px; box-shadow: var(--shadow-sm);
}
.vb-legend { display: flex; flex-wrap: wrap; gap: 10px 26px; margin: 0 0 24px; }
.vb-key { display: inline-flex; align-items: center; gap: 8px; color: var(--ink-soft); font-size: .84rem; }
.vb-sw { width: 14px; height: 14px; border-radius: 4px; flex: 0 0 auto; }
.vb-sw.is-price    { background: color-mix(in srgb, var(--ink) 22%, transparent); }
.vb-sw.is-workflow { background: var(--accent-vivid); }

.vb-row { display: grid; grid-template-columns: 170px minmax(0, 1fr) minmax(0, 250px); gap: 18px; align-items: center; margin-bottom: 20px; }
.vb-row:last-child { margin-bottom: 0; }
.vb-label { color: var(--ink); font-weight: 600; font-size: .96rem; }
.vb-bar { display: flex; height: 22px; border-radius: 999px; overflow: hidden; background: color-mix(in srgb, var(--ink) 6%, transparent); }
.vb-seg { flex: 0 0 calc(var(--s, .5) * 100%); transform-origin: left; }
.vb-seg.is-price    { background: color-mix(in srgb, var(--ink) 22%, transparent); }
.vb-seg.is-workflow { background: var(--accent-vivid); }
/* Grow the whole bar in from the left once AOS has run; static without JS. */
[data-aos].aos-init .vb-seg { transform: scaleX(0); transition: transform .8s cubic-bezier(.22, .61, .36, 1); }
[data-aos].aos-animate .vb-seg { transform: scaleX(1); }
.vb-note { color: var(--ink-soft); font-size: .85rem; line-height: 1.55; }
/* The VATECH rung is the conclusion — weight it. */
.vb-row:last-child .vb-label { font-weight: 700; color: var(--accent); }

@media (max-width: 767px) {
  .vb-row { grid-template-columns: 1fr; gap: 8px; }
  .vb-ladder { padding: 22px; }
}

@media (prefers-reduced-motion: reduce) {
  [data-aos].aos-init .wd-bar-fill { transform: none; transition: none; }
  [data-aos].aos-init .calc-year-fill { transform: none; transition: none; }
  [data-aos].aos-init .vb-seg { transform: none; transition: none; }
  .wd-hub-card:hover { transform: none; }
  .wd-hub-card:hover .wd-hub-go { transform: none; }
  .cv-day { scroll-behavior: auto; }
}

/* ======================================================================
   Segmented control + events list
   (events.php filter rail · direct.php customer switch)
   Behaviour: assets/js/site.js → segControl / initEventFilter / initRegSwitch

   The rail is the same control as the products store, ported from
   assets/css/product-store.css and re-pointed at the global tokens — that
   file namespaces everything to body.pstore-page and only loads on that page.
   ====================================================================== */

/* #posts is the blog grid, which lands here from every ?cat= / ?tag= link. */
#list, #enquire, #posts { scroll-margin-top: 96px; }
/* direct.php anchors. #concern-form especially: it is the 301 landing target
   of register-concern.php, so a mis-scrolled anchor there is a real defect. */
#remote, #onsite, #contact, #concern-form { scroll-margin-top: 96px; }

/* ---- The segmented control --------------------------------------------
   One capsule track holding transparent segments, with a single brand capsule
   sliding between them — not a row of pills each painting its own background
   on and off. Shared by the events filter (.ev-*) and the direct.php customer
   switch (.reg-switch*); the thumb is .seg-thumb in both, injected by JS. */
.ev-filters-wrap, .sv-switch-wrap { display: flex; justify-content: center; }
.ev-filters,
.reg-switch {
  position: relative;                 /* the sliding thumb is absolute against this */
  display: inline-flex; max-width: 100%; gap: 2px; padding: 5px;
  border-radius: 999px; background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  backdrop-filter: blur(14px) saturate(140%);
  overflow-x: auto; scrollbar-width: none; -ms-overflow-style: none;
  margin-bottom: clamp(22px, 3vw, 34px);
}
.ev-filters::-webkit-scrollbar,
.reg-switch::-webkit-scrollbar { display: none; }
@supports not ((backdrop-filter: blur(14px)) or (-webkit-backdrop-filter: blur(14px))) {
  .ev-filters, .reg-switch { background: var(--surface); }
}

/* Positioned by JS. The transition only switches on once .is-ready lands, or
   the capsule animates in from x=0 on first paint. */
.seg-thumb {
  position: absolute; top: 5px; left: 0; height: 40px;
  border-radius: 999px; background: var(--grad-brand);
  pointer-events: none; z-index: 0;
}
.ev-filters.is-ready .seg-thumb,
.reg-switch.is-ready .seg-thumb {
  transition: transform .38s cubic-bezier(.22, 1, .36, 1),
              width .38s cubic-bezier(.22, 1, .36, 1);
}

.ev-pill,
.reg-switch-btn {
  position: relative; z-index: 1;     /* above the sliding thumb */
  flex: 0 0 auto; height: 40px; padding-inline: 18px;
  display: inline-flex; align-items: center; gap: 8px;
  border: 0; border-radius: 999px; background: transparent;
  color: var(--ink); font-family: var(--nav-font); font-weight: 600; font-size: .88rem;
  white-space: nowrap; cursor: pointer; transition: color .28s linear;
  /* The news and blog rails render their segments as links (the server filters,
     see initSegRail), so the anchor underline has to go. Everything else above
     already applies to an <a> — these are class selectors, not tag ones. */
  text-decoration: none;
}
/* Hover tint only on inactive segments — under the active one the thumb
   already supplies the ground. */
.ev-pill:not(.is-active):hover,
.reg-switch-btn:not(.is-active):hover { background: rgba(var(--accent-rgb), .12); }
.ev-pill.is-active,
.reg-switch-btn.is-active { background: transparent; color: var(--on-fill); }
.ev-pill:focus-visible,
.reg-switch-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* No-JS / pre-measure fallback: without the thumb the active segment would
   have no ground at all. */
.ev-filters:not(.is-ready) .ev-pill.is-active,
.reg-switch:not(.is-ready) .reg-switch-btn.is-active { background: var(--grad-brand); }

.ev-n {
  font-variant-numeric: tabular-nums; font-size: .78rem; font-weight: 700;
  opacity: .72;
}

/* ---- The list ---------------------------------------------------------
   Rows, not cards: with no real event photography every card would lead with a
   ph() panel, spending most of the grid on grey boxes that carry nothing.

   Spacing is a 4px scale (4 / 8 / 12 / 16 / 20) declared once below, so the row
   and the panel inside it cannot drift apart. This block had no scale: 2, 4, 5,
   6, 7, 8, 10, 14, 18, 20 and 22 all appeared, most of them chosen against a
   neighbour rather than against a system. 4 rather than 8 because the half-step
   is load-bearing — a title and its subtitle want 4, and doubling that to 8
   visually detaches them. */
.ev-list { list-style: none; margin: 0; padding: 0; }
.ev-row {
  --ev-inset: 20px;   /* == --ev-gap, so the inset from the card edge to the date
                         matches the gutter between date and title: the 150px
                         column then reads as a column rather than an offset */
  --ev-pad-y: 20px;   /* symmetric with --ev-inset; was 18/22, asymmetric */
  --ev-gap:   20px;   /* COLUMNS only — see row-gap below */
  --ev-stack: 12px;   /* between blocks of copy inside the panel */

  display: grid; grid-template-columns: 150px minmax(0, 1fr) auto;
  /* row-gap: 0 is a FIX, not a style. .ev-panel is a grid item in row 2, and a
     row-gap is inserted between tracks whether or not the second one has any
     height — so with a blanket gap every COLLAPSED row carried 20px of dead
     space below its title: 26px above the title block and 46px below it,
     measured, on all seventeen rows. The panel supplies its own separation
     through .ev-panel-in's padding instead. */
  column-gap: var(--ev-gap); row-gap: 0;
  align-items: center;
  padding: var(--ev-pad-y) var(--ev-inset);
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-sm); margin-bottom: 8px;
  transition: transform .25s ease, box-shadow .25s ease, border-color .25s ease,
              background-color .25s ease;
}
.ev-row:hover {
  transform: translateY(-2px); box-shadow: var(--shadow-md);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
}
.ev-row.is-hidden { display: none; }   /* leave the flow, not just fade out */

/* Open is signalled by the row, not only by the 36px chevron: someone who opens
   a row halfway down a list of seventeen needs to see WHICH one answered.

   Deliberately about a third of what .faq-card.open does. That repaints to
   --accent-fill / --on-fill, which over seventeen rows is a wall of colour and
   needs a `color: inherit` chain through five descendants to stay legible. Only
   the chrome changes here, so every colour inside keeps working untouched:
     - the border reaches FULL --accent, where hover stops at 40% of it;
     - the shadow lifts to --shadow-md, so an open row still reads as raised once
       the pointer has moved away;
     - a 4% accent wash mixed against --surface, NOT --accent-soft: that token is
       an opaque pink in light mode and a translucent mint in dark, and a
       translucent tint under --surface-coloured descendants is exactly where
       this kind of thing breaks. 4% sits below the quietest .ev-type tint (9%),
       so it cannot touch any text contrast. */
.ev-row.is-open {
  border-color: var(--accent);
  box-shadow: var(--shadow-md);
  background: color-mix(in srgb, var(--accent) 4%, var(--surface));
}

.ev-date {
  color: var(--accent); font-family: var(--heading-font); font-weight: 700;
  font-size: .92rem; font-variant-numeric: tabular-nums; line-height: 1.35;
}
.ev-body { min-width: 0; }
.ev-name {
  display: block; font-family: var(--heading-font); font-weight: 700;
  color: var(--ink); font-size: 1.04rem; line-height: 1.35; text-wrap: balance;
}

/* Everything under the name, in one flex row — see the note in events.php.
   column-gap 8 / row-gap 4: when the two wrap onto separate lines the vertical
   gap should be the tighter half-step, because they are then one stacked group
   rather than two things side by side. */
.ev-meta {
  display: flex; flex-wrap: wrap; align-items: center;
  column-gap: 8px; row-gap: 4px; margin-top: 4px;
}
.ev-place {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--ink-soft); font-size: .88rem; line-height: 1.35;
}
.ev-place i { color: var(--accent); font-size: .9rem; }

/* Type badge. Tinted with color-mix so one declaration works in both themes.
   Tracking in em, not px: this and .ev-up are the same uppercase micro-label at
   two sizes, and a shared .4px tracked them differently (.033em at .76rem,
   .036em at .7rem). .04em is the same optical result, proportionally, and keeps
   working if either size ever changes. */
.ev-type {
  flex: 0 0 auto; justify-self: end; white-space: nowrap;
  border-radius: 999px; padding: 6px 14px;
  font-size: .76rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}
/* Seminar uses --warn, not --ok: in the dark theme --accent IS mint, so a green
   --ok badge sits almost on top of the mint Exhibition badge. Amber is the one
   hue clearly distinct from both the accent and the neutral in BOTH themes. */
.ev-type.is-exhibition { background: var(--accent-soft); color: var(--accent); }
.ev-type.is-seminar    { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
/* The three keys these two replaced, kept for ONE deploy cycle. New code can meet
   an older content cache, or fall back to a data/events.php that has not been
   regenerated yet, and a badge with no rule renders as bare uppercase text on every
   row. Delete once production has published at least once on exhibition/seminar. */
.ev-type.is-expo       { background: var(--accent-soft); color: var(--accent); }
.ev-type.is-show       { background: color-mix(in srgb, var(--ink) 9%, transparent);   color: var(--ink-soft); }
.ev-type.is-conference { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }

/* Not a type badge, and deliberately not styled like one: it sits in .ev-meta
   under the event name rather than in the badge column, and it is the single most
   emphatic thing on the row. A solid accent fill, where every type badge is a
   tint, so the two cannot be confused in either theme.
   No margins of its own — .ev-meta owns the spacing. */
.ev-up {
  display: inline-flex; align-items: center;
  border-radius: 999px; padding: 3px 10px;
  background: var(--accent-fill); color: var(--on-fill);
  font-size: .7rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
}

.ev-empty {
  text-align: center; color: var(--ink-soft); padding: 40px 0 10px; margin: 0;
}

/* Stagger the surviving rows back in after a filter change. Opacity only —
   .ev-row owns transform for its hover lift, and background-color for .is-open. */
.ev-list.is-swapping .ev-row:not(.is-hidden) {
  animation: ev-row-in .42s cubic-bezier(.22, .61, .36, 1) both;
  animation-delay: var(--ev-d, 0ms);
}
@keyframes ev-row-in { from { opacity: 0; } }

/* ---- The expandable panel ---------------------------------------------
   A fourth column holds the chevron. A row with nothing to show gets .is-flat
   and the original three, rather than an empty column — the column gap applies
   whether or not the column has content, so the type badges would otherwise sit
   out of line with each other down the list. */
.ev-row { grid-template-columns: 150px minmax(0, 1fr) auto 44px; }
.ev-row.is-flat { grid-template-columns: 150px minmax(0, 1fr) auto; }

.ev-toggle {
  justify-self: end; width: 36px; height: 36px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line); border-radius: 50%;
  background: var(--surface); color: var(--ink-soft); cursor: pointer;
  transition: background-color .22s ease, color .22s ease, border-color .22s ease, transform .3s ease;
}
.ev-toggle:hover { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); }
.ev-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ev-row.is-open .ev-toggle {
  background: var(--accent-fill); color: var(--on-fill); border-color: transparent; transform: rotate(180deg);
}

/* The FAQ's technique: animate a grid row between 0fr and 1fr, with the inner
   element carrying the overflow. Open is the DEFAULT — .is-expandable is added
   by initEventExpand(), so with no JavaScript every panel renders expanded and
   nothing is hidden from a reader or a crawler.

   grid-column: 1 / -1 counts EXPLICIT grid lines only. That is why every narrow
   rule below declares the chevron's column: when it was placed in an implicit
   one (a grid-column: 2 against a one-column template), -1 resolved to line 2
   and the panel rendered 44px narrower than its own row — measured 279px inside
   a 323px content box at 400px wide, with nothing in the panel lining up with
   anything in the row above it. */
.ev-panel { grid-column: 1 / -1; display: grid; grid-template-rows: 1fr; transition: grid-template-rows .35s ease; }
.ev-list.is-expandable .ev-panel { grid-template-rows: 0fr; }
.ev-list.is-expandable .ev-row.is-open .ev-panel { grid-template-rows: 1fr; }
.ev-panel > .ev-panel-in { overflow: hidden; min-height: 0; }

/* Padding-top only. Three things that fixes:
     - the row's own padding-bottom now closes the panel, so the gap above the
       bottom border is --ev-pad-y rather than "whatever margin the last child
       happens to carry" (10px after a venue, 14px after a list, 2px after the
       buttons). The same idea as .faq-a p's padding: 0 22px 22px.
     - it is symmetric with the row's padding-top: 20 above the header, 20 below
       the footer.
     - it is ON BY DEFAULT and removed only while collapsed, where the old rule
       was the other way round (.is-expandable .is-open) and so gave the no-JS
       always-open state no padding at all.

   The second rule is not optional and is easy to get wrong: a 0fr track sizes the
   grid ITEM's content box, and padding sits outside that box — so padding-top on
   .ev-panel-in is NOT clipped by the collapsed track. Left unscoped it adds
   --ev-pad-y to the height of every collapsed row, which is the same dead space
   row-gap: 0 above was removed to fix. Measured: 20px per row, all seventeen. */
.ev-panel-in { padding-top: var(--ev-pad-y); }
.ev-list.is-expandable .ev-row:not(.is-open) .ev-panel-in { padding-top: 0; }

/* Poster beside the copy, not above it: these run from 16:9 photographs to tall
   show posters, and a full-width band of either pushes the text that explains the
   event off the bottom of the screen. Two rows, because the action bar below
   spans both columns. */
.ev-panel-in.has-banner {
  display: grid; grid-template-columns: minmax(0, 280px) minmax(0, 1fr);
  column-gap: var(--ev-gap); row-gap: var(--ev-pad-y);
  align-items: start;
}

/* The box hugs the picture in both axes. It used to be aspect-ratio: 4/3 with
   object-fit: contain — a contradiction: the contain promised not to crop a
   portrait poster, and the 4/3 box then framed it in a landscape hole with grey
   bars either side, which is the letterboxing contain existed to avoid.
   With no ratio and no object-fit, max-width/max-height scale the image down to
   fit inside 280 x 420 with its own proportions intact, and width: max-content
   makes the figure follow it: a 16:9 photo becomes 280 x 158, an A4 poster
   280 x 396. Nothing is ever cropped and no bars are ever drawn. 420 is 1 : 1.5
   against the 280px column — just past A4 (1 : 1.414), so the shape these
   actually arrive in is shown whole. */
.ev-banner {
  margin: 0; border-radius: var(--radius); overflow: hidden;
  background: var(--mist); border: 1px solid var(--line);
  width: max-content; max-width: 100%;
}
.ev-banner img { display: block; width: auto; height: auto; max-width: 100%; max-height: 420px; }

/* The rule this file did not have. Spacing between the blocks of copy was four
   unrelated per-element margins (.ev-desc 0 0 10, .ev-venue 0 0 10,
   .ev-highlights 0 0 14, .ev-cta margin-top 2), so the gap between any two
   blocks depended on which two they were, and the gap below the LAST one
   depended on which one was last. A flex column with one gap makes every gap the
   same and the trailing gap zero — which is what lets the row's own
   padding-bottom close the panel. Same grammar as the blog card.
   The reset comes FIRST: it ties with .ev-desc et al on specificity, so source
   order is what decides it. */
.ev-panel-text { display: flex; flex-direction: column; gap: var(--ev-stack); min-width: 0; }
.ev-panel-text > * { margin: 0; }

/* 1.65 was a long-form reading leading, borrowed from an article body. This is a
   two- or three-line description in a ~45ch measure beside a 280px poster, and a
   narrower measure needs LESS leading, not more: the lines are short enough to
   track without the extra air, and at 1.65 the block read as unmoored from the
   title above it. */
.ev-desc { color: var(--ink-soft); line-height: 1.55; max-width: 68ch; }

/* .88rem and a 6px icon gap to match .ev-place exactly: a place with a pin icon,
   rendered at two different sizes in two different places for no reason. */
.ev-venue {
  font-size: .88rem; color: var(--ink-soft);
  display: flex; align-items: center; gap: 6px;
}
.ev-venue i { color: var(--accent); }

.ev-highlights { padding-left: 1.15rem; max-width: 68ch; }
/* 1.5, for the same reason as .ev-desc, and more so: list items are shorter than
   paragraph lines. 4px between items, not 8: items in a list are a tighter group
   than the blocks around them. li + li rather than a margin on every item,
   because a trailing margin after the last would add to the flex gap below. */
.ev-highlights li { color: var(--ink-soft); line-height: 1.5; }
.ev-highlights li + li { margin-top: 4px; }
.ev-highlights li::marker { color: var(--accent); }

.ev-gal {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 9px 18px; border-radius: 999px;
  border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent);
  font-family: var(--nav-font); font-weight: 600; font-size: .88rem; cursor: pointer;
  transition: background-color .22s ease, color .22s ease;
}
.ev-gal:hover { background: var(--accent-fill); color: var(--on-fill); }
.ev-gal:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Only ever seen with JS off, where the button is absent. */
.ev-gal-links { margin: 0; padding-left: 1.15rem; font-size: .9rem; }

/* Two labels ship; one is hidden at every width. The short one is for the mobile
   action bar, where the two buttons split the row in half and "View gallery" no
   longer fits on one line. display:none rather than .visually-hidden because the
   hidden one must leave the accessibility tree too — the button has to announce
   exactly one name. */
.ev-gal-short { display: none; }

/* An empty text column must not cost a grid track. events.php decides emptiness
   (see $hasText there) because CSS cannot: :empty does not match a div holding a
   newline, and a <noscript> inside reports display:block to getComputedStyle even
   with scripting on. Scoped to .is-expandable, which initEventExpand() adds — so
   it is the page's existing "JavaScript is running" signal, and with JS off the
   rule never applies and the <noscript> gallery links inside still render.
   Below 768px .ev-panel-in is one column, so the zero-height item took a row-gap
   on each side: 32px between the picture and the action bar instead of 20, on 16
   of the 17 events. */
.ev-list.is-expandable .ev-panel-text.is-quiet { display: none; }

/* The panel's action bar: both buttons on one full-width row at the foot, with a
   hairline above them. The hairline does two jobs — it separates the buttons from
   the copy (the old .ev-cta had margin-top: 2px and read as one more paragraph),
   and it gives the panel the bottom edge it never had.
   20 above the line and 20 below it, symmetric: a footer rule that is tighter on
   one side reads as belonging to that side.
   Inset to the panel's content box rather than bled out to the row's padding
   edges, because the row already carries a 1px --line border at a 14px radius,
   and a second full-bleed hairline 20px inside it reads as a double rule where
   the outer one curves. */
.ev-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 12px;
  margin-top: var(--ev-pad-y); padding-top: var(--ev-pad-y);
  border-top: 1px solid var(--line);
}
/* .has-banner is a grid, so its row-gap already supplies the 20px above the
   line — the margin would double it. Spanning 1 / -1 is what makes this a footer
   for the whole panel rather than for the text column. */
.ev-panel-in.has-banner > .ev-actions { grid-column: 1 / -1; margin-top: 0; }

/* .btn-brand already supplies the filled brand pill used by every other primary
   action on the site. The two additions are corrections, not decoration, and both
   are invisible until this button sits beside .ev-gal:
     - the 1px TRANSPARENT border buys back the 1px .ev-gal spends on a real one.
       Without it the two pills are 39.11px and 41.11px tall and sit 1px apart
       vertically, which is what align-items: center does with unequal heights.
     - the gap is the icon-to-label space. .btn-brand declares none and relies on
       the whitespace in the markup, but a flex container collapses a whitespace-only
       run away entirely — so the icon touched the "I" while .ev-gal's own gap: 8px
       held. Scoped here rather than added to .btn-brand, which is on every page. */
.ev-interest {
  padding: 9px 18px; font-size: .88rem; cursor: pointer;
  border: 1px solid transparent; gap: 8px;
}

/* ---- 768-991px: the date moves above the name --------------------------
   The 150px date column squeezes to ~60px across the whole tablet band, so it
   stops being a column. Badge and chevron stay on the right, where they are on
   desktop, and the row stays two text lines tall.

   This replaces the old 991.98px rule, which collapsed the row to a single 1fr
   column and placed NOTHING — so the chevron auto-flowed into a fourth stacked
   grid row of its own, right-aligned under the badge, and a collapsed row
   measured 179px against 94px on desktop. Seventeen of those.
   .is-flat is listed separately because its desktop template is (0,2,0) and
   silently outranks a bare .ev-row media rule — which is how the old pair of
   rules came to behave differently for flat and non-flat rows. */
@media (max-width: 991.98px) {
  .ev-row { grid-template-columns: minmax(0, 1fr) auto 44px; }
  .ev-row.is-flat { grid-template-columns: minmax(0, 1fr) auto; }
  /* Spacing on the child, not row-gap: a row-gap would also apply above
     .ev-panel, which is the dead space this pass exists to remove. */
  .ev-date { grid-column: 1; grid-row: 1; }
  .ev-body { grid-column: 1; grid-row: 2; margin-top: 8px; }
  .ev-type { grid-column: 2; grid-row: 1 / 3; align-self: center; justify-self: end; }
  .ev-toggle { grid-column: 3; grid-row: 1 / 3; align-self: center; }
  .ev-row.is-flat .ev-toggle { display: none; }
}

@media (max-width: 767px) {
  /* Poster back above the copy — side by side there is no room for either. The
     action bar is then the third row of a one-column grid and keeps its 1 / -1,
     which is still the full width. */
  .ev-panel-in.has-banner { grid-template-columns: minmax(0, 1fr); row-gap: 16px; }
}

/* ---- Under 576px: a header strip, then the event -----------------------
   Row 1 is date, badge and chevron on one line, all centred on each other. The
   badge used to take a fourth line of its own under the location, which made the
   heaviest thing on the card the last thing on it and cost 20px a row; and the
   chevron used to span rows 1-3 with align-self: start, which aligns its TOP to a
   19.86px line box and so hung a 36px circle 8px below the date it belongs to.
   Now the badges run down one right-aligned column, exactly as they already do
   above 992px, so the two layouts read as the same card at two sizes.

   The date column is minmax(0, 1fr) rather than auto on purpose. The date is text
   an editor types, with no length limit; given auto it would push the badge off
   the card, and given 1fr it wraps inside its own column instead. Measured at
   320px — the narrowest width worth supporting — it wraps to two lines and
   nothing overflows.

   All three columns are EXPLICIT. grid-column: 1 / -1 counts explicit lines only,
   so the panel's -1 resolves to line 4 and it reaches the row's right edge; the
   previous generation of this rule left the chevron in an implicit track and the
   panel rendered 44px narrower than its own row. */
@media (max-width: 575.98px) {
  .ev-row { --ev-inset: 16px; --ev-pad-y: 16px; --ev-gap: 12px; }
  .ev-row { grid-template-columns: minmax(0, 1fr) auto 44px; }
  .ev-row.is-flat { grid-template-columns: minmax(0, 1fr) auto; }
  .ev-date   { grid-column: 1; grid-row: 1; align-self: center; }
  .ev-type   { grid-column: 2; grid-row: 1; justify-self: end; align-self: center; margin-top: 0; }
  .ev-toggle { grid-column: 3; grid-row: 1; align-self: center; }
  .ev-body   { grid-column: 1 / -1; grid-row: 2; margin-top: 10px; }
  .ev-row.is-flat .ev-toggle { display: none; }
  .ev-pill { padding-inline: 14px; font-size: .8rem; }

  /* The action bar splits the row evenly instead of packing to one side. auto-fit
     does three jobs without a second breakpoint, all measured:
       - two buttons at 360-575px get one equal track each, flush to both edges
         (137.5px apiece at 360px);
       - a lone button — fourteen of the seventeen events — has its empty track
         collapsed by auto-fit and fills the width;
       - at 320px two tracks of 135 plus the gutter no longer fit, so it falls to
         one column and they stack. No overflow at any width.
     135px is set against a measured 134.7px: the wider of the two buttons,
     "I'm interested", at the reduced inline padding below. The padding is what
     buys the room for two labels side by side on a 360px phone. */
  .ev-actions { display: grid; grid-template-columns: repeat(auto-fit, minmax(135px, 1fr)); gap: 12px; }
  .ev-interest, .ev-gal { padding-inline: 12px; }
  .ev-gal-long  { display: none; }
  .ev-gal-short { display: inline; }
}

@media (prefers-reduced-motion: reduce) {
  .ev-filters.is-ready .seg-thumb,
  .reg-switch.is-ready .seg-thumb { transition: none; }
  .ev-list.is-swapping .ev-row:not(.is-hidden) { animation: none; }
  .ev-row:hover { transform: none; }
  .sv-btn:hover { transform: none; }
  .ev-panel { transition: none; }
  .ev-toggle { transition: none; }
  /* .ev-row keeps its background-color transition on purpose: the open-state
     wash is a colour fade, not motion, and removing it would make the row snap. */
}

/* ======================================================================
   Direct123 SVC — support console (direct.php)
   ====================================================================== */

/* Hero action row: the two immediate channels, then the hours. Someone with a
   machine down should be able to call without scrolling.
   .sv-actions / .sv-btn are shared with dealer-registration.php, which needs the
   same thing in the same place — do not fold either into a #direct-only scope. */
.sv-actions {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 12px; margin-top: 28px;
}
.sv-btn { gap: 9px; padding: 12px 24px; font-size: 1rem; }
.sv-btn i { font-size: 1.1rem; }
/* WhatsApp keeps its own brand green rather than a theme token. Two reasons:
   .whatsapp-float above already establishes #25d366 as WhatsApp's colour on
   this site, and in the dark theme --accent IS mint — so a tokened button would
   sit almost on top of the Call button beside it. The literal is the point. */
.sv-wa { background: #25d366; color: #fff; }
.sv-wa:hover { background: #1eb457; color: #fff; }
.sv-hours {
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--ink-soft); font-size: .9rem; font-weight: 600;
  padding: 10px 16px; border-radius: 999px;
  background: color-mix(in srgb, var(--ink) 5%, transparent);
}
.sv-hours i { color: var(--accent); }

.sv-lead { color: var(--ink-soft); font-size: 1rem; line-height: 1.7; margin: 0; }

/* Checklist inside the New Customer panel */
.sv-list { list-style: none; margin: 18px 0 0; padding: 0; }
.sv-list li {
  display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px;
  color: var(--ink-soft); font-size: .95rem; line-height: 1.6;
}
.sv-list i { color: var(--accent); margin-top: 3px; flex-shrink: 0; }

/* Was an inline style attribute on the iframe. */
.sv-map {
  display: block; width: 100%; height: 340px; border: 1px solid var(--line);
  border-radius: var(--radius);
}

@media (max-width: 575px) {
  .sv-actions { flex-direction: column; align-items: stretch; }
  .sv-btn { justify-content: center; }
  .sv-hours { justify-content: center; }
  .sv-map { height: 280px; }
}


/* ======================================================================
   Dealer registration (dealer-registration.php)

   Three sections, almost all of it borrowed: .story-hero + .sv-actions for
   the hero, .feature-card for the pitch, and the shared .vf-* enquiry sheet
   for the form. Only the two rules below are new to this page.
   ====================================================================== */

/* #form is where form-handler.php sends the JS-off ?sent=1#form redirect, and
   main.css's 72px is short of the sticky header once .branding is counted — so
   the confirmation lands under the header. Same 96px correction the anchor group
   above makes.

   Scoped to `section.vf-bg#form`, NOT a bare `#form`, and that is deliberate:
   the id is an anchor on seven pages, two of which are the product store, where
   product-store.css:210 sets its own offset with `body.pstore-page [id]`. A bare
   `#form` carries an id's specificity and would beat that selector, quietly
   re-breaking the anchor it fixes here. Same trap that file already flags for
   #enquire. */
section.vf-bg#form { scroll-margin-top: 96px; }

/* "See the VATECH group" → the link that lets a .feature-card hand off to the
   page that proves its claim. .feature-card p is margin: 0, so the spacing has
   to come from here. */
.dr-more {
  display: inline-flex; align-items: center; gap: 7px; margin-top: 14px;
  color: var(--accent); font-weight: 600; font-size: .9rem; text-decoration: none;
}
.dr-more i { font-size: .95rem; transition: transform .2s ease; }
.feature-card:hover .dr-more i { transform: translateX(4px); }
.dr-more:hover { color: var(--accent-hover); text-decoration: underline; }

/* The one control the enquiry sheet had no vocabulary for: a radio row. Without
   these, Bootstrap's :checked state paints --bs-primary blue — the only blue
   control anywhere on the site, in either theme. */
.vf-choice { display: flex; flex-wrap: wrap; gap: 10px 24px; margin-top: 4px; }
.vf-choice .form-check { margin: 0; min-height: 0; }
.vf-choice .form-check-input { margin-top: .2em; cursor: pointer; }
.vf-choice .form-check-input:checked { background-color: var(--accent); border-color: var(--accent); }
.vf-choice .form-check-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.vf-choice .form-check-label { font-size: .92rem; color: var(--ink); cursor: pointer; }

/* ======================================================================
   MOBILE BUTTON PASS — size / alignment / spacing / positioning
   ----------------------------------------------------------------------
   Everything here is scoped to max-width queries. The desktop layout is
   signed off and must not shift, so nothing below touches a base rule.

   Baseline: 44x44 CSS px touch target (WCAG 2.5.5), >=8px between
   adjacent targets. Measured with Playwright at 390px before/after.
   ====================================================================== */

/* ---- CTA band: stacked buttons were touching (0px gap, ragged widths) ----
   The two anchors are inline-block with only .ms-2 between them, so when the
   column goes full-width they wrap onto separate lines with NO vertical gap.
   Below lg the column is already full width, so this runs to 991.98px. */
@media (max-width: 991.98px) {
  .cta-band .text-lg-end {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
  }
  .cta-band .text-lg-end > .btn-brand,
  .cta-band .text-lg-end > .btn-ghost {
    width: 100%;
    max-width: 320px;
    text-align: center;
    margin-left: 0 !important;   /* neutralise Bootstrap's .ms-2 utility */
  }
}
@media (max-width: 575.98px) {
  .cta-band { padding: 30px 22px; }   /* 46px of inset is a lot of a 375px screen */
}

/* ---- Filter pill rails: 40px tall with a 2px gutter between targets ----
   .faq-filters and .ev-filters are independent filters (not a segmented
   control), so they get a real gutter and a 44px target on touch. The
   sliding capsule is absolutely positioned at a hardcoded 40px, so it has
   to grow with the pills or it sits short behind them. */
@media (pointer: coarse) {
  .faq-filters,
  .ev-filters { gap: 8px; }

  .faq-pill,
  .ev-pill { height: 44px; }

  .faq-pill-thumb,
  .ev-filters .seg-thumb { height: 44px; }
}

/* ---- .reg-switch: overflowed its own rail and clipped "New Customer" ----
   24 + 188 + 2 + 166 = 380px inside a 375px viewport. This one IS a true
   two-tab segmented control, so it keeps its tight 2px gutter (the pair
   reads as a single unit); the fix is equal-width tabs that fit. */
@media (max-width: 767.98px) {
  .reg-switch { display: flex; width: 100%; overflow-x: hidden; }
  .reg-switch-btn {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: 10px;
    justify-content: center;
  }
}
/* Height is a touch concern, not a width one — tablets need it too. */
@media (pointer: coarse) {
  .reg-switch-btn { height: 44px; }
  .reg-switch .seg-thumb { height: 44px; }
}

/* ---- Hero carousel dots: 9x9 with a 9px gutter ----------------------
   A 44px target is not reachable here — at an 18px pitch the hit areas of
   adjacent dots would overlap and steal each other's taps. Widen the pitch
   and hang a larger transparent target off each dot instead: the dots stay
   visually small, the tappable region roughly triples without overlapping. */
@media (pointer: coarse) {
  .hero-dots { gap: 20px; }
  .hero-dot { position: relative; }
  .hero-dot::before {
    content: "";
    position: absolute;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 28px; height: 28px;
  }
}

/* ---- Theme toggle + header CTA: 40x40 and 160x33 ---------------------
   Gated on pointer:coarse rather than width. A 768px tablet is a touch
   device and was missing out on a width-based rule, while a mouse-driven
   desktop never matches this at any width — so the signed-off desktop
   header is untouched. */
@media (pointer: coarse) {
  .theme-toggle { width: 44px; height: 44px; }
  .header .cta-btn { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ---- Mobile nav dropdown chevrons: 30x30 ----------------------------- */
@media (max-width: 1199px) {
  .navmenu .toggle-dropdown {
    width: 44px; height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }

  /* The chevron must show whether the submenu is OPEN, not which section you are
     in. main.css draws `.active i` filled and rotated 180deg, and header.php also
     writes .active on the current page's top-level parent — so on /about the
     About Us chevron sat filled and pointing up while its panel was shut, and
     flipped to the collapsed look when opened. Exactly inverted.

     Expanded state is .is-open (see toggleDropdown in main.js). Here .active is
     put back to the resting look, and .is-open takes over the open one. The
     .is-open rule follows so that a row which is both wins on source order. */
  .navmenu .active > .toggle-dropdown {
    background-color: color-mix(in srgb, var(--accent-color), transparent 90%);
    color: var(--accent-color);
    transform: none;
  }
  .navmenu .is-open > .toggle-dropdown {
    background-color: var(--accent-color);
    color: var(--contrast-color);
    transform: rotate(180deg);
  }
}

/* ---- Demo modal close button: 36x36 ---------------------------------- */
@media (pointer: coarse) {
  .demo-modal .btn-close,
  .demo-modal .dm-close { width: 44px; height: 44px; }
}

/* ---- Over-inset cards on phones (A5) ---------------------------------
   These cards carry 26-30px of padding on top of the container's 12px
   gutter, so on a 375px screen ~60px of a 351px card goes to whitespace and
   the content column lands around 290px. Tighten the inset on small screens
   only; the desktop cards keep their generous padding. */
@media (max-width: 575.98px) {
  .form-card,
  .vb-ladder { padding: 20px 16px; }
  .ds-dose { padding: 20px 16px; }
}

/* ---- File input: give the native control the field's own shape --------
   Was keyed to .php-email-form and scoped to phones, which left the desktop
   career page rendering a raw browser file control. career.php is on the .vf-*
   ajax sheet now (fetch sends multipart fine), so this follows it — and it is
   unscoped, because a file input on a 1440px screen looks just as unfinished.

   The padding matches .vf-form .form-control exactly. That is the point of the
   change: the old 11px/1.35 made this control ~5px shorter than the fields
   around it, and the previous media-query scoping was avoiding that symptom
   rather than the cause. The negative margins bleed the button to the field's
   inner edges so it reads as one control, not a box inside a box. */
.vf-form input[type="file"].form-control { padding: 12px 14px; line-height: 1.5; }
.vf-form input[type="file"].form-control::file-selector-button {
  margin: -12px 14px -12px -14px;
  padding: 12px 14px;
  border: 0;
  border-right: 1px solid var(--line);
  background: var(--mist);
  color: var(--ink);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  transition: background .2s ease;
}
.vf-form input[type="file"].form-control::file-selector-button:hover { background: var(--line); }

/* ---- Testimonial marquee on phones (B3) ------------------------------
   .tw-card is a hard flex: 0 0 340px and .twv-card 300px. On a 360px device
   (345px of content) a 340px card plus its 28px gutter is wider than the
   screen, so cards bleed off both edges and names clip mid-word. The pause
   is also :hover-only, which no touch device can trigger — the wall never
   stops and there is no way to hold a card still to read it.

   Narrow the cards to fit, and make the row a real swipeable scroller on
   touch: the animation is dropped in favour of native horizontal scrolling
   with snap, so a reader can move at their own pace. This mirrors what the
   reduced-motion block below already does. */
@media (max-width: 767.98px) {
  .tw-card  { flex-basis: 268px; width: 268px; margin-right: 14px; }
  .twv-card { flex-basis: 248px; width: 248px; margin-right: 14px; }
}
@media (hover: none) {
  .tw-row {
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -webkit-mask-image: none;
    mask-image: none;
  }
  .tw-row::-webkit-scrollbar { display: none; }
  .tw-track { animation: none !important; width: max-content; }
  .tw-card, .twv-card { scroll-snap-align: start; }
}

/* ---- convenience.php day scroller (B6) -------------------------------
   6 columns of 210px in a 336px window. It scrolls correctly, but nothing
   signals that, and a card clipped mid-word at the fold reads as broken
   rather than as "there is more this way". Widen the snap column so one
   card sits square in the viewport with the next just peeking. */
@media (max-width: 575.98px) {
  .cv-day { grid-template-columns: repeat(6, minmax(270px, 1fr)); }
}

/* ---- Footer newsletter field at 360px --------------------------------
   A flex item defaults to min-width:auto, so the email input refuses to
   shrink below the intrinsic width of its placeholder and shoves the
   Subscribe button ~5px past the right edge on a 360px device. Present on
   every page (the footer band). min-width:0 lets the input yield. */
@media (max-width: 575.98px) {
  .newsletter-field input { min-width: 0; }
  /* Font-size stays at 16px — anything less and iOS zooms the page on focus.
     Reclaim the width from the horizontal padding instead. */
  .newsletter-field-lg input { padding: 12px 12px; }
  .newsletter-field-lg button { padding: 0 16px; }
}


/* ======================================================================
   Software hub — software.php

   NAMESPACE: sw-hub-, not sw-. assets/css/software.css owns .sw-hero,
   .sw-shot, .sw-aside, .sw-pills and .sw-platforms for the DETAIL pages, all
   scoped to body.sw-page. The hub carries no body class, so a bare .sw-*
   name here would be a different component wearing a colliding prefix.
   .sw-hub-shot above (line ~695) already set this convention.

   Everything else the page needs already exists: .story-hero, .about-stats,
   .chapter-head, .essay-col, .essay-media, .feature-card, .pullband,
   .cta-band. Only the two components below are new.
   ====================================================================== */

/* ---- The layer diagram (#layers) --------------------------------------
   Four slabs: the practice over the volume over the image over the machine.

   The axis is WIDENING SCOPE — what each layer operates on — and deliberately
   not a dependency chain. data/software.php documents no handoff between
   EzDent-i and Ez3D-i; the two only list each other in 'integrations'. Drawing
   routed connectors between adjacent slabs would assert a chain the data does
   not support, so there are none: each slab states what arrives in it and
   names its own source (.sw-hub-flow), rendered only where the data documents
   the handoff.

   Shell is the five declarations .vb-ladder and .ds-lat already share, rather
   than a third card recipe. Zero JS, zero [data-bs-theme="dark"] rules, and
   nothing animated — the diagram is static geometry, so unlike .wd-bar-fill
   it needs no AOS-layered fallback and no reduced-motion entry. */
.sw-hub-stack {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 30px; box-shadow: var(--shadow-sm);
}
.sw-hub-layers { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }

/* scope │ slab │ runs-on — .vb-row's proportions. DOM order is top-down
   (practice first, machine last), so reading and tab order match the picture;
   no column-reverse, which would invert both. */
.sw-hub-layer { display: grid; grid-template-columns: 132px minmax(0, 1fr) 124px; gap: 18px; align-items: center; }
.sw-hub-scope {
  font-family: var(--heading-font); font-weight: 700; font-size: .95rem;
  color: var(--accent); text-align: right; text-wrap: balance;
}
/* The second, fully data-backed axis: clever-dent runs on Cloud while both
   imaging titles are Windows desktop. Cloud over desktop over hardware is why
   this is a stack and not a list. */
.sw-hub-runs { color: var(--ink-soft); font-size: .78rem; font-weight: 600; }

/* The 3px bottom edge is the whole trick — it gives each row thickness, so the
   group reads as slabs resting on one another rather than as more list items. */
.sw-hub-slab {
  padding: 16px 20px; border-radius: 12px;
  background: color-mix(in srgb, var(--accent) 7%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--accent) 26%, transparent);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--accent) 18%, transparent);
}
.sw-hub-name { display: block; font-family: var(--heading-font); font-weight: 700; color: var(--ink); font-size: 1.04rem; }
.sw-hub-what { display: block; color: var(--ink-soft); font-size: .9rem; line-height: 1.55; margin-top: 3px; }
.sw-hub-flow {
  display: flex; align-items: flex-start; gap: 7px; margin-top: 11px;
  color: var(--accent); font-size: .82rem; font-weight: 600; line-height: 1.45;
}
.sw-hub-flow i { flex: 0 0 auto; margin-top: 2px; }

/* The base is the machine, not a program. Dashed and drained of accent so the
   stack reads as three titles sitting on something the practice already owns.
   Mixed from --ink rather than a grey literal, so it needs no dark override. */
.sw-hub-layer.is-base .sw-hub-slab {
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  border: 1px dashed color-mix(in srgb, var(--ink) 24%, transparent);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--ink) 8%, transparent);
}
.sw-hub-layer.is-base .sw-hub-scope { color: var(--ink-soft); }

.sw-hub-note { margin: 24px 0 0; padding-top: 18px; border-top: 1px solid var(--line); }

/* ---- Title bands (#cleverdent / #ezdent / #ez3d) ----------------------
   Two-column editorial bands. Only the type needs rules; the column geometry is
   Bootstrap's and the screenshot frame is .sw-hub-shot, further up this file.

   .sw-hub-lead is the same shape as .sv-lead (direct.php). Not reused across
   the prefix boundary on purpose — if a third page wants it, that is the point
   to promote both into one generic soft-lead class rather than to keep
   borrowing a page-scoped name. */
.sw-hub-lead { color: var(--ink); font-size: 1.1rem; font-weight: 600; line-height: 1.6; margin: 0 0 10px; text-wrap: balance; }
.sw-hub-body { color: var(--ink-soft); font-size: 1rem; line-height: 1.75; margin: 0; }

/* Clever Dent's wordmark — the only title that ships one. Height-capped so a
   logo swap cannot resize the band. (software.css has .sw-logo for the same job
   on the detail pages, but it is scoped to body.sw-page and cannot reach here.) */
.sw-hub-logo { height: 40px; width: auto; display: block; margin-bottom: 14px; }

/* ---- Neutral pill row -------------------------------------------------
   The platform list under each title band. Replaces three inline
   style="background:var(--mist);color:var(--ink-soft)" attributes that were
   overriding .eyebrow's accent treatment in place — these are facts about
   where the software runs, not calls to action, so they should not shout. */
.sw-hub-tags { display: flex; flex-wrap: wrap; gap: 8px; margin: 18px 0 0; padding: 0; list-style: none; }
.sw-hub-tags li {
  background: var(--mist); color: var(--ink-soft); border: 1px solid var(--line);
  border-radius: 999px; padding: 5px 13px; font-size: .8rem; font-weight: 600;
}

@media (max-width: 767px) {
  .sw-hub-stack { padding: 22px; }
  /* One column, same DOM order — scope label, slab, then where it runs. */
  .sw-hub-layer { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .sw-hub-scope { text-align: left; }
}
