/* ==========================================================================
   AU landing V3 — Round 47 form surfaces
   --------------------------------------------------------------------------
   Six real forms, two presentation modes:

     .v3Popup  — top trio. Centred card on desktop, bottom sheet under 640px.
     .v3Sheet  — lower trio. Bottom sheet at every width, capped 480px and
                 centred on desktop.

   The sheet motion is ported verbatim from the shipped country sites
   (stunningdentistry-ca-main, src/components/layout/MobileStickyBar.tsx:1112-1145):
   300ms, cubic-bezier(0.22, 1, 0.36, 1), translate-y-full -> 0 plus opacity,
   black/45 scrim, drag handle, max-h 85svh, rounded-t 32px. That is the
   "scrolls up from there" behaviour he asked for.

   EVERY rule is scoped `body.v3` so V1, V2 and the deployed index.html are
   untouched. Loaded only by index-v3.html, after css/v3.css.

   Motion law: transform + opacity only, nothing that triggers layout.
   ========================================================================== */

body.v3 {
  --v3f-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --v3f-dur: 300ms;
  --v3f-line: #D0D5DD;
  --v3f-line-soft: #EAECF0;
  --v3f-ink: #101828;
  --v3f-ink-soft: #667085;
  --v3f-surface: #fff;
}

/* ---------- scrim ---------- */
body.v3 .v3fx {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  justify-content: center;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--v3f-dur) var(--v3f-ease);
}
body.v3 .v3fx.open {
  opacity: 1;
  pointer-events: auto;
}
body.v3.v3-locked {
  overflow: hidden;
}
/* the page's own sticky chrome must not sit above an open surface.
   R56: .sideRail came out of this list with the rail itself — V3 no longer
   renders one, and a selector for an element that cannot exist is the dead
   limb R52 and R53 both paid for. V1/V2 keep their rail and their own rules. */
body.v3.v3-locked .bottomBar,
body.v3.v3-locked .subStrip {
  opacity: 0;
  pointer-events: none;
}

/* ---------- shared card shell ----------
   R49: these values are the CA HeroCtaPopup's, read from .caPopup in
   css/style.css:1406-1416 rather than re-invented. style.css is shared with V1,
   V2 and the deployed index.html so it is not edited — but the two popups on
   this page must not drift apart, so the source is named here. */
body.v3 .v3Card {
  position: relative;
  width: 100%;
  max-width: 480px;
  max-height: 85svh;
  display: flex;
  flex-direction: column;
  background: var(--section-surface);
  border: 1px solid #EAECF0;
  border-radius: 24px;
  box-shadow: 0 18px 45px rgba(15, 23, 42, 0.30);
  will-change: transform;
  transition: transform var(--v3f-dur) var(--v3f-ease),
              opacity var(--v3f-dur) var(--v3f-ease);
}

/* ---------- bottom-sheet presentation ---------- */
body.v3 .v3fx.asSheet {
  align-items: flex-end;
}
body.v3 .v3fx.asSheet .v3Card {
  background: var(--section-surface);
  border-radius: 32px 32px 0 0;
  transform: translateY(100%);
  opacity: 0;
}
body.v3 .v3fx.asSheet.open .v3Card {
  transform: translateY(0);
  opacity: 1;
}

/* ---------- centred-popup presentation (ALL widths) ----------
   R48: was a bottom sheet under 640px and a popup only above it. He asked for a
   popup, once per form, three times. It is a popup everywhere now. Only the
   lower trio slides up. */
/* ---- R90 — the inline surface --------------------------------------------
   The footer's "Plan Your Dental Journey to India" form is the same surface
   the popups are, mounted on the page instead of over it. So it has to undo
   every dialog property the base .v3fx sets: the fixed position, the full
   viewport inset, the scrim, the stacking context, and the opacity/pointer
   gate that waits for .open (nothing ever opens this one). */
body.v3 .v3fx.asInline {
  position: static;
  inset: auto;
  z-index: auto;
  background: none;
  opacity: 1;
  pointer-events: auto;
  padding: 0;
  align-items: stretch;
}
body.v3 .v3fx.asInline .v3Card {
  width: 100%;
  max-width: 460px;
  max-height: none;      /* a page section scrolls with the page, not inside itself */
  border-radius: 24px;
  transform: none;
  opacity: 1;
  box-shadow: 0 14px 34px rgba(15, 23, 42, 0.16);
}
/* the head reads as a section heading here, not as dialog chrome: no close
   button sits beside it, so the title takes the width it needs */
body.v3 .v3fx.asInline .v3HeadText { text-align: center; }
/* the body is the page's scroll, so its own scroller must not double up */
body.v3 .v3fx.asInline .v3Body { overflow: visible; }
/* the mount centres the card in the footer section */
body.v3 [data-v3-inline] { display: flex; justify-content: center; }

body.v3 .v3fx.asPopup {
  align-items: center;
  padding: 16px;
}
body.v3 .v3fx.asPopup .v3Card {
  max-width: 420px;
  max-height: 88svh;
  border-radius: 24px;
  transform: translateY(28px) scale(0.98);
  opacity: 0;
  box-shadow: 0 18px 45px rgba(15, 23, 42, 0.30);
}
body.v3 .v3fx.asPopup.open .v3Card {
  transform: translateY(0) scale(1);
  opacity: 1;
}

/* the sheet keeps a hair of breathing room from the viewport edge on mobile */
body.v3 .v3fx.asSheet .v3Card {
  width: 100%;
}

@media (prefers-reduced-motion: reduce) {
  body.v3 .v3fx,
  body.v3 .v3Card {
    transition-duration: 1ms;
  }
  body.v3 .v3fx.asSheet .v3Card,
  body.v3 .v3fx.asPopup .v3Card {
    transform: none;
  }
}

/* ---------- drag handle (sheets only) ---------- */
body.v3 .v3Grab {
  flex: 0 0 auto;
  width: 36px;
  height: 4px;
  margin: 10px auto 2px;
  border-radius: 999px;
  background: var(--v3f-line);
}

/* ---------- head ----------
   CA arrangement (index-v3.html:1097-1101 / css/style.css:1417-1426): close
   LEFT, title CENTRED, a same-size spacer right so the title sits true centre. */
body.v3 .v3Head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 16px;
  background: var(--section-surface);
  border-bottom: 1px solid #F5F5F5;
  border-radius: 24px 24px 0 0;
}
body.v3 .v3fx.asSheet .v3Head {
  background: transparent;
  border-bottom: none;
  padding-top: 4px;
}
body.v3 .v3HeadText { flex: 1 1 auto; min-width: 0; text-align: center; }
body.v3 .v3Head h3 {
  text-align: center;
  font-family: var(--font-sans);
  font-size: 15px;
  font-weight: 600;
  color: var(--v3f-ink);
  margin: 0;
  letter-spacing: -0.01em;
}
body.v3 .v3HeadSpacer { flex: 0 0 auto; width: 32px; height: 32px; }
/* R50: the small mark before the title, as on the reference sheet. It sits
   INSIDE .v3HeadText so it rides with the centred title rather than becoming a
   fourth flex item and pushing it off centre (the R49 bug, twice is enough). */
body.v3 .v3HeadIcon {
  display: inline-flex;
  width: 22px; height: 22px;
  margin-right: 6px;
  vertical-align: -5px;
  align-items: center; justify-content: center;
  border-radius: 50%;
  background: rgba(0, 88, 200, 0.10);
  color: var(--brand-blue);
}
body.v3 .v3HeadIcon svg { width: 14px; height: 14px; }
body.v3 .v3Head .v3Sub {
  display: block;
  font-size: 11.5px;
  font-weight: 500;
  color: var(--v3f-ink-soft);
  margin-top: 2px;
  letter-spacing: 0;
}
body.v3 .v3Close,
body.v3 .v3CloseX,
body.v3 .v3Call {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  line-height: 1;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease, filter 150ms ease;
}
body.v3 .v3Close,
body.v3 .v3CloseX { background: #F2F4F7; color: #344054; }
body.v3 .v3Close:hover,
body.v3 .v3CloseX:hover { background: #DC2626; color: #fff; }
body.v3 .v3Close svg,
body.v3 .v3CloseX svg,
body.v3 .v3Call svg { width: 17px; height: 17px; }
/* R74: the call glyph gained an aria-hidden wrapper, so the flex centring
   needs a box that is not an inline span of its own line-height. */
body.v3 .v3Call > span,
body.v3 .v3CloseX > span { display: inline-flex; }

/* R50: the two glyphs live in the button together and CSS picks. At step 2 of a
   chat surface this button goes BACK; everywhere else it closes. JS keeps the
   aria-label honest in step with it. */
body.v3 .v3Close .icClose,
body.v3 .v3Close .icBack { display: none; align-items: center; }
body.v3 .v3Close .icClose { display: inline-flex; }
body.v3 .v3fx.atStep2 .v3Close .icClose { display: none; }
body.v3 .v3fx.atStep2 .v3Close .icBack { display: inline-flex; }

/* R74 — the button dials now, so WhatsApp's #25D366 goes with it. That green
   was kept off-palette on purpose because it was a platform mark; a call
   button carries no such obligation.
   R75 — blue, at his call.
   R76 — green, at his call. #16A34A is the site's own action-green (used on
   the hero CTA), distinct from WhatsApp's #25D366. */
body.v3 .v3Call {
  background: #16A34A;
  color: #fff;
  text-decoration: none;
}
body.v3 .v3Call:hover { filter: brightness(1.06); }

/* ---------- scrollable body ---------- */
body.v3 .v3Body {
  flex: 1 1 auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 12px 12px calc(12px + env(safe-area-inset-bottom));
  background: var(--section-surface);
  border-radius: 0 0 24px 24px;
}
/* the sheet keeps its own 32px top corners and squares off at the viewport floor */
body.v3 .v3fx.asSheet .v3Head { border-radius: 32px 32px 0 0; }
body.v3 .v3fx.asSheet .v3Body { border-radius: 0; }
/* Brand book rule 1: warm cream is the canvas, never pure white. Sheets match
   the popups rather than going white. */
body.v3 .v3fx.asSheet .v3Body { background: var(--section-surface); }

/* ---------- the white panel on cream (.caInnerCard, css/style.css:1448-1449) ----
   R49: THE missing piece. R47/R48 laid the fields flat on the cream body, so the
   form had no object to sit in. The CA card floats a white card inside the cream
   one — two surface levels — and that is what makes it read as finished. */
body.v3 .v3Panel {
  border-radius: 10px;
  border: 1px solid #EAECF0;
  background: #fff;
  padding: 10px 12px;
}
body.v3 .v3Panel + .v3Panel { margin-top: 8px; }
body.v3 .v3PanelTitle {
  font-size: 12px;
  font-weight: 600;
  color: var(--v3f-ink);
  margin: 0;
}
body.v3 .v3PanelTitle + .v3PanelFields { margin-top: 8px; }
body.v3 .v3PanelFields {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

body.v3 .v3Body form {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* ---------- fields ---------- */
body.v3 .v3Field { display: flex; flex-direction: column; gap: 5px; }
/* [hidden] is display:none in the UA sheet, but the class rule above outranks
   it. Without this the Others describe box renders even while hidden=true. */
body.v3 .v3Field[hidden] { display: none; }
body.v3 .v3Label {
  font-size: 11.5px;
  font-weight: 600;
  color: #344054;
  letter-spacing: 0.01em;
}
body.v3 .v3Body input[type="text"],
body.v3 .v3Body input[type="tel"],
body.v3 .v3Body input[type="email"],
body.v3 .v3Body input[type="number"],
body.v3 .v3Body select,
body.v3 .v3Body textarea {
  width: 100%;
  border-radius: 8px;
  border: 1px solid var(--v3f-line);
  background: #fff;
  padding: 9px 12px;
  font-size: 13px;
  font-family: inherit;
  color: var(--v3f-ink);
  margin: 0;
  -webkit-appearance: none;
  appearance: none;
}
body.v3 .v3Body textarea { min-height: 74px; resize: vertical; line-height: 1.45; }
body.v3 .v3Body select {
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23667085' d='M1 1.5 6 6.5l5-5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}
body.v3 .v3Body input:focus,
body.v3 .v3Body select:focus,
body.v3 .v3Body textarea:focus {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 1px var(--brand-blue);
}
body.v3 .v3Body .field-invalid { border-color: #DC2626; }
body.v3 .v3Body .fieldError {
  display: none;
  font-size: 11px;
  color: #DC2626;
  margin: 0;
}
body.v3 .v3Body .fieldError.show { display: block; }

/* ---------- segmented control (audio / video, male / female) ---------- */
body.v3 .v3Seg {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 6px;
}
body.v3 .v3Seg label {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 42px;
  padding: 8px 10px;
  border: 1px solid var(--v3f-line);
  border-radius: 10px;
  background: #fff;
  font-size: 12.5px;
  font-weight: 600;
  color: #344054;
  cursor: pointer;
  text-align: center;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}
body.v3 .v3Seg input { position: absolute; opacity: 0; pointer-events: none; }
body.v3 .v3Seg label:has(input:checked) {
  border-color: var(--v3-gold);
  background: rgba(201, 168, 76, 0.12);
  color: var(--v3-gold-text);
}
body.v3 .v3Seg label:has(input:focus-visible) {
  box-shadow: 0 0 0 2px var(--brand-blue);
}
body.v3 .v3Seg .segIcon { font-size: 14px; line-height: 1; }

/* ---------- phone with the +61 affix ---------- */
body.v3 .v3Phone {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--v3f-line);
  border-radius: 8px;
  background: #fff;
  overflow: hidden;
}
body.v3 .v3Phone:focus-within {
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 1px var(--brand-blue);
}
body.v3 .v3Phone .v3Cc {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0 10px;
  background: #F9FAFB;
  border-right: 1px solid var(--v3f-line);
  font-size: 13px;
  font-weight: 600;
  color: #344054;
  white-space: nowrap;
}
body.v3 .v3Phone .v3Cc .ccFlag { font-size: 13px; }
body.v3 .v3Phone input {
  border: none !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  flex: 1 1 auto;
  min-width: 0;
}
body.v3 .v3Phone.field-invalid { border-color: #DC2626; }

/* ---------- contact row: [+61 | number]  or  [email], ONE line (R48) ----------
   Was a stacked phone / full-width rule / email block. He asked for the country
   code, the number and the email on the same line. */
body.v3 .v3Contact {
  display: flex;
  align-items: center;
  gap: 7px;
}
/* The phone side needs MORE than half, not less: it spends ~42px on the +61
   affix before the placeholder starts, while "Email address" leaves the email
   side slack. R48 shipped 44/56 and clipped; 50/50 clipped again once R49's
   panel padding took 24px off the row. 56/44 is measured to fit both, and the
   probe now checks rendered placeholder width rather than trusting this. */
body.v3 .v3Contact .v3Phone { flex: 1 1 56%; min-width: 0; }
body.v3 .v3Contact input[type="email"] { flex: 1 1 44%; min-width: 0; }
/* the affix tightens inside the row so the pair survives a 390px viewport */
body.v3 .v3Contact .v3Phone .v3Cc { padding: 0 8px; gap: 4px; }
body.v3 .v3Contact .v3Phone input,
body.v3 .v3Contact input[type="email"] { padding-left: 9px; padding-right: 9px; }
/* R96 — this holds a single "&" now, not the word AND. At 10px/700 with 0.06em
   tracking, metrics tuned for a three-letter word, one glyph read as a speck
   between two fields. Sized up and untracked; still the same grey, because it
   is furniture between two inputs and not something to read. */
body.v3 .v3Or {
  flex: 0 0 auto;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1;
  color: #98A2B3;
}

/* ---------- photo upload ---------- */
body.v3 .v3Upload {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 11px 12px;
  border: 1px dashed var(--v3f-line);
  border-radius: 10px;
  background: #FCFCFD;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease;
}
body.v3 .v3Upload:hover { border-color: var(--v3-gold); background: rgba(201, 168, 76, 0.06); }
body.v3 .v3Upload input[type="file"] { display: none; }
/* R95 — the icon is a CIRCLE on the left, his word for it. The radius and the
   glyph sizing were the two things worth keeping from the R56 dropzone; they
   move onto the base rule as that skin retires. Without the explicit svg size
   the camera renders at its 24px default and outgrows the 38px well. */
body.v3 .v3Upload .upIcon {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(201, 168, 76, 0.14);
  font-size: 17px;
  overflow: hidden;
}
body.v3 .v3Upload .upIcon svg { width: 20px; height: 20px; color: var(--v3-gold-text); }
/* the thumbnail fills the circle once a file is picked */
body.v3 .v3Upload .upIcon img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  border-radius: 50%;
}
body.v3 .v3Upload .upText { min-width: 0; }
body.v3 .v3Upload .upText strong {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--v3f-ink);
}
body.v3 .v3Upload .upText em {
  display: block;
  font-style: normal;
  font-size: 11px;
  line-height: 1.4;
  color: var(--v3f-ink-soft);
  margin-top: 1px;
}
/* only once a file is chosen does the text become a filename worth truncating */
body.v3 .v3Upload .upText em.isFile {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-top: 1px;
}
/* R48: the "photo delivery is not switched on" note (.v3UpNote) and the
   .v3Upload.pending state are gone — he named that text and did not want it.
   The admission moved into the CRM message instead, see js/v3-forms.js §7. */

/* ---------- R95: the in-form treatment dropdown ----------
   Replaces the native <select>, whose list was drawn by the OS. The closed
   button borrows the exact field metrics above (8px radius, 1px --v3f-line,
   9px/12px padding, 13px) so the control reads as the same family of box the
   patient has already filled twice; only the caret and the open list are new.

   The list expands INLINE rather than overlaying, because .v3Body scrolls and
   an absolutely-positioned list would be clipped at its edge. It gets its own
   max-height so eight treatments cannot push the submit button off the card. */
body.v3 .v3Dd { position: relative; }
body.v3 .v3Body .v3DdBtn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  border-radius: 8px;
  border: 1px solid var(--v3f-line);
  background: #fff;
  padding: 9px 12px;
  font-size: 13px;
  font-family: inherit;
  color: var(--v3f-ink);
  text-align: left;
  cursor: pointer;
  margin: 0;
  -webkit-appearance: none;
  appearance: none;
}
body.v3 .v3Body .v3DdBtn:focus-visible {
  outline: none;
  border-color: var(--brand-blue);
  box-shadow: 0 0 0 1px var(--brand-blue);
}
/* the unchosen state is a placeholder, so it takes placeholder ink — the same
   signal every other empty field in this form gives */
body.v3 .v3DdVal.isPlace { color: var(--v3f-ink-soft); }
body.v3 .v3DdVal { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
body.v3 .v3DdCaret {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  color: #667085;
  transition: transform 160ms var(--ease-out, ease);
}
body.v3 .v3DdCaret svg { width: 16px; height: 16px; }
body.v3 .v3Dd.isOpen .v3DdCaret { transform: rotate(180deg); }
body.v3 .v3Dd.isOpen .v3DdBtn { border-color: var(--brand-blue); }

body.v3 .v3DdList {
  list-style: none;
  margin: 6px 0 0;
  padding: 4px;
  border: 1px solid var(--v3f-line);
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 4px 14px rgba(16, 24, 40, 0.08);
  /* MEASURED, and re-measured: a row is 38.14px, its text starts 24% down
     (9px of padding), the list pads 4px and carries a 1px border. 216px showed
     5.45 rows and sliced the sixth through its letterforms — broken, not "more
     below". 206px still cut at 27%, three points INTO the glyphs, because the
     border was not in the first calculation. 202px lands the cut at ~16%,
     inside that row's own top padding: five whole rows plus a blank sliver,
     the peek that says scroll without mangling a word. */
  max-height: 202px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
body.v3 .v3DdList li {
  padding: 9px 10px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--v3f-ink);
  cursor: pointer;
}
body.v3 .v3DdList li:hover,
body.v3 .v3DdList li.isCursor { background: rgba(0, 88, 200, 0.07); }
body.v3 .v3DdList li[aria-selected="true"] {
  font-weight: 600;
  background: rgba(0, 88, 200, 0.1);
}

/* ---------- consent ---------- */
body.v3 .v3Consent {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 11.5px;
  line-height: 1.45;
  color: #475467;
  cursor: pointer;
}
body.v3 .v3Consent input {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 1px;
  accent-color: var(--v3-gold-text);
}
body.v3 .v3Consent.field-invalid { color: #DC2626; }

/* ---------- the required dot (R72) ----------
   His ask: make it visible which fields are compulsory. These three hide their
   labels, so the mark cannot ride on label text — it sits inside the control's
   right edge instead. Same #DC2626 as .fieldError above: a required mark and a
   validation error are one conversation, not two reds.

   Decorative only. `required` on the input is what assistive tech announces,
   so the dot is aria-hidden by construction (a ::after on a wrapper is never
   in the a11y tree) and nothing here changes what a screen reader hears. */
body.v3 .v3Field.isRequired { position: relative; }
body.v3 .v3Field.isRequired::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 11px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #DC2626;
  transform: translateY(-50%);
  pointer-events: none;
}
/* the inputs give the dot its own gutter so a long value never runs under it */
body.v3 .v3Field.isRequired > input,
body.v3 .v3Field.isRequired .v3Phone input { padding-right: 26px; }
/* R95 — the contact row has ONE dot, at the right end of the row, which is the
   email half. Two corrections follow from that:
   ① the email input is nested, so the rule above never reached it and a typed
     address ran under the dot;
   ② the phone input is NOT under the dot, but the rule above reached it anyway
     and spent 17px of a 78px placeholder budget on a gutter for a mark that is
     not there. Measured, not eyeballed: "4XX XXX XXX" needs 79px and had 78,
     so the placeholder clipped at 390 — the same failure this row's own history
     records twice. Give the gutter only to the half that carries the dot. */
body.v3 .v3Field.isRequired .v3Contact input[type="email"] { padding-right: 26px; }
body.v3 .v3Field.isRequired .v3Contact .v3Phone input { padding-right: 9px; }

/* R95 — the dropdown already spends its right edge on a caret, so the dot
   landed on top of it. The gutter pushes the caret in (the button is a
   space-between flex row), and the dot is pinned to the BUTTON's centre line
   rather than the wrapper's: choosing "Others" reveals a textarea inside this
   same wrapper, and a wrapper-centred dot would drift into the middle of it. */
body.v3 .v3Field.isRequired .v3DdBtn { padding-right: 26px; }
body.v3 .v3Field.isRequired:has(.v3Dd)::after { top: 20px; }
/* a visible label means the field is taller than one control, so centring on
   the wrapper would drop the dot into the middle of the box */
body.v3 .v3Field.isRequired:has(.v3Label)::after { top: 33px; }

/* R77 — a required SEGMENTED field. The rules above assume the control is a
   single input the dot can sit inside; a seg is a row of tiles, and both
   placements land the dot ON one of them (11px from the right edge is inside
   the last tile, and 33px down is mid-card on the tall `cards` variant). It
   rides the LABEL's line instead — the only part of a seg that behaves like
   one row — and stays on the RIGHT, where every other field's dot is. Moving
   it to the left of the label would put one of the form's dots on a different
   side from all the others, which reads as a different mark rather than the
   same one. */
/* R104: the date strip and the slot grid are multi-row controls with a visible
   label, exactly the shape this rule was written for, so they join it rather
   than getting a second placement of the same mark. */
body.v3 .v3Field.isRequired:has(.v3Seg)::after,
body.v3 .v3Field.isRequired:has(.v3Label):has(.v3Seg)::after,
body.v3 .v3Field.isRequired.v3Date::after,
body.v3 .v3Field.isRequired.v3Slots::after {
  top: 7px;
  right: 2px;
  transform: none;
}
body.v3 .v3Field.isRequired:has(.v3Seg) > .v3Label,
body.v3 .v3Field.isRequired.v3Date > .v3Label,
body.v3 .v3Field.isRequired.v3Slots > .v3Label { padding-right: 14px; }

/* the seg's own invalid state — a red rim on the group, matching the
   field-invalid treatment the inputs get */
body.v3 .v3Seg.field-invalid label {
  border-color: #DC2626;
}

/* (R71 widened this one surface to 620px to fit a two-column row. R73 removed
   the row, so the reason went with it and e-consult is back on the shared
   480px shell — a width with no remaining justification is drift.) */

/* ---------- the one-line condition box (R73) ----------
   His call: the condition is one line, with the dropzone under it. It stays a
   <textarea> so a patient with more to say can still type it — the box scrolls
   instead of truncating, which an <input> would not do. min-height has to be
   overridden explicitly: the shared rule above sets 74px and `rows="1"` loses
   to it every time. */
body.v3 .v3Body textarea.isOneLine {
  min-height: 0;
  height: 40px;
  padding-top: 10px;
  padding-bottom: 10px;
  resize: none;
  overflow-y: auto;
}

/* (R71's .v3Row — condition and photo in two columns — is gone at R73. He
   stacked the pair full-width instead, so no surface declares a row and the
   renderer went with it. See js/v3-forms.js, renderField.) */

/* ---------- captcha (R70) ----------
   The widget is a Google iframe at a FIXED 304x78. We do not get to restyle
   it, only to fit it. On desktop the popup's inner width clears 304 easily and
   this block does nothing but space it. The narrow case is handled below. */
body.v3 .v3Captcha {
  min-width: 0;
}
body.v3 .v3Captcha .g-recaptcha {
  /* the iframe is inline-block; this stops the 78px box collapsing before
     Google's script has swapped the div for the real widget */
  min-height: 78px;
}
body.v3 .v3Captcha .fieldError { margin-top: 2px; }

/* ---------- submit ---------- */
body.v3 .v3Submit {
  width: 100%;
  border: none;
  border-radius: 10px;
  padding: 12px 16px;
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 700;
  color: #111827;
  cursor: pointer;
  background: var(--grad-gold-cta);
  box-shadow: 0 8px 18px rgba(141, 110, 27, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.35);
  transition: box-shadow 150ms ease, opacity 150ms ease;
}
body.v3 .v3Submit:hover { box-shadow: 0 10px 24px rgba(141, 110, 27, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.40); }
body.v3 .v3Submit[disabled] { opacity: 0.6; cursor: default; }
body.v3 .v3Submit.pending { color: transparent; position: relative; }
body.v3 .v3Submit.pending::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  border: 2px solid rgba(17, 24, 39, 0.3);
  border-top-color: #111827;
  border-radius: 50%;
  animation: v3spin 700ms linear infinite;
}
@keyframes v3spin { to { transform: rotate(360deg); } }

/* ---------- result panel ---------- */
body.v3 .v3Result {
  display: none;
  padding: 18px 4px 6px;
  text-align: center;
}
body.v3 .v3Result.show { display: block; }
body.v3 .v3Result .rTick {
  width: 46px; height: 46px;
  margin: 0 auto 10px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 22px;
  background: rgba(18, 183, 106, 0.12);
  color: #12B76A;
}
body.v3 .v3Result.bad .rTick { background: rgba(220, 38, 38, 0.1); color: #DC2626; }
body.v3 .v3Result h4 { font-size: 15px; font-weight: 700; color: var(--v3f-ink); margin: 0 0 5px; }
body.v3 .v3Result p { font-size: 12.5px; line-height: 1.5; color: #475467; margin: 0 0 12px; }
body.v3 .v3Result .rWa {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 10px 18px;
  border-radius: 10px;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  text-decoration: none;
  background: linear-gradient(135deg, #16A34A 0%, #25D366 100%);
}
/* the slot a supplied per-treatment price infographic drops into later */
body.v3 .v3ArtSlot { display: none; margin-top: 12px; }
body.v3 .v3ArtSlot.show { display: block; }
body.v3 .v3ArtSlot img { width: 100%; height: auto; border-radius: 10px; display: block; }

/* ======================================================================
   8b. CHAT SURFACES  (R50)
   ======================================================================
   The country-site "24/7 CRM Support" sheet, ported:
   Project Web/stunningdentistry-au/src/components/layout/MobileStickyBar.tsx
   lines 2059-2161. Its exact values are used where they hold up
   (bubble #EFF6FF on #1E3A5F ink, user bubble #0058C8, 12px/10px/8px radius
   ladder), and dropped where they don't: its emoji are line icons here, and
   the greeting sits on the CREAM body rather than its near-white, because
   brand book rule 1 makes cream the canvas.

   The old calculator chooser lived here. The chips replaced it. */

body.v3 .v3Step { display: none; }
body.v3 .v3Step.on { display: block; }
/* the step swap is a transform+opacity move only, per the motion law */
@media (prefers-reduced-motion: no-preference) {
  body.v3 .v3Step.on { animation: v3StepIn 180ms var(--v3f-ease) both; }
}
@keyframes v3StepIn {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ---- the bubbles ---- */
body.v3 .v3Say {
  border-radius: 12px;
  background: #EFF6FF;
  padding: 10px 12px;
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: #1E3A5F;
}
body.v3 .v3Say p { margin: 0; font-size: 13px; line-height: 1.45; color: #1E3A5F; }
body.v3 .v3Say:focus { outline: none; }
body.v3 .v3Say:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
body.v3 .v3SayCta {
  margin-top: 7px !important;
  font-size: 12px;
  font-weight: 600;
  color: #4B7BB5 !important;
}
/* the choice, echoed back — right-aligned, the way a sent message reads.
   The <p> is the alignment box; the <span> inside it is the pill, so the pill
   hugs the label instead of stretching the full width. */
body.v3 .v3Said {
  margin: 0 0 8px;
  text-align: right;
}
body.v3 .v3Said:empty { display: none; }
body.v3 .v3Said span {
  display: inline-block;
  max-width: 100%;
  padding: 6px 12px;
  border-radius: 10px;
  background: var(--brand-blue);
  color: #fff;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  text-align: left;
}
body.v3 .v3Tape { margin-bottom: 10px; }

/* ---- the chips ---- */
body.v3 .v3Chips {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  margin-top: 10px;
}
body.v3 .v3Chip {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 48px;
  padding: 9px 10px;
  border: 1px solid #D0D5DD;
  border-radius: 10px;
  background: #fff;
  text-align: left;
  cursor: pointer;
  font-family: inherit;
  color: #344054;
  transition: border-color 150ms ease, background 150ms ease, transform 150ms ease;
}
body.v3 .v3Chip:hover {
  border-color: var(--brand-blue);
  background: rgba(0, 88, 200, 0.05);
}
body.v3 .v3Chip:active { transform: scale(0.985); }
body.v3 .v3Chip:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }
body.v3 .v3Chip .chIcon {
  flex: 0 0 auto;
  width: 28px; height: 28px;
  display: inline-flex;
  align-items: center; justify-content: center;
  border-radius: 8px;
  background: rgba(0, 88, 200, 0.08);
  color: var(--brand-blue);
}
body.v3 .v3Chip .chIcon svg { width: 16px; height: 16px; }
body.v3 .v3Chip .chText { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
body.v3 .v3Chip strong {
  font-size: 12px; font-weight: 600; line-height: 1.25;
  color: var(--v3f-ink);
}
body.v3 .v3Chip em {
  font-style: normal; font-size: 10.5px; line-height: 1.3;
  color: var(--v3f-ink-soft);
}

/* ---- the one-layer selector (R54) ----
   Price List and Video Consult ask everything on one screen now, so their chips
   are a control inside the form rather than a gate in front of it. Same chip,
   two differences it has to carry that a step-1 grid never did: a SELECTED
   state (nothing else on the screen records the answer once the step is gone)
   and an invalid state (it is required, and it validates with the fields). */
body.v3 .v3ChipPanel .v3Chips { margin-top: 0; }
body.v3 .v3Chip.on {
  border-color: var(--brand-blue);
  background: rgba(0, 88, 200, 0.07);
  box-shadow: inset 0 0 0 1px var(--brand-blue);
}
body.v3 .v3Chip.on strong { color: var(--brand-blue-deep); }
/* the same #DC2626 the field-invalid rule at the top of this file uses — one
   error colour on the surface, not a second one for chips */
body.v3 .v3Chips.field-invalid .v3Chip { border-color: #DC2626; }
body.v3 .v3ChipPanel .fieldError { margin-top: 8px; }

/* ---- back to the choices ---- */
body.v3 .v3Restep {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 10px;
  padding: 4px 2px;
  border: none;
  background: none;
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--v3f-ink-soft);
  cursor: pointer;
}
body.v3 .v3Restep svg { width: 14px; height: 14px; }
body.v3 .v3Restep:hover { color: var(--brand-blue); }
body.v3 .v3Restep[hidden] { display: none; }

/* ---------- calculator panes ---------- */
body.v3 .v3Calc { display: none; }
body.v3 .v3Calc.on { display: block; }
/* R50: the pane's controls live in the same white .v3Panel the forms use, so
   both faces of a chat sheet sit on the cream at the same level. */
body.v3 .v3Calc .v3Panel { padding: 12px; }
body.v3 .v3Calc .v3Panel .v3Field + .v3Field { margin-top: 10px; }
body.v3 .v3Back {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 11px;
  padding: 5px 10px 5px 7px;
  border: 1px solid var(--v3f-line);
  border-radius: 999px;
  background: #fff;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  color: #344054;
  cursor: pointer;
}
body.v3 .v3Back:hover { border-color: var(--v3-gold); }

/* Brand book rule 1: Palette C, and never near-black navy as a surface. This is
   the consideration-tier trust-strip gradient (#0058C8 -> #001B5E), the same
   pair the country sites use. */
body.v3 .v3Out {
  margin-top: 13px;
  padding: 14px;
  border-radius: 12px;
  background: var(--grad-trust-strip);
  color: #fff;
  text-align: center;
}
body.v3 .v3Out .oLabel,
body.v3 .v3Out .oValue { display: block; }
body.v3 .v3Out .oLabel {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: rgba(255, 255, 255, 0.62);
}
body.v3 .v3Out .oValue {
  font-size: 27px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--v3-sand);
  margin-top: 3px;
  font-variant-numeric: tabular-nums;
}
body.v3 .v3Out .oNote {
  font-size: 10.5px;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.62);
  margin: 7px 0 0;
}
body.v3 .v3Break {
  list-style: none;
  margin: 11px 0 0;
  padding: 11px 0 0;
  border-top: 1px solid rgba(255, 255, 255, 0.16);
  display: flex;
  flex-direction: column;
  gap: 6px;
}
body.v3 .v3Break li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 11.5px;
  color: rgba(255, 255, 255, 0.82);
}
body.v3 .v3Break li b { font-weight: 700; color: #fff; font-variant-numeric: tabular-nums; }

/* ---------- R77. THE BRAND BOOK SECTIONS ----------------------------------
   The four calculator panes now carry the Brand Book's own sections (see the
   note above calcPanes() in js/v3-forms.js for what each one replaced). The
   drill-downs are pill rows rather than <select>s, which is how the Brand Book
   presents them and reads better on a phone than three stacked dropdowns.

   No new palette: gold comes from --brand-gold / --grad-gold-cta, ink and
   hairlines from the tokens already in this file, and the price/fare card is
   the existing .v3Out. Everything is inside .v3Panel-equivalent white on the
   sheet's cream, same as the forms. */
body.v3 .bbSec {
  padding: 12px;
  border-radius: 12px;
  background: #fff;
  border: 1px solid var(--v3f-line);
}
/* R79 — .bbTag and .bbIntro are GONE, not merely unused: the pane intros and
   the visa tagline they styled were deleted when each pane stopped introducing
   itself three times over. The one surviving sentence per pane is the chip's
   chat `reply`, which is styled by the transcript, not here. */

/* ---- R90 — the pane pictures ------------------------------------------
   His dictation: "all these need to have pictures inside them ... can be
   copied from the brand book". One 16:9 picture at the head of the visa and
   flight panes; on the treatment pane the same frame carries the picked
   treatment's own still and appears with the price. The stay tiers each carry
   a real named hotel from that tier instead (see .bbTierShot below).
   Sized in the same 12px/10px rhythm as .bbSec and .bbTier so the picture
   reads as part of the card, not a banner pasted above it. */
body.v3 .bbShot {
  margin: 0 0 11px;
  border-radius: 10px;
  overflow: hidden;
  background: var(--v3-cream-soft);
  border: 1px solid var(--v3f-line);
}
/* R97: the treatment media is a <video> now (the Brand Book plays the clip),
   so every rule that shaped the still has to shape the clip identically —
   otherwise the media column silently changes size the moment it swaps. */
body.v3 .bbShot img,
body.v3 .bbShot video {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
/* the treatment shot sits BELOW its drill-down, next to the price it explains,
   so the pane still opens on its first control (the R79 rule) */
body.v3 .v3Calc[data-pane="treat"] .bbShot { margin: 11px 0 0; }
body.v3 .bbTierShot {
  margin: 0 0 9px;
  border-radius: 8px;
  overflow: hidden;
}
body.v3 .bbTierShot img {
  display: block;
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
}

/* a drill-down step: its label, then its row of choices */
body.v3 .bbStep + .bbStep { margin-top: 11px; }
body.v3 .bbStepLbl {
  display: block;
  margin-bottom: 6px;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--v3f-ink-soft);
}
body.v3 .bbPills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
body.v3 .bbPill {
  padding: 7px 12px;
  border-radius: 999px;
  border: 1px solid var(--v3f-line);
  background: #fff;
  font-family: inherit;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--v3-ink);
  cursor: pointer;
  transition: border-color 160ms ease-out, background-color 160ms ease-out,
              transform 140ms ease;
}
body.v3 .bbPill:hover { border-color: var(--brand-gold); }
body.v3 .bbPill:active { transform: translateY(1px) scale(0.98); }
body.v3 .bbPill:focus-visible {
  outline: 2px solid var(--brand-blue);
  outline-offset: 2px;
}
/* the chosen one wears the gold, so the drill-down's trail stays visible while
   you move down it — three rows, three answered questions */
body.v3 .bbPill[aria-pressed="true"] {
  background: var(--grad-gold-cta);
  border-color: var(--brand-gold-dark);
  color: var(--v3-ink);
}
body.v3 .bbHint {
  margin: 0;
  font-size: 11.5px;
  color: var(--v3f-ink-soft);
}

/* visa: the three numbered steps */
body.v3 .bbSteps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 11px;
}
body.v3 .bbSteps li { display: flex; gap: 10px; align-items: flex-start; }
body.v3 .bbNum {
  flex: 0 0 auto;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--grad-gold-cta);
  color: var(--v3-ink);
  font-size: 11.5px;
  font-weight: 800;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
body.v3 .bbStepBody { min-width: 0; }
body.v3 .bbStepBody strong {
  display: block;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--v3-ink);
}
body.v3 .bbStepBody em {
  display: block;
  margin-top: 2px;
  font-style: normal;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--v3f-ink-soft);
}
body.v3 .bbLink {
  display: inline-flex;
  margin-top: 12px;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--brand-gold);
  font-size: 12px;
  font-weight: 700;
  color: var(--v3-ink);
  text-decoration: none;
  transition: background-color 160ms ease-out, transform 140ms ease;
}
body.v3 .bbLink:hover { background: var(--v3-gold-light); }
body.v3 .bbLink:active { transform: translateY(1px) scale(0.99); }
/* R79 — .bbPayoff is gone with the visa payoff line it styled. */

/* accommodation: the three tiers */
body.v3 .bbTiers {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
body.v3 .bbTier {
  padding: 11px;
  border-radius: 10px;
  border: 1px solid var(--v3f-line);
  background: var(--v3-cream-soft);
}
body.v3 .bbTierHead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
body.v3 .bbTierHead strong { font-size: 12.5px; font-weight: 700; color: var(--v3-ink); }
body.v3 .bbStars { font-size: 10px; letter-spacing: 0.06em; color: var(--brand-gold); }
body.v3 .bbTierAccess,
body.v3 .bbTierBlurb {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--v3f-ink-soft);
}
body.v3 .bbTierPrice {
  margin: 5px 0 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--v3-ink);
}

/* the quote button closes the treatment pane, so it gets the sheet's own
   spacing rather than sitting flush against the disclaimer above it */
body.v3 .bbSec > .v3Submit { margin-top: 12px; }

@media (hover: none) {
  body.v3 .bbPill:hover { border-color: var(--v3f-line); }
  body.v3 .bbPill[aria-pressed="true"]:hover { border-color: var(--brand-gold-dark); }
  body.v3 .bbLink:hover { background: transparent; }
}

/* R79 — .v3Disc is gone. It styled the three qualifier paragraphs under the
   generated figures ("estimates, not quotes"); he asked for all three cut at a
   checkpoint, so the class has no call site left anywhere in the page. The
   trade that comes with it is recorded in NOTES.md R79, not hidden here. */

/* ======================================================================
   9. STICKY BOTTOM BAR — the Canada look  (R51)
   ======================================================================
   "I want the look to be changed only, okay, as the Canada website one."
   So: behaviour untouched (js/v3-forms.js is not edited this round), only the
   skin. Every value below is READ from the reference rather than invented —
   Project Web/stunningdentistry-ca-main/src/components/layout/MobileStickyBar.tsx,
   the bar container div and ActionPill, with the Tailwind resolved to literals.

   The two bars were different objects. Ours was edge-to-edge, flush, divided by
   a hairline. Canada's is a row of SEPARATED pills, each a free-standing shell
   with a 4px halo, on a white bar carrying a gold hairline and a diagonal wash.

   .bottomBar / .bottomBtns live in css/style.css, which V1, V2 and the deployed
   index.html all load, so it is NOT edited. Everything here is a body.v3
   override, same discipline as R48-R50.

   Three of his calls are encoded below:
     - the mark is a DOT, not an icon (the icons came out of the markup)
     - Video Consult keeps its gold (brand rule 6: gold marks the booking)
     - solid state only; the reference's glass branch is not ported */

/* ---- the bar ----
   Two elements do the reference's one job, so the chrome and the row split:
   .bottomBar is the full-bleed surface (white, gold hairline, gold wash) and
   .bottomBtns is the capped, centred pill row inside it. Putting the white on
   the row instead would turn the bar into a 520px island floating on the page. */
body.v3 .bottomBar {
  position: fixed;
  isolation: isolate;
  background: #fff;
  border-top: 1px solid rgba(234, 236, 240, 0.9);
  /* the gold hairline is the bar's signature: a 1px inset line, not a border */
  box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.08),
              inset 0 1px 0 0 rgba(201, 168, 76, 0.22);
}
/* the reference paints its gold wash with a spare absolutely-positioned div;
   a pseudo-element does the same job without touching the markup */
body.v3 .bottomBar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(118deg,
    rgba(201, 168, 76, 0.14) 0%,
    rgba(255, 255, 255, 0) 40%,
    rgba(232, 213, 168, 0.10) 58%,
    rgba(201, 168, 76, 0.08) 100%);
}

/* ---- the row ----
   R54: his call, "decrease the height of the section a bit". The 4px saved at
   each end takes the row from 56 to 52; the height does NOT come out of the
   pill, which holds its 44px tap target below md:. Shrinking a control to make
   a bar shorter is the wrong trade. */
body.v3 .bottomBtns {
  position: relative;
  z-index: 1;
  gap: 8px;
  height: auto;
  min-height: 44px;
  padding: 4px 8px calc(4px + env(safe-area-inset-bottom));
  align-items: center;
  justify-content: center;
  background: transparent;
}

/* ---- the pill: the sub-header strip's pill, brought down to the bar (R53) ----
   His call, from two shots: the bar wears .subStrip's pill, so the page's top
   row and its bottom row read as one system instead of two.

   Every value below is copied from css/style.css:311-321 (.subStrip a,
   .subStrip button). That sheet is shared with V1/V2/index.html and is never
   edited, so the source is named here and the two cannot drift silently:
       #fff fill · 1px var(--brand-gold) hairline · 999px · --brand-blue-deep 700

   Out with the CA shell (R51): the 8px radius, the #D5D7DA border, the 4px
   #F5F5F5 halo, the white-to-#FAFAFA gradient, the sheen layer and the 6px
   colour dot. The strip carries none of them, and a gold-hairline pill wearing a
   coloured dot would be a third look rather than the one he pointed at. The dot
   markup came out of index-v3.html with this CSS — a span nothing styles is the
   R52 lesson, and the ::after sheen went with it for the same reason: R52
   deleted the only rule that ever raised it above opacity 0.

   Kept from R51 on purpose: the 44px tap target below md:, the press transform,
   the blue focus ring, and the gold conversion pill (brand rule 6). */
body.v3 .bottomBtns .bbV3 {
  position: relative;
  isolation: isolate;
  z-index: 1;
  overflow: hidden;
  /* R54, "increase the width of the buttons": on the phone the pills take the
     whole track instead of hugging three short labels, which is what left the
     bar looking empty. Grown from `auto`, NOT from 0 — an equal-thirds basis
     puts "Cost Calculator" (118px of nowrap label) into a 119px slot and clips
     it under the overflow:hidden above. R53's hug returns at md:; see the note
     on the 768px block for why stretching there would be wrong. */
  flex: 1 1 auto;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;                    /* tap target, not decoration */
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--brand-gold);
  box-shadow: none;
  /* an inset 1px outline mirroring the border, purely so focus can swap its
     colour without the border moving anything */
  outline: 1px solid var(--brand-gold);
  outline-offset: -1px;
  background: transparent;
  font-family: var(--font-sans);
  font-size: 0.76rem;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0;
  text-align: center;
  white-space: nowrap;
  color: var(--brand-blue-deep);
  cursor: pointer;
  transition: border-color 200ms ease-out, outline-color 200ms ease-out,
              color 150ms ease, transform 150ms ease;
}

/* the fill stays a pseudo-element rather than a background, because the gold
   pill needs a gradient in the same slot and the press transform reads better
   with the paint isolated. Flat #fff now, as on the strip. */
body.v3 .bottomBtns .bbV3::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: #fff;
  opacity: 1;
  transition: filter 200ms ease;
}
body.v3 .bottomBtns .bbV3:hover {
  border-color: var(--brand-gold-dark);
  outline-color: var(--brand-gold-dark);
}
body.v3 .bottomBtns .bbV3:active { transform: translateY(1px) scale(0.98); }
body.v3 .bottomBtns .bbV3:focus-visible {
  outline: 2px solid var(--brand-blue);
  outline-offset: -1px;
}

body.v3 .bottomBtns .bbV3 .bbLabel {
  position: relative;
  z-index: 1;
  min-width: 0;
}

/* ---- there is deliberately NO open state on these pills (R52) ----
   R50 built one (the pill fills with its own colour, dot goes white) and R51
   re-skinned it. It reached a screen in neither round: section 3 hides the
   whole bar while a surface is open (body.v3.v3-locked .bottomBar {opacity:0})
   and the sheet card reaches the viewport floor regardless. Measured in both
   rounds — bar opacity 0, card bottom 900 of a 900px viewport — and now
   asserted live in automation/out/r52-probe.mjs so it stays measured.

   Before rebuilding it: the Canada site sits its sheet at bottom:48px, ON TOP
   of a visible bar, which is the whole reason a filled pill can anchor the
   sheet to the button that summoned it. Reproducing that needs .bottomBar
   dropped from the v3-locked hide list AND .asSheet given a bottom offset
   FIRST — a change to how sheets sit, not how they look. Styling alone renders
   to nobody, which is exactly the round this comment replaces. */

/* ---- the gold conversion pill ----
   Video Consult stays gold: it takes the strip's shell, radius and spacing like
   the other two, and keeps the gold fill so the booking action still reads as
   the one that converts (brand rule 6). The strip itself has no gold pill, so
   this is the one deliberate departure from it — his standing call since R50.
   Text stays #111827 rather than the strip's navy: measured against the dark end
   of --grad-gold-cta, near-black holds the better contrast. */
body.v3 .bottomBtns .bbV3.isGold {
  color: #1D1D1F;   /* R68: Charcoal Grey — was #111827 */
  border-color: var(--brand-gold-dark);
  outline-color: var(--brand-gold-dark);
  box-shadow: none;
}
body.v3 .bottomBtns .bbV3.isGold::before { background: var(--grad-gold-cta); }
body.v3 .bottomBtns .bbV3.isGold:hover::before { filter: brightness(1.05); }
/* The reference caps its pill row and centres it — max-w 520 on mobile, 860 at
   md:, 1320 at lg: — rather than letting three pills stretch a 1440px bar. Ours
   was flush and full-bleed so it never needed the cap; separated pills do, or
   each label floats alone in the middle of a 460px slab. Same three stops,
   applied to .bottomBtns since that is our row. */
body.v3 .bottomBtns {
  max-width: 520px;
  margin-inline: auto;
  width: 100%;
}
@media (min-width: 768px) {
  /* R54: 860 → 720. The pills stop sharing the track here and go back to
     hugging, because an equal-share pill on a wide row is the 430px slab with a
     small label adrift in it that the 1024px note below records. Wider buttons
     is a phone instruction; on desktop the same intent is served by more
     padding inside a tighter row, not by stretching. */
  body.v3 .bottomBtns { max-width: 720px; }
  /* R53: the shared sheet bumps the strip at md: (css/style.css:1057) but that
     rule reads `.subStrip a` and this page's strip is three BUTTONS, so the
     strip never actually bumps here — it stays 0.76rem at every width. Copying
     the bump would make the bar diverge from the row it is supposed to match,
     so only the padding opens up and the type does not move. */
  body.v3 .bottomBtns .bbV3 {
    flex: 0 1 auto;
    padding: 9px 26px;
    min-height: 0;
  }
}
@media (min-width: 1024px) {
  /* The reference's lg: cap is 1320px, but it holds SIX pills there and we hold
     three. Copying 1320 verbatim gave three 430px slabs with a small label adrift
     in each, which looks nothing like the reference even though the number
     matched. Matching its pill DENSITY is the faithful read, not its max-width. */
  body.v3 .bottomBtns { max-width: 680px; gap: 20px; }
}
@media (max-width: 360px) {
  /* three nowrap labels have to fit a 320-360px track. The strip solves this the
     same way — tighten the gap and the padding before touching the type. */
  body.v3 .bottomBtns { gap: 6px; padding-left: 6px; padding-right: 6px; }
  body.v3 .bottomBtns .bbV3 { font-size: 0.7rem; padding: 7px 8px; }
}

/* ---------- small-screen tightening ---------- */
@media (max-width: 400px) {
  body.v3 .v3Choose { grid-template-columns: 1fr; }
  body.v3 .v3Choose button { min-height: 0; flex-direction: row; align-items: center; gap: 10px; }
  body.v3 .v3Choose .cText { min-width: 0; }
  body.v3 .v3Seg label { font-size: 11.5px; padding: 8px 6px; }
  body.v3 .v3Contact { gap: 5px; }
  body.v3 .v3Contact .v3Phone .v3Cc { padding: 0 6px; font-size: 12px; }
  /* R95 — "AND" is a wider divider than "or" and the row now carries a required
     dot, so both halves lost budget. Measured at 360px: the phone placeholder
     was 1px over and the email 2px. These four lines buy back 4px a side. */
  body.v3 .v3Contact .v3Phone input,
  body.v3 .v3Contact input[type="email"] { padding-left: 7px; padding-right: 7px; }
  body.v3 .v3Field.isRequired .v3Contact input[type="email"] { padding-right: 24px; }
  body.v3 .v3Field.isRequired .v3Contact .v3Phone input { padding-right: 7px; }
  /* R96: the divider is one glyph now, so the narrow-width shrink that was
     buying letters back has nothing left to buy. Held at a legible size. */
  body.v3 .v3Or { font-size: 12.5px; }

}

/* R95 — below ~350px the row runs out of budget no matter how it is tightened
   (measured: both placeholders 7px over at 340), so it STACKS rather than
   clips. The AND keeps its line between the two, which is the whole point of
   the row; the dot moves to the first control's centre because a wrapper-
   centred dot would land on the divider. Both halves take the gutter here —
   full-width inputs have the room to spare and it saves a second rule. */
@media (max-width: 349px) {
  body.v3 .v3Contact { flex-wrap: wrap; }
  body.v3 .v3Contact .v3Phone,
  body.v3 .v3Contact input[type="email"] { flex: 1 1 100%; }
  body.v3 .v3Or { flex: 1 1 100%; text-align: center; }
  body.v3 .v3Field.isRequired .v3Contact .v3Phone input,
  body.v3 .v3Field.isRequired .v3Contact input[type="email"] { padding-right: 24px; }
  body.v3 .v3Field.isRequired:has(.v3Contact)::after { top: 20px; }
}

/* ==========================================================================
   R56 — the section-by-section polish pass.
   Everything below refines controls that already exist; nothing here
   introduces a new component or a colour outside the V3 palette.
   ========================================================================== */

/* ---------- 1. the seg control, as cards (E-Consult only) ----------
   Same radios as .v3Seg above; this is the `variant: "cards"` skin. The strip
   version stays for Male/Female, where two words need no more room than that.
   Here the choice is the first thing on the form and it gets a tile. */
body.v3 .v3Seg.v3SegCards {
  gap: 8px;
}
body.v3 .v3Seg.v3SegCards label {
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  min-height: 84px;
  padding: 13px 10px;
  border-radius: 12px;
  font-size: 13px;
}
/* the glyph gets a tinted tile of its own, the same 38px chip the upload icon
   uses — one icon-plate size across the surface, not two */
body.v3 .v3Seg.v3SegCards .segIcon {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  display: grid;
  place-items: center;
  background: rgba(201, 168, 76, 0.14);
  color: var(--v3-gold-text);
  transition: background 150ms ease;
}
body.v3 .v3Seg.v3SegCards .segIcon svg { width: 20px; height: 20px; }
body.v3 .v3Seg.v3SegCards label:has(input:checked) .segIcon {
  background: rgba(201, 168, 76, 0.26);
}

/* ---------- R72: the meeting choice, explained ----------
   His read was right — as two matched tiles with a faint tint between them,
   the control said "a setting is toggled" rather than "you are choosing how
   this call happens". Three changes, all inside the existing palette:

   1. A SUB-LINE under each label. The words "Audio call" and "Video call" name
      the format but not the consequence; "Voice only" / "Face to face" is what
      the patient is actually picking between. The difference becomes readable
      instead of inferable from an icon.
   2. A TICK on the chosen tile. Tint alone carries the whole selected state
      today, and a warm cream tint on a white card is a weak signal — at a
      glance neither tile looks chosen. A tick is unambiguous.
   3. A firmer selected edge. 1px gold on 1px grey is a hairline difference;
      the chosen tile now sits on a 2px gold ring with a soft lift, so it reads
      as raised rather than merely coloured.

   Deliberately NOT done: no new colour, no motion beyond the existing 150ms
   transitions, and the radios underneath are untouched — same names, same
   values, same `Video call` default reaching the CRM. */
body.v3 .v3Seg.v3SegCards label {
  min-height: 96px;
  padding: 14px 12px 13px;
  gap: 6px;
  line-height: 1.25;
}
body.v3 .v3Seg.v3SegCards .segSub {
  display: block;
  font-size: 11px;
  font-weight: 500;
  color: var(--v3f-ink-soft);
  margin-top: 1px;
  transition: color 150ms ease;
}
body.v3 .v3Seg.v3SegCards label:has(input:checked) {
  border-width: 2px;
  border-color: var(--v3-gold);
  box-shadow: 0 3px 10px rgba(141, 110, 27, 0.16);
}
/* 2px of border on the chosen tile would shift it 1px against its neighbour;
   the unchosen one carries a transparent ring of the same width to hold the row */
body.v3 .v3Seg.v3SegCards label { border-width: 2px; border-color: var(--v3f-line); }
body.v3 .v3Seg.v3SegCards label:has(input:checked) .segSub { color: var(--v3-gold-text); }

/* the tick, top-right of the chosen tile only */
body.v3 .v3Seg.v3SegCards label::after {
  content: "";
  position: absolute;
  top: 9px;
  right: 9px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--v3-gold) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M2.5 6.2 5 8.7l4.5-5'/%3E%3C/svg%3E") center no-repeat;
  opacity: 0;
  transform: scale(0.7);
  transition: opacity 150ms ease, transform 150ms ease;
}
body.v3 .v3Seg.v3SegCards label:has(input:checked)::after {
  opacity: 1;
  transform: scale(1);
}

/* ---------- 2. Male / Female get their glyphs ----------
   The strip seg sizes its icon by font-size (the set used to be text); these
   are SVGs, so they need real dimensions or they inherit the 24px the sprite
   was drawn at and blow the row height. */
body.v3 .v3Seg:not(.v3SegCards):not(.v3SegRows) .segIcon {
  display: inline-grid;
  place-items: center;
  width: 17px;
  height: 17px;
  color: #667085;
  transition: color 150ms ease;
}
body.v3 .v3Seg:not(.v3SegCards) .segIcon svg { width: 17px; height: 17px; }
/* the glyph joins the label in the checked state instead of staying grey and
   reading as a disabled icon next to live text */
body.v3 .v3Seg label:has(input:checked) .segIcon { color: var(--v3-gold-text); }

/* ---------- R96: the seg as ROWS ----------
   His dictation for the meeting preference: the icon on the left and the words
   to the right of it, "like we did for upload a selfie", and the sub-lines off.

   A THIRD skin rather than an edit to .v3SegCards, which videoconsult's
   Morning / Afternoon / Evening picker still uses — that control needs the tall
   tile AND its sub-lines, and he did not ask for it to change. The metrics are
   borrowed from .v3Upload (38px circular well, 20px glyph, 11px gap) so the two
   row controls in this form read as one idiom rather than two near-misses. */
body.v3 .v3Seg.v3SegRows { gap: 8px; }
body.v3 .v3Seg.v3SegRows label {
  flex-direction: row;
  justify-content: flex-start;
  text-align: left;
  gap: 11px;
  min-height: 0;
  padding: 11px 12px;
  border-radius: 12px;
  border-width: 2px;
  border-color: var(--v3f-line);
  font-size: 13px;
  line-height: 1.25;
}
body.v3 .v3Seg.v3SegRows .segIcon {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(201, 168, 76, 0.14);
  color: var(--v3-gold-text);
  transition: background 150ms ease;
}
body.v3 .v3Seg.v3SegRows .segIcon svg { width: 20px; height: 20px; }
body.v3 .v3Seg.v3SegRows label:has(input:checked) .segIcon {
  background: rgba(201, 168, 76, 0.26);
}
/* the selected state keeps what the cards earned — 2px gold and the lift */
body.v3 .v3Seg.v3SegRows label:has(input:checked) {
  border-color: var(--v3-gold);
  box-shadow: 0 3px 10px rgba(141, 110, 27, 0.16);
}
/* and its checkmark, moved to the vertical centre: on a 60px row the cards'
   top-right corner placement would sit beside the icon rather than clear of it */
body.v3 .v3Seg.v3SegRows label::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 10px;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: var(--v3-gold) url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7.5'/%3E%3C/svg%3E") center no-repeat;
  opacity: 0;
  transform: translateY(-50%) scale(0.7);
  transition: opacity 150ms ease, transform 150ms ease;
}
body.v3 .v3Seg.v3SegRows label:has(input:checked)::after {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}
/* the label text needs room beside a 38px well and a 17px tick */
body.v3 .v3Seg.v3SegRows label > span:not(.segIcon) { min-width: 0; padding-right: 16px; }

/* The narrow-width trim for the rows lives HERE, immediately after the block it
   overrides, and not up in the shared max-width:400px section — a media query
   adds no specificity, so the base rules above would simply win on source order
   and the override would do nothing. (It did nothing, first try.)
   MEASURED: the two rows sit side by side, so each gets about half the card.
   "Phone call" needs 59px and had 56 at 390px and 41 at 360, wrapping to two
   lines at both. Trimming the well, the gap, the padding and the tick's
   reserved gutter buys ~19px back — enough at 360 with room over. Side by side
   is kept deliberately: stacking would add ~70px of height to a control he
   asked to make SMALLER. The well stays a circle; the glyph tracks it down. */
@media (max-width: 400px) {
  body.v3 .v3Seg.v3SegRows label { padding: 10px; gap: 8px; font-size: 12.5px; }
  body.v3 .v3Seg.v3SegRows .segIcon { width: 30px; height: 30px; }
  body.v3 .v3Seg.v3SegRows .segIcon svg { width: 17px; height: 17px; }
  body.v3 .v3Seg.v3SegRows label::after { right: 8px; width: 15px; height: 15px; }
  body.v3 .v3Seg.v3SegRows label > span:not(.segIcon) { padding-right: 12px; }
}

/* Below ~350 there is no trim left: half a card cannot hold "Phone call" beside
   a well and a tick, so the pair stacks one per line and each gets the full
   width. This is the same breakpoint at which the contact row above stacks —
   past it, the card has already accepted height in exchange for legibility, and
   leaving one of the two controls wrapping would be the inconsistency. */
@media (max-width: 349px) {
  body.v3 .v3Seg.v3SegRows { grid-auto-flow: row; }
}

/* ---------- 3. the photo upload ----------
   R56 stacked this into a tall centred dropzone. R95 takes it back to the
   compact row at his instruction — circular icon left, text right, less height
   spent — so `.isDrop` reaches nothing and is gone rather than left to rot
   (the R73 rule). What it contributed that survives — the circular icon well
   and the glyph sizing — now lives on the base .v3Upload rule above. */

/* ---------- 4. the time chips, three across ----------
   Scoped to the video-consult surface by id, because the 2-column .v3Chips
   grid is right for the eight-item grids and wrong only here: three chips in a
   2-col grid leaves Evening alone on a second row, which reads as a different
   kind of option rather than the third of three. */
#v3fx-videoconsult .v3Chips {
  grid-template-columns: repeat(3, 1fr);
  gap: 7px;
}
#v3fx-videoconsult .v3Chip {
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  padding: 12px 6px;
}
#v3fx-videoconsult .v3Chip .chText strong { font-size: 12.5px; }
/* "12pm to 5pm AEST" will not sit on one line in a third of 390px, and it is
   the information the chip exists to give, so it wraps rather than truncates */
#v3fx-videoconsult .v3Chip .chText em {
  font-size: 10px;
  line-height: 1.3;
  white-space: normal;
}
@media (max-width: 360px) {
  #v3fx-videoconsult .v3Chips { gap: 5px; }
  #v3fx-videoconsult .v3Chip { padding: 11px 4px; }
  #v3fx-videoconsult .v3Chip .chText em { font-size: 9.5px; }
}

/* ---------- 5. the cost calculator, tidied ----------
   Craft only. No figure, no formula and no source line is touched here — those
   survived /verify-claims and changing them is a different kind of edit.

   The accommodation pane stacked four fields, two of which are two-character
   numbers. Nights and Rooms now share a row (the .v3Row wrapper added in
   calcPanes), so the pane is three rows of related things instead of four of
   unrelated ones. */
body.v3 .v3Calc .v3Row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  align-items: start;
}
/* .v3Field is a flex column, so a field inside the grid needs no extra rules;
   it just stops being full-width */
body.v3 .v3Calc .v3Row .v3Field { min-width: 0; }

/* the breakdown is a ledger and reads DOWN the column, so the rule between
   lines does more than the gap did. White alpha, not ink: .v3Out is the
   trust-strip gradient and anything darker than it disappears. */
body.v3 .v3Break { gap: 0; }
body.v3 .v3Break li { padding-block: 5px; }
body.v3 .v3Break li + li { border-top: 1px solid rgba(255, 255, 255, 0.12); }
/* the figures line up as a column of money, which they did not while each row
   sized its own label */
body.v3 .v3Break li b { white-space: nowrap; }

/* (R79 — the desktop overrides for .v3Disc went with the class itself.) */

/* ==========================================================================
   R59 — sbTourism, the single tourism slide
   --------------------------------------------------------------------------
   The R56-R58 journey stepper (jrStage crossfade, jrSteps, jrTicks) is gone
   with its engine; the strip is one looping clip in the page's OWN
   .splitBanner/.sbMedia component (css/style.css:1747-1776), full-bleed as
   before, wearing the standard .sbMedia bottom scrim — no more full-height
   near-black wash, so the strip sits inside the light page.
   ========================================================================== */
body.v3 .sbSection.sbTourism { padding: 0; }
body.v3 .sbTourism .splitBanner {
  max-width: none;
  margin: 0;
  padding: 0;
  gap: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
/* R59 set this to 1:1 to match the consult banner, on his call that "the two
   full-bleed banners on this page are one system and must not sit at different
   ratios". R65 moved sbConsult to 16:9/21:9 on his later call, so the square
   here was what broke the pairing, not what kept it.
   R67: sbTourism follows sbConsult back into the pair. The packages reel is a
   native 16:9 cut of 16:9 sources, so a 1:1 box would crop 44% off every beat —
   the terminal doors, the car at the kerb and the market stalls are all framed
   wide, and a square window cuts exactly the context that makes them read as
   travel. The ratio therefore moves off the container onto the video, the sbCrm
   idiom (css/v3.css:957), and the video returns to normal flow so its own ratio
   drives the band height. Same edit, same reasoning as R65 on sbConsult
   (css/v3.css:365-395).
   R88 REVERSED R67 on his dictation: sbTourism (and sbCrm) are 1:1 squares
   again, riding the R46.3 recipe in css/v3.css. The R67 sbMedia/video rules
   that put the video back in normal flow at 16:9 / 21:9 lived here and — this
   file loading LAST — silently beat the square recipe, so they are retired in
   place rather than out-specificity'd. The crop cost R67 named is accepted
   again, the same call as the R86 choose-film cell. */
/* DIRECT children only — a bare `.sbMedia img` also matches the WhatsApp icon
   inside an overlay CTA and blows it up into a full-width band
   (css/v3.css:373-375 paid for this once). */
body.v3 .sbTourism .sbMedia > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
body.v3 .sbTourism .sbMedia,
body.v3 .sbTourism .sbMedia video {
  border-radius: 0;
  box-shadow: none;
}

/* the eyebrow/caption pair, values carried from the deleted jr* kit so the
   overlay type does not change size with the engine's removal */
body.v3 .sbTourism .sbEyebrow {
  margin: 0;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--brand-gold-light);             /* style.css:30 */
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
body.v3 .sbTourism .sbCaption {
  margin: 0;
  max-width: 46ch;
  font-size: clamp(0.78rem, 2.4vw, 0.95rem);
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.92);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
body.v3 .sbTourism .sbOverlay { gap: 8px; }

/* phone: the eyebrow carries nothing the title does not — the R56 call, kept */
@media (max-width: 640px) {
  body.v3 .sbTourism .sbEyebrow { display: none; }
}


/* ============ R91 — the treatment result: picture LEFT, text RIGHT ============
   His dictation: "the picture should be in the left and the text should be in
   the right". The R90 stack becomes a two-column row, so the shot and the
   price it explains read as one unit. Both children keep their own styling;
   only the axis is new. The row collapses to a stack under 420px, where a 40%
   column cannot carry a 27px price. */
body.v3 .bbResultRow {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 11px;
}
body.v3 .bbResultRow .bbShot {
  flex: 0 0 38%;
  max-width: 145px;
  aspect-ratio: 1 / 1;
  border-radius: 10px;
  overflow: hidden;
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--v3-cream-soft);
  align-self: center;
}
body.v3 .bbResultRow .bbShot img,
body.v3 .bbResultRow .bbShot video {
  width: 100%;
  height: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  object-position: center;
  display: block;
}
body.v3 .bbResultRow .v3Out {
  flex: 1 1 0;
  min-width: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-self: stretch;
}
/* when no treatment is picked the figure is [hidden]; the price card then owns
   the full width rather than sitting in a 60% column with dead space beside it */
body.v3 .bbResultRow .bbShot[hidden] { display: none; }
/* R107 — THE "BLUE LINE". This is the bar he saw under the category pills
   before anything was chosen. The rule above sets `display: flex` on .v3Out,
   which is more specific than the UA stylesheet's `[hidden] { display: none }`
   — so the EMPTY navy price card kept its box and rendered as a dark rule.
   .bbShot got this guard when it was written; .v3Out never did. */
body.v3 .bbResultRow .v3Out[hidden] { display: none; }
body.v3 .v3Out .oValue .oOnly {
  font-style: normal;
  font-size: 0.5em;
  font-weight: 700;
  letter-spacing: 0.02em;
  /* explicit, not inherited: the sand at 0.85 opacity read as near-black on
     the navy card in the R91 render check */
  color: rgba(255, 255, 255, 0.78);
}
/* the row holds on phones too — "picture left, text right" is the instruction,
   and a 393px phone still gives the shot ~140px beside a legible price card.
   Only below 330px, where the price would wrap mid-figure, does it stack. */
@media (max-width: 430px) {
  body.v3 .bbResultRow { gap: 8px; align-items: center; }
  body.v3 .bbResultRow .bbShot { flex: 0 0 36%; max-width: 125px; aspect-ratio: 1 / 1; }
  body.v3 .bbResultRow .v3Out { padding: 12px 10px; }
  body.v3 .bbResultRow .v3Out .oValue { font-size: 23px; }
  body.v3 .bbResultRow .v3Out .oNote { font-size: 9.5px; }
}
@media (max-width: 330px) {
  body.v3 .bbResultRow { flex-direction: column; align-items: center; }
  body.v3 .bbResultRow .bbShot { flex: 0 0 auto; width: 140px; aspect-ratio: 1 / 1; }
  body.v3 .bbResultRow .bbShot img,
  body.v3 .bbResultRow .bbShot video { aspect-ratio: 1 / 1; height: 100%; width: 100%; }
}

/* ======================================================================
   R97 — THE BRAND BOOK'S OWN SECTIONS

   His call this round: the calculator looks exactly like the Brand Book,
   inside and out. What that adds here:

     .bbHeadMedia   the tour's DetailHeader band (visa's plane clip)
     .bbStepRail    StepRailCards — a gold rail threading numbered discs
     .bbPlaceGrid   PlaceWalk — the tier's real named hotels, two up
     .chMedia       TourMediaTile — the entry chips as media cards
     .bbCaveat      the tour's own qualifying line under a figure
     .rPdf          (R97 part 2) the treatment guide on the success panel

   THE GOLD. At his checkpoint he chose the Brand Book's own gold for these
   elements rather than the page's. They are declared here as local variables
   scoped to the calculator, NOT as edits to the page tokens: the page keeps
   --v3-gold-dark #B8832F everywhere else, and only the ported sections carry
   the tour's #8D6E1B family. Two golds on one page is the deliberate outcome,
   confined to the popup that is meant to read as the Brand Book.
   ====================================================================== */
body.v3 .v3Calc {
  --bb-gold:      #C9A84C;
  --bb-gold-dark: #8D6E1B;
  --bb-gold-mid:  #D4AF37;
  --bb-gold-text: #856915;
  --bb-cream:     #FBF6EA;
  --bb-navy:      #001070;
  --bb-hair:      #EAECF0;
  --bb-body:      #667085;
}

/* ---- DetailHeader: the pane's own media band ---- */
body.v3 .bbHeadMedia {
  position: relative;
  display: block;
  margin: 0 0 14px;
  height: 132px;
  border-radius: 12px;
  overflow: hidden;
  background: var(--bb-cream);
}
body.v3 .bbHeadMedia video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* the tour's gold wash, bottom-weighted so the cards below read as continuing
   the same surface rather than starting a new one */
body.v3 .bbHeadScrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(141, 110, 27, 0.25) 0%,
    rgba(212, 175, 55, 0.14) 60%,
    rgba(212, 175, 55, 0.05) 100%);
  pointer-events: none;
}

/* ---- StepRailCards: the gold rail, the navy discs, the cream cards ----
   Three to a row with the rail behind the badges. The disc is NAVY, not gold:
   it sits ON the gold line, and a gold disc on a gold rail loses its own edge
   (the Brand Book's own note). White numeral on navy measures ~13:1. */
body.v3 .bbStepRail {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 16px 0 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
body.v3 .bbStepRail::before {
  content: "";
  position: absolute;
  top: 16px;
  left: 16.66%;
  right: 16.66%;
  height: 2px;
  background: linear-gradient(to right, var(--bb-gold), var(--bb-gold-mid));
}
body.v3 .bbRailItem {
  position: relative;
  display: flex;
  flex-direction: column;
  text-align: center;
  min-width: 0;
}
body.v3 .bbRailNum {
  position: relative;
  z-index: 1;
  margin: -16px auto 0;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--bb-navy);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 2px rgba(201, 168, 76, 0.6);
}
body.v3 .bbRailCard {
  margin-top: 8px;
  flex: 1;
  min-height: 96px;
  overflow: hidden;
  border-radius: 12px;
  border: 1px solid rgba(201, 168, 76, 0.3);
  background: var(--bb-cream);
  padding: 10px;
}
body.v3 .bbRailCard strong {
  display: block;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--bb-navy);
}
body.v3 .bbRailCard em {
  display: block;
  margin-top: 4px;
  font-style: normal;
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--bb-body);
  overflow-wrap: break-word;
}
/* The rail stays three-across on phones because that IS the Brand Book — its
   tour renders at phone width. What does not survive the narrow column is step
   one's URL: "indianvisaonline.gov.in" is a single 23-character token, and at
   the full size it broke mid-domain as "indianvisaonline.go / v.in", which
   reads as a typo rather than a link. A step down in size lets it sit on one
   line in the same three-column layout, so the fix is the type, not the grid. */
@media (max-width: 480px) {
  body.v3 .bbRailCard { padding: 9px 7px; }
  body.v3 .bbRailCard strong { font-size: 11px; }
  body.v3 .bbRailCard em { font-size: 9.5px; }
}
/* below ~360px three cards cannot hold a readable line, so the rail becomes a
   single column and drops — a rail through one badge is decoration, not a
   thread */
@media (max-width: 360px) {
  body.v3 .bbStepRail { grid-template-columns: 1fr; gap: 12px; }
  body.v3 .bbStepRail::before { display: none; }
  body.v3 .bbRailItem { flex-direction: row; align-items: flex-start; gap: 10px; text-align: left; }
  body.v3 .bbRailNum { margin: 0; flex: 0 0 auto; }
  body.v3 .bbRailCard { margin-top: 0; min-height: 0; }
}

/* ---- PlaceWalk: the tier's real named hotels ----
   Same silhouette as the Brand Book's menu tiles — a hairline card with a 3px
   gold left edge — so the walk reads as the tour rather than a gallery bolted
   on. */
body.v3 .bbPlaceGrid {
  margin-top: 10px;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
}
body.v3 .bbPlace {
  overflow: hidden;
  border-radius: 12px;
  border: 1px solid var(--bb-hair);
  border-left: 3px solid var(--bb-gold);
  background: #fff;
}
body.v3 .bbPlaceShot {
  position: relative;
  margin: 0;
  height: 92px;
  overflow: hidden;
  background: var(--bb-cream);
}
body.v3 .bbPlaceShot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
body.v3 .bbPlaceScrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(141, 110, 27, 0.2), rgba(212, 175, 55, 0.1) 55%, transparent);
}
body.v3 .bbPlaceBody { padding: 8px 10px 10px; }
body.v3 .bbPlaceBody strong {
  display: block;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.3;
  color: #111827;
}
body.v3 .bbPlaceDist {
  display: inline-flex;
  margin-top: 4px;
  padding: 1px 7px;
  border-radius: 999px;
  border: 1px solid rgba(201, 168, 76, 0.4);
  background: var(--bb-cream);
  font-size: 9.5px;
  font-weight: 700;
  color: var(--bb-gold-text);
}
body.v3 .bbPlaceBody p {
  margin: 5px 0 0;
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--bb-body);
}

/* ---- the tour's qualifying line under a figure ---- */
body.v3 .bbCaveat {
  margin: 8px 0 0;
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--v3f-ink-soft);
}
body.v3 .bbCaveat[hidden] { display: none; }

/* ---- TourMediaTile: the four entry chips as media cards ----
   The band, then the caption bar. The tile is a column, unlike the icon chips
   elsewhere, which is why the grid is declared here rather than inherited. */
body.v3 .v3Chips.hasMedia {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}
body.v3 .v3Chip.chTile {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: 0;
  overflow: hidden;
  text-align: left;
}
body.v3 .v3Chip.chTile .chMedia {
  position: relative;
  display: block;
  height: 86px;
  background: var(--bb-cream, #FBF6EA);
}
body.v3 .v3Chip.chTile .chMedia img,
body.v3 .v3Chip.chTile .chMedia video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
body.v3 .v3Chip.chTile .chScrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(141, 110, 27, 0.28), rgba(212, 175, 55, 0.12) 55%, transparent);
}
body.v3 .v3Chip.chTile .chText { padding: 9px 11px 11px; }

/* ---- R97 part 2: the treatment guide on the success panel ---- */
body.v3 .v3Result .rPdf {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 12px;
  padding: 11px 16px;
  border-radius: 10px;
  background: var(--grad-gold-cta);
  color: var(--v3-ink);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  line-height: 1.25;
}
body.v3 .v3Result .rPdf svg { width: 17px; height: 17px; flex: 0 0 auto; }
body.v3 .v3Result .rPdf:focus-visible { outline: 2px solid var(--brand-blue); outline-offset: 2px; }

/* ======================================================================
   R104 — the Brand Book's two booking controls: the date strip with its
   month calendar, and the flat slot grid.

   Both are new CONTROLS, not a new visual language: they reuse the seg
   tile's exact vocabulary (white fill, --v3f-line rim, 10px radius, gold
   rim + 12% gold wash when chosen, charcoal text, --brand-blue focus ring)
   so a patient reads them as the same form, not a bolted-on widget.
   ====================================================================== */

body.v3 .v3PickNote {
  margin: 0 0 8px;
  font-size: 11.5px;
  line-height: 1.45;
  color: #667085;
}

/* ---------- the 7-day strip + the More chip ---------- */
body.v3 .v3DateStrip {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  gap: 5px;
}
body.v3 .v3DateChip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  min-height: 48px;
  padding: 6px 2px;
  border: 1px solid var(--v3f-line);
  border-radius: 10px;
  background: #fff;
  color: #344054;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}
body.v3 .v3DateChip .dcDow { font-size: 9.5px; font-weight: 600; letter-spacing: 0.02em; }
body.v3 .v3DateChip .dcNum { font-size: 14px; font-weight: 700; line-height: 1; }
body.v3 .v3DateChip.isOn {
  border-color: var(--v3-gold);
  background: rgba(201, 168, 76, 0.12);
  color: var(--v3-gold-text);
}
body.v3 .v3DateChip:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }

/* ---------- the month calendar behind "More" ---------- */
body.v3 .v3Cal {
  margin-top: 8px;
  padding: 10px;
  border: 1px solid var(--v3f-line);
  border-radius: 12px;
  background: #fff;
}
body.v3 .v3CalHead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-bottom: 8px;
}
body.v3 .v3CalTitle { font-size: 13px; font-weight: 700; color: var(--v3-ink); }
body.v3 .v3CalNav {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 1px solid var(--v3f-line);
  border-radius: 8px;
  background: #fff;
  color: #344054;
  cursor: pointer;
}
/* the caret glyph points down; the two nav buttons rotate it rather than
   shipping a second icon the rest of the form never uses */
body.v3 .v3CalNav[data-cal-prev] svg { transform: rotate(90deg); }
body.v3 .v3CalNav[data-cal-next] svg { transform: rotate(-90deg); }
body.v3 .v3CalNav[disabled] { opacity: 0.35; cursor: not-allowed; }
body.v3 .v3CalNav:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }

body.v3 .v3CalDows,
body.v3 .v3CalGrid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 3px;
}
body.v3 .v3CalDows {
  margin-bottom: 4px;
  font-size: 10px;
  font-weight: 700;
  color: #98A2B3;
  text-align: center;
}
body.v3 .v3CalDay {
  min-height: 32px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: transparent;
  font-size: 12.5px;
  font-weight: 600;
  color: #344054;
  cursor: pointer;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}
body.v3 .v3CalDay:hover:not([disabled]) { border-color: var(--v3f-line); }
body.v3 .v3CalDay.isOn {
  border-color: var(--v3-gold);
  background: rgba(201, 168, 76, 0.12);
  color: var(--v3-gold-text);
}
/* a past day stays VISIBLE and dimmed rather than blank — an empty cell reads
   as a missing day, not a closed one */
body.v3 .v3CalDay[disabled] { opacity: 0.28; cursor: not-allowed; }
body.v3 .v3CalDay:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }

/* ---------- the slot grid (R105: 10 hourly slots; was 21 half-hour) ---------- */
body.v3 .v3SlotGrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 5px;
}
body.v3 .v3Slot {
  min-height: 36px;
  padding: 7px 4px;
  border: 1px solid var(--v3f-line);
  border-radius: 10px;
  background: #fff;
  font-size: 11px;
  font-weight: 600;
  color: #344054;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 150ms ease, background 150ms ease, color 150ms ease;
}
body.v3 .v3Slot.isOn {
  border-color: var(--v3-gold);
  background: rgba(201, 168, 76, 0.12);
  color: var(--v3-gold-text);
}
body.v3 .v3Slot:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }

/* the invalid rim, matching what .v3Seg.field-invalid does to its tiles */
body.v3 .v3Field.field-invalid .v3DateChip,
body.v3 .v3Field.field-invalid .v3Slot { border-color: #DC2626; }

/* ---------- narrow phones ---------- */
@media (max-width: 420px) {
  /* eight columns of a 7-day strip cannot hold a two-digit date under 360px,
     so the strip drops to four across and wraps to two rows */
  body.v3 .v3DateStrip { grid-template-columns: repeat(4, 1fr); }
  body.v3 .v3SlotGrid  { grid-template-columns: repeat(3, 1fr); }
  body.v3 .v3Slot      { font-size: 10.5px; padding: 7px 2px; }
}

/* ======================================================================
   R106 — THE COST WIZARD

   Five steps on the calculator surface. Everything here is scoped to .v3Wiz,
   so the chat surfaces' own .v3Step rules are untouched.
   ====================================================================== */

/* ⚠️ NO min-height here. `min-height:100%` stretched .v3Wiz to its content
   height (2117px against a 611px scroll port), which left position:sticky with
   no clipping ancestor to stick inside — the foot then rendered at the natural
   END of the element instead of pinning to the visible bottom. The scrolling
   box is .v3Body; the wizard must be free to be exactly as tall as its content
   and let that box do the scrolling. */
body.v3 .v3Wiz { display: flex; flex-direction: column; }

/* ---- the progress rail ---- */
/* The rail pins to the TOP for the same reason the total pins to the bottom:
   in a five-step flow the two questions a person keeps asking are "where am I"
   and "what is this costing", and the tall panes (stay is ~2100px) scrolled
   both answers off the screen. */
body.v3 .wizRail {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 10px 14px 8px;
  background: #FCFBF8;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin: 0 0 12px;
  list-style: none;
}
body.v3 .wizDot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  flex: 0 1 auto;
  min-width: 56px;
  cursor: pointer;
  user-select: none;
}
body.v3 .wizDot:hover .wizDotN {
  border-color: var(--v3-gold, #C9A84C);
  transform: scale(1.08);
}
body.v3 .wizDotN {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  border: 1.5px solid var(--v3f-line, #D0D5DD);
  background: #fff;
  font-size: 11px;
  font-weight: 700;
  color: #667085;
  transition: all 180ms ease;
  position: relative;
}
body.v3 .wizDotNum {
  display: block;
  line-height: 1;
}
body.v3 .wizDotCheck {
  display: none;
  line-height: 1;
}
body.v3 .wizDotCheck svg {
  width: 13px;
  height: 13px;
  stroke: #fff;
  stroke-width: 2.75;
}
body.v3 .wizDotL {
  font-size: 10px;
  font-weight: 600;
  color: #667085;
  text-align: center;
  letter-spacing: -0.01em;
  transition: color 150ms ease;
}

/* Active Step: glowing! */
body.v3 .wizDot.on .wizDotN {
  border-color: var(--v3-gold, #C9A84C);
  background: var(--v3-gold, #C9A84C);
  color: #fff;
  box-shadow: 0 0 0 4px rgba(201, 168, 76, 0.28), 0 2px 8px rgba(201, 168, 76, 0.45);
}
body.v3 .wizDot.on .wizDotL {
  color: var(--v3-gold-text, #8C7026);
  font-weight: 700;
}

/* Completed Step: completed checkmark */
body.v3 .wizDot.done .wizDotN {
  border-color: var(--v3-gold, #C9A84C);
  background: var(--v3-gold, #C9A84C);
  color: #fff;
  box-shadow: none;
}
body.v3 .wizDot.done .wizDotNum {
  display: none;
}
body.v3 .wizDot.done .wizDotCheck {
  display: grid;
  place-items: center;
}
body.v3 .wizDot.done .wizDotL {
  color: #475467;
}

/* Arrow in between */
body.v3 .wizArrow {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 5px;
  color: #CBD5E1;
  transition: color 180ms ease, transform 180ms ease;
  flex: 1 1 0;
}
body.v3 .wizArrow svg {
  display: block;
}
body.v3 .wizArrow.done {
  color: var(--v3-gold, #C9A84C);
}

/* the sticky foot floats OVER the scrolling steps, so the last pane needs
   room to clear it or its final card sits under the total bar */
body.v3 .wizSteps { flex: 1; padding-bottom: 6px; }
body.v3 .v3Wiz .v3Step.on { padding-bottom: 24px; }
/* inside the wizard the STEP hides its pane, so a hosted .v3Calc is always on */
body.v3 .v3Wiz .v3Calc.on { display: block; }

/* ---- the foot: total + navigation ---- */
body.v3 .wizFoot {
  position: sticky;
  bottom: 0;
  z-index: 2;
  margin-top: 14px;
  /* the fade has to be tall enough to hide a card sliding under it, and the
     solid half must be genuinely opaque — a translucent foot over a playing
     video is what made the first cut unreadable */
  padding: 14px 0 2px;
  background: linear-gradient(to bottom, rgba(252, 251, 248, 0), #FCFBF8 40%, #FCFBF8);
}
body.v3 .wizTotal {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 9px 12px;
  border: 1px solid var(--v3f-line);
  border-radius: 12px;
  background: #fff;
}
body.v3 .wizTotL { font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: #667085; }
body.v3 .wizTotV { font-size: 19px; font-weight: 800; color: var(--brand-navy, #0B2545); line-height: 1.2; }
body.v3 .wizTotN { font-size: 10.5px; color: #667085; }

body.v3 .wizNav { display: flex; gap: 8px; margin-top: 8px; }
body.v3 .wizBtn {
  flex: 1;
  min-height: 42px;
  border-radius: 12px;
  border: 1px solid var(--v3f-line);
  background: #fff;
  font-size: 13px; font-weight: 700; color: #344054;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease, opacity 150ms ease;
}
body.v3 .wizNext {
  flex: 2;
  border-color: transparent;
  background: var(--grad-gold, linear-gradient(135deg, #E8D9A8, #C9A84C));
  color: #23303F;
}
body.v3 .wizBtn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }
/* a blocked Next stays READABLE — it carries the reason as its own label
   ("Choose to continue"), so dimming it to invisibility would hide the
   instruction along with the button */
body.v3 .wizNext[disabled] { opacity: 0.55; cursor: not-allowed; }
/* R107: the last step's button is the surface's CTA, not a nav step — it leaves
   the wizard for the video consult form. It carries the deeper gold and takes
   the whole row, so the ending reads as an ask.
   .wizTotN is now unused: the note line under the total was removed in R107. */
body.v3 .wizNext.wizCta {
  flex: 1 1 auto;
  background: var(--grad-gold-cta, linear-gradient(135deg, #E2CE93, #B8912F));
  font-size: 13.5px;
  letter-spacing: 0.01em;
}
/* and Back shrinks to its own width beside it. A full-width secondary button
   under the CTA read as a second primary action — on the step that finally
   makes the ask, only one control should carry weight. */
body.v3 .wizNav:has(.wizCta) .wizBack { flex: 0 0 auto; padding: 0 18px; }

/* ---- stay tiers become selectable in the wizard ---- */
body.v3 .v3Wiz .bbTier { cursor: pointer; }
body.v3 .v3Wiz .bbTier[aria-pressed="true"] {
  border-color: var(--v3-gold);
  background: rgba(201, 168, 76, 0.08);
}
body.v3 .v3Wiz .bbTier:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }
body.v3 .wizRate { margin: 6px 0 0; font-size: 11.5px; font-weight: 600; color: var(--v3-gold-text); }

body.v3 .wizNights { margin-top: 12px; }
body.v3 .wizNightsIn {
  width: 96px;
  min-height: 40px;
  padding: 8px 10px;
  border: 1px solid var(--v3f-line);
  border-radius: 10px;
  background: #fff;
  font-size: 14px; font-weight: 700; color: #344054;
}
body.v3 .wizNightsIn:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-blue); }

/* ---- transport: the included list ---- */
body.v3 .wizIncl .bbTier { cursor: default; }
body.v3 .wizFree {
  margin-left: auto;
  padding: 2px 8px;
  border-radius: 999px;
  background: rgba(201, 168, 76, 0.16);
  font-size: 10px; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--v3-gold-text);
}

@media (max-width: 420px) {
  body.v3 .wizDotL { font-size: 8.5px; }
  body.v3 .wizTotV { font-size: 17px; }
}

/* ==========================================================================
   §R110 — THE TWO CTA ROWS BECOME ONE BOX
   ==========================================================================
   His call: "make sure the bar of Free E-Consult / Get A Quote / Free Implant
   Analysis has the same padding and everything as the bar that is below,
   because they look different."

   They did, and the cause is mechanical rather than stylistic. R53 gave the
   BOTTOM bar the strip's pill so the two rows would read as one system, and the
   comment at line 1332 says so. Then R54 grew the bottom pill on his
   instruction — 44px tap target, full-track stretch, 9px 26px at md: — and
   nothing followed on the top. The strip has stayed at style.css:311's
   `padding: 7px 12px` with no height at every width ever since, because
   style.css:1171's md: bump reads `.subStrip a` and this page's strip is three
   BUTTONS. That rule has never once fired. It is left alone rather than fixed:
   style.css is shared with V1, V2 and the deployed index.html.

   So the bottom bar is the reference (his checkpoint answer) and every value
   below is COPIED from `.bbV3` above — line numbers are not quoted because they
   move, but each value is named as copied so the two rows cannot drift silently
   a third time.

   The fill, the 1px gold hairline, the 999px radius, the navy 700 label and the
   0.76rem type were already identical in both rows. Only the BOX was out of
   sync, so only the box changes here. */

/* ---- a. the strip takes the bar's height ----
   44px pill + 4px above + 4px below = 52px, which is the bottom row's box
   exactly (min-height 44, padding 4px 8px). --substrip-h is declared on :root
   in css/style.css:53 and read by BOTH `.subStrip { height }` and
   `.hero { margin-top }`, so overriding it here moves the strip and the hero
   together and no second rule is needed. Nothing else reads it — no JS, no
   other sheet. Was 46px.

   53, not 52: the strip's height is border-box and it carries a 1px bottom
   hairline, so 44 + 4 + 4 + 1 is what actually matches the bottom bar's
   measured 53. Measured, not derived. */
body.v3 { --substrip-h: 53px; }

body.v3 .subStrip {
  padding: 4px 8px;          /* copied from .bottomBtns (was 0 10px) */
  align-items: center;
  gap: 8px;                  /* already matched .bottomBtns; restated, not changed */
}

/* ---- b. the strip's pill takes .bbV3's box ---- */
body.v3 .subStrip button {
  flex: 1 1 auto;            /* copied from .bbV3 — fills the track on a phone */
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;          /* copied from .bbV3 — tap target, not decoration */
  padding: 8px 14px;         /* copied from .bbV3 (was 7px 12px) */
  line-height: 1.25;         /* copied from .bbV3 */
  text-align: center;
}

/* ---- c. both rows wrap on a phone ----
   The R110 labels are far longer than the ones they replace: measured on one
   line at 0.76rem they need roughly 500px of a 360px track, and both pills
   carry `overflow: hidden`, so an unwrapped label does not overflow visibly —
   it DISAPPEARS. His checkpoint answer was two lines over smaller type.

   Two lines at 0.76rem x 1.15 is ~28px, which still sits inside the 44px tap
   target, so the wrap costs neither row any height. This also closes a
   pre-existing clip on the top row: "Free Implant Analysis" alone was already
   pushing three nowrap labels past a 360px track.

   `text-wrap: balance` splits "Complete Cost / Calculator" evenly instead of
   leaving one word stranded; browsers without it fall back to a normal wrap. */
@media (max-width: 767px) {
  body.v3 .subStrip button,
  body.v3 .bottomBtns .bbV3 {
    white-space: normal;
    line-height: 1.15;
    text-wrap: balance;
    /* 7px, not the 8px above, and measured: two lines of 0.76rem x 1.15 is
       27.97px, which with 8px block padding and the 1px hairline came to 45.97
       and pushed the wrapped row 2px taller than the unwrapped one. At 7px it
       is 43.97, so BOTH rows sit on the 44px min-height whether their label
       takes one line or two. That 2px was the last visible difference between
       them. */
    padding-block: 7px;
  }
}

/* ---- d. the desktop step, on both rows ----
   The pills stop sharing the track here and hug their labels again, with the
   same generous padding. Values copied from the .bbV3 md: block above. The type
   does NOT move: R53's note explains why bumping it would make the rows diverge
   again. */
@media (min-width: 768px) {
  body.v3 .subStrip button {
    flex: 0 1 auto;
    padding: 9px 26px;       /* copied from .bbV3 @768 */
    min-height: 0;
  }
}

/* ---- e. the narrow track ----
   The pre-R110 rule dropped the bottom pill to 0.7rem / 7px 8px here to keep
   three NOWRAP labels on one line. Wrapping removes that need, so the type goes
   back to 0.76rem and only the horizontal padding tightens — the strip's own
   documented order (tighten the gap and the padding before touching the type).
   Both rows take it, so they still match. */
@media (max-width: 360px) {
  body.v3 .subStrip { gap: 6px; padding-left: 6px; padding-right: 6px; }
  body.v3 .subStrip button,
  body.v3 .bottomBtns .bbV3 { font-size: 0.76rem; padding: 7px 8px; }
}

/* ---- f. the two BARS match too, not just the pills ----
   Below md: both are 44px of pill inside 4px of row padding. At md: the pills
   drop to 35.2px and the bottom bar follows them down to 45px, but the strip's
   height is a fixed variable and stayed at 52 — the pills would have matched
   inside two bars of visibly different depth. 45px is the bottom bar's own
   measured height at this width (35.2 pill + 4px + 4px + its 1px hairline).
   The hero's margin-top reads the same variable, so it moves up with it. */
@media (min-width: 768px) {
  body.v3 { --substrip-h: 45px; }
}

/* ---- g. the gap at lg: ----
   `.bottomBtns` opens its gap from 8px to 20px at 1024 (the pill-density read
   recorded in the 1024 block above) and the strip did not, so at desktop the
   two rows had matching pills set at visibly different spacing. Copied across,
   at the same breakpoint. */
@media (min-width: 1024px) {
  body.v3 .subStrip { gap: 20px; }
}


/* ---- Country / Currency Selector & Treatment Dropdown ---- */
body.v3 .wizCountryStep {
  margin-bottom: 12px;
}
body.v3 .wizCountryPills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
body.v3 .wizCountryPill {
  padding: 6px 11px;
  border-radius: 999px;
  border: 1px solid var(--v3f-line, #D0D5DD);
  background: #fff;
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--v3-ink, #0B2545);
  cursor: pointer;
  transition: all 150ms ease;
}
body.v3 .wizCountryPill:hover {
  border-color: var(--brand-gold, #C9A84C);
}
body.v3 .wizCountryPill.on,
body.v3 .wizCountryPill[aria-pressed="true"] {
  border-color: var(--brand-gold-dark, #8C7026);
  background: var(--grad-gold-cta, linear-gradient(135deg, #ECC870, #C9A84C));
  color: #0B2545;
  font-weight: 700;
  box-shadow: 0 1px 3px rgba(201, 168, 76, 0.25);
}

body.v3 .wizSelectWrap {
  position: relative;
  width: 100%;
}
body.v3 .wizSelect {
  width: 100%;
  padding: 10px 36px 10px 14px;
  border-radius: 12px;
  border: 1.5px solid var(--v3f-line, #D0D5DD);
  background: #FFFFFF;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  color: #0B2545;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%23667085' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
body.v3 .wizSelect:focus {
  outline: none;
  border-color: var(--v3-gold, #C9A84C);
  box-shadow: 0 0 0 3px rgba(201, 168, 76, 0.2);
}

body.v3 .wizFree.wizPrivilege {
  background: #FEF3C7;
  color: #92400E;
  border: 1px solid #FCD34D;
  font-weight: 800;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* ---- Select Implant Brand and System ---- */
body.v3 .bbBrandSec {
  margin-top: 20px;
  padding-top: 18px;
  border-top: 1px solid var(--v3f-line, #E5E7EB);
}
body.v3 .bbBrandGrid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  margin-top: 10px;
}
@media (max-width: 540px) {
  body.v3 .bbBrandGrid {
    grid-template-columns: 1fr;
  }
}
body.v3 .bbBrandCard {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border: 1.5px solid var(--v3f-line, #E5E7EB);
  border-radius: 12px;
  background: #fff;
  text-align: left;
  cursor: pointer;
  transition: all 180ms ease;
  font: inherit;
  color: inherit;
}
body.v3 .bbBrandCard:hover {
  border-color: #98A2B3;
  background: #FDFAF5;
}
body.v3 .bbBrandCard[aria-pressed="true"],
body.v3 .bbBrandCard.on {
  border-color: var(--v3-gold, #C9A84C);
  background: rgba(201, 168, 76, 0.08);
}
body.v3 .bbBrandRadio {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 1.5px solid #D0D5DD;
  background: #fff;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  transition: all 150ms ease;
}
body.v3 .bbBrandDot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: transparent;
  transition: all 150ms ease;
}
body.v3 .bbBrandCard[aria-pressed="true"] .bbBrandRadio,
body.v3 .bbBrandCard.on .bbBrandRadio {
  border-color: var(--v3-gold, #C9A84C);
}
body.v3 .bbBrandCard[aria-pressed="true"] .bbBrandDot,
body.v3 .bbBrandCard.on .bbBrandDot {
  background: var(--v3-gold, #C9A84C);
}
body.v3 .bbBrandName {
  font-size: 13px;
  font-weight: 700;
  color: #1D2939;
  line-height: 1.3;
}
body.v3 .bbBrandGuarantee {
  margin: 10px 0 0;
  font-size: 12px;
  font-weight: 600;
  color: #667085;
  display: flex;
  align-items: center;
  gap: 6px;
}
body.v3 .bbBrandGuarantee::before {
  content: "★";
  color: var(--v3-gold, #C9A84C);
  font-size: 13px;
}

/* ---- Optional Clinical Add-ons ---- */
body.v3 .bbAddonSec {
  margin-top: 24px;
  padding-top: 18px;
  border-top: 1px solid var(--v3f-line, #E5E7EB);
}
body.v3 .bbAddonLead {
  margin: 4px 0 12px;
  font-size: 12px;
  color: #667085;
}
body.v3 .bbAddonGrid {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
body.v3 .bbAddonCard {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 13px;
  border: 1.5px solid var(--v3f-line, #E5E7EB);
  border-radius: 12px;
  background: #fff;
  text-align: left;
  cursor: pointer;
  transition: all 180ms ease;
  font: inherit;
  color: inherit;
}
body.v3 .bbAddonCard:hover {
  border-color: #98A2B3;
  background: #FDFAF5;
}
body.v3 .bbAddonCard[aria-pressed="true"] {
  border-color: var(--v3-gold, #C9A84C);
  background: rgba(201, 168, 76, 0.08);
}
body.v3 .bbAddonCheck {
  width: 22px;
  height: 22px;
  border-radius: 6px;
  border: 1.5px solid #D0D5DD;
  background: #fff;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  transition: all 150ms ease;
}
body.v3 .bbAddonBox {
  display: none;
  font-size: 13px;
  font-weight: 800;
  color: #fff;
  line-height: 1;
}
body.v3 .bbAddonCard[aria-pressed="true"] .bbAddonCheck {
  border-color: var(--v3-gold, #C9A84C);
  background: var(--v3-gold, #C9A84C);
}
body.v3 .bbAddonCard[aria-pressed="true"] .bbAddonBox {
  display: block;
}
body.v3 .bbAddonBody {
  flex: 1;
  min-width: 0;
}
body.v3 .bbAddonTitle {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: #1D2939;
}
body.v3 .bbAddonSub {
  display: block;
  font-size: 11.5px;
  color: #667085;
  margin-top: 1px;
}
body.v3 .bbAddonPrice {
  font-size: 13px;
  font-weight: 800;
  color: #8C7026;
  white-space: nowrap;
}

/* ---- Side-by-Side Breakdown Boxes (4 equal 25% columns) ---- */
body.v3 .wizTotalBar {
  width: 100%;
}
body.v3 .wizTotalBoxes {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 6px;
  width: 100%;
}
body.v3 .wizColBox {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 8px 4px;
  min-height: 64px;
  border-radius: 10px;
  border: 1.5px solid var(--v3f-line, #E5E7EB);
  background: #F8FAFC;
  text-align: center;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
  transition: border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
  cursor: pointer;
  user-select: none;
}
body.v3 .wizColBox:hover {
  border-color: #C9A84C;
  transform: translateY(-1px);
}
body.v3 .wizColBox.current {
  border-color: #C9A84C;
  background: #FFFFFF;
  box-shadow: 0 0 0 2px rgba(201, 168, 76, 0.35), 0 2px 6px rgba(201, 168, 76, 0.15);
}
body.v3 .wizColBox.active {
  border-color: #C9A84C;
  background: #FFFFFF;
  box-shadow: 0 2px 6px rgba(201, 168, 76, 0.12);
}
body.v3 .wizColBox.wizColFree {
  border-color: #10B981;
  background: #F0FDF4;
}
body.v3 .wizMiniVal {
  font-size: 11.5px;
  font-weight: 800;
  color: #0B2545;
  line-height: 1.25;
  display: block;
}
body.v3 .wizMiniVal.wizRange {
  font-size: 10px;
  line-height: 1.2;
}
body.v3 .wizMiniLbl {
  display: block;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #64748B;
  margin-top: 3px;
}
body.v3 .wizColBox.active .wizMiniLbl {
  color: #8C7026;
}
body.v3 .wizColBox.wizColFree .wizMiniLbl {
  color: #047857;
}
body.v3 .wizMiniSub {
  display: block;
  font-size: 8px;
  font-weight: 600;
  color: #8C7026;
  margin-top: 1px;
  text-transform: none;
}
body.v3 .wizTotalSummary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 8px;
  padding: 9px 14px;
  border-radius: 10px;
  background: linear-gradient(135deg, rgba(201, 168, 76, 0.16), rgba(201, 168, 76, 0.06));
  border: 1.5px solid #C9A84C;
  box-shadow: 0 2px 6px rgba(201, 168, 76, 0.15);
}
body.v3 .wizSummaryLbl {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #8C7026;
}
body.v3 .wizSummaryVal {
  font-size: 15px;
  font-weight: 800;
  color: #0B2545;
  letter-spacing: -0.01em;
}

/* ---- Download Itinerary PDF Action ---- */
body.v3 .wizPdfBox {
  margin-top: 18px;
  padding: 16px 18px;
  background: #FFFFFF;
  border: 1px solid var(--v3-gold, #B8832F);
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  box-shadow: 0 4px 14px rgba(184, 131, 47, 0.1);
}
body.v3 .wizPdfBoxHead strong {
  display: block;
  font-size: 15px;
  font-weight: 700;
  color: var(--v3-blue, #0D395E);
  margin-bottom: 3px;
}
body.v3 .wizPdfBoxHead p {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: #667085;
}
body.v3 .wizPdfActionBtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  height: 44px;
  padding: 0 22px;
  background: var(--v3-gold-grad, linear-gradient(105deg, #F7E3B5 0%, #F0D49B 50%, #E7C078 100%));
  border: 1px solid #B8832F;
  border-radius: 999px;
  color: #1D1D1F;
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(184, 131, 47, 0.2);
  transition: transform 150ms ease, box-shadow 150ms ease, filter 150ms ease;
  width: 100%;
}
body.v3 .wizPdfActionBtn:hover {
  filter: brightness(1.03);
  transform: translateY(-1px);
  box-shadow: 0 4px 14px rgba(184, 131, 47, 0.3);
}
body.v3 .wizPdfActionBtn:active {
  transform: translateY(0);
}
body.v3 .wizPdfActionBtn svg {
  stroke: #1D1D1F;
  flex-shrink: 0;
}
body.v3 .wizPdfActionBtn.isGenerating,
body.v3 .wizNext.isGenerating,
body.v3 .sdModalSubmitBtn.isGenerating {
  opacity: 0.75;
  pointer-events: none;
  cursor: wait;
}

@keyframes wizSpin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
body.v3 .wizBtnSpinner {
  display: inline-block;
  width: 14px;
  height: 14px;
  border: 2px solid rgba(0, 0, 0, 0.25);
  border-top-color: #1D1D1F;
  border-radius: 50%;
  animation: wizSpin 0.7s linear infinite;
  margin-right: 6px;
  vertical-align: middle;
}

/* ---------- Download Itinerary Modal Popup ---------- */
body.v3 .sdModalOverlay {
  position: fixed;
  inset: 0;
  background: rgba(11, 37, 69, 0.68);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  z-index: 100000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
body.v3 .sdModalOverlay.open {
  opacity: 1;
  pointer-events: auto;
}
body.v3 .sdModalCard {
  position: relative;
  background: #FAF8F2;
  border: 1px solid #E5DFD1;
  border-radius: 20px;
  max-width: 440px;
  width: 100%;
  padding: 30px 28px;
  box-shadow: 0 24px 54px rgba(11, 37, 69, 0.35);
  transform: translateY(18px) scale(0.96);
  transition: transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
  box-sizing: border-box;
}
body.v3 .sdModalOverlay.open .sdModalCard {
  transform: translateY(0) scale(1);
}
body.v3 .sdModalClose {
  position: absolute;
  top: 16px;
  right: 18px;
  width: 32px;
  height: 32px;
  background: none;
  border: none;
  font-size: 24px;
  line-height: 1;
  color: #6E6E73;
  cursor: pointer;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 150ms ease, color 150ms ease;
}
body.v3 .sdModalClose:hover {
  background: rgba(0, 0, 0, 0.06);
  color: #1D1D1F;
}
body.v3 .sdModalHead {
  margin-bottom: 22px;
}
body.v3 .sdModalTag {
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: #B8832F;
  text-transform: uppercase;
  margin-bottom: 6px;
}
body.v3 .sdModalTitle {
  font-size: 22px;
  font-weight: 800;
  color: #0B2545;
  margin: 0;
  letter-spacing: -0.01em;
}
body.v3 .sdModalForm {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
body.v3 .sdFormField {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
body.v3 .sdPhoneGroup {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}
body.v3 .sdCountryCodeSelect {
  flex: 0 0 auto;
  width: 115px;
  height: 44px;
  border: 1px solid #DCD6C8;
  border-radius: 10px;
  background-color: #FFFFFF;
  padding: 0 24px 0 10px;
  font-size: 13.5px;
  font-weight: 600;
  font-family: inherit;
  color: #1D1D1F;
  outline: none;
  cursor: pointer;
  box-sizing: border-box;
  transition: border-color 150ms ease, box-shadow 150ms ease;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%236E6E73' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
}
body.v3 .sdCountryCodeSelect:focus {
  border-color: #B8832F;
  box-shadow: 0 0 0 3px rgba(184, 131, 47, 0.15);
}
body.v3 .sdPhoneInput {
  flex: 1 1 auto;
  min-width: 0;
}
body.v3 .sdFormInput {
  width: 100%;
  height: 44px;
  border: 1px solid #DCD6C8;
  border-radius: 10px;
  background: #FFFFFF;
  padding: 0 14px;
  font-size: 14px;
  color: #1D1D1F;
  box-sizing: border-box;
  outline: none;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}
body.v3 .sdFormInput:focus {
  border-color: #B8832F;
  box-shadow: 0 0 0 3px rgba(184, 131, 47, 0.15);
}
body.v3 .sdFormFooter {
  margin-top: 4px;
}
body.v3 .sdModalSubmitBtn {
  width: 100%;
  height: 48px;
  background: linear-gradient(135deg, #E2CE93 0%, #B8912F 100%);
  border: 1px solid #B8832F;
  border-radius: 12px;
  color: #111827;
  font-size: 15px;
  font-weight: 700;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-shadow: 0 4px 14px rgba(184, 131, 47, 0.25);
  transition: transform 150ms ease, filter 150ms ease, box-shadow 150ms ease;
}
body.v3 .sdModalSubmitBtn:hover {
  filter: brightness(1.03);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px rgba(184, 131, 47, 0.35);
}
body.v3 .sdModalSubmitBtn:active {
  transform: translateY(0);
}
body.v3 .sdModalSuccess {
  text-align: center;
  padding: 10px 0;
}
body.v3 .sdSuccessIcon {
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: #E8F5E9;
  color: #2E7D32;
  font-size: 26px;
  font-weight: bold;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 14px;
}
body.v3 .sdModalSuccess h4 {
  font-size: 19px;
  font-weight: 800;
  color: #0B2545;
  margin: 0 0 8px;
}
body.v3 .sdModalSuccess p {
  font-size: 13.5px;
  color: #555B68;
  line-height: 1.5;
  margin: 0 0 20px;
}
body.v3 .sdSuccessCloseBtn {
  height: 42px;
  padding: 0 28px;
  background: #0B2545;
  color: #fff;
  border: none;
  border-radius: 10px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms ease;
}
body.v3 .sdSuccessCloseBtn:hover {
  background: #143d66;
}


