/* =========================================================
   Freezon — refinements on top of Tailwind CDN
   Keep this file small. Most styling is in index.html / Tailwind.
   ========================================================= */

/* ---------------------------------------------------------
   Fraunces — local font reserved for titles only.
   Embeds the OTF straight from ../fonts/ so we don't rely
   on Google Fonts for the typography signature.
   --------------------------------------------------------- */
@font-face {
  font-family: 'Fraunces';
  font-style: italic;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/Fraunces72pt-SemiBoldItalic.otf') format('opentype');
}

/* ---------------------------------------------------------
   Satoshi — local Bold weight, used as the body font.
   Only Bold is shipped for now, so the page renders with
   Satoshi for every <body>-derived weight request.
   --------------------------------------------------------- */
@font-face {
  font-family: 'Satoshi';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/Satoshi-Bold.otf') format('opentype');
}

:root {
  --accent: #3B82F6;
  --accent-hover: #60A5FA;
  --ink-900: #FFFFFF;
  --ink-700: rgba(255,255,255,0.92);
  --ink-600: rgba(255,255,255,0.82);
  --ink-400: rgba(255,255,255,0.55);
  --canvas: rgba(10,10,31,0.65);
  --canvas-alt: rgba(255,255,255,0.06);
  --canvas-line: rgba(255,255,255,0.12);
  --aurora-base: #0A0A1F;
}

html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

/* ---------------------------------------------------------
   Title typography — Fraunces SemiBold Italic exclusive.
   Spans with .font-script (Caveat) keep their hand-drawn
   accent look because the utility class has higher
   specificity than this bare element selector.
   font-style: italic + font-weight: 600 are required here
   so the browser's font-matching algorithm actually picks
   the Fraunces @font-face declared above (it only exists
   for the oblique Semibold variant, and without an
   explicit request the element defaults to normal/400
   which would skip the face entirely).
   --------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 {
  font-family: 'Fraunces', Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-weight: 600;
}

/* =========================================================
   Aurora / mesh gradient background
   3 large radial blobs, blurred, animated with transform only.
   Applied globally via .aurora-bg on <body> or any section.
   ========================================================= */
.aurora-bg {
  position: relative;
  background-color: var(--aurora-base);
  color: var(--ink-700);
  isolation: isolate;
  overflow-x: hidden;
}
.aurora-bg::before,
.aurora-bg::after {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
}
.aurora-bg::before {
  /* base dark layer */
  background: var(--aurora-base);
}
.aurora-bg::after {
  /* subtle noise/texture to reduce banding on the gradient */
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E");
  opacity: 0.35;
}
.aurora-blob {
  position: fixed;
  border-radius: 50%;
  filter: blur(90px);
  opacity: 0.85;
  will-change: transform;
  pointer-events: none;
  z-index: -1;
}
.aurora-blob--mauve {
  width: 70vw;
  height: 70vw;
  min-width: 520px;
  min-height: 520px;
  max-width: 900px;
  max-height: 900px;
  top: -18%;
  left: -12%;
  background: radial-gradient(circle at 30% 30%, rgba(162,28,175,0.95), rgba(107,33,168,0.75) 55%, transparent 75%);
  animation: aurora-drift-1 24s ease-in-out infinite alternate;
}
.aurora-blob--indigo {
  width: 65vw;
  height: 65vw;
  min-width: 480px;
  min-height: 480px;
  max-width: 820px;
  max-height: 820px;
  bottom: -22%;
  right: -14%;
  background: radial-gradient(circle at 60% 40%, rgba(36,64,255,0.92), rgba(11,26,94,0.78) 65%, transparent 78%);
  animation: aurora-drift-2 28s ease-in-out infinite alternate;
}
.aurora-blob--violet {
  width: 50vw;
  height: 50vw;
  min-width: 360px;
  min-height: 360px;
  max-width: 640px;
  max-height: 640px;
  top: 32%;
  left: 34%;
  background: radial-gradient(circle, rgba(91,33,182,0.88), rgba(30,27,75,0.72) 70%, transparent 82%);
  animation: aurora-drift-3 19s ease-in-out infinite alternate;
}
@keyframes aurora-drift-1 { from { transform: translate(0,0) scale(1); } to { transform: translate(60px,90px) scale(1.12); } }
@keyframes aurora-drift-2 { from { transform: translate(0,0) scale(1); } to { transform: translate(-70px,-60px) scale(1.08); } }
@keyframes aurora-drift-3 { from { transform: translate(-25px,-15px) scale(0.92); } to { transform: translate(45px,30px) scale(1.06); } }

/* Reduce blur on small screens to keep scroll smooth on weaker GPUs. */
@media (max-width: 640px) {
  .aurora-blob { filter: blur(50px); opacity: 0.75; }
}

@media (prefers-reduced-motion: reduce) {
  .aurora-blob { animation: none; opacity: 0.2; }
}

/* Smooth anchor offset under the sticky header */
section[id] { scroll-margin-top: 88px; }

/* ---------- Selection (dark background) ---------- */
::selection { background: rgba(96, 165, 250, 0.35); color: #ffffff; }

/* ---------- Focus rings across the page ---------- */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 6px;
}
button:focus-visible,
a:focus-visible {
  outline-offset: 4px;
}

/* Hide default focus on mouse, keep on keyboard */
button:focus:not(:focus-visible),
a:focus:not(:focus-visible) {
  outline: none;
}

/* ---------- Native details/summary — nicer chevron rotation ---------- */
details > summary { list-style: none; }
details > summary::-webkit-details-marker { display: none; }

/* ---------- Mobile menu animation (full-screen slide-down) ---------- */
#mobile-menu {
  /* Default closed state: invisible, slightly above viewport. */
  opacity: 0;
  transform: translateY(-12px) scale(0.985);
  pointer-events: none;
  transition: opacity 200ms ease, transform 220ms cubic-bezier(.2,.7,.2,1);
}
#mobile-menu[data-open="true"] {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* When the menu is open, prevent the page underneath from scrolling. */
body.menu-open { overflow: hidden; }

/* Toggle burger icon glyphs (close vs open) */
#burger .burger-close,
#burger[aria-expanded="true"] .burger-open { display: none; }
#burger[aria-expanded="true"] .burger-close { display: block; }

/* ---------- Header: transparent at top, opaque on scroll ---------- */
#site-header.is-scrolled {
  background-color: rgba(10, 10, 31, 0.92);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom-color: rgba(255, 255, 255, 0.08);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06), 0 4px 16px -8px rgba(0, 0, 0, 0.25);
}

/* Header logo: force white on dark header (original logo is dark). */
#site-header img[src="logo.svg"] {
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.25));
}

/* Footer logo: subtle light backdrop so any logo color reads on dark footer. */
.footer-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: rgba(255, 255, 255, 0.08);
  padding: 8px 12px;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.1);
  transition: transform 0.25s ease, background 0.25s ease, box-shadow 0.25s ease;
}
.footer-logo:hover,
.footer-logo:focus-visible {
  transform: translateY(-3px) scale(1.03);
  background: rgba(255, 255, 255, 0.14);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
.footer-logo img {
  object-fit: contain;
  display: block;
  max-height: 80px;
  width: auto;
}

/* ---------- Modal animation (Calendly surface) ---------- */
#booking-modal {
  /* Hidden by Tailwind `.hidden` until JS removes it. We add a fade-out
     for graceful closure. */
  transition: opacity 220ms ease;
}
#booking-modal.is-closing { opacity: 0; }
#booking-modal:not(.hidden) .bg-white {
  animation: slideUp 260ms cubic-bezier(.2,.7,.2,1) both;
}
@keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } }
@keyframes slideUp {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}

/* Lock body scroll when modal open */
body.modal-open { overflow: hidden; }

/* Calendly iframe visibility.
   The iframe in the modal ships with `opacity-0` (Tailwind) and fades in
   once JS adds `.is-loaded` after the iframe `load` event.
   THIS RULE MUST LIVE OUTSIDE any breakpoint-scoped media query — the
   previous build had it inside the mobile `max-width: 1023.98px` block,
   which kept the iframe permanently invisible on desktop. */
#calendly-iframe.is-loaded { opacity: 1 !important; }

/* On mobile, the booking modal becomes a true full-screen sheet. */
@media (max-width: 1023.98px) {
  #booking-modal .bg-white {
    border-radius: 0 !important;
    max-width: 100% !important;
    max-height: 100dvh !important;
    height: 100dvh !important;
  }
  #booking-modal .flex.items-center.sm\:items-start {
    align-items: stretch !important;
    padding: 0 !important;
  }
}

/* ---------- Soft pulse for primary CTA (mobile sticky bar) ---------- */
@keyframes softPulse {
  0%, 100% { box-shadow: 0 4px 14px -2px rgba(37, 99, 235, 0.35), 0 0 0 0 rgba(37, 99, 235, 0.35); }
  50%      { box-shadow: 0 4px 14px -2px rgba(37, 99, 235, 0.35), 0 0 0 8px rgba(37, 99, 235, 0); }
}
.cta-glow { animation: softPulse 2.6s ease-in-out infinite; }

/* ---------- Press feedback (cards, article) ---------- */
/* Only apply on cards that contain a tappable child (button/a) so we never
   imply "press me" on informational cards (e.g. "Pourquoi Freezon"). */
.sector-card,
article:has(> button),
article:has(> a[data-press]) {
  transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
}
.sector-card:active,
article:has(> button):active,
article:has(> a[data-press]):active { transform: scale(0.992); }

/* ---------- Scrollbar (subtle, optional) ---------- */
@media (min-width: 768px) {
  ::-webkit-scrollbar { width: 10px; height: 10px; }
  ::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.18);
    border-radius: 8px;
    border: 2px solid #0A0A1F;
  }
  ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.28); }
}

/* ---------- Print friendliness ---------- */
@media print {
  #site-header, #booking-modal, header button[data-open-booking], footer { display: none !important; }
  html, body { overflow-x: hidden; }
body { color: black; background: white; }
}

/* ---------- Form input autofill ---------- */
input:-webkit-autofill,
textarea:-webkit-autofill {
  -webkit-box-shadow: 0 0 0 1000px #0A0A1F inset;
  -webkit-text-fill-color: #ffffff;
}

/* ---------- Card hover lift (subtle, performant) ---------- */
.card-lift {
  transition: transform 260ms cubic-bezier(.2,.7,.2,1), box-shadow 260ms cubic-bezier(.2,.7,.2,1);
}
.card-lift:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 28px -8px rgba(0,0,0,0.25), 0 4px 10px -4px rgba(0,0,0,0.15);
}

/* ---------- Hero mockup gentle float ---------- */
@keyframes hero-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-8px); }
}
.hero-mockup-float {
  animation: hero-float 6s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .hero-mockup-float { animation: none; }
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  html { scroll-behavior: auto; }
  .cta-glow { animation: none; }
  #mobile-menu { transition: none; }
  .card-lift { transition: none; }
}

/* ============================================================
   Pro polish layer
   Hero background, scroll progress, scroll-reveal, shimmer,
   nav-link animations, magnetic CTA. All behind feature-detect.
   ============================================================ */

/* ---------- Hero background (light variant of the dark final CTA) ----------
   Same VISUAL LANGUAGE as the dark CTA panel: a regular grid overlay +
   the FRZN sigil as a large, faded, slightly rotated watermark. Tuned for
   a white background so it reads as quiet structure, not foreground. */
.hero-section {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.hero-section > .max-w-content { position: relative; z-index: 1; }

.hero-grid-overlay {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(255, 255, 255, 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
  background-size: 56px 56px;
  -webkit-mask-image: radial-gradient(ellipse 70% 60% at center, #000 30%, transparent 85%);
          mask-image: radial-gradient(ellipse 70% 60% at center, #000 30%, transparent 85%);
  pointer-events: none;
  z-index: 0;
}

.sigil-hero-watermark {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 620px;
  height: 620px;
  transform: translate(-50%, -50%) rotate(-12deg);
  color: #ffffff;
  opacity: 0.04;
  pointer-events: none;
  z-index: 0;
}
.sigil-hero-watermark svg, .sigil-hero-watermark use { width: 100%; height: 100%; }
/* Override the symbol's chunky 2.4 stroke-width: at hero scale (~620–820px)
   the thick reads heavy; 1.4 keeps the lines fine, closer to the dark CTA
   panel's strokes. */
.sigil-hero-watermark path { stroke-width: 1.4; }
@media (min-width: 1024px) {
  .sigil-hero-watermark {
    width: 820px;
    height: 820px;
    left: auto;
    right: -160px;
    top: -120px;
    transform: rotate(-14deg);
    opacity: 0.05;
  }
}

/* Soft accent-colored radial glows to add depth without darkening. */
.hero-glow {
  position: absolute;
  pointer-events: none;
  z-index: 0;
  filter: blur(70px);
  /* border-radius intentionally omitted: filter:blur fuses the edges, so the
     radius has no visual effect. */
}

/* Trim the blur on small screens / weaker GPUs so phone scroll stays
   fluid during the reveal animations. */
@media (max-width: 640px) {
  .hero-glow { filter: blur(38px); }
}
.hero-glow-accent {
  width: 320px;
  height: 320px;
  top: -60px;
  left: -90px;
  background: radial-gradient(circle, rgba(162, 28, 175, 0.22), transparent 70%);
}
.hero-glow-cyan {
  width: 280px;
  height: 280px;
  bottom: -40px;
  right: -40px;
  background: radial-gradient(circle, rgba(36, 64, 255, 0.18), transparent 70%);
}
@media (min-width: 1024px) {
  .hero-glow-accent { width: 520px; height: 520px; top: -120px; left: -160px; }
  .hero-glow-cyan   { width: 440px; height: 440px; bottom: -80px; right: -100px; }
}

/* ---------- Scroll progress bar (top of viewport) ---------- */
.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--accent), #60a5fa);
  transform-origin: left center;
  transform: scaleX(0);
  z-index: 60;
  pointer-events: none;
  /* No CSS transition: the bar must track scroll frame-by-frame; a CSS
     transition would visibly trail on fast scrolls. JS writes the
     transform on every scroll event. */
}

/* ---------- Scroll-reveal (IntersectionObserver in JS) ---------- */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition:
    opacity 760ms cubic-bezier(.2,.7,.2,1),
    transform 760ms cubic-bezier(.2,.7,.2,1);
  will-change: opacity, transform;
}
.reveal.is-in-view {
  opacity: 1;
  transform: translateY(0);
}
.reveal[data-reveal-delay="1"] { transition-delay: 80ms; }
.reveal[data-reveal-delay="2"] { transition-delay: 160ms; }
.reveal[data-reveal-delay="3"] { transition-delay: 240ms; }
.reveal[data-reveal-delay="4"] { transition-delay: 320ms; }
.reveal[data-reveal-delay="5"] { transition-delay: 400ms; }
.reveal[data-reveal-delay="6"] { transition-delay: 480ms; }

/* Reveal from a side (used on the laptop mockup half of the hero). */
.reveal-left  { opacity: 0; transform: translateX(-32px);  transition: opacity 760ms cubic-bezier(.2,.7,.2,1), transform 760ms cubic-bezier(.2,.7,.2,1); }
.reveal-right { opacity: 0; transform: translateX(32px);   transition: opacity 760ms cubic-bezier(.2,.7,.2,1), transform 760ms cubic-bezier(.2,.7,.2,1); will-change: opacity, transform; }
.reveal-left.is-in-view, .reveal-right.is-in-view { opacity: 1; transform: translateX(0); }

/* Reveal-stagger: a single trigger that cascades every direct child on the
   same beat. The parent fires the IO, then CSS staggers each child via the
   `:nth-child` delays below. Children keep their relative layout — only
   opacity + translateY animate. Used on FAQ details list and any other
   grid that wants a coordinated wave instead of per-item reveals. */
.reveal-stagger > * {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 640ms cubic-bezier(0.22, 1, 0.36, 1),
    transform 640ms cubic-bezier(0.22, 1, 0.36, 1);
}
.reveal-stagger.is-in-view > * {
  opacity: 1;
  transform: translateY(0);
}
.reveal-stagger.is-in-view > *:nth-child(1) { transition-delay:   0ms; }
.reveal-stagger.is-in-view > *:nth-child(2) { transition-delay:  70ms; }
.reveal-stagger.is-in-view > *:nth-child(3) { transition-delay: 140ms; }
.reveal-stagger.is-in-view > *:nth-child(4) { transition-delay: 210ms; }
.reveal-stagger.is-in-view > *:nth-child(5) { transition-delay: 280ms; }
.reveal-stagger.is-in-view > *:nth-child(6) { transition-delay: 350ms; }
.reveal-stagger.is-in-view > *:nth-child(7) { transition-delay: 420ms; }
.reveal-stagger.is-in-view > *:nth-child(n+8) { transition-delay: 480ms; }

/* ---------- Glassmorphism buttons ----------
   Frosted glass effect for interactive buttons.
   Replaces solid bg-accent with translucent + backdrop blur.
   The aurora gradient breathes through the glass surface.
   --------------------------------------------------------- */
.btn-glass {
  background: rgba(59, 130, 246, 0.25);
  backdrop-filter: blur(12px) saturate(150%);
  -webkit-backdrop-filter: blur(12px) saturate(150%);
  border: 1px solid rgba(59, 130, 246, 0.35);
  box-shadow: 0 1px 2px rgba(0,0,0,0.1), 0 1px 3px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.08);
  transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.btn-glass:hover {
  background: rgba(96, 165, 250, 0.35);
  border-color: rgba(96, 165, 250, 0.5);
  box-shadow: 0 4px 12px rgba(59,130,246,0.15), inset 0 1px 0 rgba(255,255,255,0.12);
}
.btn-glass-outline {
  background: rgba(10, 10, 31, 0.45);
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
  transition: background 0.2s ease, border-color 0.2s ease;
}
.btn-glass-outline:hover {
  background: rgba(255, 255, 255, 0.08);
}

.btn-glass-icon {
  background: rgba(10, 10, 31, 0.3);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

@media (prefers-reduced-motion: reduce) {
  .btn-glass, .btn-glass-outline, .btn-glass-icon {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
}

/* ---------- Button shimmer (subtle gradient sweep on hover) ----------
   The shine is layered with `mix-blend-mode: screen` so it only ADDS light,
   never blocks the underlying text. Alpha tuned low (0.28) so the sheen
   reads as a polished highlight, not a hotspot. */
.btn-shimmer {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.btn-shimmer::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 30%, rgba(255,255,255,0.28) 50%, transparent 70%);
  transform: translateX(-120%);
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 0;
}
.btn-shimmer:hover::after {
  animation: shimmerSweep 900ms cubic-bezier(.2,.7,.2,1) forwards;
}
@keyframes shimmerSweep {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(220%); }
}

/* ---------- Animated nav link underline + active state ---------- */
.nav-link-anim {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding-block: 2px;
}
.nav-link-anim::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -4px;
  height: 2px;
  width: 0;
  background: var(--accent);
  border-radius: 2px;
  transition: width 220ms cubic-bezier(.2,.7,.2,1);
}
.nav-link-anim:hover::after,
.nav-link-anim.is-active::after {
  width: 100%;
}
.nav-link-anim.is-active {
  color: var(--ink-900);
}

/* ---------- Magnetic CTA (desktop pointer-fine only via JS no-op fallback) ---------- */
.magnetic {
  transition: transform 220ms cubic-bezier(.2,.7,.2,1);
  will-change: transform;
}

/* ---------- Phone ring: "dringdring dringdring" ----------
   Every SVG inside an `<a href="tel:...">` link across the page (header,
   mobile menu, FAQ card, contact section, footer, sticky mobile-cta-bar)
   shakes like a ringing phone. Targets the SVG specifically so the icon
   wiggles, not the whole link.

   Rhythm is two distinct small trills separated by a brief settle, then a
   long rest. Reads as the French onomatopoeia of an old-school phone:
   "dring dring  dring dring  [silence]  dring dring  dring dring...".

   - 0–9%   (≈0.27s): first trill, larger amplitude (14° → -7°)
   - 9–13%  (≈0.12s): settle back to 0°
   - 13–22% (≈0.27s): second trill, slightly smaller amplitude (12° → -4°)
   - 22–100%(≈2.34s): silence until the cycle

   `transform-box: fill-box` is set so `transform-origin: 50% 35%` resolves
   against the icon's actual bounding box across browsers (default
   `view-box` is unreliable on Safari for inline SVG). `overflow: visible`
   prevents the rotated receiver's swing peaks from being clipped at the
   default SVG box edges. */
@keyframes phone-ring {
  0%, 16%, 100% { transform: rotate(0deg) scale(1); }
  2%   { transform: rotate(18deg) scale(1.08); }
  4%   { transform: rotate(-14deg) scale(1.05); }
  6%   { transform: rotate(11deg) scale(1.08); }
  8%   { transform: rotate(-8deg) scale(1.05); }
  10%  { transform: rotate(5deg) scale(1.02); }
  12%  { transform: rotate(0deg) scale(1); }
  /* second trill */
  18%  { transform: rotate(16deg) scale(1.06); }
  20%  { transform: rotate(-12deg) scale(1.04); }
  22%  { transform: rotate(9deg) scale(1.06); }
  24%  { transform: rotate(-6deg) scale(1.03); }
  26%  { transform: rotate(0deg) scale(1); }
}
a[href^="tel:"] svg {
  transform-box: fill-box;
  transform-origin: 50% 35%;
  overflow: visible;
  animation: phone-ring 2.4s ease-in-out infinite;
}

/* ---------- Reduced motion: disable the new polish layer ---------- */
@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal-left, .reveal-right { opacity: 1 !important; transform: none !important; }
  .reveal-stagger > * { opacity: 1 !important; transform: none !important; transition: none !important; }
  .btn-shimmer::after { display: none; mix-blend-mode: normal; }
  /* No `.btn-shimmer > *` rule needed: with mix-blend-mode:screen the pseudo
     composes as an additive light pass so inline text stays visible without
     needing an explicit z-index. */
  .scroll-progress { display: none; }
  .magnetic { transition: none !important; }
  .hero-grid-overlay, .sigil-hero-watermark, .hero-glow { display: none; }
  a[href^="tel:"] svg { animation: none; }
}

/* =========================================================
   Hero parallax (2 badges, scroll-driven, snapshot-safe)
   ------------------------------------------------------------
   Targets added in index.html: .hero-parallax-up / -down.
   JS reads window.scrollY each animation frame and writes
   translate3d — at scrollY = 0 the transforms are 0 0 0 (no visual
   diff), so the diff against .shots/baseline stays at exactly
   0.00% on every clean run.

   Why only the 2 badges (and not the mockup wrapper / the hero
   glows):
   - .hero-glow-* carries `filter: blur(70px)`; promoting those
     forces the blur backdrop to re-rasterize every frame.
   - The mockup wrapper was considered (`.hero-parallax-slow`,
     -0.10× scrollY) but promoting it with will-change shifts the
     subpixel AA of all the text + SVG icons inside the mockup,
     which the .shots/baseline diff detector picks up as 1.2% on
     mobile. Cheap GPU promotion doesn't pay off here — the wrapper
     stays static, and only the two floating badges parallax.
   ------------------------------------------------------------ */
.hero-parallax-up,
.hero-parallax-down {
  /* Promote to their own compositor layers: 2 simple white boxes
     (text + icon), cheap GPU caching, no rasterization cost. */
  will-change: transform;
}

/* Reduced-motion short-circuits the JS itself, but we keep this CSS
   guard as a belt-and-suspenders defense. */
@media (prefers-reduced-motion: reduce) {
  .hero-parallax-up,
  .hero-parallax-down {
    will-change: auto;
  }
}

/* ---------- xs breakpoint helper (Tailwind doesn't ship 480px by default) ----------
   Used on the ~very small phone range so we have finer control of hero text + prices. */
@media (min-width: 480px) {
  .xs\:text-\[30px\] { font-size: 30px; line-height: 1.15; }
  .xs\:text-\[32px\] { font-size: 32px; line-height: 1.12; }
}

/* ---------- Freezon sigil (hand-drawn 6-arm asterisk, reused 3x) ----------
   Defined once in the document via <symbol id="freezon-sigil">, rendered
   three times via <use>. Each instance sits behind or near structural
   moments and never competes with the typography. */
.sigil-cta-watermark {
  position: absolute;
  right: -32px;
  bottom: -32px;
  width: 320px;
  height: 320px;
  color: #ffffff;
  opacity: 0.05;
  pointer-events: none;
  transform: rotate(-14deg);
}
@media (min-width: 1024px) {
  .sigil-cta-watermark {
    width: 400px;
    height: 400px;
    right: -56px;
    bottom: -56px;
    opacity: 0.06;
  }
}
.sigil-footer-watermark {
  position: absolute;
  right: 18px;
  bottom: 18px;
  width: 96px;
  height: 96px;
  color: var(--ink-900);
  opacity: 0.08;
  pointer-events: none;
  transform: rotate(-9deg);
}
@media (min-width: 1024px) {
  .sigil-footer-watermark {
    width: 130px;
    height: 130px;
    right: 28px;
    bottom: 24px;
    opacity: 0.07;
  }
}
@media (prefers-reduced-motion: reduce) {
  .sigil-cta-watermark,
  .sigil-footer-watermark { transform: none; }
}

/* ---------- Sector chooser (demo) ---------- */
.sector-card .sector-icon {
  transition: transform 180ms ease, color 180ms ease, background-color 180ms ease, border-color 180ms ease;
}
.sector-card:hover .sector-icon,
.sector-card:focus-visible .sector-icon,
.sector-card-active .sector-icon {
  transform: scale(1.04);
}
#demo-empty {
  transition: opacity 220ms ease;
}

/* ---------- Domain dropdown ---------- */
.domain-dropdown-panel {
  position: absolute;
  left: 0;
  top: 100%;
  min-width: 100%;
  width: 100%;
  z-index: 20;
  animation: dropdown-in 220ms cubic-bezier(.2,.7,.2,1) both;
}
@keyframes dropdown-in {
  from { opacity: 0; transform: translateY(-8px) scale(0.985); }
  to   { opacity: 1; transform: translateY(0)    scale(1); }
}
.domain-dropdown-panel.hidden { display: none; }
@media (min-width: 640px) {
  .domain-dropdown-panel {
    min-width: 420px;
    width: auto;
  }
}
@media (prefers-reduced-motion: reduce) {
  .sector-card .sector-icon,
  .sector-card:hover .sector-icon,
  .sector-card:focus-visible .sector-icon,
  .sector-card-active .sector-icon,
  #demo-empty { transform: none; transition: none; }
}

/* =========================================================
   .placeholder-required  (À REMPLIR markers for legal pages)
   ------------------------------------------------------------
   Inline markup emitted by hand-edits of mentions-legales.html /
   politique-confidentialite.html / index.html footer / contact
   form RGPD line. Visually distinct (amber dashed underline +
   tone-on-tone background) so Cameron notices everything that
   still needs real data before going live. The `data-fill`
   hook is a stable key for find-and-replace: a quick
   data-fill="siret" search-and-replace covers every match.
   Native CSS only (no Tailwind utilities) so the inner pages —
   which share style.css — get the styling for free.
   ========================================================= */
.placeholder-required {
  position: relative;
  display: inline-block;
  padding: 0.05em 0.4em;
  margin: 0 -0.05em;
  background: rgba(251, 191, 36, 0.18);   /* amber-300 @ 18% */
  color: #FDE68A;                          /* amber-200 */
  border: 1px dashed rgba(251, 191, 36, 0.65);  /* amber-300 @ 65% */
  border-radius: 4px;
  font-weight: 500;
  font-size: 0.94em;
  letter-spacing: 0.01em;
  white-space: nowrap;
  transition: background 160ms ease, border-color 160ms ease;
}
.placeholder-required::before {
  content: "⚠";
  margin-right: 0.35em;
  font-size: 0.92em;
  opacity: 0.85;
}
.placeholder-required:hover {
  background: rgba(251, 191, 36, 0.28);
  border-color: rgba(251, 191, 36, 0.85);
  cursor: help;
}
.placeholder-required::after {
  /* Tiny kbd-style chip showing the data-fill key, helps find/replace. */
  content: attr(data-fill);
  position: absolute;
  top: -1.4em;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.15em 0.45em;
  background: #1f2937;
  color: #fbbf24;
  font-size: 10.5px;
  letter-spacing: 0.02em;
  border-radius: 3px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}
.placeholder-required:hover::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .placeholder-required,
  .placeholder-required::after { transition: none; }
}

/* Phone ringing animation */
@keyframes phone-ring {
  0%, 100% { transform: rotate(0deg); }
  10% { transform: rotate(-12deg); }
  20% { transform: rotate(12deg); }
  30% { transform: rotate(-12deg); }
  40% { transform: rotate(12deg); }
  50% { transform: rotate(-8deg); }
  60% { transform: rotate(8deg); }
  70%, 100% { transform: rotate(0deg); }
}
.phone-ring {
  animation: phone-ring 1.2s ease-in-out 2s 3;
  transform-origin: center top;
}
.phone-ring:hover,
.phone-ring:focus-visible {
  animation: phone-ring 0.7s ease-in-out 0s infinite;
}

/* Footer logo hover */
.footer-logo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem 0.75rem;
  border-radius: 0.75rem;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.1);
  transition: transform 0.25s ease, background 0.25s ease, box-shadow 0.25s ease;
}
.footer-logo:hover,
.footer-logo:focus-visible {
  transform: translateY(-3px) scale(1.03);
  background: rgba(255, 255, 255, 0.14);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}
@media (prefers-reduced-motion: reduce) {
  .phone-ring { animation: none; }
}


