/* ============================================================
   GALVANIZE FLOATING CTA — sitewide "Call me back" FAB (SHELL ONLY)
   ============================================================
   Ported from Subash's comp: website-migration/tmp/
   subash_callback_comp__floating_cta_subash_2026-07-04.html — the launcher
   tab, expandable card frame, header, and body chrome, verbatim per
   PLAYBOOK.md §3 (verbatim doctrine).

   This is a SITEWIDE widget (injected on every front-end page by
   CallbackFloater.php, gated by galv_floater_active()) — unlike a per-page
   comp skin, which is the reason for two sanctioned reconciliations below.
   Both are scoping/architecture fixes, NOT visual redesigns — flag
   [VERIFY] to Subash for a pixel-diff confirmation against the comp:

   1. DROPPED the comp's demo-only canvas styles (`body{...min-height:220vh}`,
      `.demo-note*`) — filler for the standalone preview page, not the widget.

   2. SCOPED the comp's bare `:root { --bg-navy:...; --magenta:...; }` token
      block + bare `* { box-sizing:border-box; margin:0; padding:0; }` reset
      down to `.galv-fab` (the widget's own wrapper), instead of the document
      root / a universal selector. Reason: many per-page skins in this repo
      (course.css, destination.css, exam-hub.css, etc.) ALSO declare
      `:root { --magenta: ...; --bg-navy: ...; }` — since those only ever
      load one-at-a-time per page, a global :root redeclaration is harmless
      there. This widget instead loads on EVERY page ALONGSIDE whichever
      page skin is active, so a second `:root` block competing for the same
      custom-property names is exactly the hazard galvanize-forms.css's own
      `.galv-v4card` block already calls out ("scoped to card root; not
      relying on nav :root"). Same fix, same reasoning, same values —
      nothing here changes what renders, only where the tokens live.

   3. DROPPED the comp's `.galv-fab-intro` paragraph element + its CSS rule.
      The comp's demo <form> carried its own intro copy inside the card
      body; the REAL form (the v4 skin, see below) already renders its own
      `.sub` intro paragraph from the FORMS config's `intro` key, so keeping
      both would double the messaging. [VERIFY] with Subash — this is a
      content/architecture call, not a pure mechanical extraction.

   The comp's field CSS (.galv-cb-form, .galv-cb-name-row, .galv-cb-code,
   .galv-cb-select, .galv-cb-note, .galv-cb-submit, .galv-cb-success, etc.)
   is INTENTIONALLY NOT ported. The real form is
   [galv_form id="callback_global"] rendered by FormRenderer's v4 skin,
   which already styles fields via the shared .galv-v4card rules in
   galvanize-forms.css — the same locked tokens (44px inputs, magenta
   #cc00cc, 10px radius, Poppins/Inter) the comp itself reused, so the
   rendered result should already visually match without touching this
   file. The comp's own .galv-fab-body padding (15px) + background
   (#e6ebff) are kept as-is below — #e6ebff is the same --accent-blue value
   the other v4 card skins (.gate-wrapper, .events-form) already use as
   their own card background, so the v4 form should sit correctly inside
   with no extra reconciliation CSS needed. [VERIFY] the visual seam in
   staging review — flag to Subash if the fit reads wrong.

   Card heading note [VERIFY]: the shell header below (navy bar) carries
   the comp's own "Request a callback" / "Our team will call you back
   shortly" copy verbatim. The v4 form rendered inside the body ALSO prints
   its own heading + intro (FORMS config `heading`/`intro` keys, per the
   locked spec). This may read as a duplicated headline (shell header +
   form h4) — flagging for Subash's design-parity pass rather than
   silently dropping either one; either is a one-line change once decided.
   ============================================================ */

.galv-fab {
  font-family: var(--font-body);
  --bg-navy: #13217c;
  --magenta: #cc00cc;
  --magenta-hover: #a600a6;
  --text-white: #fffdfe;
  --font-heading: 'Poppins', sans-serif;
  --font-body: 'Inter', sans-serif;
  --transition-fast: 0.15s ease;
  --transition-med: 0.25s ease;
}
.galv-fab,
.galv-fab *,
.galv-fab *::before,
.galv-fab *::after { box-sizing: border-box; }

/* ---- Callback card (the "expanded" state) ----
   Fixed + vertically centred in the viewport with a max-height + internal
   scroll, so the (taller) form never clips off-screen on short laptops. It
   covers the launcher while open; the launcher shows again once closed. */
.galv-fab-card {
  position: fixed;
  right: 24px;
  top: 118px;                          /* open right next to the Call me back tab (tab top ~130) */
  z-index: 9999;                       /* [PLAYBOOK §9 fix] comp had z-index:2 with no site chrome; on the live site the open card must sit ABOVE the sticky header + launcher (9998) */
  width: 320px;
  max-width: calc(100vw - 48px);
  max-height: calc(100vh - 142px);     /* fit between the top offset and a bottom margin. [bug#17 2026-07-18] Do NOT shrink this to add bottom clearance — on short (~720px) viewports it pushes the submit below the card's scroll fold. The real fix for the bottom-widget collision is suppressing those widgets while the card is open (see the is-open block below), not shrinking the card. */
  overflow-y: auto;
  background: var(--text-white);
  border-radius: 18px;
  box-shadow: 0 18px 50px rgba(19, 33, 124, 0.22);
  opacity: 0;
  transform: translateX(16px);         /* slide in from the right (toward the tab) */
  pointer-events: none;
  transition: opacity var(--transition-med), transform var(--transition-med);
}
.galv-fab.is-open .galv-fab-card {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}

/* ---- [BUGFIX 2026-07-18 · bug#17 — CRITICAL] callback submit was unclickable ----
   The open card's submit button lands in the bottom viewport strip, where two
   bottom-anchored THIRD-PARTY Zoho widgets — absent from Subash's comp — sit and
   steal the click:
     • #gdpr_banner (.zoho-msgbanr) — full-width cookie bar at z-index 2147483647
       (INT_MAX, so it CANNOT be out-z-indexed), and
     • #zsiq_float (.zsiq-float) — the SalesIQ chat bubble, bottom-right.
   A live hit-test on the submit centre returned #accept_all (the banner's
   "Accept all"), so a real, human-speed visitor's "Request a callback" click
   actually hit "Accept all" and the callback lead was silently dropped (a fast
   scripted submit beat the widgets' paint and passed — which is why it hid).
   Since z-index can't beat INT_MAX, while the card is OPEN we (a) lift it to the
   top of our own chrome and (b) suppress the two blockers — both restored the
   instant the card closes (callback-floater.js toggles body.galv-fab-open).
   Selectors captured from prod (elementFromPoint), not guessed; class-based
   fallbacks included in case a Zoho variant renders differently on some pages. */
.galv-fab.is-open .galv-fab-card { z-index: 2147483646; }
body.galv-fab-open #gdpr_banner,
body.galv-fab-open .zoho-msgbanr,
body.galv-fab-open #zsiq_float,
body.galv-fab-open [id^="zsiq_float"],
body.galv-fab-open .zsiq_floatmain { display: none !important; }

/* .galv-fab-qualified (0,0,2,0) — same padding-defense as the body above,
   so a host `* { padding: 0 }` reset can't collapse the header inset. */
.galv-fab .galv-fab-header {
  position: relative;
  background: var(--bg-navy);
  color: var(--text-white);
  padding: 15px 40px 15px 15px;
  display: flex;
  align-items: center;
  gap: 12px;
}
.galv-fab-avatar {
  width: 44px; height: 44px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--magenta);
  display: flex; align-items: center; justify-content: center;
}
.galv-fab-avatar svg { width: 22px; height: 22px; }
.galv-fab-heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.galv-fab-heading strong { font-family: var(--font-heading); font-size: 15px; font-weight: 600; line-height: 1.3; }
.galv-fab-heading span { font-size: 12px; color: #fffdfe; }
.galv-fab-close {
  position: absolute; top: 12px; right: 12px;
  width: 26px; height: 26px; border: none; border-radius: 50%;
  background: rgba(255, 255, 255, 0.14); color: var(--text-white);
  font-size: 16px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background var(--transition-fast);
}
.galv-fab-close:hover { background: rgba(255, 255, 255, 0.28); }

/* Body chrome only — the rendered [galv_form] fields live directly inside
   this padded/tinted well (see file header note 3 re: dropped .galv-fab-intro).
   [PADDING-DEFENSE] Qualified with .galv-fab (0,0,2,0) so a host page reset
   like `body.<tpl> * { padding: 0 }` (0,0,1,1) can't zero the field inset —
   the same defense the shared v4 cards use via [data-galv-form-id]. A bare
   .galv-fab-body (0,0,1,0) lost that battle and the fields hit the edges. */
.galv-fab .galv-fab-body { padding: 15px; background: #e6ebff; }

/* [SUBASH REVIEW 2026-07-08] Compact the form inside the floater. Subash's
   review gave these as bare `.galv-form__fields` / `.galv-form__note`, but
   those are SHARED v4-form classes (galvanize-forms.css) used by every form
   on the site — applied bare they'd regress form spacing site-wide. Scoped to
   .galv-fab so the tightening lands ONLY inside the floater (specificity beats
   the shared bare rule; no global leak).

   Subash's grid-gap tweak alone did NOT read as compact because our v4
   renderer spaces fields with TWO stacked mechanisms: the .galv-form__fields
   grid `gap` AND a 14px `margin-bottom` on every `.galv-v4card .fieldrow`
   (line ~890; his standalone comp has neither, so he couldn't see it).
   Zeroing the fieldrow margin makes the 5px grid gap the single, uniform
   inter-field spacer; tightening the label->input gap and the optional-message
   textarea removes the remaining slack. Inputs stay at the locked 44px token.
   All .galv-fab-scoped — the site's other forms are untouched. [VERIFY] the
   final rhythm with Subash. */
.galv-fab .galv-form__fields { gap: 5px; }
.galv-fab .galv-form__note { margin: 0 0 10px; }
.galv-fab .galv-v4card .fieldrow { margin-bottom: 0; }
.galv-fab .galv-v4card .field { gap: 4px; }
.galv-fab .galv-v4card .field textarea { min-height: 46px; }

/* ---- Launcher: vertical "Call me back" tab pinned to the right
   edge, upper area — mirrors galvanizetestprep.com's Call Me Back
   tab position (right:0, upper). Rotated text; magenta CTA. ---- */
.galv-fab-launcher {
  position: fixed;
  right: 0;
  top: 220px;                     /* [SUBASH REVIEW 2026-07-08] 130px (orig) -> 300 (v1) -> 220 (v2) */
  z-index: 9998;
  width: 46px;
  height: 168px;
  padding: 0;
  border: none;
  border-radius: 12px 0 0 12px;   /* rounded on the inner (left) side */
  background: var(--magenta);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: -2px 4px 12px rgba(0, 0, 0, 0.22);
  transition: background var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast), opacity var(--transition-fast);
}
.galv-fab-launcher:hover { background: var(--magenta-hover); }
.galv-fab-launcher:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(204, 0, 204, 0.35); }
.galv-fab-launcher-inner {
  display: inline-flex; align-items: center; gap: 8px;
  transform: rotate(-90deg);      /* horizontal content becomes a vertical tab (reads bottom-to-top) */
  white-space: nowrap;
  font-family: var(--font-heading); font-weight: 600; font-size: 16px; letter-spacing: 0.01em;
  color: var(--text-white);
}
.galv-fab-launcher-inner svg { width: 18px; height: 18px; transform: rotate(0deg); }
/* Hide the tab while the card is open */
.galv-fab.is-open .galv-fab-launcher { opacity: 0; pointer-events: none; }

/* ---- SANCTIONED ADDITION — one-time collapsed-tab attention pulse ----
   DELTA 2 [VERIFY]: replaces the comp's auto-peek (setTimeout force-open +
   localStorage 'galvFabSeen', see callback-floater.js header comment). The
   floater is reactive-only; this pulse draws the eye WITHOUT auto-opening
   the card. callback-floater.js adds `.is-pulsing` once, ~1.2s after load,
   and it stops for good the moment the visitor opens the card (reusing the
   comp's own `.was-opened` class — the comp's JS already comments "kills
   the attention pulse for good" on that class, even though the comp never
   actually wired a pulse to it). */
@keyframes galv-fab-pulse {
  0%, 100% { transform: scale(1); box-shadow: -2px 4px 12px rgba(0, 0, 0, 0.22); }
  50% { transform: scale(1.06); box-shadow: -2px 6px 18px rgba(204, 0, 204, 0.45); }
}
.galv-fab-launcher.is-pulsing { animation: galv-fab-pulse 1.8s ease-in-out 3; }
.galv-fab.was-opened .galv-fab-launcher { animation: none; }

@media (max-width: 560px) {
  .galv-fab-card { right: 16px; top: 88px; max-height: calc(100vh - 112px); }
  .galv-fab-launcher { top: 270px; width: 38px; height: 144px; }  /* [SUBASH REVIEW 2026-07-08] lowered from 96px */
  .galv-fab-launcher-inner { font-size: 14px; line-height: 1; gap: 7px; }
  .galv-fab-launcher-inner svg { width: 16px; height: 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .galv-fab-card, .galv-fab-launcher, .galv-fab-launcher-label { transition: none; }
  .galv-fab-launcher-icon::after { animation: none; }
  .galv-fab-launcher.is-pulsing { animation: none; }
}
