/* Maroc-SaaS landing — self-hosted, no external CDNs/fonts. System font
   stack chosen for solid Arabic + Latin coverage without a webfont download.
   Uses CSS logical properties throughout so the SAME rules serve both
   dir="rtl" (ar) and dir="ltr" (fr/en/es) pages without duplication. */

:root {
  --teal-900: #0d3f3c;
  --teal-700: #146d66;
  --teal-500: #1f8c82;
  --terracotta-600: #c1602f;
  --terracotta-500: #d97a44;
  --brass-500: #b8923f;
  --brass-300: #e0c98a;
  --cream-50: #faf6ee;
  --cream-100: #f3ead9;
  --ink-900: #211d16;
  --ink-700: #4a4438;
  --ink-500: #6f6858;
  --white: #ffffff;
  /* Card/panel SURFACE token -- distinct from --white on purpose (see the
     "dark mode" comment near the bottom of this file). Defaults to --white
     in light mode (identical rendering to before this token existed); only
     --surface (never --white itself) is redefined for dark mode, so every
     var(--white) usage below stays literal white -- correct for the many
     "white text/border on a brand-colored fill" usages (button text,
     chat-toggle border, etc.) that would otherwise go invisible if --white
     also flipped dark. */
  --surface: var(--white);
  --line: #e4dac2;
  --shadow: 0 12px 32px -18px rgba(13, 63, 60, 0.35);
  --radius-lg: 20px;
  --radius-md: 14px;
  --radius-sm: 9px;
  /* -------------------------------------------------------- button tokens
     Owner-reported bug (2026-07-16, D46, both 390px + 1440px): teal/green
     buttons blended into the teal-tinted cream backgrounds; outline
     ("ghost") buttons nearly vanished. One token pass, verified against
     WCAG contrast math (see tests/leadbot_gate.mjs's computed-style
     assertions): every button below clears >=4.5:1 text contrast AND
     >=3:1 button-vs-page-background contrast at both viewports.
       - --btn-primary-bg (#0d3f3c, i.e. --teal-900) vs white text: 11.7:1
       - --btn-secondary-bg is a DARKENED terracotta (not --terracotta-500,
         which only clears 3.08:1 with white text -- fails AA); this ink
         darkens it to clear 5.8:1 text / 5.4:1 vs --cream-50.
       - --btn-ghost is no longer transparent-on-wash (unmeasurable/
         disappearing against the hero teal wash) -- solid opaque fill +
         2px border, same math as primary.
       - --btn-whatsapp is a dedicated darkened WhatsApp green (brighter
         brand green #25D366 only clears ~2:1) so it stays recognizable
         AND accessible. */
  --btn-primary-bg: var(--teal-900);
  --btn-primary-bg-hover: var(--teal-700);
  --btn-primary-text: var(--white);
  --terracotta-ink: #a8481f;
  --terracotta-ink-hover: #8a3a18;
  --btn-whatsapp-bg: #0b7a55;
  --btn-whatsapp-bg-hover: #086847;
  --shadow-btn: 0 10px 24px -10px rgba(13, 63, 60, 0.55), 0 2px 6px -1px rgba(13, 63, 60, 0.3);
  --font-sans:
    "Segoe UI", "Noto Naskh Arabic", "Geeza Pro", "Tahoma", system-ui,
    -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* Display face for headings on the Latin-script pages (fr/en/es) — a
     serif carries the "official record" register of a court filing without
     resorting to a webfont. Arabic pages keep the sans stack throughout
     (see :lang(ar) override below): system Arabic serifs are unreliable
     across platforms, and Naskh-style sans reads more legible at heading
     sizes anyway. */
  --font-display: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Noto Naskh Arabic", var(--font-sans);
  --container: 1180px;
  /* --------------------------------------------------------- theme tokens
     Light-mode defaults for the handful of rules that DON'T already flip
     automatically via the tokens above (they used literal colors or a
     hardcoded teal-900/white pair instead of a var). Redefined ONLY inside
     the dark-preference block near the bottom of this file -- see that
     block's comment for the full "why" and the toggle mechanism. */
  --header-bg: rgba(250, 246, 238, 0.92);
  --accent-ink: var(--teal-900);
  --on-accent-ink: var(--white);
  --brand-mark-blend: multiply;
}

* { box-sizing: border-box; }
/* Explicit override: several elements toggled via the `hidden` attribute
   (.card-form, .chat-panel) also carry a class that sets `display` for
   their visible state, which otherwise beats the UA [hidden] rule in the
   cascade (author > UA at equal specificity). */
[hidden] { display: none !important; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-sans);
  color: var(--ink-900);
  background: var(--cream-50);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
h1, h2, h3 { line-height: 1.2; margin: 0 0 0.5em; font-weight: 700; color: var(--accent-ink); font-family: var(--font-display); letter-spacing: -0.01em; }
h1 { font-size: clamp(2rem, 1.5rem + 2vw, 3.4rem); }
h2 { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.3rem); }
h3 { font-size: 1.2rem; font-family: var(--font-sans); letter-spacing: 0; }
:lang(ar) h1, :lang(ar) h2, :lang(ar) h3 { font-family: var(--font-sans); letter-spacing: 0; }
p { margin: 0 0 1em; }
a { color: inherit; }
.wrap { max-width: var(--container); margin-inline: auto; padding-inline: 1.25rem; }

.skip-link {
  position: absolute;
  inset-inline-start: -999px;
  top: 0;
  background: var(--btn-primary-bg);
  color: var(--white);
  padding: 0.75rem 1.25rem;
  z-index: 200;
  border-end-end-radius: var(--radius-sm);
}
.skip-link:focus { inset-inline-start: 0; }

:focus-visible { outline: 3px solid var(--brass-500); outline-offset: 2px; }

/* ---------------------------------------------------------------- header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--header-bg);
  backdrop-filter: blur(8px);
  border-block-end: 1px solid var(--line);
}
.header-inner { display: flex; align-items: center; gap: 1.5rem; padding-block: 0.85rem; }
.brand { display: flex; align-items: center; gap: 0.65rem; text-decoration: none; font-weight: 800; font-size: 1.15rem; color: var(--accent-ink); font-family: var(--font-display); }
:lang(ar) .brand { font-family: var(--font-sans); }
/* Signature mark: an eight-pointed zellige star — the union silhouette of
   two overlapping squares (one rotated 45°), the classic khatam
   construction the bg-zellige artwork is also built from. Pure CSS, no
   image request. */
.brand-mark { position: relative; width: 26px; height: 26px; flex: none; }
.brand-mark::before, .brand-mark::after {
  content: ""; position: absolute; inset: 3px; background: var(--teal-700);
}
.brand-mark::after { transform: rotate(45deg); background: var(--brass-500); mix-blend-mode: var(--brand-mark-blend); }
.main-nav { display: flex; gap: 1.4rem; margin-inline-start: auto; flex-wrap: wrap; }
.main-nav a { text-decoration: none; font-weight: 600; color: var(--ink-700); font-size: 0.95rem; }
.main-nav a:hover { color: var(--teal-700); }
.header-actions { display: flex; align-items: center; gap: 1rem; }

/* -------------------------------------------------------- theme toggle
   Sun/moon icon button, header-actions' first child (see templates/
   page.html) -- flexbox + logical properties already used throughout this
   file mean it mirrors correctly under dir="rtl" (ar) with zero extra CSS.
   The button's OWN [data-theme] attribute (set by public/js/main.js's
   initThemeToggle(), independent of <html>'s -- it reflects the RESOLVED
   theme even when <html> carries no explicit override and OS preference is
   the only signal) drives which icon shows. */
.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 2.25rem; height: 2.25rem; padding: 0; border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); color: var(--accent-ink);
  cursor: pointer;
}
.theme-toggle:hover { background: var(--accent-ink); color: var(--on-accent-ink); }
.theme-toggle svg { width: 1.15rem; height: 1.15rem; }
.theme-toggle .icon-sun { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.theme-toggle .icon-moon { fill: currentColor; stroke: none; display: none; }
.theme-toggle[data-theme="dark"] .icon-sun { display: none; }
.theme-toggle[data-theme="dark"] .icon-moon { display: block; }
.lang-switch { display: flex; gap: 0.35rem; }
.lang-switch a {
  text-decoration: none; font-size: 0.82rem; font-weight: 700;
  padding: 0.3rem 0.55rem; border-radius: 999px; color: var(--ink-700);
  border: 1px solid var(--line);
}
.lang-switch a[aria-current="page"] { background: var(--btn-primary-bg); color: var(--white); border-color: var(--btn-primary-bg); }

/* ----------------------------------------------------- mobile nav (hamburger)
   Audit #22 (2026-07-18): at narrow widths .main-nav is hidden (>=900px block
   below) AND the header login .btn-ghost is hidden (<=480px block below),
   leaving a phone visitor with NO way to reach any section or the app login.
   This hamburger + dropdown restores that path. It is display:none on desktop
   (revealed only inside the <=900px block, exactly when .main-nav collapses),
   so the desktop header is untouched. Logical properties throughout mean it
   mirrors correctly under dir="rtl". */
.nav-toggle {
  display: none; /* desktop: never shown -- revealed in the <=900px block */
  align-items: center; justify-content: center; flex: none;
  width: 2.75rem; height: 2.75rem; padding: 0; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface); color: var(--accent-ink);
  cursor: pointer;
}
.nav-toggle:hover { background: var(--accent-ink); color: var(--on-accent-ink); }
.nav-toggle svg { width: 1.4rem; height: 1.4rem; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.nav-toggle .icon-close { display: none; }
.nav-toggle[aria-expanded="true"] .icon-menu { display: none; }
.nav-toggle[aria-expanded="true"] .icon-close { display: block; }

/* The dropdown panel. display:none by default (desktop + the closed state --
   the global [hidden]{display:none!important} rule handles closed; this base
   rule guarantees it never shows on desktop even if left open across a resize).
   Turned into a visible column ONLY inside the <=900px block. */
.mobile-menu {
  display: none;
  flex-direction: column; gap: 0.35rem;
  padding: 0.6rem 1.25rem calc(0.9rem + env(safe-area-inset-bottom, 0px));
  border-block-start: 1px solid var(--line);
  background: var(--header-bg);
}
.mobile-menu-link {
  display: flex; align-items: center; min-height: 44px;
  padding-inline: 0.5rem; border-radius: var(--radius-sm);
  text-decoration: none; font-weight: 600; font-size: 1rem; color: var(--ink-700);
}
.mobile-menu-link:hover { color: var(--teal-700); background: var(--surface); }
.mobile-menu-login { width: 100%; min-height: 44px; margin-block-start: 0.4rem; }
.mobile-menu-langs { margin-block-start: 0.6rem; }
.mobile-menu-langs a {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px; font-size: 0.95rem;
}

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0.7rem 1.4rem; border-radius: 999px; font-weight: 700;
  text-decoration: none; border: 2px solid transparent; cursor: pointer;
  font-size: 0.95rem; font-family: inherit; box-shadow: var(--shadow-btn);
  transition: background-color 0.15s ease, transform 0.15s ease;
}
.btn-primary { background: var(--btn-primary-bg); color: var(--btn-primary-text); }
.btn-primary:hover { background: var(--btn-primary-bg-hover); }
/* Solid, darkened terracotta -- NOT --terracotta-500 (white text on it only
   clears 3.08:1, well under the 4.5:1 text minimum). */
.btn-secondary { background: var(--terracotta-ink); color: var(--white); }
.btn-secondary:hover { background: var(--terracotta-ink-hover); }
/* Was transparent-on-wash (the reported "nearly vanishes" bug, worst on the
   hero's teal-tinted background at 390px). Now an opaque fill + 2px border
   -- same solid-vs-background math as .btn-primary regardless of what's
   behind it. */
.btn-ghost { background: var(--surface); color: var(--accent-ink); border-color: var(--accent-ink); box-shadow: 0 4px 14px -8px rgba(13, 63, 60, 0.3); }
.btn-ghost:hover { background: var(--accent-ink); color: var(--on-accent-ink); }
.btn-whatsapp { background: var(--btn-whatsapp-bg); color: var(--white); gap: 0.5rem; }
.btn-whatsapp:hover { background: var(--btn-whatsapp-bg-hover); }
.btn-whatsapp svg { width: 1.1em; height: 1.1em; fill: currentColor; flex: none; }

/* ------------------------------------------------------------------ hero */
.hero { position: relative; overflow: clip; padding-block: 3.5rem 4.5rem; }
.hero-bg {
  position: absolute; inset: 0;
  background: radial-gradient(60% 60% at 80% 20%, rgba(184, 146, 63, 0.14), transparent),
    radial-gradient(50% 50% at 10% 90%, rgba(20, 109, 102, 0.12), transparent);
  z-index: -1;
}
.hero-inner { display: grid; grid-template-columns: 1.1fr 1fr; gap: 2.5rem; align-items: center; }
.eyebrow {
  text-transform: uppercase; letter-spacing: 0.08em; font-weight: 800; font-size: 0.8rem;
  color: var(--terracotta-600); margin-bottom: 0.6rem;
  display: flex; align-items: center; gap: 0.5rem;
}
.eyebrow::before {
  content: ""; width: 8px; height: 8px; flex: none;
  background: var(--terracotta-600);
  clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%);
}

/* section divider: a thin strip of the zellige artwork itself, the one
   recurring motif tying every section together. */
.zellige-divider {
  height: 10px;
  background-image: url("/img/bg-zellige-2.webp");
  background-repeat: repeat;
  background-size: 240px;
  opacity: 0.5;
}
.hero .lede { font-size: 1.1rem; color: var(--ink-700); max-width: 46ch; }
.hero-ctas { display: flex; gap: 0.9rem; flex-wrap: wrap; margin-top: 1.6rem; }
.hero-media img {
  border-radius: var(--radius-lg); box-shadow: var(--shadow); width: 100%; height: auto;
  aspect-ratio: 16 / 9; object-fit: cover;
}

/* -------------------------------------------------------------- features */
.features { padding-block: 3rem; }
.section-lede { color: var(--ink-700); max-width: 60ch; }
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; margin-top: 2rem; }
.feature-card {
  background: var(--surface); border-radius: var(--radius-md); overflow: hidden;
  border: 1px solid var(--line); box-shadow: var(--shadow);
}
.feature-card img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; height: auto; }
.feature-card h3, .feature-card p { padding-inline: 1.1rem; }
.feature-card h3 { margin-top: 1rem; }
.feature-card p { color: var(--ink-700); font-size: 0.95rem; padding-bottom: 1.1rem; }

/* --------------------------------------------------------------- pricing */
.pricing { position: relative; padding-block: 3.5rem; background: var(--cream-100); overflow: clip; }
.zellige-bg {
  position: absolute; inset: 0;
  background-image: url("/img/bg-zellige-1.webp");
  background-repeat: repeat;
  background-size: 420px;
  opacity: 0.12;
  z-index: 0;
}
.pricing .wrap { position: relative; z-index: 1; }
.pricing-tiers {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; margin-top: 2rem;
  min-height: 3rem;
}
.pricing-status { color: var(--ink-700); }
.tier-card {
  background: var(--surface); border-radius: var(--radius-md); padding: 1.6rem;
  border: 1px solid var(--line); box-shadow: var(--shadow); display: flex; flex-direction: column; gap: 0.5rem;
}
.tier-card h3 { margin-bottom: 0.2rem; }
.tier-price { font-size: 1.7rem; font-weight: 800; color: var(--accent-ink); }
.tier-price small { font-size: 0.9rem; font-weight: 600; color: var(--ink-500); }
.tier-meta { display: flex; flex-wrap: wrap; gap: 0.5rem; font-size: 0.85rem; color: var(--ink-700); margin: 0.4rem 0; }
.tier-meta span { background: var(--cream-100); border-radius: 999px; padding: 0.25rem 0.65rem; }
.tier-desc { color: var(--ink-700); font-size: 0.92rem; }

/* ------------------------------------------------------- invite/waitlist */
.invite, .waitlist { padding-block: 3.5rem; }
.waitlist { background: var(--surface); }
.invite-inner, .waitlist-inner { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 2.5rem; align-items: start; }
.invite-inner > *, .waitlist-inner > * { min-width: 0; }
.card-form {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 1.75rem; box-shadow: var(--shadow); display: flex; flex-direction: column; gap: 1rem;
}
.waitlist .card-form { background: var(--cream-50); }
.field { display: flex; flex-direction: column; gap: 0.35rem; }
.field label { font-weight: 700; font-size: 0.9rem; color: var(--ink-900); }
.field input, .field select {
  width: 100%; min-width: 0;
  padding: 0.65rem 0.85rem; border-radius: var(--radius-sm); border: 1.5px solid var(--line);
  font-size: 1rem; font-family: inherit; background: var(--cream-50); color: var(--ink-900);
}
.field input:focus, .field select:focus { border-color: var(--teal-700); background: var(--surface); }
.field-hint { font-size: 0.8rem; color: var(--ink-500); margin: 0; }
.form-message { font-size: 0.9rem; font-weight: 600; padding: 0.6rem 0.8rem; border-radius: var(--radius-sm); margin: 0; }
.form-message[data-kind="error"] { background: #fbe4dd; color: #8f2f11; }
.form-message[data-kind="success"] { background: #e0f0e6; color: #1c5c34; }
.invite-success { text-align: center; }
.invite-success #invite-success-body { font-weight: 600; }

/* ---------------------------------------------------- self-serve signup */
.signup-points { list-style: none; margin: 1rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.signup-points li { position: relative; padding-inline-start: 1.6rem; color: var(--ink-700); font-weight: 600; }
.signup-points li::before { content: "✓"; position: absolute; inset-inline-start: 0; color: var(--teal-700); font-weight: 800; }
.slug-input { display: flex; align-items: stretch; border: 1px solid var(--border, #d6cfc4); border-radius: var(--radius-sm); overflow: hidden; background: var(--cream-50, #fff); }
.slug-input input { border: 0; border-radius: 0; flex: 1 1 auto; min-width: 0; background: transparent; }
.slug-input input:focus { background: transparent; }
.slug-input:focus-within { border-color: var(--teal-700); }
.slug-suffix { display: inline-flex; align-items: center; padding-inline: 0.6rem; background: rgba(0,0,0,0.05); color: var(--ink-500); font-size: 0.85rem; white-space: nowrap; }
.slug-status { font-size: 0.82rem; font-weight: 700; margin: 0; }
.slug-status[data-state="checking"] { color: var(--ink-500); }
.slug-status[data-state="available"] { color: #1c5c34; }
.slug-status[data-state="available"]::before { content: "✓ "; }
.slug-status[data-state="taken"] { color: #8f2f11; }
/* Honeypot: kept in the accessibility + layout tree only for bots; removed
   from the visual flow and from tab/AT order for humans. */
.hp-field { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; padding: 0; margin: -1px; }
.signup-terms { margin-top: 0.25rem; }
.signup-success-url { margin: 0.4rem 0; }
.signup-success-url a { font-weight: 800; color: var(--teal-700); word-break: break-all; }
.signup-success-trial { font-weight: 600; color: var(--ink-700); margin: 0.25rem 0 0.75rem; }

/* ------------------------------------------------------------------ footer */
/* color is a literal (not var(--cream-100)) -- this footer band stays
   dark-teal in BOTH schemes by design, so its text must stay a fixed
   light cream rather than following --cream-100, which the dark-mode
   block below redefines to a near-black "surface" shade (that reuse is
   correct for --cream-100's OTHER job as a light background tint, but
   would silently invert this literal on-brand text usage). */
.site-footer { background: var(--teal-900); color: #f3ead9; padding-block: 2.5rem; margin-top: 2rem; }
.footer-inner { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; align-items: center; }
.site-footer .brand-name { font-weight: 800; font-size: 1.05rem; }
.footer-tagline, .footer-rights { color: rgba(243, 234, 217, 0.75); font-size: 0.88rem; margin: 0.2rem 0 0; }

/* ------------------------------------------------------------- chat widget */
.chat-widget { position: fixed; inset-block-end: 1.25rem; inset-inline-end: 1.25rem; z-index: 300; }
.chat-toggle {
  display: flex; align-items: center; gap: 0.5rem; border: 2px solid var(--white); border-radius: 999px;
  background: var(--btn-primary-bg); color: var(--white); padding: 0.8rem 1.25rem; font-weight: 700;
  box-shadow: var(--shadow-btn); cursor: pointer; font-family: inherit; font-size: 0.92rem;
}
.chat-toggle:hover { background: var(--btn-primary-bg-hover); }
.chat-toggle-icon { width: 10px; height: 10px; border-radius: 50%; background: var(--brass-300); }

/* ------------------------------------------------------ chat nudge bubble
   Owner directive (LEAD-BOT-SPEC.md, D46): make the launcher visible/
   prominent via an auto-nudge after a few seconds -- localized, dismissible,
   not obnoxious (shown once per session, auto-hides itself too). */
.chat-nudge {
  position: absolute; inset-block-end: 4.25rem; inset-inline-end: 0;
  width: max-content; max-width: 220px; background: var(--surface); color: var(--ink-900);
  border: 1px solid var(--line); border-radius: var(--radius-md); padding: 0.75rem 2rem 0.75rem 0.9rem;
  box-shadow: var(--shadow-btn); font-size: 0.85rem; line-height: 1.4;
}
:lang(ar) .chat-nudge, [dir="rtl"] .chat-nudge { padding: 0.75rem 0.9rem 0.75rem 2rem; }
.chat-nudge p { margin: 0; }
.chat-nudge-dismiss {
  position: absolute; top: 0.35rem; inset-inline-end: 0.4rem; background: none; border: none;
  color: var(--ink-500); font-size: 1.1rem; line-height: 1; cursor: pointer; padding: 0.2rem;
}
@media (prefers-reduced-motion: no-preference) {
  .chat-nudge { animation: fade-up 0.35s ease-out both; }
}

/* -------------------------------------------------------- WhatsApp float */
.whatsapp-float {
  position: fixed; inset-block-end: 6.25rem; inset-inline-end: 1.25rem; z-index: 299;
  display: flex; align-items: center; justify-content: center; width: 52px; height: 52px;
  border-radius: 50%; background: var(--btn-whatsapp-bg); color: var(--white);
  box-shadow: var(--shadow-btn); border: 2px solid var(--white);
}
.whatsapp-float:hover { background: var(--btn-whatsapp-bg-hover); }
.whatsapp-float svg { width: 26px; height: 26px; fill: currentColor; }

/* -------------------------------------------------------------- modal */
.modal-overlay {
  position: fixed; inset: 0; background: rgba(13, 20, 18, 0.55); z-index: 400;
  display: flex; align-items: center; justify-content: center; padding: 1rem;
}
.modal-card {
  position: relative; background: var(--surface); border-radius: var(--radius-lg);
  box-shadow: var(--shadow); padding: 1.75rem; max-width: 380px; width: 100%;
}
.modal-close {
  position: absolute; top: 0.75rem; inset-inline-end: 0.85rem; background: none; border: none;
  font-size: 1.4rem; line-height: 1; color: var(--ink-700); cursor: pointer;
}
.modal-card h3 { margin-top: 0; }
.modal-card .card-form { box-shadow: none; border: none; padding: 0; margin-top: 1rem; }
/* -------------------------------------------------------------------------
   Deep Chat widget (2026-07-17 rewrite -- replaces the hand-rolled message
   list/forms with the vendored <deep-chat> element; see
   public/vendor/deep-chat/). Owner directive: BIG by default, not a
   cramped little box -- desktop default is the large size below; the
   maximize/restore button (kept from the previous build, same #chat-expand
   id) now toggles the OTHER direction -- DOWN to a compact size and back --
   since "expanded" is now the default rather than an opt-in. Mobile default
   is true full-screen; its own toggle restores a smaller floating panel.
   See public/js/main.js's initChatWidget() for the state machine (class
   name is-compact, sessionStorage persistence, aria-pressed direction). */
.chat-panel {
  position: absolute; inset-block-end: 4rem; inset-inline-end: 0;
  width: min(440px, 94vw); height: min(640px, 86vh); max-height: 86vh;
  background: var(--surface);
  border-radius: var(--radius-lg); box-shadow: var(--shadow); border: 1px solid var(--line);
  display: flex; flex-direction: column; overflow: hidden;
}
@media (prefers-reduced-motion: no-preference) {
  .chat-panel { transition: width 0.2s ease, height 0.2s ease, max-height 0.2s ease; }
}
/* Compact state (toggled ON via #chat-expand -- see main.js): the smaller,
   previous-generation footprint, still anchored to the same bottom-end
   corner as the default large panel -- only the size shrinks. */
.chat-panel.is-compact {
  width: min(340px, 88vw); height: min(520px, 70vh); max-height: 70vh;
}
.chat-panel-header {
  background: var(--btn-primary-bg); color: var(--white); padding: 0.85rem 1rem; font-weight: 700;
  display: flex; justify-content: space-between; align-items: center; flex: none;
}
.chat-panel-header-actions { display: flex; gap: 0.25rem; align-items: center; }
.chat-panel-header button { background: none; border: none; color: var(--white); font-size: 1.3rem; cursor: pointer; line-height: 1; }
.chat-expand-btn {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; min-width: 44px; min-height: 44px;
  border-radius: var(--radius-sm);
}
.chat-expand-btn:hover, .chat-expand-btn:focus-visible { background: rgba(255, 255, 255, 0.15); }
.chat-expand-icon { font-size: 1.1rem; line-height: 1; }
/* Default (large/full-screen) state: offer to shrink -- "restore" glyph.
   .is-compact (shrunk): offer to grow back -- "expand" glyph. Inverted vs.
   the pre-rewrite icon logic, since the default state itself flipped. */
.chat-expand-icon::before { content: "\2921"; } /* ⤡ restore/collapse */
.chat-panel.is-compact .chat-expand-icon::before { content: "\2922"; } /* ⤢ expand */
.chat-panel-body { flex: 1; min-height: 0; display: flex; padding: 0; }
#chat-deep { display: block; width: 100%; height: 100%; }

/* -------------------------------------------------- inline lead-capture
   panel (D-lead-bot follow-up, 2026-07-17): rendered by main.js as a Deep
   Chat `html` message when the assistant's reply carries the [[LEAD_FORM]]
   marker (stripped before display -- see systemPromptFor() in
   server/index.mjs and initChatWidget() in main.js). Plain CSS classes
   (not scoped/shadow) because Deep Chat injects this HTML into its OWN
   shadow-DOM message bubble, so these rules must be either inlined or
   registered via Deep Chat's own auxiliaryStyle mechanism -- see
   `AUXILIARY_STYLE` in main.js, which is this exact block, injected into
   the component's shadow root at configure time (duplicated in this file
   too so a designer can find/edit it without reading JS). */
.leadform-panel {
  display: flex; flex-direction: column; gap: 0.6rem; padding: 0.85rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-sm);
  font-family: var(--font-sans); color: var(--ink-900);
}
.leadform-title { margin: 0; font-weight: 700; font-size: 0.9rem; }
.leadform-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.leadform-btn {
  border: none; border-radius: 999px; padding: 0.55rem 1rem; font-weight: 700;
  font-size: 0.85rem; font-family: inherit; cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--btn-primary-bg); color: var(--white);
}
.leadform-callback-btn:hover, .leadform-submit-btn:hover { background: var(--btn-primary-bg-hover); }
.leadform-whatsapp-btn { background: var(--btn-whatsapp-bg); }
.leadform-whatsapp-btn:hover { background: var(--btn-whatsapp-bg-hover); }
.leadform-form { display: flex; flex-direction: column; gap: 0.55rem; }
.leadform-form label {
  display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.82rem; font-weight: 600; color: var(--ink-700);
}
.leadform-form input[type="text"], .leadform-form input[type="tel"] {
  padding: 0.5rem 0.65rem; border-radius: var(--radius-sm); border: 1.5px solid var(--line);
  font-family: inherit; font-size: 0.9rem; background: var(--cream-50); color: var(--ink-900);
}
.leadform-consent-label { flex-direction: row; align-items: flex-start; gap: 0.45rem; font-weight: 500; }
.leadform-consent-label input { margin-block-start: 0.2rem; }
.leadform-message { margin: 0; font-size: 0.82rem; font-weight: 600; padding: 0.45rem 0.6rem; border-radius: var(--radius-sm); }
.leadform-message[data-kind="error"] { background: #fbe4dd; color: #8f2f11; }
.leadform-message[data-kind="success"] { background: #e0f0e6; color: #1c5c34; }


/* --------------------------------------------------------------- motion
   One orchestrated moment (hero enters on load) plus quiet hover feedback
   on interactive cards — nothing scattered, all gated behind
   prefers-reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .hero-copy, .hero-media { animation: fade-up 0.7s ease-out both; }
  .hero-media { animation-delay: 0.12s; }
  @keyframes fade-up {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: translateY(0); }
  }
  .feature-card, .tier-card { transition: transform 0.2s ease, box-shadow 0.2s ease; }
  .feature-card:hover, .tier-card:hover { transform: translateY(-4px); box-shadow: 0 18px 36px -18px rgba(13, 63, 60, 0.4); }
}

/* ------------------------------------------------------------- responsive */
@media (max-width: 900px) {
  .hero-inner, .invite-inner, .waitlist-inner { grid-template-columns: 1fr; }
  .hero-media { order: -1; }
  .main-nav { display: none; }
  /* Audit #22: reveal the hamburger + dropdown exactly when .main-nav
     collapses, so every section AND the app login stay reachable on phones. */
  .nav-toggle { display: inline-flex; }
  .mobile-menu { display: flex; }
  /* Language switching moves into the dropdown on this layout -- keeping the
     header lang pills here too would crowd the bar past the viewport once the
     44px touch targets (audit #38) are applied. */
  .header-actions .lang-switch { display: none; }
}
@media (max-width: 480px) {
  .header-actions .btn-ghost { display: none; }
  /* Mobile DEFAULT (owner request 2026-07-17): true full-screen, not just a
     bigger box -- covers the whole viewport regardless of the widget's
     normal bottom/inline-end anchor. 100dvh (dynamic viewport height)
     shrinks automatically when the on-screen keyboard opens so the input
     row stays visible above it in browsers that support it; 100vh is a
     fallback: there, plus in any browser where dvh under-reacts, main.js's
     visualViewport keyboard shim (see initChatWidget()) forces the exact
     visible height explicitly. Safe-area insets keep the header/footer
     clear of notches and the home-indicator bar. */
  .chat-panel {
    position: fixed; inset: 0; width: 100%; height: 100vh; height: 100dvh;
    max-height: none; border-radius: 0;
  }
  .chat-panel-header {
    padding-block-start: calc(0.85rem + env(safe-area-inset-top, 0px));
  }
  .chat-panel-body {
    padding-block-end: env(safe-area-inset-bottom, 0px);
  }
  /* Compact opt-in (toggled via #chat-expand -- see main.js): restores a
     smaller floating panel instead of full-screen, anchored back to the
     launcher's corner like the desktop compact state. */
  .chat-panel.is-compact {
    position: absolute; inset-block-end: 4rem; inset-inline-end: 0; inset-block-start: auto; inset-inline-start: auto;
    width: 96vw; height: min(520px, 80vh); max-height: 80vh; border-radius: var(--radius-lg);
  }
  .chat-panel.is-compact .chat-panel-header { padding-block-start: 0.85rem; }
  .chat-panel.is-compact .chat-panel-body { padding-block-end: 0; }

  /* Owner report (real iPhone, 2026-07-16): the fixed chat pill, its nudge
     bubble, and the WhatsApp float overlapped the hero CTAs/lede on short
     mobile viewports. Two compounding causes, both fixed here:
       1. .hero-media rendered ABOVE the copy on mobile (order:-1, ~200px
          tall incl. margin) pushing the CTA row down into the same screen
          band the floats occupy -- put it back below the copy on narrow
          screens (nothing to look at before the pitch/CTAs anyway).
       2. Both floats anchored to the SAME bottom-end corner, stacking
          ~150px of reserved height there -- split them to opposite bottom
          corners (mirrors correctly under dir="rtl" via logical insets),
          each hugging the true edge with safe-area clearance for the home
          indicator, so the reserved footprint per corner drops to ~70px.
     Verified against the real rendered DOM (see tests/darkmode_gate.mjs's
     bounding-box assertions), not just visually. */
  .hero-media { order: 0; margin-block-start: 1.5rem; }
  .hero-ctas { flex-direction: column; align-items: stretch; margin-top: 1rem; gap: 0.6rem; }
  .hero-ctas .btn { width: 100%; }
  /* Trim the vertical budget above the CTAs too -- the fr/es copy runs
     longer than ar/en, and measurement against the real DOM (all 4
     locales, both color schemes) showed the nudge bubble still clipping
     the CTA row without this. */
  .hero { padding-block: 1.25rem 3rem; }
  .hero .lede { line-height: 1.45; }

  .chat-widget {
    inset-block-end: calc(0.75rem + env(safe-area-inset-bottom, 0px));
    inset-inline-end: 0.75rem;
  }
  .whatsapp-float {
    inset-block-end: calc(0.75rem + env(safe-area-inset-bottom, 0px));
    inset-inline-end: auto;
    inset-inline-start: 0.75rem;
  }
  /* Shrink the nudge bubble's own footprint (shorter vertical reach above
     the pill) so its top edge clears the CTA row even for the longest
     locale copy. */
  .chat-nudge { padding: 0.5rem 1.8rem 0.5rem 0.75rem; font-size: 0.8rem; line-height: 1.3; max-width: 190px; }
  :lang(ar) .chat-nudge, [dir="rtl"] .chat-nudge { padding: 0.5rem 0.75rem 0.5rem 1.8rem; }

}

/* SE-class phones (375x667 and shorter -- iPhone SE, still a real, current
   device): the 480px-wide tightening above isn't enough headroom once the
   fr/es copy (longer than ar/en) is factored in -- measurement against the
   real DOM showed the CTA row still clipping the floats there. Same
   approach as the 480px block (spacing/stacking/float placement only, no
   new elements hidden), just squeezed further for the shorter viewport. */
@media (max-width: 480px) and (max-height: 700px) {
  .hero { padding-block: 0.6rem 2rem; }
  .eyebrow { margin-bottom: 0.3rem; }
  h1 { font-size: 1.5rem; margin-bottom: 0.3em; }
  .hero .lede { font-size: 0.92rem; line-height: 1.3; margin-bottom: 0.6em; }
  .hero-ctas { margin-top: 0.6rem; gap: 0.4rem; }
  .hero-ctas .btn { padding-block: 0.5rem; }

  .chat-widget, .whatsapp-float { inset-block-end: calc(0.5rem + env(safe-area-inset-bottom, 0px)); }
  .chat-toggle { padding: 0.6rem 0.9rem; }
  .whatsapp-float { width: 44px; height: 44px; }
  .whatsapp-float svg { width: 22px; height: 22px; }
  .chat-nudge { inset-block-end: 3.5rem; padding: 0.4rem 1.6rem 0.4rem 0.6rem; font-size: 0.75rem; line-height: 1.25; max-width: 165px; }
  :lang(ar) .chat-nudge, [dir="rtl"] .chat-nudge { padding: 0.4rem 0.6rem 0.4rem 1.6rem; }
}

/* -------------------------------------------------------- dark preference
   Owner report (real iPhone, 2026-07-16, dark mode): wordmark, active
   language chip, hero primary CTA, and the chat pill all went invisible
   (dark-teal on near-black); the header logo mark rendered as a plain
   solid square instead of its star silhouette; footer text nearly
   vanished. Root cause -- this block existed but was an INCOMPLETE,
   never-tested pass: it repurposed --white as a "dark surface" color, but
   --white was ALSO used verbatim everywhere as literal white text/borders
   on brand-colored fills (button text, chat-toggle border, etc.) -- once
   --white went dark, "white text on teal-900" became "dark-teal text on
   dark-teal", i.e. invisible. It also left every SOLID teal-900 component
   fill (buttons, active chip, chat pill) unbrightened, so even once text
   contrast was fixed, the fill only cleared ~1.4:1 against the new
   near-black page background (component contrast needs >=3:1). And the
   logo mark's mix-blend-mode:multiply silhouette trick depends on a LIGHT
   backdrop to render two distinct tones; against near-black it collapses
   to black, leaving only the plain teal square visible.
   Chose to BUILD OUT real dark mode (not force light) because the intent
   was already there (this block already re-themed 7 tokens + heading
   color) and the brand assets support it -- fixing the handful of broken
   derived values is a smaller, more targeted change than ripping out an
   existing (if broken) dark design and forcing color-scheme:light.

   THEME TOGGLE (2026-07-16 UX pass): every rule that used to need its own
   dark override below has since been refactored to read one of the
   --accent-ink / --on-accent-ink / --header-bg / --brand-mark-blend tokens
   (defined with LIGHT defaults in the base :root block up top) instead of
   a literal color or a bare var(--teal-900)/var(--white) pair -- so the
   ENTIRE dark theme is now just custom-property overrides, applied under
   THREE independent triggers instead of only OS preference:
     1. OS preference alone, when no explicit override is stored --
        `@media (prefers-color-scheme: dark)` + `:not([data-theme="light"])`
        so an explicit light override (below) still wins even when the OS
        itself prefers dark.
     2. An explicit `data-theme="dark"` attribute on <html> -- the user
        clicked the toggle (public/js/main.js's initThemeToggle()), or the
        no-flash inline script in templates/page.html's <head> restored a
        prior choice from localStorage before first paint.
   `data-theme="light"` on <html> FORCES the light defaults even when the
   OS prefers dark (excluded from the media-query rule above, and nothing
   below applies since no `[data-theme="dark"]` attribute is present). See
   tests/darkmode_gate.mjs (OS-driven case, regression-guarded) and
   tests/ux_gate.mjs (the toggle + both override directions + no-flash). */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --cream-50: #16201e;
    --cream-100: #1c2725;
    /* NOTE: --white is deliberately NOT redefined here -- see the
       --surface token comment near the top of this file. Card/panel
       backgrounds use --surface (redefined below); --white stays literal
       white everywhere it's used for text/borders on brand-colored fills. */
    --surface: #21302c;
    --ink-900: #f2ecdd;
    --ink-700: #cfc6b0;
    --ink-500: #a89e88;
    --line: #35443f;
    /* Brightened accent variants for SOLID teal-900 component fills
       (buttons/chips/pills that pair a fill with white text) -- teal-900
       itself only clears ~1.4:1 fill-vs-page contrast against the new
       near-black page bg (needs >=3:1); this shade clears ~3.2:1 fill
       contrast while still clearing >=4.5:1 for white text on top of it.
       Only --btn-primary-bg is repointed (not the bare --teal-900 token)
       so text/heading/border usages of --teal-900 elsewhere are
       untouched. */
    --btn-primary-bg: #1c7971;
    --btn-primary-bg-hover: #175f58;
    /* Same reasoning, terracotta family: --terracotta-ink only clears
       ~2.9:1 fill contrast in dark mode; this shade clears ~3.4:1 fill /
       ~4.9:1 white-text. */
    --terracotta-ink: #b55529;
    --terracotta-ink-hover: #9c481f;
    /* Wordmark/headings/tier-price/ghost-pill text+border: teal-900 over
       the now near-black header/surfaces is unreadable (~1.4:1) -- the
       same brass used elsewhere clears ~10:1. */
    --accent-ink: var(--brass-300);
    /* Hover-state text/icon color for anything filled with --accent-ink
       (the ghost pill's hover fill, the theme toggle's own hover fill) --
       near-black (matches the new --cream-50) reads correctly against the
       now-light brass fill, mirroring --on-accent-ink's light-mode
       "white text on dark fill" job. */
    --on-accent-ink: var(--cream-50);
    /* Logo mark: swap multiply -> screen so the second (brass) square
       still reads as a distinct tone against a dark backdrop instead of
       collapsing to black. */
    --brand-mark-blend: screen;
    --header-bg: rgba(22, 32, 30, 0.9);
  }
  /* .field input/.field select need no override here: they already read
     var(--cream-50)/var(--ink-900), both redefined above. */
}
/* Explicit override -- see the theme-toggle comment above. Same value set
   as the media-query block; kept as a literal duplicate rather than a
   shared mixin (no CSS-native reuse across a plain @media block otherwise)
   -- deliberately small/flat so it stays easy to diff against the block
   above if either ever needs to change. */
:root[data-theme="dark"] {
  --cream-50: #16201e;
  --cream-100: #1c2725;
  --surface: #21302c;
  --ink-900: #f2ecdd;
  --ink-700: #cfc6b0;
  --ink-500: #a89e88;
  --line: #35443f;
  --btn-primary-bg: #1c7971;
  --btn-primary-bg-hover: #175f58;
  --terracotta-ink: #b55529;
  --terracotta-ink-hover: #9c481f;
  --accent-ink: var(--brass-300);
  --on-accent-ink: var(--cream-50);
  --brand-mark-blend: screen;
  --header-bg: rgba(22, 32, 30, 0.9);
}

/* ============================================================ legal pages
   Terms of Service + Privacy Policy (templates/legal.html). Reuses the
   shared header/footer chrome + design tokens; adds a readable long-form
   document column, a sticky-free table of contents, a DRAFT banner/stamp,
   and a dedicated @media print block (the "Print" button in the doc head
   calls window.print() -- see public/js/main.js initLegalPrint). All colors
   go through the same custom-property tokens, so dark mode is automatic. */
.legal-main { padding-block: 2rem 3.5rem; }
.legal-doc { max-width: 820px; }

.legal-draft-banner {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.9rem;
  background: color-mix(in srgb, var(--brass-500) 16%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--brass-500) 45%, var(--line));
  border-inline-start: 4px solid var(--brass-500);
  border-radius: var(--radius-md);
  padding: 0.85rem 1.1rem; margin-bottom: 2rem;
}
.legal-draft-tag {
  flex: none; font-weight: 800; font-size: 0.9rem;
  color: var(--accent-ink);
  background: color-mix(in srgb, var(--brass-500) 22%, transparent);
  border-radius: 999px; padding: 0.2rem 0.7rem; white-space: nowrap;
}
.legal-draft-text { margin: 0; font-size: 0.9rem; color: var(--ink-700); flex: 1 1 16rem; }

.legal-head { margin-bottom: 1.5rem; border-bottom: 1px solid var(--line); padding-bottom: 1.25rem; }
.legal-breadcrumb { font-size: 0.85rem; margin: 0 0 0.75rem; }
.legal-breadcrumb a { color: var(--teal-700); text-decoration: none; }
.legal-breadcrumb a:hover { text-decoration: underline; }
.legal-head h1 { font-size: clamp(1.8rem, 1.4rem + 1.6vw, 2.6rem); margin-bottom: 0.35em; }
.legal-subtitle { color: var(--ink-700); font-size: 1.05rem; max-width: 58ch; margin: 0 0 0.6em; }
.legal-updated { font-size: 0.85rem; color: var(--ink-500); margin: 0; }
.legal-actions { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-top: 1rem; }
.btn-sm { padding: 0.45rem 0.9rem; font-size: 0.85rem; }

.legal-toc {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-md); padding: 1rem 1.25rem 1.1rem;
  margin-bottom: 2rem;
}
.legal-toc-title { font-weight: 700; margin: 0 0 0.5rem; font-size: 0.95rem; }
.legal-toc ol { margin: 0; padding-inline-start: 1.2rem; columns: 2; column-gap: 2rem; }
.legal-toc li { margin-bottom: 0.3rem; break-inside: avoid; }
.legal-toc a { color: var(--teal-700); text-decoration: none; font-size: 0.9rem; }
.legal-toc a:hover { text-decoration: underline; }

.legal-content { font-size: 1rem; }
.legal-section { margin-bottom: 1.75rem; scroll-margin-top: 5rem; }
.legal-section h2 { font-size: 1.2rem; margin-bottom: 0.5em; color: var(--accent-ink); }
.legal-section p { color: var(--ink-900); }
.legal-section ul { margin: 0 0 1em; padding-inline-start: 1.3rem; }
.legal-section li { margin-bottom: 0.4rem; }

.legal-placeholder-hint {
  margin-top: 2.5rem; padding-top: 1rem; border-top: 1px dashed var(--line);
  font-size: 0.82rem; color: var(--ink-500); font-style: italic;
}

/* footer legal nav (both the marketing footer and the legal footer) */
.footer-legal { display: flex; flex-wrap: wrap; gap: 0.4rem 1.1rem; align-items: center; }
.footer-legal a { color: rgba(243, 234, 217, 0.85); font-size: 0.88rem; text-decoration: none; }
.footer-legal a:hover { color: #fff; text-decoration: underline; }
.footer-draft-stamp { color: rgba(243, 234, 217, 0.6); font-size: 0.8rem; margin: 0.2rem 0 0; font-weight: 700; }
.footer-meta { text-align: end; }

@media (max-width: 640px) {
  .legal-toc ol { columns: 1; }
  .footer-meta { text-align: start; }
}

/* --------------------------------------------------------------- print
   Clean, ink-frugal legal documents: drop all site chrome + interactive
   controls, force a light readable page, and print the full text with the
   URL after each link so a paper copy is self-describing. */
@media print {
  .no-print,
  .site-header, .site-footer,
  .legal-actions, .legal-toc, .legal-breadcrumb,
  .chat-widget, .whatsapp-float, .modal-overlay, .skip-link { display: none !important; }

  html, body { background: #fff !important; color: #000 !important; }
  .legal-main { padding: 0 !important; }
  .legal-doc { max-width: none; }
  .legal-draft-banner {
    background: #fff !important; border: 1.5px solid #000 !important;
    border-inline-start-width: 4px !important;
  }
  .legal-draft-tag { background: none !important; color: #000 !important; border: 1px solid #000; }
  .legal-draft-text, .legal-subtitle, .legal-updated, .legal-section p, .legal-section li { color: #000 !important; }
  .legal-head h1, .legal-section h2 { color: #000 !important; }
  .legal-section { page-break-inside: avoid; }
  .legal-section h2 { page-break-after: avoid; }
  a { color: #000 !important; text-decoration: none; }
  .legal-content a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 0.85em; word-break: break-all; }
}
