/* =========================================================
   Visioneering MT — Design System
   Display: Fraunces (headlines only, used sparingly)
   Body: IBM Plex Sans
   Data/Utility: IBM Plex Mono (numbers, captions, nav labels, prices)
   ========================================================= */

:root {
  /* Color tokens */
  /* Navy scale — the dominant page canvas now, not just an "on-dark" opt-in.
     900 = page canvas, 700 = elevated surface (cards/panels), 500 = borders/dividers/subtle hover on dark. */
  --color-navy-900: #0B1F3A;
  --color-navy-700: #14294A;
  --color-navy-500: #26406B;

  /* Brand blue — sampled from the real logo mark (assets/images/logo.png, dominant non-white pixel).
     Signature identity color: the logo mark, the hero animation, thin graphic accents/borders.
     NOT a text color on navy — only 2.03:1 against navy-900, fails WCAG for text. Gold stays the
     on-navy text accent; brand-blue is for marks/graphics only. */
  --color-brand-blue: #004AAD;
  --color-brand-blue-600: #003B8A; /* ~20% deepened, for hover on brand-blue graphic elements */

  --color-gold-500: #B8933F;
  --color-gold-300: #D4B571;
  --color-gold-700: #7A5D26; /* WCAG AA text on light backgrounds incl. limestone tint (4.99:1 on limestone, higher on white) */

  /* Warm secondary — Malta roof-tile / sunset terracotta */
  --color-terracotta-500: #C4623B;
  --color-terracotta-300: #E0916D;
  --color-terracotta-700: #93401F; /* WCAG AA text on light backgrounds */

  /* Cool secondary — Mediterranean sea teal */
  --color-teal-500: #1B6E6B;
  --color-teal-300: #5CA6A2;
  --color-teal-700: #124D4A; /* WCAG AA text on light backgrounds */

  /* Warm neutral — Malta limestone, an alternate surface tint (used sparingly, not as the dominant background) */
  --color-limestone: #EFE7D6;
  --color-limestone-line: rgba(147, 64, 31, 0.08);

  /* --color-surface (#FFFFFF) is no longer a section/page background — reserved for form
     input fields and anywhere pure white is functionally required (device-frame proof previews). */
  --color-surface: #FFFFFF;
  --color-surface-alt: #F5F6F8;
  --color-surface-dark: var(--color-navy-900);
  --color-text: #14192A;
  --color-text-muted: #4B5468;
  /* Default body text color site-wide now that navy is the base canvas. */
  --color-text-on-dark: #F5F6F8;
  --color-text-on-dark-muted: #B9C1D6;
  --color-border: #DFE2E8;
  --color-border-on-dark: var(--color-navy-500);
  --color-success: #1E7A46;
  --color-success-bg: #E9F5EE;
  --color-error: #B3261E;
  --color-error-bg: #FBEAE9;

  /* Type scale */
  --font-display: 'Newsreader', Georgia, serif;
  --font-body: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --font-mono: 'IBM Plex Mono', 'Courier New', monospace;

  --step--1: clamp(0.82rem, 0.8rem + 0.1vw, 0.9rem);
  --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1: clamp(1.2rem, 1.1rem + 0.4vw, 1.4rem);
  --step-2: clamp(1.5rem, 1.3rem + 0.8vw, 1.9rem);
  --step-3: clamp(1.9rem, 1.6rem + 1.4vw, 2.6rem);
  --step-4: clamp(2.4rem, 1.9rem + 2.2vw, 3.6rem);
  --step-5: clamp(3rem, 2.1rem + 4vw, 5.2rem);

  /* Spacing scale (4/8px base) */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  --space-9: 6rem;
  --space-10: 8rem;

  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;

  --shadow-sm: 0 1px 2px rgba(11, 31, 58, 0.06), 0 1px 1px rgba(11, 31, 58, 0.04);
  --shadow-md: 0 6px 20px rgba(11, 31, 58, 0.10);
  --shadow-lg: 0 16px 40px rgba(11, 31, 58, 0.16);
  --shadow-glow-gold: 0 10px 32px rgba(184, 147, 63, 0.28);
  --shadow-glow-teal: 0 10px 32px rgba(27, 110, 107, 0.24);
  --shadow-glow-terracotta: 0 10px 32px rgba(196, 98, 59, 0.24);

  --content-max: 1180px;
  --ease-standard: cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* =========================================================
   Reset
   ========================================================= */
*, *::before, *::after { box-sizing: border-box; }
/* Smooth in-page jumps. The reduced-motion block below forces
   `scroll-behavior: auto !important` on everything, so that preference wins. */
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
/* Anchor targets clear the sticky header (~84px) instead of landing under it. */
:target, [id] { scroll-margin-top: 104px; }
body {
  margin: 0;
  min-height: 100dvh;
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  color: var(--color-text-on-dark);
  background: var(--color-navy-900);
  -webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg { display: block; max-width: 100%; height: auto; }
input, button, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }
ul, ol { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4, p, figure { margin: 0; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* Cross-page fade instead of a hard cut, for browsers that support it — silently
   ignored elsewhere, so unsupported browsers just navigate instantly as before. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

:focus-visible {
  outline: 3px solid var(--color-gold-500);
  outline-offset: 2px;
  border-radius: 2px;
}

/* =========================================================
   Typography
   ========================================================= */
/* Display face: Newsreader at 600, with its optical-size axis enabled.
   Weight 700 read blunt and dense at display sizes; 600 opens the counters and
   reads considerably more refined without losing authority. Newsreader is a
   screen-first variable face, so `font-optical-sizing: auto` gives genuinely
   higher-contrast letterforms at headline sizes and sturdier ones at small
   sizes — premium and precise without Didone-style fragile hairlines. */
h1, h2, h3, .font-display {
  font-family: var(--font-display);
  font-weight: 600;
  font-optical-sizing: auto;
  line-height: 1.12;
  letter-spacing: -0.018em;
  color: var(--color-text-on-dark);
}
h1 { font-size: var(--step-5); letter-spacing: -0.022em; }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); }
h4 { font-size: var(--step-1); font-family: var(--font-body); font-weight: 600; color: var(--color-text-on-dark); }

/* Italic display accent — a single emphasised word/phrase, used sparingly inside a headline.
   Navy is the default canvas now (verified 5.5:1 against worst-case photo pixel + overlay). */
h1 em, h2 em, .font-display em {
  font-style: italic;
  font-weight: 500;
  color: #E6CD9B;
}

/* Visual size of h4 for headings that must be a different semantic level to keep hierarchy sequential */
.text-h4-size { font-size: var(--step-1); font-family: var(--font-body); font-weight: 600; color: var(--color-text-on-dark); }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-gold-300);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 500;
}

.lede { font-size: var(--step-1); color: var(--color-text-on-dark-muted); max-width: 60ch; }
.text-muted { color: var(--color-text-on-dark-muted); }

.mono-figure {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* =========================================================
   Layout
   ========================================================= */
.wrap {
  max-width: var(--content-max);
  margin-inline: auto;
  padding-inline: var(--space-5);
}
@media (min-width: 768px) { .wrap { padding-inline: var(--space-7); } }

section { padding-block: var(--space-9); }
.section-tight { padding-block: var(--space-8); }

.stack { display: flex; flex-direction: column; }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.gap-5 { gap: var(--space-5); }
.gap-6 { gap: var(--space-6); }

.grid { display: grid; gap: var(--space-6); }
.grid-2 { grid-template-columns: 1fr; }
.grid-3 { grid-template-columns: 1fr; }
.grid-4 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 480px) {
  .grid-4 { grid-template-columns: 1fr; }
}
@media (min-width: 720px) {
  .grid-2 { grid-template-columns: 1fr 1fr; }
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}

.on-dark { background: var(--color-surface-dark); color: var(--color-text-on-dark); }

/* Flat navy-900 canvas — matches the page default. Several of these sections hold .card
   elements (navy-700); keeping the panel itself at navy-900 is what makes those cards read
   as an elevated surface instead of blending into an equally-navy-700 panel behind them.
   Text/eyebrow/step-num already resolve correctly via the dark-canvas defaults. */
.on-alt { background: var(--color-navy-900); }
.on-warm { background: var(--color-navy-900); }

/* Same navy-900 canvas — teal is demoted from section-level backgrounds (see design-audit notes). */
.on-teal { background: var(--color-navy-900); }

/* Gradient mesh — navy base plus brand-blue + gold color blobs, so no gap between them reads as flat navy */
.mesh-bg { position: relative; isolation: isolate; background: var(--color-navy-900); }
.mesh-bg::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(ellipse 70% 60% at 15% 10%, rgba(0, 74, 173, 0.38), transparent 62%),
    radial-gradient(ellipse 65% 55% at 90% 15%, rgba(184, 147, 63, 0.26), transparent 62%),
    radial-gradient(ellipse 60% 55% at 50% 100%, rgba(38, 64, 107, 0.55), transparent 68%),
    var(--color-navy-900);
}
/* Two alternate blob compositions so page openings that share the mesh-bg
   mechanic don't read as the exact same background reskinned with new text. */
.mesh-bg--cool::before {
  background:
    radial-gradient(ellipse 65% 55% at 85% 8%, rgba(0, 74, 173, 0.42), transparent 62%),
    radial-gradient(ellipse 60% 50% at 10% 85%, rgba(38, 64, 107, 0.5), transparent 65%),
    radial-gradient(ellipse 50% 45% at 40% 30%, rgba(184, 147, 63, 0.16), transparent 68%),
    var(--color-navy-900);
}
.mesh-bg--warm::before {
  background:
    radial-gradient(ellipse 60% 55% at 12% 85%, rgba(184, 147, 63, 0.3), transparent 62%),
    radial-gradient(ellipse 65% 55% at 80% 20%, rgba(0, 74, 173, 0.3), transparent 65%),
    radial-gradient(ellipse 55% 50% at 50% 0%, rgba(38, 64, 107, 0.45), transparent 68%),
    var(--color-navy-900);
}

/* Small recurring brand touch on page openings — the same star from the mark,
   quick and quiet, not a redraw-in. */
.eyebrow-mark { flex-shrink: 0; }
.eyebrow-mark path { fill: var(--color-brand-blue); }

/* Rating stars — the same mark glyph as the brand, in gold since this is the one
   place it reads as a conventional rating, not the identity mark itself. */
.star-row { display: flex; gap: var(--space-2); margin-bottom: var(--space-3); }
.star-row path { fill: var(--color-gold-500); }
@media (prefers-reduced-motion: no-preference) {
  /* Quick sequential pop-in (main.js initStarRow) instead of all five appearing at once.
     Base state is fully visible; JS adds .pending synchronously right before it starts
     observing, so if JS never runs at all, the stars simply stay at their visible default. */
  .star-row svg { transition: opacity 220ms var(--ease-standard), transform 220ms var(--ease-standard); }
  .star-row.pending svg { opacity: 0; transform: scale(0.4); }
}

/* Floating WhatsApp button — fixed, quiet, present on every page. */
.whatsapp-float {
  position: fixed;
  right: var(--space-5);
  bottom: var(--space-5);
  z-index: 60;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: #25D366;
  color: #08331A;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
  transition: transform 180ms var(--ease-standard);
}
.whatsapp-float:hover { transform: scale(1.06); }
.whatsapp-float:active { transform: scale(0.96); }
@media (prefers-reduced-motion: no-preference) {
  /* Quiet entrance on first paint — communicates "help is here" without demanding attention.
     animation-fill-mode: backwards applies the from-state during the delay; nothing here can
     get stuck, since the element is fully visible in its plain CSS state the instant the
     animation ends (or immediately, for reduced-motion users, since this block is skipped). */
  .whatsapp-float { animation: waFloatIn 420ms var(--ease-standard) 500ms backwards; }
}
@keyframes waFloatIn {
  from { opacity: 0; transform: scale(0.6); }
  to { opacity: 1; transform: scale(1); }
}

/* Real Malta / workspace photography as atmospheric, blurred section backgrounds — decoration only,
   never implying to depict Roberto, Mylene, or a client. Photo lives on ::before so it can be blurred
   without blurring the text content sitting above it. */
.photo-bg {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}
.photo-bg::before {
  content: "";
  position: absolute;
  inset: -14px;
  z-index: 0;
  background-image: var(--photo);
  background-size: cover;
  background-position: center;
  filter: blur(4px);
  transform: scale(1.06);
}
/* Subtle desktop-only parallax: the photo drifts a few percent slower than the page
   as the section scrolls through view. Pure CSS scroll-driven animation — silently
   ignored by browsers without support, so there's no JS/scroll-listener cost anywhere
   and nothing that can get "stuck": unsupported browsers just render the static image. */
@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .photo-bg::before {
      animation: photoParallax linear;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
  }
}
@keyframes photoParallax {
  from { transform: scale(1.06) translateY(-2.5%); }
  to { transform: scale(1.06) translateY(2.5%); }
}
.photo-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  /* Navy-only tint — a single flat wash, no color-shifting gradient endpoint. Per-instance
     overridable via --photo-overlay-1 (same precedent as --photo); each real instance is
     re-checked against its own brightest sampled pixel — see design-audit/ for the numbers. */
  background: rgba(11, 31, 58, var(--photo-overlay-1, 0.72));
}
.photo-bg > * { position: relative; z-index: 2; }
.photo-bg.on-dark, .photo-bg .eyebrow { color: var(--color-text-on-dark); }
.photo-bg .eyebrow { color: var(--color-gold-300); }

/* =========================================================
   Buttons
   ========================================================= */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 48px;
  padding: 0.85rem 1.5rem;
  border-radius: var(--radius-md);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--step-0);
  border: 2px solid transparent;
  transition: transform 180ms var(--ease-standard), background-color 180ms var(--ease-standard), border-color 180ms var(--ease-standard), box-shadow 180ms var(--ease-standard);
  white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(0); }

.btn-primary { background: var(--color-gold-500); color: var(--color-navy-900); }
.btn-primary:hover { background: var(--color-gold-300); box-shadow: var(--shadow-md); }

.btn-secondary { background: transparent; border-color: currentColor; color: var(--color-text-on-dark); }
.btn-secondary:hover { background: rgba(255, 255, 255, 0.08); }

.btn-whatsapp { background: #25D366; color: #08331A; }
.btn-whatsapp:hover { background: #34e075; }

.btn-block { width: 100%; }

/* =========================================================
   Nav
   ========================================================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--color-border);
  /* The header stays a light surface even though the page canvas is navy now —
     scope dark text here explicitly rather than relying on the (now light) body default. */
  color: var(--color-navy-900);
  box-shadow: 0 0 0 rgba(11, 31, 58, 0);
  transition: box-shadow 200ms var(--ease-standard);
}
/* Communicates the page has scrolled — a quiet cue that the header is now floating over content. */
.site-header.is-scrolled { box-shadow: 0 4px 16px rgba(11, 31, 58, 0.12); }
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-3);
}
.nav-logo { display: flex; align-items: center; gap: var(--space-3); }
.nav-logo-mark { height: 34px; width: auto; }
.nav-logo-word { font-family: var(--font-display); font-weight: 600; font-optical-sizing: auto; font-size: 1.3rem; letter-spacing: -0.015em; color: var(--color-navy-900); }

/* Footer wordmark — the real full lockup (mark + wordmark + tagline), alpha-matted
   and recolored for legibility against navy, in place of the small icon+word pair */
.footer-logo-lockup { height: 46px; width: auto; margin-bottom: var(--space-4); }

.nav-links {
  display: none;
  align-items: center;
  /* Fluid so the full nav (logo + 5 links + CTA) still fits at the 1024px
     breakpoint. At a fixed 2rem the links collided with the logo and CTA
     between 900-1023px and sat 6px apart at 1024px. */
  gap: clamp(1.25rem, 1.6vw, 2rem);
  /* Body face, not the mono utility face. Mono is reserved for genuine data
     (prices, figures, captions, eyebrows); using it for nav labels made the
     bar read technical and mismatched against the serif wordmark beside it
     and the serif headlines below. */
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.nav-links a { padding: var(--space-2) 0; border-bottom: 2px solid transparent; transition: border-color 200ms var(--ease-standard); }
.nav-links a:hover, .nav-links a:active, .nav-links a[aria-current="page"] { border-color: var(--color-gold-500); }
@media (min-width: 1024px) { .nav-links { display: flex; } }

.nav-cta { display: none; }
@media (min-width: 1024px) { .nav-cta { display: inline-flex; } }

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}
@media (min-width: 1024px) { .nav-toggle { display: none; } }

/* Smooth expand/collapse via grid-template-rows (0fr -> 1fr) rather than animating
   height directly, and rather than a hard display:none/flex snap. */
.mobile-menu {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 280ms var(--ease-standard);
}
.mobile-menu.is-open { grid-template-rows: 1fr; }
.mobile-menu-inner {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4) 0 var(--space-6);
  border-top: 1px solid var(--color-border);
  opacity: 0;
  transition: opacity 200ms var(--ease-standard);
}
.mobile-menu.is-open .mobile-menu-inner { opacity: 1; }
.mobile-menu a {
  padding: var(--space-3) var(--space-2);
  min-height: 44px;
  display: flex;
  align-items: center;
  /* matches the desktop nav labels */
  font-family: var(--font-body);
  font-weight: 600;
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-radius: var(--radius-sm);
  transition: background-color 180ms var(--ease-standard);
}
.mobile-menu a:hover, .mobile-menu a:active { background: var(--color-surface-alt); }
@media (min-width: 1024px) { .mobile-menu { display: none !important; } }

/* =========================================================
   Footer
   ========================================================= */
.site-footer { background: var(--color-navy-900); color: var(--color-text-on-dark); padding-block: var(--space-8) var(--space-6); }
.footer-grid { display: grid; gap: var(--space-7); grid-template-columns: 1fr; margin-bottom: var(--space-7); }
@media (min-width: 720px) { .footer-grid { grid-template-columns: 1.4fr 1fr 1fr; } }
.footer-heading { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-gold-300); margin-bottom: var(--space-4); }
.footer-links { display: flex; flex-direction: column; gap: var(--space-3); }
.footer-links a { min-height: 44px; display: flex; align-items: center; color: var(--color-text-on-dark-muted); transition: color 180ms var(--ease-standard); }
.footer-links a:hover, .footer-links a:active { color: var(--color-text-on-dark); }
.footer-social { display: flex; gap: var(--space-4); }
.footer-social a { width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--color-border-on-dark); border-radius: var(--radius-md); transition: border-color 180ms var(--ease-standard); }
.footer-social a:hover, .footer-social a:active { border-color: var(--color-gold-500); }
.footer-bottom { border-top: 1px solid var(--color-border-on-dark); padding-top: var(--space-5); display: flex; flex-wrap: wrap; gap: var(--space-3); justify-content: space-between; font-family: var(--font-mono); font-size: var(--step--1); color: var(--color-text-on-dark-muted); }

/* =========================================================
   Cards
   ========================================================= */
.card {
  background: var(--color-navy-700);
  color: var(--color-text-on-dark);
  border: 1px solid var(--color-border-on-dark);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  border-top: 3px solid transparent;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.22), 0 1px 3px rgba(0, 0, 0, 0.16);
}
.card-dark {
  background: var(--color-navy-700);
  border: 1px solid var(--color-border-on-dark);
  color: var(--color-text-on-dark);
}
/* Frosted glass variant — for a card floating over a photo background */
.card-glass {
  background: rgba(255, 255, 255, 0.90);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-color: rgba(255, 255, 255, 0.5);
}
.card-hover { transition: transform 200ms var(--ease-standard), box-shadow 200ms var(--ease-standard), border-color 200ms var(--ease-standard); }
/* :active mirrors :hover throughout so touch users get the same colour/glow
   feedback — a card's primary visual response shouldn't be desktop-only. The
   press-scale in the motion block below overrides the lift on tap, which is the
   intended tactile difference between pointing and pressing. */
.card-hover:hover, .card-hover:active { transform: translateY(-4px); box-shadow: var(--shadow-md); border-top-color: var(--color-gold-500); }
/* Cycle the hover accent color across a grid so color does more of the work than one repeated gold.
   Kept deliberately: teal/terracotta are demoted from section-level backgrounds (navy-dominant pass),
   but this is a small, transient, contained hover accent, not a background — verified it still reads
   clearly against the new navy-700 .card default, and it's useful counter-balance now that gold
   appears more often elsewhere (eyebrows, icon badges, CTAs) — see design-audit/ notes. */
.stagger > .card-hover:nth-child(3n+2):hover, .stagger > .card-hover:nth-child(3n+2):active { border-top-color: var(--color-teal-500); box-shadow: var(--shadow-glow-teal); }
.stagger > .card-hover:nth-child(3n+3):hover, .stagger > .card-hover:nth-child(3n+3):active { border-top-color: var(--color-terracotta-500); box-shadow: var(--shadow-glow-terracotta); }
.stagger > .card-hover:nth-child(3n+1):hover, .stagger > .card-hover:nth-child(3n+1):active { box-shadow: var(--shadow-glow-gold); }

.step-num {
  font-family: var(--font-mono);
  font-size: var(--step-1);
  color: var(--color-gold-300);
  font-weight: 600;
}

.check-list { display: flex; flex-direction: column; gap: var(--space-3); }
.check-list li { display: flex; gap: var(--space-3); align-items: flex-start; }
.check-icon { flex-shrink: 0; margin-top: 1px; }
/* Tick draws in via stroke-dashoffset when the list scrolls into view — dasharray/
   dashoffset are set precisely by JS (initCheckDraw in main.js); with no JS or under
   reduced-motion, a plain stroked path with no dasharray just renders solid, so the
   checkmark is never stuck invisible. */
@media (prefers-reduced-motion: no-preference) {
  .check-tick { transition: stroke-dashoffset 350ms var(--ease-standard); }
}

/* =========================================================
   Calculator (signature element)
   ========================================================= */
.calculator {
  background: var(--color-navy-900);
  color: var(--color-text-on-dark);
  border-radius: var(--radius-lg);
  padding: var(--space-7) var(--space-6);
  border: 1px solid var(--color-border-on-dark);
  position: relative;
  overflow: hidden;
}
.calculator::before {
  content: "";
  position: absolute;
  inset: -40% -20% auto auto;
  width: 340px;
  height: 340px;
  background: radial-gradient(circle, rgba(184, 147, 63, 0.25), transparent 70%);
  pointer-events: none;
}
.calc-grid { display: grid; gap: var(--space-7); grid-template-columns: 1fr; position: relative; z-index: 1; }
@media (min-width: 860px) { .calc-grid { grid-template-columns: 1fr 1fr; align-items: center; } }

.calc-field label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-gold-300);
  margin-bottom: var(--space-2);
}
.calc-slider-row { display: flex; align-items: center; gap: var(--space-4); }
.calc-slider-row output {
  font-family: var(--font-mono);
  font-size: var(--step-2);
  min-width: 3.5ch;
  text-align: right;
}
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 44px;
  background: transparent;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 3px;
  background: var(--color-border-on-dark);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  margin-top: -9px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--color-gold-500);
  border: 3px solid var(--color-navy-900);
  box-shadow: 0 0 0 1px var(--color-gold-500);
}
input[type="range"]::-moz-range-track { height: 6px; border-radius: 3px; background: var(--color-border-on-dark); }
input[type="range"]::-moz-range-thumb {
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--color-gold-500); border: 3px solid var(--color-navy-900);
}

.calc-result { text-align: left; }
.calc-result-label { font-family: var(--font-mono); font-size: var(--step--1); text-transform: uppercase; letter-spacing: 0.06em; color: var(--color-text-on-dark-muted); }
.calc-result-value {
  font-family: var(--font-display);
  font-size: clamp(3rem, 6vw, 5rem);
  color: var(--color-gold-300);
  line-height: 1;
  margin-block: var(--space-2);
}
.calc-result-sub { color: var(--color-text-on-dark-muted); max-width: 42ch; }

/* =========================================================
   Forms
   ========================================================= */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field label { font-weight: 600; font-size: var(--step--1); color: var(--color-text-on-dark); }
.field .hint { font-size: var(--step--1); color: var(--color-text-on-dark-muted); }
.field input, .field textarea {
  min-height: 48px;
  padding: 0.75rem 1rem;
  border-radius: var(--radius-md);
  border: 1.5px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: 16px;
  transition: border-color 180ms var(--ease-standard), box-shadow 180ms var(--ease-standard);
}
.field textarea { min-height: 120px; resize: vertical; }
.field input:focus, .field textarea:focus { border-color: var(--color-gold-500); box-shadow: 0 0 0 3px rgba(184, 147, 63, 0.18); }
.field.has-error input, .field.has-error textarea { border-color: var(--color-error); }
/* Grid-rows rather than display:none/block, so the error eases in instead of snapping. */
.field-error {
  display: grid;
  grid-template-rows: 0fr;
  font-size: var(--step--1);
  color: var(--color-error);
  font-weight: 500;
  transition: grid-template-rows 220ms var(--ease-standard);
}
.field-error > span { overflow: hidden; min-height: 0; display: block; }
.field.has-error .field-error { grid-template-rows: 1fr; }

.form-status {
  display: grid;
  grid-template-rows: 0fr;
  border-radius: var(--radius-md);
  font-weight: 600;
  transition: grid-template-rows 280ms var(--ease-standard);
}
.form-status > span { overflow: hidden; min-height: 0; display: block; padding: var(--space-4) var(--space-5); }
.form-status.is-visible { grid-template-rows: 1fr; }
.form-status.is-success { background: var(--color-success-bg); color: var(--color-success); }
.form-status.is-error { background: var(--color-error-bg); color: var(--color-error); }

/* =========================================================
   Utility
   ========================================================= */
.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
/* Self-contained light chip — its own background stays light regardless of the
   surrounding page canvas, so its text color is set explicitly, not inherited. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: 999px;
  background: var(--color-surface-alt);
  color: var(--color-navy-900);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  border: 1px solid var(--color-border);
}
.divider { height: 1px; background: var(--color-border-on-dark); border: 0; }
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--color-gold-500);
  color: var(--color-navy-900);
  padding: var(--space-3) var(--space-5);
  z-index: 100;
  border-radius: 0 0 var(--radius-sm) 0;
}
.skip-link:focus { left: 0; }

/* =========================================================
   Device frame — real, live proof embeds (Mylene's actual site/scheduler)
   Non-interactive preview: fixed virtual window scaled to fit the card.
   ========================================================= */
.device-frame {
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 1px solid var(--color-border-on-dark);
  box-shadow: 0 20px 48px rgba(11, 31, 58, 0.28);
  background: var(--color-navy-900);
  transition: transform 220ms var(--ease-standard), box-shadow 220ms var(--ease-standard);
}
/* Invites a closer look — it's a real, interactive-looking preview, worth noticing. */
.device-frame:hover { transform: translateY(-3px); box-shadow: 0 28px 60px rgba(11, 31, 58, 0.36); }
.device-frame-bar {
  height: 40px;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 0 14px;
  background: linear-gradient(180deg, var(--color-navy-700), var(--color-navy-900));
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.device-frame-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; background: rgba(255, 255, 255, 0.22); }
.device-frame-url {
  margin-left: 10px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--color-text-on-dark-muted);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 4px;
  padding: 4px 10px;
  flex: 1;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.device-frame-viewport {
  position: relative;
  width: 100%;
  aspect-ratio: 8 / 5;
  overflow: hidden;
  background: var(--frame-bg, var(--color-surface-alt));
}
.device-frame-viewport iframe {
  position: absolute;
  top: 0;
  left: 0;
  border: 0;
  transform-origin: top left;
  pointer-events: none;
}
/* Scheduler embed loads real, live client data — blur it. This shows a real working tool without exposing anyone's name or number. */
.device-frame-viewport.is-sensitive iframe {
  filter: blur(7px);
}
.device-frame-viewport.is-sensitive::before {
  content: "Client details blurred for privacy";
  position: absolute;
  bottom: var(--space-3);
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--color-navy-900);
  background: rgba(255, 255, 255, 0.85);
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
}
/* Subtle brand duotone tying the real screenshot to the palette, without hiding what it actually shows */
.device-frame-viewport::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(11, 31, 58, 0.16), rgba(184, 147, 63, 0.08) 60%, transparent 100%);
  mix-blend-mode: multiply;
  pointer-events: none;
}
.device-frame-caption {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  /* Was --color-text-muted (#4B5468), a light-surface slate left over from before
     the navy rebrand — effectively illegible on the navy sections these captions
     actually sit on. */
  color: var(--color-text-on-dark-muted);
  margin-top: var(--space-3);
}

/* Clean small-caps kicker label — replaces the old mono/leader-line "annotation" treatment */
.annotation {
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-gold-300);
  display: inline-flex;
  align-items: center;
}
.on-teal .annotation, .photo-bg .annotation { color: var(--color-text-on-dark); }

/* Hero stat highlight — a single large, elegant number, not a technical readout.
   Navy glass now, not white, so it reads as part of the navy-dominant hero rather
   than a bright cutout floating on the photo. */
.stat-highlight {
  text-align: center;
  /* Sits over the brightest part of the hero video. Verified end-to-end against the
     video's gold peak: at this opacity its text still measures 6.5:1+, so the panel
     stays glassy rather than being darkened unnecessarily. */
  background: rgba(20, 41, 74, 0.74);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-lg);
  padding: var(--space-8) var(--space-6);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
}
.stat-highlight .annotation { margin-bottom: var(--space-3); justify-content: center; color: var(--color-gold-300) !important; }
.stat-highlight-value {
  font-size: clamp(3.4rem, 7vw, 5rem);
  font-weight: 600;
  line-height: 1;
  color: var(--color-text-on-dark);
  margin: 0 0 var(--space-3);
}
.stat-highlight-unit {
  font-family: var(--font-body);
  font-size: 1.1rem;
  font-weight: 500;
  color: var(--color-text-on-dark-muted);
  margin-left: 0.4rem;
}
.stat-highlight-caption {
  font-size: var(--step-0);
  color: var(--color-text-on-dark-muted);
  max-width: 32ch;
  margin: 0 auto var(--space-5);
}
/* A real anchor to the calculator, so it works with keyboard, screen readers
   and open-in-new-tab. Underlined at rest so it reads as clickable before
   any hover; hover/focus lifts the colour and thickens the rule. */
.stat-highlight-cta {
  display: block;
  font-size: var(--step--1);
  color: var(--color-gold-300);
  font-weight: 600;
  margin: 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border-on-dark);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: rgba(212, 181, 113, 0.5);
  transition: color 180ms var(--ease-standard), text-decoration-color 180ms var(--ease-standard);
}
.stat-highlight-cta:hover,
.stat-highlight-cta:focus-visible {
  color: #EBD9AE;
  text-decoration-thickness: 2px;
  text-decoration-color: currentColor;
}

/* Accent band — a bold color-block chapter break between major sections, used sparingly */
.accent-band {
  height: 5px;
  background: linear-gradient(90deg, var(--color-navy-900), var(--color-gold-500) 50%, var(--color-navy-900));
}

/* FAQ accordion — custom chevron icon, consistent with the site icon system */
details.card summary {
  list-style: none;
  position: relative;
  padding-right: var(--space-7);
}
details.card summary::-webkit-details-marker { display: none; }
details.card summary::after {
  content: "";
  position: absolute;
  right: 2px;
  top: 50%;
  width: 14px;
  height: 14px;
  margin-top: -7px;
  background-color: var(--color-gold-700);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center/contain no-repeat;
  transition: transform 200ms var(--ease-standard);
}
details.card[open] summary::after { transform: rotate(180deg); }

/* Smooth open via grid-template-rows (0fr -> 1fr), not raw height. Native <details>
   still fully controls open/closed with zero JS — this only smooths the reveal;
   close remains a native instant collapse, which is a safe, functional degradation. */
details.card .faq-panel { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 280ms var(--ease-standard); }
details.card[open] .faq-panel { grid-template-rows: 1fr; }
details.card .faq-panel > p { overflow: hidden; min-height: 0; }

/* Custom icon system — one consistent stroke weight, used for services/process/FAQ */
.icon-badge {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--color-navy-900);
  flex-shrink: 0;
}
.icon-badge.is-gold { background: rgba(184, 147, 63, 0.22); color: var(--color-gold-300); }
.icon-badge.is-teal { background: rgba(27, 110, 107, 0.22); color: var(--color-teal-300); }
.icon-badge.is-terracotta { background: rgba(196, 98, 59, 0.22); color: var(--color-terracotta-300); }

/* Oversized numeral treatment — typography carrying visual weight in place of imagery */
.hero-figure {
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--color-gold-500);
  opacity: 0.15;
  line-height: 0.8;
  user-select: none;
  pointer-events: none;
}

/* =========================================================
   Hero signature animation — the mark drawing itself in, then the
   wordmark and headline resolving. Structural rules (layout/type) apply
   always; the hidden "from" states only apply when motion is allowed,
   so a reduced-motion user (or a browser that never runs the JS) sees
   the finished, fully-visible hero immediately — see main.js initHero().
   ========================================================= */
.hero-brand-intro { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-1); }
.hero-mark { flex-shrink: 0; overflow: visible; }
.hero-wordmark { font-size: 1.55rem; font-weight: 600; color: var(--color-text-on-dark); letter-spacing: -0.01em; }

/* =========================================================
   Hero video background — the signature moment. The video is
   decorative (aria-hidden); all meaning lives in the text above it.
   ========================================================= */
.hero { position: relative; isolation: isolate; overflow: hidden; }

/* =========================================================
   Hero vertical fit — the essential content (mark, eyebrow, both headline
   lines, primary CTA) must clear the fold on real laptop resolutions. At the
   previous sizing the CTA sat at y=835 on a 768px-tall viewport: entirely
   below the fold on 1366x768, and clipped on 1536x864.
   ========================================================= */
/* Contact page: the form is the point of the page, so the heading above it stays
   compact rather than using the full display scale and pushing the form down. */
.contact-top h1 { font-size: clamp(1.9rem, 1.5rem + 1.5vw, 2.6rem); }
.contact-top { padding-block: var(--space-6); }
/* The floating button overlapped the enquiry fields here, and it is redundant on
   this page — a full-width "Message on WhatsApp" button sits beside the form. */
.page-contact .whatsapp-float { display: none; }

.hero.section-tight { padding-block: var(--space-6); }
.hero .hero-anim { gap: var(--space-4); }
/* Smaller than the global --step-5 so four headline lines fit above the fold. */
.hero h1 { font-size: clamp(2.4rem, 1.9rem + 2.5vw, 4rem); }

/* Short viewports (most laptops) get a further tightening — height, not just
   width, is what actually decides whether the CTA is reachable. */
@media (max-height: 900px) and (min-width: 900px) {
  .hero.section-tight { padding-block: var(--space-5); }
  .hero h1 { font-size: clamp(2.2rem, 1.6rem + 2.1vw, 3.3rem); }
  .hero .hero-anim { gap: var(--space-3); }
  .hero .lede { font-size: var(--step-0); }
  .hero .stat-highlight { padding: var(--space-6) var(--space-5); }
}
/* Short viewports at ANY width — deliberately not gated on min-width, because
   the shortest real viewports are small phones (e.g. iPhone SE at 375x667),
   where the CTA was being clipped by a few pixels. */
@media (max-height: 700px) {
  .hero.section-tight { padding-block: var(--space-5); }
  .hero .hero-anim { gap: var(--space-3); }
}
.hero-bg-video,
.hero-bg-scrim {
  position: absolute;
  pointer-events: none;
}
.hero-bg-scrim { inset: 0; width: 100%; height: 100%; }

/* Absolutely positioned = out of flow, so the video cannot shift layout while
   it loads. The hero's height is driven by its text content, as before.

   Sizing is fully fluid (%, never fixed px), but `object-fit: cover` alone is
   not enough: the source is a fixed 16:9 1280x720, while the hero box ranges
   from ~0.24 aspect at 320px wide to ~2.7 at 2560px. Cover resolves that by
   scaling to whichever axis is larger, which at the extremes either magnifies
   the source ~2x (soft, blurry, gold glow becomes an oversized mass) or crops
   away ~85% of the frame. Both ends are bounded below rather than left to
   scale freely. The video's own canvas is the same navy as the page and its
   edges are feathered, so wherever it stops is invisible. */
.hero-bg-video {
  z-index: 0;
  top: 0;
  height: 46%;              /* narrow screens: a band, not a full-height crop */
  bottom: auto;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  object-fit: cover;
  background: var(--color-navy-900);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 62%, transparent 100%);
}
@media (min-width: 900px) {
  .hero-bg-video {
    height: 100%;
    bottom: 0;
    /* Caps magnification of the 1280px-wide source at ~1.25x. Beyond this the
       video is bounded and centred instead of upscaling toward 2x. */
    max-width: 1600px;
    -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 7%, #000 93%, transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0%, #000 7%, #000 93%, transparent 100%);
  }
}
.hero > .wrap { position: relative; z-index: 2; }

/* Data-saver / 2g fallback: keep MOTION but spend no bandwidth — an animated
   gradient built from the same three brand colours replaces the video entirely.
   Only `transform` animates, so it stays on the compositor and is cheap on the
   low-end devices most likely to hit this path. */
.hero-gradient .hero-bg-video { display: none; }
.hero-gradient::before {
  content: "";
  position: absolute;
  inset: -45%;
  z-index: 0;
  background:
    radial-gradient(circle at 30% 42%, rgba(0, 74, 173, 0.55), transparent 58%),
    radial-gradient(circle at 66% 58%, rgba(184, 147, 63, 0.34), transparent 58%);
  animation: ambientDrift 26s linear infinite;
  will-change: transform;
}

/* Navy scrim tuned against the video's BRIGHTEST sampled frame (the gold-glow
   peak near t≈5s, rgb(217,169,95) on the text side — not frame 0, which is one
   of the darkest). Narrow screens collapse the hero to one column, so text spans
   the full width and the scrim stays near-uniform there. */
.hero-bg-scrim {
  z-index: 1;
  /* Narrow screens: text spans the full width and the video's cover-crop pushes the
     gold ribbon directly behind it, so this stays heavier than the desktop scrim.
     Measured: at 0.66 the 768px lede and 375px WhatsApp link both dropped below 4.5,
     so 0.76/0.72 are kept wherever real text sits. Only the top ~10% is lightened —
     the sole content there is the large bold wordmark, which clears 3:1 easily — so
     the video is actually visible in the band rather than smothered flat. */
  background: linear-gradient(180deg,
    rgba(11, 31, 58, 0.44) 0%,
    rgba(11, 31, 58, 0.52) 9%,
    rgba(11, 31, 58, 0.76) 17%,
    rgba(11, 31, 58, 0.72) 100%);
}
@media (min-width: 900px) {
  /* Two columns: text occupies roughly the left half, so the scrim stays heavier
     there and thins right out on the right — letting the gold glow actually be seen
     rather than smothering the whole frame. The stat card carries its own
     navy-glass backing, so the light right side is still comfortably legible.
     Raised from 0.18 to 0.30: with the source no longer upscaling toward 2x, the
     glow reads as a soft accent instead of an oversized blurry mass. */
  .hero-bg-scrim {
    background:
      linear-gradient(90deg, rgba(11, 31, 58, 0.66) 0%, rgba(11, 31, 58, 0.60) 48%, rgba(11, 31, 58, 0.30) 100%);
  }
}

/* Headline lines are separate blocks so each can arrive on its own beat. */
.hero h1 .hero-line-1,
.hero h1 .hero-line-2 { display: block; }

/* Hero intro — a staggered sequence where each element has its own motion,
   not one uniform settle: the mark scales up, the eyebrow slides, the two
   headline lines rise (the italic resolving out of a soft blur), supporting
   copy and CTAs follow, and the stat card scales in like the calculator does.
   Pure CSS: runs unconditionally on load, no JS and no scroll trigger, so it
   cannot get stuck part-way. Reduced-motion skips this block entirely and the
   elements render at their natural, fully-visible state. */
@media (prefers-reduced-motion: no-preference) {
  .hero-brand-intro { opacity: 0; animation: heroMarkIn 520ms var(--ease-standard) 60ms forwards; }
  .hero-anim > .eyebrow { opacity: 0; animation: heroEyebrowIn 440ms var(--ease-standard) 160ms forwards; }
  .hero h1 .hero-line-1 { opacity: 0; animation: heroLineIn 560ms var(--ease-standard) 260ms forwards; }
  .hero h1 .hero-line-2 { opacity: 0; animation: heroLineBlurIn 620ms var(--ease-standard) 370ms forwards; }
  .hero-anim > .lede { opacity: 0; animation: heroTextIn 480ms var(--ease-standard) 480ms forwards; }
  .hero-anim > .stack { opacity: 0; animation: heroCtaIn 460ms var(--ease-standard) 580ms forwards; }
  .hero-anim > .annotation { opacity: 0; animation: heroTextIn 440ms var(--ease-standard) 660ms forwards; }
  .hero .stat-highlight { opacity: 0; animation: heroCardIn 560ms var(--ease-standard) 420ms forwards; }
}

/* micro-scale-fade */
@keyframes heroMarkIn {
  from { opacity: 0; transform: scale(0.94); }
  to { opacity: 1; transform: none; }
}
/* small slide */
@keyframes heroEyebrowIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: none; }
}
/* pronounced rise */
@keyframes heroLineIn {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: none; }
}
/* soft-blur-in — gives the italic line a different character from line 1 */
@keyframes heroLineBlurIn {
  from { opacity: 0; transform: translateY(24px); filter: blur(7px); }
  to { opacity: 1; transform: none; filter: blur(0); }
}
@keyframes heroTextIn {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: none; }
}
@keyframes heroCtaIn {
  from { opacity: 0; transform: translateY(10px) scale(0.98); }
  to { opacity: 1; transform: none; }
}
/* matches the calculator's slightly-more-considered entrance */
@keyframes heroCardIn {
  from { opacity: 0; transform: translateY(16px) scale(0.98); }
  to { opacity: 1; transform: none; }
}

/* Ambient twinkle on the gold star only — begins after the entrance settles.
   Opacity-only on purpose: these SVG paths carry a `transform` attribute for
   positioning, and a CSS transform would override it and displace the star.
   The 0%/100% keyframes are the settled state, so the global reduced-motion
   rule (duration ~0, one iteration) lands it fully visible and static. */
@media (prefers-reduced-motion: no-preference) {
  .hero-star-gold {
    animation: heroStarTwinkle 4.6s ease-in-out 1400ms infinite;
  }
}
@keyframes heroStarTwinkle {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* =========================================================
   Ambient echo — a quiet, CSS-only nod to the hero video using the same three
   brand colours. Deliberately reserved for ONE section per page (the final CTA)
   so it stays a thread rather than a repeated pattern. No image, no video: just
   a slowly rotating gradient on a pseudo-element, which stays on the compositor
   because only `transform` animates. Under reduced-motion the global rule
   freezes it, leaving a static gradient that still looks intentional.
   ========================================================= */
.ambient-echo { position: relative; isolation: isolate; overflow: hidden; }
.ambient-echo::before {
  content: "";
  position: absolute;
  /* oversized + centred so the rotation never exposes an empty corner */
  inset: -45%;
  z-index: -1;
  background:
    radial-gradient(circle at 32% 38%, rgba(0, 74, 173, 0.34), transparent 55%),
    radial-gradient(circle at 68% 62%, rgba(184, 147, 63, 0.16), transparent 55%);
  animation: ambientDrift 28s linear infinite;
  will-change: transform;
}
@keyframes ambientDrift {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: no-preference) {
  .reveal { opacity: 0; transform: translateY(16px); transition: opacity 500ms var(--ease-standard), transform 500ms var(--ease-standard); }
  .reveal.is-visible { opacity: 1; transform: translateY(0); }

  /* Stagger — a subtle sequential entrance for process-step grids, at most one moment per view */
  .stagger > *:nth-child(1) { transition-delay: 0ms; }
  .stagger > *:nth-child(2) { transition-delay: 130ms; }
  .stagger > *:nth-child(3) { transition-delay: 260ms; }
  .stagger > *:nth-child(4) { transition-delay: 390ms; }
  .stagger > *:nth-child(5) { transition-delay: 520ms; }
  .stagger > *:nth-child(6) { transition-delay: 650ms; }

  /* Calculator — a slightly more considered entrance, since it's the one deliberate signature moment */
  .calculator.reveal { transform: translateY(24px) scale(0.98); transition: opacity 600ms var(--ease-standard), transform 600ms var(--ease-standard); }
  .calculator.reveal.is-visible { transform: translateY(0) scale(1); }

  .btn:active, .card-hover:active { transform: scale(0.98); }
}
