/* =============================================================================
   C-Link landing page styles.
   -----------------------------------------------------------------------------
   Color comes exclusively from the app's semantic tokens (assets/tokens/*,
   copied verbatim from Frontend/src/styles/tokens). Type, spacing, radius and
   motion ride the same primitive scales the app exposes for that purpose.
   Layout uses logical properties throughout so an Arabic/RTL variant is a
   `dir` flip — the same contract the app follows.
============================================================================= */

/* Landing-scoped additions (not part of the app's token files — defined here,
   never written into the copied tokens). */
:root {
  --landing-display: clamp(34px, 5.2vw, 58px);
  /* Section titles. The floor is what a 320px screen gets, so it stays inside
     the gutter without hyphenation; the ceiling is the desktop presence. */
  --landing-h2: clamp(27px, 3.9vw, 44px);
  --landing-h2-lg: clamp(31px, 4.9vw, 54px);
  --landing-shell: 1140px;
  --landing-shell-narrow: 760px;
  /* The plan CARD ROW only — not the plans section, whose eyebrow, H2 and sub
     stay on --landing-shell with every other section's.

     DERIVED, NOT CHOSEN. Measured from the shipped fonts (Hanken Grotesk 400/600
     and IBM Plex Sans Arabic 400/600 at --fs-secondary, with Arabic joining
     forms applied), the longest bullet on any card in either locale is
     "Automated appointment reminders" at 211.8px; the longest Arabic one is
     206.5px. Per card that needs 211.8 + 26 tick + 44 padding + 2 border =
     283.8px, and four of those plus three 14px gaps is 1177.3px.

     1180 is that number and nothing beyond it — about 0.7px of slack. Re-derive
     it if a bullet gets longer; do not pad it "just in case", and do not raise
     it to fix a bullet that should have been shortened instead. */
  --landing-plan-grid: 1180px;
  --header-h: 64px;

  /* Shared interaction vocabulary for hoverable surfaces (feature cards,
     security points, steps, milestones, contact buttons) so every section
     lifts, glows and settles with the same physics. */
  --lift-t: 240ms;
  --lift-y: -4px;
  --glow-brand: 0 0 0 1px var(--color-primary-ring), 0 14px 34px -12px var(--color-primary-ring);
  --glow-success: 0 0 0 1px color-mix(in srgb, var(--status-success) 34%, transparent),
    0 14px 34px -12px color-mix(in srgb, var(--status-success) 45%, transparent);

  /* Width the browser's scrollbar occupies; set by landing.js and used to
     pad the body while the mobile menu locks scrolling, so nothing shifts. */
  --scrollbar-gap: 0px;

  /* Direction multiplier. This file has ZERO inline-axis physical properties
     (verified by grep across it and assets/tokens/*), so `dir` alone flips the
     whole layout. Exactly five rules do not follow, because `translateX` and
     the `translate` property are PHYSICAL and have no logical form: the step
     icon nudge, the roadmap row nudge, the contact arrow nudge, the WhatsApp
     icon nudge, and the .section-lg glow. Multiplying their offset by --dir
     fixes all five with ONE mechanism instead of five [dir="rtl"] overrides.
     Search for `var(--dir)` to find every one. */
  --dir: 1;
}
[dir="rtl"] { --dir: -1; }

/* ------------------------------------------------------------------ reset -- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  scroll-padding-top: calc(var(--header-h) + 12px);
}
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  font-family: var(--font-sans);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-relaxed);
  color: var(--text-primary);
  background: var(--background-page);
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
h1, h2, h3, p, ul, ol, figure { margin: 0; }
button { font: inherit; cursor: pointer; }

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--sp-4);
  top: -48px;
  z-index: 100;
  padding: var(--sp-2) var(--sp-4);
  background: var(--surface-elevated);
  color: var(--color-primary-ink);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  text-decoration: none;
  transition: top var(--t) var(--ease);
}
.skip-link:focus { top: var(--sp-3); }

.shell {
  max-width: var(--landing-shell);
  margin-inline: auto;
  padding-inline: clamp(18px, 4vw, 32px);
}
.shell-narrow { max-width: var(--landing-shell-narrow); }

/* ----------------------------------------------------------------- header -- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--surface-primary) 88%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-subtle);
}
.header-row {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  min-height: var(--header-h);
}

/* --- brand lockup ---
   The C-Link symbol plus the name. The name is set in the SANS, not the
   display serif the rest of the page uses for headings: the brand's own
   wordmark is a geometric sans, and "C-Link" in Fraunces italic beside this
   mark reads as two different companies sharing a header.

   Both marks are in the DOM and CSS shows one — same `data-theme` switch the
   token layer uses, so the logo changes in the same paint as the palette and
   never has to wait for an <img> to re-decode. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--text-primary);
  text-decoration: none;
  /* The lockup is artwork, and artwork does not mirror. On /ar/ the header row
     is RTL and correctly moves the whole lockup to the right edge, but WITHIN
     it the mark must stay left of the name — the brand ships symbol-then-name
     as a single horizontal file, and a reversed lockup is a different logo.
     Same reason the WhatsApp mark is on the do-not-mirror list below. */
  direction: ltr;
}
.wordmark-mark {
  display: block;
  flex: none;
  /* The artwork is fixed-ratio and sized by its own width/height attributes;
     `contain` guarantees a stray rule can never stretch the mark. */
  object-fit: contain;
}
.wordmark-mark--dark { display: none; }
[data-theme="dark"] .wordmark-mark--light { display: none; }
[data-theme="dark"] .wordmark-mark--dark { display: block; }
.wordmark-word {
  font-family: var(--font-sans);
  font-weight: var(--fw-bold);
  font-size: 24px;
  line-height: 1;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.wordmark-sm .wordmark-word { font-size: 20px; }

/* Desktop: the panel is transparent to layout, so .site-nav is a direct flex
   item of .header-row exactly as before. Below the mobile breakpoint the same
   element becomes the dropdown. */
.nav-panel { display: contents; }
.nav-panel-cta { display: none; }

.site-nav {
  display: flex;
  gap: var(--sp-5);
  margin-inline-start: auto;
}
.site-nav a {
  position: relative;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  padding: var(--sp-2) var(--sp-1);
  border-radius: var(--radius-xs);
  transition: color var(--t-fast) var(--ease);
}
.site-nav a:hover,
.site-nav a:focus-visible { color: var(--color-primary-ink); }

/* On desktop the nav carries the auto margin that pushes itself and the
   actions to the trailing edge; below the mobile breakpoint the nav leaves the
   flow and the actions take the auto margin over (see the media query). */
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-primary);
  color: var(--text-secondary);
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.icon-btn:hover { background: var(--surface-hover); color: var(--text-primary); }
.icon-btn svg { width: 19px; height: 19px; }

/* Theme toggle shows the icon of the theme you'd switch TO. */
.icon-moon { display: none; }
[data-theme="dark"] .icon-moon { display: none; }
[data-theme="dark"] .icon-sun { display: block; }
[data-theme="light"] .icon-sun { display: none; }
[data-theme="light"] .icon-moon { display: block; }

/* --------------------------------------------------------- hamburger btn -- */
.nav-toggle { display: none; }
.nav-toggle-bars {
  position: relative;
  display: block;
  width: 18px;
  height: 12px;
}
.nav-toggle-bars > span {
  position: absolute;
  inset-inline: 0;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition:
    transform var(--t) var(--ease),
    opacity 100ms var(--ease);
}
.nav-toggle-bars > span:nth-child(1) { top: 0; }
.nav-toggle-bars > span:nth-child(2) { top: 5px; }
.nav-toggle-bars > span:nth-child(3) { top: 10px; }

/* Bars fold into an X. Middle bar fades, outer bars meet in the centre. */
.nav-open .nav-toggle-bars > span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-open .nav-toggle-bars > span:nth-child(2) { opacity: 0; }
.nav-open .nav-toggle-bars > span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
.nav-open .nav-toggle { background: var(--surface-hover); color: var(--text-primary); }

/* ---------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: 9px 18px;
  font-size: var(--fs-btn);
  font-weight: var(--fw-semibold);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  text-decoration: none;
  transition:
    background var(--t-fast) var(--ease),
    color var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}
.btn-lg { padding: 12px 24px; font-size: 15px; border-radius: var(--radius-md); }

.btn-primary {
  background: var(--color-primary);
  color: var(--text-on-primary);
  box-shadow: var(--shadow-xs);
}
.btn-primary:hover { background: var(--color-primary-hover); }
.btn-primary:active { background: var(--color-primary-pressed); transform: translateY(1px); }

.btn-ghost {
  background: var(--color-tertiary-soft);
  color: var(--color-tertiary-ink);
  border-color: var(--border-subtle);
}
.btn-ghost:hover { background: var(--color-tertiary-soft-hover); }

.link-btn {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-primary-ink);
  font-weight: var(--fw-semibold);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.link-btn:hover { color: var(--color-primary-hover); }

/* ------------------------------------------------------------------- hero -- */
.hero { padding-block: clamp(44px, 7vw, 92px) clamp(36px, 5vw, 64px); }
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}

/* Small decorative label above every section title. The leading rule is the
   one shared ornament, so the eyebrows read as one family page-wide. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-secondary-ink);
  margin-block-end: var(--sp-3);
}
.eyebrow::before {
  content: "";
  flex: none;
  width: 24px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  opacity: 0.65;
}

.hero h1 {
  font-size: var(--landing-display);
  line-height: var(--lh-tight);
  font-weight: var(--fw-bold);
  letter-spacing: -0.015em;
  margin-block-end: var(--sp-4);
}
.hero h1 em {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: var(--fw-medium);
  color: var(--color-primary-ink);
}

.hero-sub {
  font-size: clamp(15px, 1.4vw, 17.5px);
  color: var(--text-secondary);
  max-width: 52ch;
  margin-block-end: var(--sp-6);
}

.cta-row { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.cta-center { justify-content: center; }

.trust-line {
  margin-block-start: var(--sp-4);
  font-size: var(--fs-secondary);
  color: var(--text-muted);
}

/* ------------------------------------------------------------ hero visual -- */
.hero-visual { display: grid; gap: var(--sp-3); justify-items: center; }

.phone {
  width: min(100%, 390px);
  background: var(--surface-elevated);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.phone-top {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  background: var(--surface-secondary);
  border-bottom: 1px solid var(--border-subtle);
}
.phone-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-soft);
  font-size: 17px;
}
.phone-title { font-weight: var(--fw-semibold); font-size: var(--fs-body); }
.phone-status {
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-meta);
  color: var(--text-muted);
}
.phone-status .dot {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-pill);
  background: var(--status-success);
}

.phone-thread {
  display: grid;
  gap: var(--sp-2);
  padding: var(--sp-4);
  background: var(--background-page);
  min-height: 330px;
  align-content: start;
}

.chat-msg {
  max-width: 82%;
  padding: 8px 12px;
  border-radius: 14px;
  font-size: var(--fs-secondary);
  line-height: var(--lh-snug);
}
.from-patient {
  justify-self: start;
  background: var(--bubble-patient);
  color: var(--bubble-patient-ink);
  border-start-start-radius: 4px;
}
.from-patient[lang="ar"] { font-family: 'IBM Plex Sans Arabic', var(--font-sans); }
.from-ai {
  justify-self: end;
  background: var(--bubble-ai);
  color: var(--bubble-ai-ink);
  border-start-end-radius: 4px;
}
.msg-time {
  display: inline-block;
  margin-inline-start: 8px;
  font-family: var(--font-mono);
  font-size: 10px;
  opacity: 0.65;
  vertical-align: bottom;
}

.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  background: none;
  padding: 0;
  max-width: 88%;
}
.chat-chip {
  padding: 5px 11px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--status-ai);
  color: var(--status-ai-ink);
  background: var(--status-ai-soft);
  font-size: 12.5px;
  font-weight: var(--fw-medium);
  white-space: nowrap;
}
.chat-chip.is-tapped {
  background: var(--status-ai);
  color: var(--text-on-status);
  border-color: var(--status-ai);
}

.phone-caption {
  font-size: var(--fs-secondary);
  color: var(--text-muted);
  text-align: center;
}

/* Staged chat playback — bubbles appear one by one once the hero is on
   screen. Reduced motion (or no JS): everything is simply visible. */
@media (prefers-reduced-motion: no-preference) {
  .js .phone-thread .chat-msg {
    opacity: 0;
    transform: translateY(8px);
  }
  .js .phone-thread.play .chat-msg {
    animation: msg-in 0.4s var(--ease) forwards;
  }
  .js .phone-thread.play .chat-msg:nth-child(1) { animation-delay: 0.15s; }
  .js .phone-thread.play .chat-msg:nth-child(2) { animation-delay: 0.9s; }
  .js .phone-thread.play .chat-msg:nth-child(3) { animation-delay: 1.7s; }
  .js .phone-thread.play .chat-msg:nth-child(4) { animation-delay: 2.6s; }
  .js .phone-thread.play .chat-msg:nth-child(5) { animation-delay: 3.4s; }
  .js .phone-thread.play .chat-msg:nth-child(6) { animation-delay: 4.4s; }
}
@keyframes msg-in {
  to { opacity: 1; transform: none; }
}

/* -------------------------------------------------------------- proof bar -- */
.proof {
  background: var(--surface-tertiary);
  border-block: 1px solid var(--border-subtle);
}
.proof-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--sp-5);
  padding-block: clamp(22px, 3vw, 34px);
}
.proof-item { display: grid; gap: 2px; text-align: center; }
.proof-kpi {
  font-family: var(--font-mono);
  font-weight: var(--fw-semibold);
  font-size: clamp(24px, 2.6vw, 32px);
  color: var(--color-primary-ink);
}
/* The proof figures that are WORDS rather than numbers. Two of the four are now
   — the bar reads 24/7 · Auto · Google · 100% — so the rule is: numerals mono,
   words sans. The mono face is here for numerals and has NO Arabic coverage, so
   on /ar/ an Arabic value would fall back glyph by glyph to whatever the system
   offers, beside siblings rendering in the intended face. The sans is also the
   face of the label directly under it, which is what makes the two lines read
   as one phrase. */
.proof-kpi-word { font-family: var(--font-sans); }
.proof-label { font-size: var(--fs-secondary); color: var(--text-secondary); }

/* --------------------------------------------------------------- sections -- */
.section { padding-block: clamp(52px, 7vw, 92px); }
.section-alt { background: var(--surface-sunken); }

.section h2 {
  font-size: var(--landing-h2);
  line-height: 1.08;
  font-weight: var(--fw-bold);
  letter-spacing: -0.022em;
  margin-block-end: var(--sp-4);
  max-width: 22ch;
  /* Long compound words (Arabic/RTL variant, narrow phones) break instead of
     pushing the shell sideways. */
  overflow-wrap: break-word;
}
/* Highlighted keyword — the one accent every section title shares. The plain
   colour is the fallback; the clip only applies where it is supported. */
.section h2 .hl {
  color: var(--color-primary-ink);
}
@supports (background-clip: text) or (-webkit-background-clip: text) {
  .section h2 .hl {
    background-image: linear-gradient(
      100deg,
      var(--color-primary-ink),
      var(--color-secondary-ink) 92%
    );
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
  }
}
.section-sub {
  color: var(--text-secondary);
  max-width: 62ch;
  margin-block-end: var(--sp-7);
}
.section h2 + .card-grid,
.section h2 + .steps,
.section h2 + .roadmap-track { margin-block-start: var(--sp-7); }

/* A section that carries extra weight in the page's rhythm: more air, a wider
   reading measure, a larger title and a soft brand wash behind it. */
.section-lg {
  position: relative;
  isolation: isolate;
  overflow: hidden; /* keeps the decoration from ever widening the page */
  padding-block: clamp(68px, 9.5vw, 128px);
}
.section-lg::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: -22%;
  inset-inline-start: 50%;
  /* T5 — inset-inline-start correctly flips to the right edge in RTL, but the
     `translate` PROPERTY is physical and would keep pulling left, landing this
     decorative glow a full element-width off centre. calc(-50% * -1) = 50%. */
  translate: calc(-50% * var(--dir)) 0;
  width: min(1100px, 130%);
  aspect-ratio: 1;
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--color-primary) 11%, transparent),
    transparent 72%
  );
  pointer-events: none;
}
.section-lg h2 {
  font-size: var(--landing-h2-lg);
  max-width: 19ch;
}
.section-sub-lg {
  font-size: clamp(15.5px, 1.35vw, 18.5px);
  max-width: 68ch;
  margin-block-end: var(--sp-8);
}
.section-lg .card-grid { gap: var(--sp-5); }
.section-lg .card { padding: var(--sp-6) var(--sp-6) var(--sp-7); }

/* ------------------------------------------------------------------ cards -- */
.card-grid { display: grid; gap: var(--sp-4); }
.cols-2 { grid-template-columns: repeat(2, 1fr); }
.cols-3 { grid-template-columns: repeat(3, 1fr); }
.cols-4 { grid-template-columns: repeat(4, 1fr); }

/* Adaptive: the column count follows the CARD count, so adding or removing a
   card can never leave a hole. `min(100%, 300px)` keeps the track from
   overflowing a 320px viewport, where 100% is narrower than the 300px floor. */
.cols-fit { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }

.card {
  background: var(--surface-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--sp-6);
  box-shadow: var(--shadow-xs);
  transition: transform var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
@media (hover: hover) and (pointer: fine) {
  .card:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
}
@media (prefers-reduced-motion: reduce) {
  .card { transition: box-shadow var(--t) var(--ease); }
  .card:hover { transform: none; }
}
.card h3 {
  font-size: var(--fs-section);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  margin-block-end: var(--sp-2);
}
.card p { font-size: var(--fs-body); color: var(--text-secondary); }

.card-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-md);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  margin-block-end: var(--sp-4);
}
.card-icon svg { width: 22px; height: 22px; }
.card-icon.tint-ai { background: var(--status-ai-soft); color: var(--status-ai-ink); }
.card-icon.tint-success { background: var(--status-success-soft); color: var(--status-success-ink); }

/* ----------------------------------------------------------- feature card --
   The premium variant: a gradient rule that wipes across the top edge, a soft
   brand glow, a lifted icon. Everything animated here is transform/opacity —
   the layout never moves, so neighbouring cards cannot be pushed around and
   no horizontal overflow can appear. */
.feature-card {
  position: relative;
  overflow: hidden;
  transition:
    transform var(--lift-t) var(--ease),
    border-color var(--lift-t) var(--ease),
    box-shadow var(--lift-t) var(--ease),
    background var(--lift-t) var(--ease);
}
.feature-card::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  background: linear-gradient(
    90deg,
    var(--color-primary),
    var(--color-secondary-bright)
  );
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 300ms var(--ease);
}
[dir="rtl"] .feature-card::before { transform-origin: right center; }
.feature-card .card-icon {
  transition: transform var(--lift-t) var(--ease), box-shadow var(--lift-t) var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .feature-card:hover {
    transform: translateY(var(--lift-y));
    border-color: color-mix(in srgb, var(--color-primary) 34%, var(--border-subtle));
    box-shadow: var(--shadow-md), var(--glow-brand);
    background: color-mix(in srgb, var(--color-primary-soft) 26%, var(--surface-primary));
  }
  .feature-card:hover::before { transform: scaleX(1); }
  .feature-card:hover .card-icon {
    transform: translateY(-2px) scale(1.06);
    box-shadow: 0 6px 16px -8px var(--color-primary-ring);
  }
}
/* Keyboard parity: a focused control inside a card lights the card too. */
.feature-card:focus-within {
  border-color: color-mix(in srgb, var(--color-primary) 34%, var(--border-subtle));
  box-shadow: var(--shadow-md), var(--glow-brand);
}

@media (prefers-reduced-motion: reduce) {
  .feature-card,
  .feature-card::before,
  .feature-card .card-icon { transition: none; }
  .feature-card:hover { transform: none; }
  .feature-card:hover .card-icon { transform: none; }
  .feature-card::before { transform: scaleX(1); opacity: 0; }
  .feature-card:hover::before { opacity: 1; }
}

/* ------------------------------------------------- capacity mock (feature) --
   A presentational rendering of the real per-doctor schedule setting. Static
   spans on purpose: this page carries no booking logic. */
.capacity-field {
  display: grid;
  gap: var(--sp-2);
  margin-block-start: var(--sp-4);
  padding: var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-secondary);
}
.capacity-label {
  font-size: var(--fs-meta);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.capacity-opts { display: flex; flex-wrap: wrap; gap: 5px; }
.capacity-opt {
  padding: 3px 10px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  background: var(--surface-primary);
  color: var(--text-secondary);
  font-size: var(--fs-meta);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}
.capacity-opt.is-selected {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--text-on-primary);
}

/* ------------------------------------------------------------------ steps --
   A vertical timeline: a numbered node column on the inline-start edge, a
   dotted connector running between consecutive nodes, and a connected card
   per step. Same shape at every width — only the rail narrows. */
.steps {
  --rail-w: 44px;
  --rail-gap: var(--sp-4);
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--sp-4);
  max-width: 840px;
}
.step {
  position: relative;
  display: grid;
  grid-template-columns: var(--rail-w) 1fr;
  column-gap: var(--rail-gap);
  align-items: start;
}

/* Connector: from just under this step's node to the next one's. */
.step:not(:last-child)::before {
  content: "";
  position: absolute;
  inset-inline-start: calc(var(--rail-w) / 2 - 1px);
  top: var(--rail-w);
  height: calc(100% - var(--rail-w) + var(--sp-4));
  border-inline-start: 2px dotted var(--border-default);
  transition: border-color var(--lift-t) var(--ease);
}

.step-rail {
  display: flex;
  justify-content: center;
  /* Nudges the node onto the card's first text line. */
  padding-block-start: 2px;
}
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--rail-w);
  height: var(--rail-w);
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-primary-ring);
  background: var(--color-primary-soft);
  color: var(--color-primary-ink);
  font-family: var(--font-mono);
  font-weight: var(--fw-semibold);
  font-size: 16px;
  transition:
    background var(--lift-t) var(--ease),
    color var(--lift-t) var(--ease),
    border-color var(--lift-t) var(--ease),
    box-shadow var(--lift-t) var(--ease);
}

.step-body {
  position: relative;
  display: grid;
  justify-items: start;
  gap: 4px;
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--surface-primary);
  box-shadow: var(--shadow-xs);
  transition:
    transform var(--lift-t) var(--ease),
    border-color var(--lift-t) var(--ease),
    box-shadow var(--lift-t) var(--ease);
}
.step-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-md);
  background: var(--surface-tertiary);
  color: var(--color-secondary-ink);
  margin-block-end: 6px;
  transition: transform var(--lift-t) var(--ease), background var(--lift-t) var(--ease);
}
.step-icon svg { width: 19px; height: 19px; }
.step h3 { font-size: var(--fs-section); font-weight: var(--fw-semibold); margin-block-end: 2px; }
.step p { color: var(--text-secondary); font-size: var(--fs-body); }

/* Hover polish, pointer devices only — touch never gets a stuck hover state. */
@media (hover: hover) and (pointer: fine) {
  .step:hover .step-body {
    transform: translateY(-3px);
    border-color: var(--color-primary-ring);
    box-shadow: var(--shadow-sm);
  }
  .step:hover .step-num {
    background: var(--color-primary);
    color: var(--text-on-primary);
    box-shadow: 0 0 0 4px var(--color-primary-ring);
  }
  .step:hover .step-icon {
    /* T1 — physical translateX; --dir keeps the nudge in the reading direction. */
    transform: translateX(calc(3px * var(--dir)));
    background: var(--color-primary-soft);
  }
  /* The segment flowing out of the hovered step lights up. */
  .step:hover::before { border-color: var(--color-primary); }
}

@media (prefers-reduced-motion: reduce) {
  .step-body,
  .step-num,
  .step-icon,
  .step::before { transition: none; }
  .step:hover .step-body { transform: none; }
  .step:hover .step-icon { transform: none; }
}

/* --------------------------------------------------------------- personas -- */
.persona h3 {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
.persona-tag {
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
}
.tag-owner { background: var(--color-primary-soft); color: var(--color-primary-ink); }
.tag-doctor { background: var(--color-secondary-soft); color: var(--color-secondary-ink); }
.tag-patient { background: var(--status-ai-soft); color: var(--status-ai-ink); }

/* --------------------------------------------------------------- security --
   Each guarantee is a quiet, self-contained panel. The hover is deliberately
   restrained — a firmer border, a lift of a couple of pixels and a green
   assurance glow — because the subject is trust, not delight. */
.security-list {
  list-style: none;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-3) var(--sp-4);
  max-width: 960px;
}
.security-list li {
  position: relative;
  padding-block: var(--sp-4);
  padding-inline: 46px var(--sp-5);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--surface-primary);
  color: var(--text-secondary);
  font-size: var(--fs-body);
  transition:
    transform var(--lift-t) var(--ease),
    border-color var(--lift-t) var(--ease),
    box-shadow var(--lift-t) var(--ease),
    background var(--lift-t) var(--ease);
}
.security-list li strong { color: var(--text-primary); }
.security-list li::before {
  content: "";
  position: absolute;
  inset-inline-start: var(--sp-5);
  top: calc(var(--sp-4) + 2px);
  width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--status-success-soft)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%23287d52' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 11px 9px no-repeat;
  transition: transform var(--lift-t) var(--ease), box-shadow var(--lift-t) var(--ease);
}
[data-theme="dark"] .security-list li::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%238ad4aa' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

@media (hover: hover) and (pointer: fine) {
  .security-list li:hover {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--status-success) 40%, var(--border-subtle));
    box-shadow: var(--shadow-sm), var(--glow-success);
    background: color-mix(in srgb, var(--status-success-soft) 22%, var(--surface-primary));
  }
  .security-list li:hover::before {
    transform: scale(1.1);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--status-success) 18%, transparent);
  }
}
/* Same treatment for keyboard users, should a guarantee ever gain a control. */
.security-list li:focus-within {
  border-color: color-mix(in srgb, var(--status-success) 40%, var(--border-subtle));
  box-shadow: var(--shadow-sm), var(--glow-success);
}

@media (prefers-reduced-motion: reduce) {
  .security-list li,
  .security-list li::before { transition: none; }
  .security-list li:hover { transform: none; }
  .security-list li:hover::before { transform: none; }
}

/* ---------------------------------------------------------------- roadmap -- */
.chip-soon {
  display: inline-block;
  vertical-align: middle;
  margin-inline-start: var(--sp-3);
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--status-warning-soft);
  color: var(--status-warning-ink);
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
/* A single connected track. Every milestone shares one status — "Planned" —
   because nothing here has shipped; the dashed node and the written tag carry
   that, so status never depends on colour alone. */
.roadmap-track {
  --node: 14px;
  --node-col: 26px;
  list-style: none;
  padding: 0;
  display: grid;
  gap: var(--sp-2);
  max-width: 720px;
  margin-block-start: var(--sp-7);
}
.roadmap-item {
  position: relative;
  display: grid;
  grid-template-columns: var(--node-col) minmax(0, 1fr) auto;
  align-items: center;
  column-gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-md);
  background: var(--surface-primary);
  transition:
    transform var(--lift-t) var(--ease),
    border-color var(--lift-t) var(--ease),
    box-shadow var(--lift-t) var(--ease),
    background var(--lift-t) var(--ease);
}

/* The rail. Each row draws its own full-height segment (plus half the gap on
   both sides) so the track stays continuous however the rows wrap; the opaque
   node sits on top and masks the line where it crosses. The first and last
   rows stop at their node, capping the track. */
.roadmap-item::before {
  content: "";
  position: absolute;
  inset-inline-start: calc(var(--sp-4) + var(--node-col) / 2 - 1px);
  top: calc(var(--sp-2) / -2);
  bottom: calc(var(--sp-2) / -2);
  border-inline-start: 2px dotted var(--border-default);
  transition: border-color var(--lift-t) var(--ease);
}
.roadmap-item:first-child::before { top: 50%; }
.roadmap-item:last-child::before { bottom: 50%; }

.roadmap-node {
  position: relative;
  z-index: 1;
  justify-self: center;
  width: var(--node);
  height: var(--node);
  border-radius: var(--radius-pill);
  border: 2px dashed var(--color-secondary);
  background: var(--surface-primary);
  transition: box-shadow var(--lift-t) var(--ease), background var(--lift-t) var(--ease);
}
.roadmap-name { color: var(--text-primary); font-size: var(--fs-body); }
.roadmap-status {
  flex: none;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--status-warning-soft);
  color: var(--status-warning-ink);
  font-size: var(--fs-meta);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
  .roadmap-item:hover {
    /* T2 */
    transform: translateX(calc(3px * var(--dir)));
    border-style: solid;
    border-color: var(--color-primary-ring);
    box-shadow: var(--shadow-sm);
    background: color-mix(in srgb, var(--color-primary-soft) 24%, var(--surface-primary));
  }
  .roadmap-item:hover .roadmap-node {
    background: var(--color-secondary-soft);
    box-shadow: 0 0 0 4px var(--color-secondary-ring);
  }
  .roadmap-item:hover::before { border-color: var(--color-primary); }
}

@media (prefers-reduced-motion: reduce) {
  .roadmap-item,
  .roadmap-item::before,
  .roadmap-node { transition: none; }
  .roadmap-item:hover { transform: none; }
}

/* ------------------------------------------------------------------ plans -- */
.plan h3 {
  font-family: var(--font-serif);
  font-style: italic;
  font-weight: var(--fw-medium);
  font-size: 21px;
  color: var(--color-primary-ink);
}
.plans-note {
  margin-block-start: var(--sp-6);
  color: var(--text-secondary);
  font-size: var(--fs-body);
}

/* Off-screen but ANNOUNCED. Used for the "Previous price:" / "Current price:"
   labels before every struck-through pair, for the comparison table's caption,
   and nowhere decorative. The clip-path/1px recipe (rather than display:none or
   visibility:hidden) is the one that keeps the text in the accessibility tree.
   Not `.sr-only`: this file has no utility layer, and a two-word class name in
   a hand-written stylesheet is worth the clarity. */
.vis-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------- plan card --
   ORDER IS THE ARGUMENT: name → price → "Start my FREE trial", then everything
   that supports the decision rather than making it. The three lines at the top
   are what a scanning reader gets; the rest rewards a reader who stopped.

   TWO CONTROLS, SIDE BY SIDE, NEVER NESTED. .plan-stretch covers the whole card
   and opens the pricing dialog; .plan-trial sits above it in the stacking order
   and runs the Book-a-demo flow. Both are real <button>s and both are children
   of the <article> — so there is no button inside a button, no card-level click
   handler racing a child control, and nothing to stopPropagation() away: a tap
   lands on exactly one element, and it is the topmost one under the finger. */
/* The badges sit ON the top edge of two cards, so the row needs headroom or
   they would collide with the section sub above it. */
/* THE CARD ROW IS WIDER THAN THE SHELL — BY EXACTLY THE MEASURED SHORTFALL.
   Every feature point is a 3–5 word label written to sit on ONE line, and at
   the shell's 1140px four cards leave 187px of text each, which wraps the
   longest bullet in both locales. This bleeds the row symmetrically out of the
   shell until each bullet has the 211.8px its longest line actually measures —
   1180px of row, 212.0px of text, and not a pixel of padding beyond it. The
   derivation is on --landing-plan-grid.

   Only the GRID moves. The eyebrow, the H2, the section sub and the note under
   the cards all stay on --landing-shell, so the section's left edge still lines
   up with every other section on the page — which is why this is not simply a
   wider .shell on #plans, where a left-aligned H2 would visibly step out.

   The bleed is a clamp, not a media query, so it closes itself: the moment the
   viewport stops having room the third argument stops winning and the row is
   back inside the shell, continuously, with no breakpoint to maintain. 24px is
   withheld because 100vw counts the classic desktop scrollbar and the layout
   does not — without it the row could overhang by exactly a scrollbar's width
   on the one platform that still paints one.

   Runs in both locales untouched: `margin-inline` and a symmetric width are
   direction-agnostic, so the Arabic row bleeds by the same amount on the same
   side of the page. */
.plan-grid {
  align-items: stretch;
  margin-block-start: var(--sp-8);
  /* The ceiling is measured against the shell's CONTENT box, not its outer
     width: --landing-shell includes the gutter, and 64px is that gutter at
     every viewport this bleed is non-zero at (the clamp() on .shell has already
     hit its 32px ceiling by ~800px). So --landing-plan-grid means what it says
     — the width of the card row itself. */
  --plan-bleed: clamp(0px, (100vw - var(--landing-shell) - 24px) / 2, (var(--landing-plan-grid) - (var(--landing-shell) - 64px)) / 2);
  width: calc(100% + 2 * var(--plan-bleed));
  margin-inline: calc(-1 * var(--plan-bleed));
}
.plan-card {
  position: relative;   /* containing block for .plan-stretch and the badge */
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding: var(--sp-7) var(--sp-6) var(--sp-6);
  border-radius: var(--radius-2xl);
}
.plan-card h3 {
  font-size: clamp(24px, 2.1vw, 30px);
  /* The plan name is the card's title, not a brand accent: it takes the
     document's own text colour, which is what makes the price beneath it — the
     one coloured, oversized thing in the top third — read as the focal point. */
  color: var(--text-primary);
  margin-block-end: var(--sp-2);
}

/* ------------------------------------------------------------ plan badge --
   Straddles the card's top edge, half in and half out, which is what gives the
   two flagged cards their lifted look without moving them out of the shared
   row grid (it is absolutely positioned, so it occupies no row).
   ⚠ Both badges are CLAIMS. "Most popular" says other clinics choose Pro most
   often, and it is only honest while that is true of the real book of business;
   "Best value" is a statement about this price list, which the page can always
   support. Do not add a third badge for decoration. */
.plan-badge {
  position: absolute;
  top: 0;
  inset-inline-start: 50%;
  transform: translate(calc(-50% * var(--dir)), -50%);
  z-index: 3;
  padding: 5px 14px;
  border-radius: var(--radius-pill);
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
  box-shadow: var(--shadow-sm);
}
.plan-card-featured .plan-badge {
  background: var(--amber-300);
  color: var(--azure-900);
}
.plan-card-value .plan-badge {
  background: var(--azure-100);
  color: var(--azure-800);
}
[lang="ar"] .plan-badge { letter-spacing: normal; }

/* The sparkle on the featured card: decoration, aria-hidden, and the one place
   the gold appears outside the badge and the button. */
.plan-mark {
  position: absolute;
  /* Level with the plan name, and on the trailing edge — so it mirrors to the
     top-left of the Arabic card without a rule of its own. */
  top: var(--sp-7);
  inset-inline-end: var(--sp-6);
  color: var(--amber-300);
  line-height: 0;
}
.plan-mark svg { width: 22px; height: 22px; }

/* ---------------------------------------------------------- featured card --
   Pro is the plan the page recommends, so it is the one card that inverts:
   a deep navy fill, gold accents, and a gold CTA. Everything in it is derived
   from the same azure and amber ramps the rest of the site uses — it is a
   different arrangement of the palette, not a second palette. */
/* RAISED WITHOUT LEAVING THE GRID. The card stands taller than its neighbours
   on both edges — but its box still occupies exactly the same seven rows, so
   its price, CTA and every line under them stay level with the other three.
   The trick is that the navy fill is painted by a pseudo-element that is
   outset 14px vertically, rather than by the card itself: the paint escapes the
   row, the content does not. `isolation` is load-bearing — it gives the card
   its own stacking context, so the z-index:-1 layer lands behind the card's
   content instead of behind the section's background, where it would vanish. */
.plan-card-featured {
  isolation: isolate;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
.plan-card-featured::before {
  content: "";
  position: absolute;
  inset: -14px 0;
  z-index: -1;
  border-radius: var(--radius-2xl);
  border: 1px solid color-mix(in srgb, var(--amber-300) 28%, transparent);
  background:
    linear-gradient(
      168deg,
      color-mix(in srgb, var(--azure-900) 78%, var(--slate-950)),
      var(--azure-900) 58%,
      color-mix(in srgb, var(--azure-900) 76%, var(--azure-700))
    );
  box-shadow: var(--shadow-lg), 0 26px 54px -26px color-mix(in srgb, var(--azure-900) 72%, transparent);
}
/* The badge rides the pseudo-element's edge, not the card's. */
.plan-card-featured .plan-badge { top: -14px; }
.plan-card-featured h3,
.plan-card-featured .price-now,
.plan-card-featured .price-setup strong { color: var(--slate-25); }
.plan-card-featured .price-label,
.plan-card-featured .plan-hint { color: var(--amber-300); }
.plan-card-featured .plan-audience,
.plan-card-featured .price-unit,
.plan-card-featured .price-setup,
.plan-card-featured .plan-includes li { color: var(--slate-300); }
.plan-card-featured .price-was { color: var(--slate-400); }
.plan-card-featured .price-setup,
.plan-card-featured .plan-hint { border-color: color-mix(in srgb, var(--slate-25) 16%, transparent); }
/* The lead-in on navy. --text-primary and --border-subtle are both tuned for a
   light card, so the seam takes the same slate-25 16% rule the two dividers
   above already use and the text takes the card's brightest ink — the lead-in
   must out-rank the six points below it here exactly as it does on white. */
.plan-card-featured .plan-includes .includes-lead {
  color: var(--slate-25);
  border-block-end-color: color-mix(in srgb, var(--slate-25) 16%, transparent);
}
/* The check mark on a navy surface: the light-on-dark variant of the same
   glyph, in a tinted well rather than the light-green one. */
.plan-card-featured .plan-includes li::before {
  background: color-mix(in srgb, var(--status-success) 30%, transparent)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%238ad4aa' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 9px 7px no-repeat;
}
/* Gold CTA. Same geometry as .btn-primary — only the fill changes, so the
   button is still the shared component, restyled for one surface. */
.plan-card-featured .plan-trial {
  background: var(--amber-300);
  color: var(--azure-900);
  border-color: transparent;
  box-shadow: 0 10px 24px -14px color-mix(in srgb, var(--amber-300) 90%, transparent);
}
.plan-card-featured .plan-trial:hover {
  background: color-mix(in srgb, var(--amber-300) 82%, #ffffff);
}
.plan-card-featured .plan-trial:active {
  background: var(--amber-500);
  color: var(--slate-25);
}
.plan-card-featured .plan-stretch:focus-visible { outline-color: var(--amber-300); }

/* The value card keeps the light surface and takes only the badge, so the two
   flagged cards do not compete for the same attention. */
.plan-card-value { border-color: color-mix(in srgb, var(--color-primary) 26%, var(--border-subtle)); }

/* ---------------------------------------------- four cards, one row grid --
   THE ALIGNMENT MECHANISM. All four cards carry the same seven rows — name,
   price, CTA, audience, setup fee, inclusions, hint — and `subgrid` hands those
   seven rows to the PARENT grid, so every row is sized once for all four cards
   and each one starts at the same y in each card. A three-line description next
   to a one-line description no longer pushes one card's setup fee below its
   neighbour's, and a card with three inclusion bullets no longer drops its own
   footer hint below the two-bullet cards'.

   Done with the layout engine rather than with reserved heights: no per-row
   magic number to maintain, and it re-measures itself for Arabic, for a longer
   translation, and at every width — which a hand-tuned min-height cannot.

   Scoped to the 4-column layout, where the cards sit in ONE grid row and the
   row-gap therefore has no other job: it is set to match the flex `gap` above,
   so the spacing inside a card is identical whether this block applies or not.
   Below 981px the cards flow as the flex columns they already are.

   The @supports guard is the fallback: a browser without subgrid keeps today's
   flex behaviour — cards that are individually correct and merely not aligned
   row-for-row. Nothing is lost, nothing overlaps. */
@media (min-width: 981px) {
  @supports (grid-template-rows: subgrid) {
    .plan-grid { row-gap: var(--sp-2); }
    .plan-card {
      display: grid;
      grid-template-rows: subgrid;
      /* The seven in-flow children. .plan-stretch is absolutely positioned, so
         it takes no track — but if a row is ever ADDED to a card, this number
         must move with it or the last row will spill outside the card. */
      grid-row: span 7;
    }
  }
}
/* The audience line and everything under it are supporting detail: they get
   air ABOVE them, which is what separates "the offer" from "the small print". */
.plan-audience {
  margin-block-start: var(--sp-5);
  color: var(--text-secondary);
  font-size: var(--fs-body);
  line-height: var(--lh-normal);
}

/* The card-wide dialog trigger. An invisible, absolutely-positioned <button>
   with an off-screen accessible name — a native control, so Enter, Space,
   focus order, and focus restoration when the dialog closes all come free.
   z-index 1 keeps it under .plan-trial (z-index 2) and over nothing else. */
.plan-stretch {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  border-radius: var(--radius-lg);
  -webkit-tap-highlight-color: transparent;
}
/* The button is invisible, so its focus ring is drawn on the card's own edge —
   inset:0 means the outline traces exactly the card. Without this rule a
   keyboard user would move focus onto the card and see nothing at all. */
.plan-stretch:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 3px;
}

/* Above the stretched trigger, so a tap on the CTA is a tap on the CTA. */
.plan-trial {
  position: relative;
  z-index: 2;
  width: 100%;
  margin-block-start: var(--sp-5);
  min-height: 50px;      /* comfortably past the 44px tap-target floor */
  font-size: 15px;
}
/* The chevron travels with the reading direction — mirrored in RTL by the rule
   in the directional-icons block, with the other two forward arrows. */
.plan-trial .btn-go {
  width: 15px;
  height: 15px;
  flex: none;
  opacity: 0.85;
}

/* "Full pricing details ›" — the sighted affordance for .plan-stretch, and
   aria-hidden because the button already announces the same thing per plan.
   Deliberately quiet: it must read as a hint, not as a second CTA competing
   with the free-trial button two lines above it. */
.plan-hint {
  /* auto: pinned to the card's bottom edge, so four cards of unequal copy line
     their footers up without forcing equal heights on the text above them. */
  margin-block-start: auto;
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--border-subtle);
  font-size: var(--fs-secondary);
  font-weight: var(--fw-semibold);
  color: var(--color-primary-ink);
  transition: color var(--t) var(--ease);
}
.plan-hint span::after {
  content: "";
  display: inline-block;
  width: 0.42em;
  height: 0.42em;
  margin-inline-start: 0.5em;
  border-inline-end: 1.6px solid currentColor;
  border-block-start: 1.6px solid currentColor;
  /* A chevron pointing along the reading direction: --dir flips it in RTL,
     the same one mechanism the four other physical transforms on this page
     use, rather than an [dir="rtl"] override of its own. */
  transform: rotate(calc(45deg * var(--dir)));
  vertical-align: middle;
}
/* The whole card is the trigger, so the whole card reacts — the hint warms up
   with it. .card:hover already supplies the lift and the shadow. */
@media (hover: hover) and (pointer: fine) {
  .plan-card:hover { border-color: color-mix(in srgb, var(--color-primary) 34%, var(--border-subtle)); }
  .plan-card:hover .plan-hint { color: var(--color-primary-hover); }
  /* The card itself paints nothing, so its lift is the pseudo-element's job —
     without this it would grow a shadow around an invisible box. */
  .plan-card-featured:hover { border-color: transparent; box-shadow: none; }
  .plan-card-featured:hover::before { border-color: color-mix(in srgb, var(--amber-300) 50%, transparent); }
  .plan-card-featured:hover .plan-hint { color: var(--amber-100); }
}


/* ------------------------------------------------------------------ price -- */
/* ONE PRICE SHAPE FOR ALL FOUR CARDS, so all four free-trial buttons start at
   the same y. Three rows, always, in every language and at every width:

     row 1   the label — "Current offer", or "Starting from" on the two clinic
             cards, whose figure is the lowest band's rate
     row 2   the struck comparison price, alone on its line
     row 3   the current price and its billing unit

   Both variants therefore cost exactly three rows. The reserve below is the
   guarantee: `per doctor/month` is roughly twice the width of `/month`, and in
   the narrow 4-column band it can wrap to a second line on the clinic cards
   only. min-block-size holds the region at its tallest shape, so that wrap
   consumes reserved space instead of pushing one pair of buttons down. It is
   uniform, so it cannot itself misalign anything — and the air it leaves when
   nothing wraps is exactly the separation the price and the CTA want anyway.

   The label is NOT a badge and must not become one: a chip here would read as a
   discount flag, and this page claims no percentage (+10% on the current price
   is not a 10% cut from the comparison price). */
.plan-price {
  min-block-size: 7.5rem;
  margin-block-start: var(--sp-3);
}
.price-label {
  font-size: 11.5px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Quiet: it names the figure below it, and the figure is what should be
     read first. On the featured card it turns gold, which is the only place
     this label carries any weight of its own. */
  color: var(--text-muted);
}

/* The price row. Logical order is [comparison] [current] [unit], so RTL reads
   it right-to-left in the same order with no override — and each amount is
   wrapped in <bdi> in the markup, which keeps "$1,098.90" internally LTR
   inside an Arabic line. baseline alignment keeps the small struck figure
   sitting on the big one's baseline rather than floating at its cap height. */
.price-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2);
  margin-block-start: var(--sp-2);
}
/* The comparison price takes its own line above the current one — the layout
   the offer is normally read in, and the one that halves the width the amount
   row needs, which is what keeps the long per-doctor unit on one line.
   (The two off-screen price labels are position:absolute, so they are out of
   flow and add no rows of their own.) */
.price-line .price-was { flex-basis: 100%; }
/* The largest thing in the card by a wide margin — the one element the eye is
   meant to land on after the plan name. */
.price-now {
  font-size: clamp(34px, 3.2vw, 46px);
  font-weight: var(--fw-bold);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--text-primary);
}
/* Lower emphasis, never illegible: --text-muted passes contrast on both
   surfaces, and the line is drawn at 1.5px so it reads as a strikethrough and
   not as a rendering artefact. The strikethrough is decoration — the meaning is
   carried by the off-screen "Previous price:" label beside it. */
.price-was {
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
  text-decoration-color: color-mix(in srgb, var(--text-muted) 70%, transparent);
}
.price-unit {
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  /* "$60/month" — the slash belongs to the amount, so the gap is hairline. */
  margin-inline-start: calc(var(--sp-2) * -1 + 1px);
}
/* "per doctor/month" is a phrase, not a suffix: it takes a real word space. */
.price-unit-wide { margin-inline-start: 0; }

/* The setup fee is ONE line: label at the start, figures at the end.
   It must stay one line on the clinic cards too, where the pair is
   "$1,098.90 $999" rather than "$275 $250" — nearly half again as wide — and in
   Arabic, where the label itself ("رسوم إعداد تُدفع مرة واحدة:") is nearly
   twice the English width. So the row never wraps, the amounts are unbreakable,
   and the LABEL is what gives: it wraps inside its own column instead of
   pushing the price onto a line of its own. Baseline alignment then keeps the
   figures on the label's first line, which is where the eye looks for them. */
.price-setup {
  display: flex;
  flex-wrap: nowrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
  /* A rule above it, matching the one above the footer hint: the card reads as
     offer / detail / footer, and the two dividers are what mark the seams. */
  margin-block-start: var(--sp-5);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--border-subtle);
  /* One step down from the body scale. This is the row that has to hold a
     label and two figures inside a quarter-width card, and it is supporting
     information, not the headline price — so the type carries the fit. */
  font-size: var(--fs-secondary);
  color: var(--text-secondary);
}
.price-setup-label {
  font-weight: var(--fw-medium);
  min-inline-size: 0;   /* allows the label to shrink and wrap, not the price */
}
.price-setup-pair {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  flex: none;           /* the amounts are never compressed or broken */
  white-space: nowrap;
}
.price-setup strong { color: var(--text-primary); font-weight: var(--fw-semibold); }

/* Inclusions. Same checkmark vocabulary as the security list, so "included"
   looks the same wherever the page says it. The tick is a background image on
   a pseudo-element: decorative, never announced, and never the only signal —
   the text beside it says what is included. */
.plan-includes,
.includes-list {
  list-style: none;
  padding: 0;
  margin-block-start: var(--sp-4);
  display: grid;
  gap: var(--sp-2);
}
.plan-includes li,
.includes-list li {
  position: relative;
  padding-inline-start: 26px;
  font-size: var(--fs-secondary);
  color: var(--text-secondary);
  line-height: var(--lh-normal);
}
.plan-includes li::before,
.includes-list li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  /* Centred on the FIRST LINE of the point, computed rather than nudged: the
     line box is 1.5em (the line-height declared just above) and the mark is
     17px, so half the difference centres it. A fixed `top` was a third of a
     pixel out at one size and visibly out at another — this holds at every
     font size, in both locales, and keeps every mark on the same optical line
     as the text beside it. */
  top: calc((1.5em - 17px) / 2);
  width: 17px;
  height: 17px;
  border-radius: var(--radius-pill);
  background: var(--status-success-soft)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%23287d52' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 9px 7px no-repeat;
}
[data-theme="dark"] .plan-includes li::before,
[data-theme="dark"] .includes-list li::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%238ad4aa' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* The inclusion list is the last thing before the hint, and the hint is pinned
   to the bottom edge by the auto margin — so four cards of unequal copy still
   line their footers up without forcing equal heights on the text above. */
.plan-includes { margin-block-end: var(--sp-2); }

/* THE PRO CARDS' FIRST POINT IS A LEAD-IN, NOT A CAPABILITY. "Everything in
   Starter, plus:" names the plan this tier INHERITS; the six checked points
   under it are what the tier ADDS. Given a check mark it read as a seventh
   feature and the six below it lost their "plus" — the one thing the point
   exists to say.

   So it leaves the check-mark column entirely and takes the card's own seam
   instead: the same 1px --border-subtle rule .price-setup and .plan-hint
   already draw, here marking inherited-above / new-below. No new colour, no
   new border token, no new glyph — the card had a word for "section break"
   and this is it.

   Weight and colour come from CSS, not from a <strong> in the dictionary, so
   neither translator carries markup for a styling decision — the same ruling
   the retired .includes-link rule was written under. */
.plan-includes .includes-lead {
  padding-inline-start: 0;
  padding-block-end: var(--sp-2);
  border-block-end: 1px solid var(--border-subtle);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
/* `content: none` suppresses the pseudo-element outright rather than hiding a
   box that still occupies the inline start — which is why the padding above
   can go to 0 and the text can sit flush with the card's other rows. */
.plan-includes .includes-lead::before { content: none; }

/* -------------------------------------------------------------------- faq -- */
.faq-item {
  background: var(--surface-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  margin-block-end: var(--sp-3);
  overflow: hidden;
}
.faq-item summary {
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-5) var(--sp-6);
  font-weight: var(--fw-semibold);
  font-size: var(--fs-body-lg);
  cursor: pointer;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "";
  flex: none;
  width: 12px;
  height: 8px;
  background: var(--select-chevron) center / contain no-repeat;
  transition: transform var(--t) var(--ease);
}
.faq-item[open] summary::after { transform: rotate(180deg); }
.faq-item summary:hover { background: var(--surface-hover); }
.faq-item p {
  padding: 0 var(--sp-6) var(--sp-5);
  color: var(--text-secondary);
  font-size: var(--fs-body);
  max-width: 68ch;
}

/* ---------------------------------------------------------------- contact -- */
/* Three cards now (WhatsApp, email, call), so the fixed 2-column track became a
   lone card stranded on the second row. auto-fit + minmax lets the count follow
   the width — three across on a desktop, two on a tablet — and the 760px rule
   further down still forces the single column on a phone. A 220px floor is what
   keeps a card wide enough for its 48px icon plus two lines of copy. */
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--sp-4);
  margin-block-start: var(--sp-7);
}
/* The number under "Call us now". Tabular figures so it does not shimmer when
   landing.js swaps the placeholder for the real one, and dir="ltr" is on the
   ELEMENT in the markup because a phone number in an RTL line reorders. */
.contact-phone {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.01em;
}
/* Whole card is the link: one target, generous padding, and a floor well past
   the 44px minimum so it stays comfortable to tap. */
.contact-card {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  min-height: 92px;
  padding: var(--sp-5) var(--sp-5);
  text-decoration: none;
  color: inherit;
  transition:
    transform var(--lift-t) var(--ease),
    border-color var(--lift-t) var(--ease),
    box-shadow var(--lift-t) var(--ease),
    background var(--lift-t) var(--ease);
}
.contact-card .card-icon {
  margin-block-end: 0;
  flex: none;
  width: 48px;
  height: 48px;
  transition: transform var(--lift-t) var(--ease), box-shadow var(--lift-t) var(--ease);
}
.contact-card .card-icon svg { width: 25px; height: 25px; }
.contact-copy { display: grid; gap: 2px; min-width: 0; }
.contact-copy strong {
  font-size: var(--fs-section);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  line-height: var(--lh-snug);
  transition: color var(--t-fast) var(--ease);
}
.contact-copy span { font-size: var(--fs-body); color: var(--text-secondary); }

/* Trailing affordance — slides forward with the pointer. */
.contact-go {
  flex: none;
  margin-inline-start: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  opacity: 0.55;
  transition: transform var(--lift-t) var(--ease), opacity var(--lift-t) var(--ease),
    color var(--lift-t) var(--ease);
}
.contact-go svg { width: 19px; height: 19px; }
.contact-card .envelope-flap {
  transform-origin: 12px 7.5px;
  transition: transform var(--lift-t) var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .contact-card:hover { transform: translateY(var(--lift-y)); box-shadow: var(--shadow-md); }
  .contact-card:hover .contact-copy strong { color: var(--color-primary-ink); }
  /* T3 */
  .contact-card:hover .contact-go { opacity: 1; transform: translateX(calc(4px * var(--dir))); }
  .contact-card:hover .card-icon { transform: scale(1.06); }

  /* WhatsApp leans on the palette's success green; email on the brand blue.
     Neither introduces a colour the page does not already use. */
  .contact-whatsapp:hover {
    border-color: color-mix(in srgb, var(--status-success) 42%, var(--border-subtle));
    box-shadow: var(--shadow-md), var(--glow-success);
    background: color-mix(in srgb, var(--status-success-soft) 26%, var(--surface-primary));
  }
  /* T4 — the nudge is direction-aware; the WhatsApp MARK itself is never
     mirrored (see the [dir="rtl"] icon rules in the i18n section). */
  .contact-whatsapp:hover .card-icon { transform: scale(1.06) translateX(calc(2px * var(--dir))); }
  .contact-whatsapp:hover .contact-copy strong { color: var(--status-success-ink); }
  .contact-whatsapp:hover .contact-go { color: var(--status-success-ink); }

  .contact-email:hover {
    border-color: color-mix(in srgb, var(--color-primary) 42%, var(--border-subtle));
    box-shadow: var(--shadow-md), var(--glow-brand);
    background: color-mix(in srgb, var(--color-primary-soft) 28%, var(--surface-primary));
  }
  .contact-email:hover .contact-go { color: var(--color-primary-ink); }
  /* The flap lifts a few degrees — the envelope reads as opening. */
  .contact-email:hover .envelope-flap { transform: translateY(-1.5px) scaleY(0.78); }
}

/* Focus parity with hover, plus the page's standard ring. */
.contact-card:focus-visible {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
.contact-whatsapp:focus-visible { border-color: color-mix(in srgb, var(--status-success) 42%, var(--border-subtle)); }
.contact-email:focus-visible { border-color: color-mix(in srgb, var(--color-primary) 42%, var(--border-subtle)); }

/* Pressed feedback — settles onto the surface. */
.contact-card:active {
  transform: translateY(1px);
  box-shadow: var(--shadow-xs);
  background: var(--surface-hover);
}
.contact-card:active .card-icon { transform: scale(0.98); }

@media (prefers-reduced-motion: reduce) {
  .contact-card,
  .contact-card .card-icon,
  .contact-go,
  .contact-card .envelope-flap { transition: none; }
  .contact-card:hover,
  .contact-card:focus-visible,
  .contact-card:active { transform: none; }
  .contact-card:hover .card-icon,
  .contact-card:active .card-icon { transform: none; }
  .contact-card:hover .contact-go { transform: none; }
}

/* --------------------------------------------------------------- cta band -- */
/* The band used to be a solid --color-primary slab, which read as a foreign
   block dropped between the sunken Contact section above and the light footer
   below. It is now a brand-TINTED surface built from the same semantic tokens
   as its neighbours: the gradient starts at the preceding section's colour and
   ends at the footer's, so both seams disappear and the tint carries the brand
   in the middle. Because every stop is a semantic token, the dark theme is not
   a second design — the same rule re-resolves against the dark palette. */
.cta-band {
  position: relative;
  isolation: isolate;
  overflow: hidden; /* the glow can never widen the page */
  background: linear-gradient(
    180deg,
    var(--surface-sunken) 0%,
    color-mix(in srgb, var(--color-primary) 8%, var(--surface-primary)) 42%,
    color-mix(in srgb, var(--color-primary) 5%, var(--surface-primary)) 68%,
    var(--surface-primary) 100%
  );
  color: var(--text-primary);
  border-block: 1px solid var(--border-subtle);
}
/* Soft brand halo behind the CTA. Decorative only: no pointer events, sits
   below the content, no animation — nothing for reduced-motion to disable. */
.cta-band::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: -38%;
  inset-inline-start: 50%;
  translate: calc(-50% * var(--dir)) 0;
  inline-size: min(880px, 120%);
  aspect-ratio: 1;
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--color-primary) 16%, transparent),
    transparent 70%
  );
  pointer-events: none;
}
[data-theme="dark"] .cta-band {
  /* Dark surfaces need a heavier tint to separate from the page at all, and a
     touch more glow — the same shape, retuned, not a different treatment. */
  background: linear-gradient(
    180deg,
    var(--surface-sunken) 0%,
    color-mix(in srgb, var(--color-primary) 20%, var(--surface-secondary)) 42%,
    color-mix(in srgb, var(--color-primary) 13%, var(--surface-secondary)) 68%,
    var(--surface-primary) 100%
  );
}
[data-theme="dark"] .cta-band::before {
  background: radial-gradient(
    closest-side,
    color-mix(in srgb, var(--color-primary) 26%, transparent),
    transparent 70%
  );
}

.cta-inner { text-align: center; display: grid; gap: var(--sp-4); justify-items: center; }
.cta-band h2 { max-width: none; margin: 0; }
.cta-band p { color: var(--text-secondary); }
/* The button is now the only saturated element in the band, so it carries the
   brand and reads as the single action. Back to the standard primary button —
   the inverted treatment only existed because the band itself was blue. */
.cta-band .btn-primary {
  background: var(--color-primary);
  color: var(--text-on-primary);
  box-shadow: var(--shadow-sm);
}
.cta-band .btn-primary:hover { background: var(--color-primary-hover); }
.cta-band .btn-primary:active { background: var(--color-primary-pressed); }

/* ----------------------------------------------------------------- footer -- */
.site-footer {
  border-top: 1px solid var(--border-subtle);
  background: var(--surface-primary);
  padding-block: var(--sp-7);
}
.footer-row {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
  flex-wrap: wrap;
}
.site-footer nav { display: flex; gap: var(--sp-5); flex-wrap: wrap; }
.site-footer nav a {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: var(--fs-secondary);
}
.site-footer nav a:hover { color: var(--color-primary-ink); }
.footer-note {
  margin-inline-start: auto;
  color: var(--text-muted);
  font-size: var(--fs-secondary);
}

/* ----------------------------------------------------------------- dialog -- */
/* Scroll lock for ANY open dialog, at every width — unlike the nav lock, which
   only exists below 760px because the drawer only exists below 760px. The
   gutter compensation reuses --scrollbar-gap (set by landing.js) so the page
   behind the scrim does not jump sideways when its scrollbar disappears.
   A modal <dialog> already makes the background inert; this stops it SCROLLING,
   which the platform does not do for us. */
body.dialog-open {
  overflow: hidden;
  padding-inline-end: var(--scrollbar-gap);
}

/* The pre-<dialog> failsafe. A browser that does not implement <dialog> treats
   it as an unknown inline element with no UA stylesheet — which means no
   `display: none` on a closed one, so all five dialogs' contents would be dumped
   at the bottom of the page. This selector matches there too. Such a browser
   never opens a dialog (landing.js feature-tests showModal), and it loses
   nothing: the price, the setup fee and both included benefits are on the card
   itself, and the dialog is the detail view. */
dialog:not([open]) { display: none; }

.demo-dialog {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--surface-elevated);
  color: var(--text-primary);
  box-shadow: var(--shadow-modal);
  padding: var(--sp-8);
  max-width: min(92vw, 440px);
  text-align: center;
}
.demo-dialog::backdrop { background: var(--scrim); }
.demo-dialog h2 { font-size: 24px; margin-block-end: var(--sp-3); }
.demo-dialog p { color: var(--text-secondary); font-size: var(--fs-body); margin-block-end: var(--sp-5); }
/* The two free benefits, in the SAME .includes-panel the four pricing dialogs
   use — no new component and no new visual language. The only two declarations
   this placement needs, and it needs both:

   text-align  the dialog centres its text, and a centred bullet list detaches
               every line from the check mark pinned to its inline start. `start`
               (not `left`) keeps that correct on the Arabic page too.
   margin      .demo-dialog p already carries a --sp-5 bottom margin, which
               collapses with the panel's --sp-5 top margin into ONE gap above;
               the matching bottom margin restores the same gap below, before
               the primary button.

   Everything else — the tint, the border, the check marks, the dark-theme
   variants — comes from .includes-panel / .includes-list unchanged. */
.demo-dialog .includes-panel { text-align: start; margin-block-end: var(--sp-5); }
.demo-dialog .btn { width: 100%; }

/* ================= THE DEMO ASSISTANT ==============================
   Applied only when landing.js has an API to talk to; without it the dialog
   keeps every rule above and looks exactly as it did.

   Two structural overrides, both consequences of a transcript living here:

   width       440px is right for three sentences and a button. A conversation
               beside a calendar needs room for two columns, so the ceiling
               rises — still capped against the viewport, never a full-bleed
               sheet.
   text-align  the dialog centres its text. Centred chat bubbles read as a
               poster rather than a conversation, and a centred time grid
               detaches every column from its heading. `start`, not `left`, so
               the Arabic page is correct by construction. */
.demo-dialog.has-agent {
  max-width: min(94vw, 760px);
  text-align: start;
  /* MUST FIT THE WINDOW. With the assistant live this dialog is a working
     surface — transcript, calendar, then a contact form — and it grows with
     every turn. Unbounded, it ran past the bottom of a 900px viewport by the
     time the times arrived, which put the thing the visitor had to act on
     off-screen. A ceiling plus internal scrolling keeps the whole flow
     reachable; dvh (not vh) so a mobile URL bar cannot crop it. */
  max-height: min(92dvh, 900px);
  overflow-y: auto;
}
/* The heading stays centred: it is the dialog's title, not part of the
   transcript, and it reads as a caption above it. */
.demo-dialog.has-agent > h2 {
  text-align: center;
}

/* THE LEAD-IN GOES WHEN THE ASSISTANT ARRIVES.
   "Tell us your clinic's name and city" is the email path's instruction, and
   it is still exactly right there — with JS off, or with no API configured,
   that is the whole dialog. But the assistant opens by introducing itself and
   then asks its own first question, so with it live the visitor was told to do
   one thing by the page and a different thing by the agent, in the same box.
   Hidden rather than rewritten: the sentence is not wrong, it is simply not
   this version's instruction, and hiding it needs no second translation of a
   string that already reads correctly in both locales. */
.demo-dialog.has-agent > h2 + p {
  display: none;
}

/* THE EMAIL PATH BECOMES THE FALLBACK IT IS.
   Two full-width primary buttons in one dialog is two primary actions, and the
   quieter one was winning on sheer size: a solid blue bar under a grey
   transcript reads as "the real way to do this". With the assistant present,
   booking IS the action and email is the escape hatch, so it takes the
   secondary treatment the rest of the page uses for exactly that. */
.demo-dialog.has-agent .includes-panel {
  margin-block-end: var(--sp-4);
}
.demo-dialog.has-agent a[data-contact-email] {
  background: transparent;
  color: var(--text-secondary);
  border: 1px solid var(--border-subtle);
  box-shadow: none;
}
.demo-dialog.has-agent a[data-contact-email]:hover {
  background: var(--surface-sunken);
  color: var(--text-primary);
}
.demo-dialog.has-agent .agent-divider,
.demo-dialog.has-agent .dialog-note {
  text-align: center;
}

/* BOTH HALVES AT ONCE — the conversation and the real calendar, side by side.
   The grid collapses to one column below 620px, where two columns would give
   each about 20 characters. The calendar is `hidden` until times arrive, and a
   hidden grid child occupies no track, so the thread is full width until there
   is genuinely something to put beside it. */
.agent-panes {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-4);
  margin-block-end: var(--sp-4);
}
.demo-agent.has-calendar .agent-panes {
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
}

.agent-thread {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  /* A floor as well as a ceiling: without min-height the panel jumps in size
     on every turn, which is the single most distracting thing a chat can do. */
  min-height: 220px;
  max-height: 46vh;
  overflow-y: auto;
  padding: var(--sp-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--surface-sunken);
  scroll-behavior: smooth;
}

/* The product's OWN chat tokens, deliberately. The hero mock is rendered in
   --bubble-ai / --bubble-patient, so the assistant that actually books the demo
   speaks in the same colours as the animation that advertised it — in both
   themes, because these are semantic tokens and not hex codes. */
.agent-msg {
  max-width: 86%;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-lg);
  font-size: var(--fs-body);
  line-height: var(--lh-snug, 1.45);
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.agent-msg-ai {
  align-self: flex-start;
  background: var(--bubble-ai);
  color: var(--bubble-ai-ink);
  border-start-start-radius: var(--radius-sm, 6px);
}
.agent-msg-you {
  align-self: flex-end;
  background: var(--bubble-patient);
  color: var(--bubble-patient-ink);
  border-start-end-radius: var(--radius-sm, 6px);
}

/* Three bouncing dots while the turn is in flight. Purely decorative and
   aria-hidden in the markup — the arriving message is announced by the log's
   own aria-live, so announcing "typing" too would double every turn. */
.agent-typing { display: inline-flex; gap: 4px; align-items: center; }
.agent-typing span {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
  animation: agent-blink 1.1s infinite ease-in-out;
}
.agent-typing span:nth-child(2) { animation-delay: 0.16s; }
.agent-typing span:nth-child(3) { animation-delay: 0.32s; }
@keyframes agent-blink {
  0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-3px); }
}

/* Tappable answers. A chip is a real <button>, so it is reachable by keyboard
   and announced as a control — the whole flow is completable without typing. */
.agent-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-self: flex-start; }
.agent-chip {
  border: 1px solid var(--border-strong, var(--border-subtle));
  background: var(--surface-elevated);
  color: var(--text-primary);
  border-radius: 999px;
  padding: var(--sp-2) var(--sp-4);
  font: inherit;
  font-size: var(--fs-secondary);
  cursor: pointer;
  min-height: 36px;
  transition: background var(--lift-t) var(--ease), border-color var(--lift-t) var(--ease);
}
.agent-chip:hover { background: var(--surface-sunken); }
.agent-chip:focus-visible { outline: 2px solid var(--focus-ring, currentColor); outline-offset: 2px; }

/* ---- the real calendar ---- */
.agent-calendar {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--sp-4);
  max-height: 46vh;
  overflow-y: auto;
}
.agent-calendar-title {
  font-size: var(--fs-secondary);
  font-weight: var(--fw-semibold);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-block-end: var(--sp-3);
}
.agent-days { display: grid; gap: var(--sp-4); }
.agent-day-label {
  font-size: var(--fs-secondary);
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  margin-block-end: var(--sp-2);
}
.agent-slots { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
/* Times are pre-formatted server-side in the CLINIC's timezone. Tabular figures
   so a column of them lines up instead of shuffling by a pixel per digit. */
.agent-slot {
  border: 1px solid var(--border-subtle);
  background: var(--surface-elevated);
  color: var(--text-primary);
  border-radius: var(--radius-md);
  padding: var(--sp-2) var(--sp-3);
  font: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  min-height: 38px;
  min-width: 66px;
  transition: background var(--lift-t) var(--ease), border-color var(--lift-t) var(--ease);
}
.agent-slot:hover { background: var(--surface-sunken); }
.agent-slot:focus-visible { outline: 2px solid var(--focus-ring, currentColor); outline-offset: 2px; }
.agent-slot[aria-pressed="true"] {
  background: var(--brand-600, var(--bubble-ai));
  border-color: transparent;
  color: #fff;
}

/* ---- the message box ---- */
.agent-input { display: flex; gap: var(--sp-2); align-items: stretch; }
.agent-text {
  flex: 1 1 auto;
  min-width: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-elevated);
  color: var(--text-primary);
  padding: var(--sp-3) var(--sp-4);
  font: inherit;
  font-size: var(--fs-body);
  min-height: 44px;
}
.agent-text:focus-visible { outline: 2px solid var(--focus-ring, currentColor); outline-offset: 1px; }
/* Overrides .demo-dialog .btn { width: 100% } — the send button sits beside the
   input, and a full-width button in a flex row pushes the field to nothing. */
.demo-dialog.has-agent .agent-input .btn { width: auto; flex: none; }
.agent-input[hidden] { display: none; }

/* ---- the contact fields the assistant asks for ---- */
.agent-fields { display: grid; gap: var(--sp-3); margin-block-start: var(--sp-3); }
.agent-field { display: grid; gap: 4px; }
.agent-field label { font-size: var(--fs-secondary); color: var(--text-secondary); }
.agent-field input {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-elevated);
  color: var(--text-primary);
  padding: var(--sp-3) var(--sp-4);
  font: inherit;
  min-height: 44px;
}
.agent-field input:focus-visible { outline: 2px solid var(--focus-ring, currentColor); outline-offset: 1px; }

.agent-error {
  color: var(--danger-600, #b42318);
  font-size: var(--fs-secondary);
  margin-block: var(--sp-3) 0;
}
.agent-divider {
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-secondary);
  margin-block: var(--sp-5) var(--sp-4) !important;
}

@media (max-width: 620px) {
  /* One column, and a shorter thread so the message box stays above the fold
     on a phone with the keyboard up. */
  .demo-agent.has-calendar .agent-panes { grid-template-columns: 1fr; }
  .agent-thread { max-height: 38vh; min-height: 170px; }
  .agent-calendar { max-height: 34vh; }
}

@media (prefers-reduced-motion: reduce) {
  .agent-typing span { animation: none; opacity: 0.55; }
  .agent-thread { scroll-behavior: auto; }
}
.dialog-note { margin-block-start: var(--sp-4); font-size: var(--fs-secondary); color: var(--text-muted); }
.dialog-close {
  position: absolute;
  inset-inline-end: var(--sp-4);
  top: var(--sp-4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: var(--radius-md);
  background: transparent;
  color: var(--text-muted);
}
.dialog-close:hover { background: var(--surface-menu-hover); color: var(--text-primary); }
.dialog-close svg { width: 17px; height: 17px; }

/* --------------------------------------------------------- pricing dialog --
   Same surface, shadow, backdrop and close control as the demo dialog — one
   dialog vocabulary, two contents. The differences are all structural: this one
   is left-aligned (it is a price list, not a single message), it is wider, and
   it scrolls INSIDE itself.

   The height contract is the important part. `max-height: min(86dvh, …)` with
   the body scrolling means the dialog can never grow past the viewport and the
   close button can never be pushed off screen — dvh, so the mobile URL bar
   collapsing does not change the answer. Background scrolling is stopped by
   `body.dialog-open` (landing.js), not by the dialog. */
.pricing-dialog {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--surface-elevated);
  color: var(--text-primary);
  box-shadow: var(--shadow-modal);
  padding: 0;
  inline-size: min(94vw, 520px);
  max-block-size: min(86dvh, 860px);
  overflow: hidden;
}
.pricing-dialog-wide { inline-size: min(94vw, 640px); }
.pricing-dialog::backdrop { background: var(--scrim); }

/* The close control sits in its own <form method="dialog">, which is what makes
   it close the dialog with no JavaScript at all. The row is zero-height and
   positioned, so the button hangs at the dialog's top corner and stays there
   while the body scrolls underneath it — the close is never scrolled away. */
.dialog-close-row {
  position: relative;
  z-index: 2;
  height: 0;
}
.pricing-dialog .dialog-close {
  top: var(--sp-3);
  inset-inline-end: var(--sp-3);
  background: color-mix(in srgb, var(--surface-elevated) 82%, transparent);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.pricing-dialog-body {
  overflow-y: auto;
  max-block-size: min(86dvh, 860px);
  padding: var(--sp-6) var(--sp-6) var(--sp-7);
  overscroll-behavior: contain;   /* a flick at the end does not scroll the page */
}
.pricing-dialog h2 {
  font-size: clamp(21px, 2.4vw, 26px);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  /* The plan name is Latin in both locales and the heading is mostly it, so the
     serif italic of .plan h3 would be wrong here — this is a document title,
     not the card's display type. */
  padding-inline-end: 40px;       /* clear of the sticky close button */
}
.pricing-lede {
  margin-block-start: var(--sp-2);
  color: var(--text-secondary);
  font-size: var(--fs-body);
}

/* Monthly and setup, side by side above 480px and stacked below it. */
.price-blocks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
  gap: var(--sp-3);
  margin-block-start: var(--sp-5);
}
.price-block {
  padding: var(--sp-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-primary);
}
.price-block h3 {
  font-size: var(--fs-meta);
  font-weight: var(--fw-bold);
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.price-block .price-line { margin-block-start: var(--sp-1); }
/* The clinic dialogs' setup fee stands alone, outside the two-up .price-blocks
   grid — one flat fee for the plan, below the three doctor bands — so it
   carries the spacing that grid would otherwise have given it. */
.price-block-wide { margin-block-start: var(--sp-5); }
.price-foot {
  margin-block-start: var(--sp-2);
  font-size: var(--fs-meta);
  color: var(--text-muted);
}

/* Doctor bands. Cards, not a five-column table: these stack to one column on a
   320px screen with no horizontal scrolling, which is the requirement a wide
   price table cannot meet. */
.tier-list {
  list-style: none;
  padding: 0;
  margin-block-start: var(--sp-5);
  display: grid;
  gap: var(--sp-3);
}
.tier {
  padding: var(--sp-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-primary);
}
.tier-title {
  font-size: var(--fs-card-title);
  font-weight: var(--fw-semibold);
  color: var(--color-primary-ink);
}
.tier .price-line { margin-block-start: var(--sp-2); }
.tier .price-now { font-size: clamp(23px, 2vw, 27px); }
.tier-note {
  margin-block-start: var(--sp-2);
  font-size: var(--fs-meta);
  color: var(--text-muted);
}

/* The inclusions panel. A tinted block, because "what you get for the setup
   fee" is the reassurance that has to survive a skim. */
.includes-panel {
  margin-block-start: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--status-success-soft) 42%, var(--surface-primary));
  border: 1px solid color-mix(in srgb, var(--status-success) 20%, var(--border-subtle));
}
.includes-panel h3 {
  font-size: var(--fs-card-title);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.includes-panel .includes-list { margin-block-start: var(--sp-3); }
.includes-panel-pro {
  background: color-mix(in srgb, var(--color-primary-soft) 46%, var(--surface-primary));
  border-color: color-mix(in srgb, var(--color-primary) 22%, var(--border-subtle));
}
.includes-panel-pro .includes-list li::before {
  background: var(--color-primary-soft)
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%230a6472' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    center / 9px 7px no-repeat;
}
[data-theme="dark"] .includes-panel-pro .includes-list li::before {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='9' viewBox='0 0 11 9' fill='none'%3E%3Cpath d='M1 4.6L4 7.4L10 1.4' stroke='%2357cdca' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

.dialog-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-block-start: var(--sp-6);
}
.dialog-actions .btn {
  flex: 1 1 190px;
  min-height: 46px;
  text-align: center;
}
.pricing-dialog .dialog-note { text-align: start; }

/* --------------------------------------------------------- reveal-on-view -- */
/* THE `translate` PROPERTY, NOT `transform`, AND THAT IS THE WHOLE POINT.
   -----------------------------------------------------------------------------
   53 elements on this page carry .reveal, and they include every surface with a
   hover: .card, .feature-card, .step, .roadmap-item, .plan-card, .contact-card.
   Written as `transform`, the settled state `.js .reveal.in { transform: none }`
   is specificity (0,3,0) — and every interaction rule that moves one of those
   surfaces is (0,2,0): .card:hover, .feature-card:hover, .roadmap-item:hover,
   .contact-card:hover and the touch press states further down. The reveal won
   all of them. MEASURED on a 1280px laptop with a real pointer: hovering any
   card left `transform: none` — the border, the glow and the shadow arrived and
   the element did not move. --lift-y, the shared -4px this file defines as one
   of five vocabulary tokens, had no effect anywhere on the page.

   `translate` and `transform` are separate animatable properties that COMPOSE
   (translate is applied first), so the reveal and the lift stop competing for
   one declaration and both simply happen. No specificity was raised and no
   !important was added — the collision was removed instead.

   `translate: 0 14px` is block-axis only. The file's rule about the `translate`
   property being physical (see --dir) is about the INLINE axis; there is no
   inline component here, so there is nothing for RTL to get backwards. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    translate: 0 14px;
    transition: opacity 0.55s var(--ease), translate 0.55s var(--ease);
  }
  .js .reveal.in { opacity: 1; translate: none; }
}

/* ------------------------------------------------------------- responsive -- */
@media (max-width: 980px) {
  .cols-4 { grid-template-columns: repeat(2, 1fr); }
  .cols-3 { grid-template-columns: repeat(2, 1fr); }
  .cols-fit { grid-template-columns: repeat(2, 1fr); }
  /* An odd card count leaves a trailing orphan on the last row. Centre it at
     exactly one column's width rather than letting it stretch to two — the
     rule is generic, so it holds for 3, 5 or 7 cards. */
  .cols-fit > :last-child:nth-child(odd) {
    grid-column: 1 / -1;
    justify-self: center;
    inline-size: calc(50% - (var(--sp-4) / 2));
  }
  .hero-grid { grid-template-columns: 1fr; }
  .hero-visual { justify-items: start; }
  .security-list { grid-template-columns: 1fr; }
}

@media (max-width: 760px) {
  .nav-toggle { display: inline-flex; }
  /* The nav has left the flow, so the actions cluster takes over pushing
     itself to the trailing edge. */
  .header-actions { margin-inline-start: auto; }
  /* The desktop CTA is replaced by the identical one inside the panel, so the
     action is never lost — only relocated. */
  .header-cta { display: none; }

  /* Dropdown panel below the header. Kept in the layout (never display:none)
     so opening and closing can transition; `visibility` keeps the closed menu
     out of the tab order and off the accessibility tree. */
  .nav-panel {
    position: absolute;
    inset-inline: 0;
    top: 100%;
    display: grid;
    gap: var(--sp-2);
    background: var(--surface-elevated);
    border-bottom: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-md);
    padding: var(--sp-3) clamp(18px, 4vw, 32px) var(--sp-5);
    visibility: hidden;
    opacity: 0;
    transform: translateY(-10px);
    pointer-events: none;
    transition:
      opacity var(--t) var(--ease),
      transform var(--t) var(--ease),
      visibility 0s linear var(--t);
  }
  .nav-open .nav-panel {
    visibility: visible;
    opacity: 1;
    transform: none;
    pointer-events: auto;
    transition-delay: 0s;
  }

  .site-nav {
    flex-direction: column;
    gap: 2px;
    margin-inline-start: 0;
  }
  .site-nav a {
    padding: var(--sp-3);
    font-size: var(--fs-body-lg);
    border-radius: var(--radius-sm);
    min-height: 44px;
    display: flex;
    align-items: center;
    transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  }
  .site-nav a:hover,
  .site-nav a:focus-visible {
    background: var(--surface-hover);
    color: var(--color-primary-ink);
  }

  .nav-panel-cta { display: block; margin-block-start: var(--sp-2); }
  .nav-panel-cta .btn { width: 100%; min-height: 46px; }

  /* Scroll lock while the drawer is open. The gutter compensation keeps the
     page from jumping on desktop-class scrollbars. */
  body.nav-open {
    overflow: hidden;
    padding-inline-end: var(--scrollbar-gap);
  }

  .proof-grid { grid-template-columns: repeat(2, 1fr); }
  .contact-grid { grid-template-columns: 1fr; }
}

@media (max-width: 760px) and (prefers-reduced-motion: reduce) {
  .nav-panel { transition: none; }
  .nav-toggle-bars > span { transition: none; }
}

@media (max-width: 560px) {
  .cols-4, .cols-3, .cols-2, .cols-fit { grid-template-columns: 1fr; }
  /* Single column: there is no orphan to centre, so hand the card back. */
  .cols-fit > :last-child:nth-child(odd) {
    grid-column: auto;
    inline-size: auto;
  }
  .footer-note { margin-inline-start: 0; }

  /* Narrower rail keeps the step cards a usable width at 320px. */
  .steps { --rail-w: 36px; --rail-gap: var(--sp-3); }
  .step-num { font-size: 14px; }
  .step-body { padding: var(--sp-4); }

  /* Status drops under the milestone name rather than squeezing it. */
  .roadmap-item {
    grid-template-columns: var(--node-col) minmax(0, 1fr);
    row-gap: 5px;
  }
  .roadmap-status { grid-column: 2; justify-self: start; }

  .security-list li { padding-inline: 40px var(--sp-4); }
  .security-list li::before { inset-inline-start: var(--sp-4); }

  .contact-card { padding: var(--sp-4); gap: var(--sp-3); }

  /* Pricing at phone widths. The dialogs take the full width they are given.
     Verified at 320px: no horizontal overflow in either language. */
  .pricing-dialog,
  .pricing-dialog-wide { inline-size: min(96vw, 520px); }
  .pricing-dialog-body { padding: var(--sp-5) var(--sp-4) var(--sp-6); }
  .pricing-dialog h2 { padding-inline-end: 34px; }
  /* One column, full-width buttons: no 44px target ever ends up half a thumb
     wide because two CTAs tried to share a 320px row. */
  .dialog-actions .btn { flex: 1 1 100%; }
}

/* ------------------------------------------------------------ tap targets --
   Measured, not assumed: a headless run with a real 320-430px CSS viewport
   found the header's theme toggle and hamburger at 38x38, the wordmark at
   39px tall, the skip link at 38px and the footer links at 22px. Apple's HIG
   and WCAG 2.5.5 both put the floor at 44, and these are the controls a phone
   visitor reaches for first.

   The VISIBLE box is left alone where it was deliberate — the icon buttons
   still look like 38px squares. What grows is the touch area, via padding on
   the wordmark and footer links, and an explicit min-size on the two icon
   controls where the box IS the control.
   ---------------------------------------------------------------------------- */
@media (pointer: coarse), (max-width: 760px) {
  .icon-btn { width: 44px; height: 44px; }
  .wordmark { display: inline-flex; align-items: center; min-height: 44px; }
  .skip-link { min-height: 44px; display: inline-flex; align-items: center; }
  /* The footer links are a row of navigation, not prose, so they take real
     vertical padding rather than relying on WCAG's spacing exception. */
  .site-footer nav { gap: var(--sp-3) var(--sp-5); }
  .site-footer nav a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    /* "FAQ" is only 25px of text. The floor applies to the TARGET, not the
       word, so the short links widen and the long ones are unaffected. */
    min-width: 44px;
  }

  /* The dialog close, measured at 34x34. It is the ONLY way out of a sheet that
     covers the whole screen, so it cannot be the smallest target on it.

     Grown as a centred overlay rather than by resizing the box: the visible
     button is positioned against the dialog's corner and the pricing dialog's
     h2 reserves padding to clear it, so a wider box would need that reserve
     re-derived in two more places. The target grows, the picture does not. */
  .dialog-close::after {
    content: "";
    position: absolute;
    /* 34 + 5 on every side = 44. `inset` is the one physical shorthand this
       file uses, and it is safe here precisely because it is symmetric: the
       same 5px on all four edges has no inline axis to get backwards in RTL. */
    inset: -5px;
  }

  /* Inline links that sit INSIDE a sentence — "…point at one — talk to us."
     WCAG 2.5.8 exempts them, but this one is a real call to action at the end
     of the pricing section and was 23.5px tall. Padding on an inline box grows
     the hit area without touching line box height, and the negative margin
     keeps the paragraph's leading exactly where it was. */
  .link-btn {
    padding-block: 10px;
    margin-block: -10px;
  }
}

/* ------------------------------------------------------- phone refinements --
   A second pass over the phone band, section by section, after the layout
   above had already collapsed the grids. These are the places where "one
   column" was correct but not yet comfortable.
   ---------------------------------------------------------------------------- */

/* Safe areas. viewport-fit=cover (see the <head>) means the page now paints
   under a notch and a home indicator, so the gutter has to allow for them —
   otherwise a landscape iPhone clips the first character of every line. The
   max() keeps the designed gutter wherever the inset is zero, which is every
   desktop and most phones in portrait. */
.shell {
  padding-inline:
    max(clamp(18px, 4vw, 32px), env(safe-area-inset-left, 0px))
    max(clamp(18px, 4vw, 32px), env(safe-area-inset-right, 0px));
}
.site-footer { padding-block-end: calc(var(--sp-6) + env(safe-area-inset-bottom, 0px)); }

/* Reading sizes on a phone.
   -----------------------------------------------------------------------------
   The type scale in assets/tokens/primitives.css is the APP's, and the app is a
   dense dashboard read at arm's length on a desk: 15px body, 14.5px card copy,
   13.5px secondary. This page is not that. It is long-form prose read at
   phone distance, one column wide, and at those three sizes a 375px screen asks
   the reader to work for every paragraph.

   ONE MECHANISM, THREE VALUES. Re-pointing the tokens inside the phone band
   lifts every place the page already uses them — section subs, card copy, FAQ
   answers, plan bullets, the proof labels, the footer — with no per-section
   rules and no second type system to keep in step. The steps are deliberately
   small (about +1px each): the page keeps its proportions, the hierarchy is
   untouched, and nothing above 560px sees any of it. --fs-meta, --fs-btn,
   --fs-label and the display sizes are NOT touched; eyebrows, badges and
   buttons are already sized for the thumb, and clamp() has been scaling the
   headings all along. */
@media (max-width: 560px) {
  :root {
    --fs-body-lg: 16px;    /* was 15   — body copy, FAQ questions, nav links */
    --fs-body: 15.5px;     /* was 14.5 — card and panel prose */
    --fs-secondary: 14px;  /* was 13.5 — labels and supporting lines */
  }
}

/* Text fields at 16px on touch, which is the size at or above which iOS Safari
   stops zooming the page in on focus. The zoom is not reversible by the user on
   a `viewport-fit=cover` page — it leaves the layout shifted sideways for the
   rest of the visit — so this is a layout fix, not a typographic preference.
   The visible size difference from --fs-body is about half a pixel. */
@media (pointer: coarse) {
  .agent-text,
  .agent-field input { font-size: 16px; }
}

@media (max-width: 760px) {
  /* The dropdown panel holds seven links plus a CTA. On a 568px-tall phone in
     landscape that is taller than the viewport, and the CTA at the bottom was
     unreachable — the panel had no height limit and the body behind it is
     scroll-locked while it is open. Bound it to what is actually on screen and
     let it scroll inside itself. */
  .nav-panel {
    max-height: calc(100dvh - var(--header-h));
    overflow-y: auto;
    overscroll-behavior: contain;
    padding-block-end: calc(var(--sp-5) + env(safe-area-inset-bottom, 0px));
  }
}

@media (max-width: 560px) {
  /* Hero. The copy already puts the headline, the sub and both CTAs above the
     fold at 375x812; this only stops the section from spending height it does
     not need on the way in, and centres the product visual under the copy it
     illustrates rather than leaving it hanging off the leading edge. */
  .hero { padding-block: clamp(28px, 6vw, 44px) clamp(28px, 5vw, 40px); }
  .hero-visual { justify-items: center; }
  .phone { width: 100%; }
  /* The demo thread is illustrative, not content: past ~48% of the viewport it
     stops adding to the argument and starts pushing the proof bar out of reach.
     It scrolls internally, so nothing in it becomes unreachable. */
  .phone-thread { max-height: 48dvh; overflow-y: auto; overscroll-behavior: contain; }
  /* Both hero CTAs go full width and stack. Two buttons sharing a 320px row
     leaves each one below a comfortable target once the padding is counted. */
  .hero .cta-row .btn { flex: 1 1 100%; justify-content: center; }

  /* Personas. The heading is a flex row of [tag] [name], and whether the pair
     fits on one line is decided by the length of the name: "Rana runs the
     clinic" and "Dr. Ahmad sees patients" stayed inline while "Karim just wants
     an appointment" pushed its tag onto a line of its own — three cards in a
     column, two shapes between them. Grid gives every card the second shape:
     tag, then name. justify-items keeps the pill at its own width instead of
     letting it stretch the full column. */
  .persona h3 {
    display: grid;
    justify-items: start;
    gap: var(--sp-2);
  }
}

/* The proof bar keeps two columns as long as two columns can hold a label. The
   longest of them ("of sensitive chats reach a human", and the longer Arabic
   counterparts) needs about 150px before it starts breaking mid-word.

   RE-DERIVED at 350, from 420. Two columns at 360px is (360 - 36 gutter - 18
   gap) / 2 = 153px per column, which clears that floor — so 360 and everything
   above it keeps the pair, and only the 320-359 band, where a column would be
   135px, drops to one. Four single-file rows for four two-word statistics was
   the emptiest screen on the page at 375 and 390. */
@media (max-width: 350px) {
  .proof-grid { grid-template-columns: 1fr; gap: var(--sp-4); }
}

/* Pricing dialogs as bottom sheets.
   These carry a plan's whole story — price blocks, an inclusions panel, and on
   two tiers a second panel — so on a phone a centred 96vw card with its own
   inner scrollbar reads as a small window onto a long document. Anchored to the
   bottom edge instead: full width, thumb-reachable close, and the same internal
   scroll it already had. `dvh` throughout, so the mobile URL bar collapsing
   never changes how much of it is reachable. */
@media (max-width: 560px) {
  .pricing-dialog,
  .pricing-dialog-wide {
    inline-size: 100%;
    max-inline-size: 100%;
    margin: 0;
    margin-block-start: auto;   /* pin to the bottom of the dialog's box */
    max-block-size: 92dvh;
    border-inline: none;
    border-block-end: none;
    border-start-start-radius: var(--radius-2xl);
    border-start-end-radius: var(--radius-2xl);
    border-end-start-radius: 0;
    border-end-end-radius: 0;
  }
  .pricing-dialog-body {
    max-block-size: 92dvh;
    padding-block-end: calc(var(--sp-6) + env(safe-area-inset-bottom, 0px));
  }
  /* Same treatment for the demo dialog, so the two never look like different
     kinds of surface on the same screen.

     .has-agent IS IN THE SELECTOR ON PURPOSE. That class carries
     `max-width: min(94vw, 760px)` (it widens the dialog once a transcript and a
     calendar have to sit side by side) at one specificity point more than a bare
     `.demo-dialog`, so without it the phone sheet lost the width contest to it —
     and because this block also sets `margin: 0`, the result was a 94vw panel
     pinned to the INLINE-START edge with a sliver of scrim down one side only.
     Measured at 390px: 366.6px wide at x=0. The assistant is the normal
     production state, so that was the state a phone visitor actually saw. */
  .demo-dialog,
  .demo-dialog.has-agent {
    inline-size: 100%;
    max-inline-size: 100%;
    margin: 0;
    margin-block-start: auto;
    border-start-start-radius: var(--radius-2xl);
    border-start-end-radius: var(--radius-2xl);
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    /* --sp-8 of inline padding is a third of a 320px screen. The sheet is
       full-bleed now and no longer needs to hold its own margin. */
    padding-inline: var(--sp-5);
    padding-block-end: calc(var(--sp-6) + env(safe-area-inset-bottom, 0px));
  }
}

/* The narrowest band the page supports. A long Arabic unit label
   ("لكل طبيب شهريًا") next to a 34px figure is the one price line that can
   still run out of room, so the amount steps down and the unit takes its own
   line rather than the card growing a scrollbar. */
@media (max-width: 380px) {
  .price-now { font-size: 27px; }
  .tier .price-now { font-size: 22px; }
  .price-unit-wide { flex-basis: 100%; margin-block-start: -2px; }
}

/* =============================================================================
   TOUCH PRESS STATES — the laptop's hover vocabulary, on a finger.
   -----------------------------------------------------------------------------
   Seven surfaces on this page lift, glow and settle on hover: .card, the
   .feature-card variant, .step, .security-list li, .roadmap-item, .plan-card and
   .contact-card. Every one of them is gated behind
   `@media (hover: hover) and (pointer: fine)`, which is correct — a phone that
   is served :hover keeps it STUCK on the last thing tapped until something else
   is tapped, which is worse than no hover at all. The consequence, though, was
   that a phone visitor got none of it: the page that reacts to every pointer on
   a laptop was completely inert under a finger.

   A hover has no touch equivalent, but a PRESS does, and it is the same visual
   event: the surface responds while the user is on it and settles when they
   leave. So each block below is its hover twin with `:hover` swapped for
   `:active` — the same tokens, the same transforms, the same durations, no new
   colour and no new motion vocabulary. Nothing is invented for mobile; the
   mobile page simply stops being the one that does not answer.

   THE MEDIA QUERY IS THE EXACT COMPLEMENT of the hover gate:
   `not (A and B)` is `(not A) or (not B)`, and a comma is `or`. So every device
   gets one treatment or the other, never both and never neither — and a hybrid
   (a touchscreen laptop, which reports `hover: hover` AND `pointer: coarse` on
   some builds) gets both hover and press, which is exactly right for hardware
   that has both.

   .contact-card is the one surface that ALREADY had a :active rule — a settle
   DOWN (translateY(1px), --shadow-xs) designed as the mouse-press half of
   hover-then-press. That rule is not deleted: it still governs pointer-fine
   devices, where hover precedes the press and the pair reads as one gesture.
   On touch there is no preceding hover, so the press has to carry the whole
   expression, and the block below overrides it with the hover treatment.
   ---------------------------------------------------------------------------- */
@media (hover: none), (pointer: coarse) {
  .card:active { transform: translateY(-2px); box-shadow: var(--shadow-sm); }

  .feature-card:active {
    transform: translateY(var(--lift-y));
    border-color: color-mix(in srgb, var(--color-primary) 34%, var(--border-subtle));
    box-shadow: var(--shadow-md), var(--glow-brand);
    background: color-mix(in srgb, var(--color-primary-soft) 26%, var(--surface-primary));
  }
  .feature-card:active::before { transform: scaleX(1); }
  .feature-card:active .card-icon {
    transform: translateY(-2px) scale(1.06);
    box-shadow: 0 6px 16px -8px var(--color-primary-ring);
  }

  .step:active .step-body {
    transform: translateY(-3px);
    border-color: var(--color-primary-ring);
    box-shadow: var(--shadow-sm);
  }
  .step:active .step-num {
    background: var(--color-primary);
    color: var(--text-on-primary);
    box-shadow: 0 0 0 4px var(--color-primary-ring);
  }
  /* T1 — physical translateX; --dir keeps the nudge in the reading direction. */
  .step:active .step-icon {
    transform: translateX(calc(3px * var(--dir)));
    background: var(--color-primary-soft);
  }
  .step:active::before { border-color: var(--color-primary); }

  .security-list li:active {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--status-success) 40%, var(--border-subtle));
    box-shadow: var(--shadow-sm), var(--glow-success);
    background: color-mix(in srgb, var(--status-success-soft) 22%, var(--surface-primary));
  }
  .security-list li:active::before {
    transform: scale(1.1);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--status-success) 18%, transparent);
  }

  /* T2 */
  .roadmap-item:active {
    transform: translateX(calc(3px * var(--dir)));
    border-style: solid;
    border-color: var(--color-primary-ring);
    box-shadow: var(--shadow-sm);
    background: color-mix(in srgb, var(--color-primary-soft) 24%, var(--surface-primary));
  }
  .roadmap-item:active .roadmap-node {
    background: var(--color-secondary-soft);
    box-shadow: 0 0 0 4px var(--color-secondary-ring);
  }
  .roadmap-item:active::before { border-color: var(--color-primary); }

  /* The plan cards are the one group here that is genuinely tappable — the whole
     card is a .plan-stretch button that opens the pricing dialog — so the press
     is feedback for a real action, not decoration. :active matches ancestors of
     the activated element, which is what carries the button's press out to the
     card around it. .card:active above already supplied the lift and shadow. */
  .plan-card:active { border-color: color-mix(in srgb, var(--color-primary) 34%, var(--border-subtle)); }
  .plan-card:active .plan-hint { color: var(--color-primary-hover); }
  .plan-card-featured:active { border-color: transparent; box-shadow: none; }
  .plan-card-featured:active::before { border-color: color-mix(in srgb, var(--amber-300) 50%, transparent); }
  .plan-card-featured:active .plan-hint { color: var(--amber-100); }

  .contact-card:active { transform: translateY(var(--lift-y)); box-shadow: var(--shadow-md); }
  .contact-card:active .contact-copy strong { color: var(--color-primary-ink); }
  /* T3 */
  .contact-card:active .contact-go { opacity: 1; transform: translateX(calc(4px * var(--dir))); }
  .contact-card:active .card-icon { transform: scale(1.06); }

  .contact-whatsapp:active {
    border-color: color-mix(in srgb, var(--status-success) 42%, var(--border-subtle));
    box-shadow: var(--shadow-md), var(--glow-success);
    background: color-mix(in srgb, var(--status-success-soft) 26%, var(--surface-primary));
  }
  /* T4 — the nudge is direction-aware; the WhatsApp MARK itself is never
     mirrored (see the [dir="rtl"] icon rules in the i18n section). */
  .contact-whatsapp:active .card-icon { transform: scale(1.06) translateX(calc(2px * var(--dir))); }
  .contact-whatsapp:active .contact-copy strong { color: var(--status-success-ink); }
  .contact-whatsapp:active .contact-go { color: var(--status-success-ink); }

  .contact-email:active {
    border-color: color-mix(in srgb, var(--color-primary) 42%, var(--border-subtle));
    box-shadow: var(--shadow-md), var(--glow-brand);
    background: color-mix(in srgb, var(--color-primary-soft) 28%, var(--surface-primary));
  }
  .contact-email:active .contact-go { color: var(--color-primary-ink); }
  .contact-email:active .envelope-flap { transform: translateY(-1.5px) scaleY(0.78); }
}

/* Reduced motion, for the press states specifically. The existing per-section
   reduced-motion blocks all sit ABOVE this one in the file and neutralise the
   HOVER transforms; a later rule cannot be un-set by an earlier one, so the
   press transforms need their own pass. The colour, border, glow and shadow
   changes are deliberately kept — none of them moves anything, and stripping
   them would leave a reduced-motion visitor with no feedback at all. */
@media (prefers-reduced-motion: reduce) {
  .card:active,
  .feature-card:active,
  .feature-card:active .card-icon,
  .step:active .step-body,
  .step:active .step-icon,
  .security-list li:active,
  .security-list li:active::before,
  .roadmap-item:active,
  .contact-card:active,
  .contact-card:active .card-icon,
  .contact-card:active .contact-go,
  .contact-card:active .envelope-flap { transform: none; }
  /* The wipe is already drawn at full width and faded out when motion is
     reduced (see the .feature-card block), so the press reveals it by opacity
     rather than by scaling it in. */
  .feature-card:active::before { transform: scaleX(1); opacity: 1; }
}

/* =============================================================================
   INTERNATIONALISATION — RTL, Arabic typography, and the language switcher.
   -----------------------------------------------------------------------------
   Everything below is scoped to [dir="rtl"] or [lang="ar"] and therefore costs
   the English page nothing except the two rules that deliberately target the
   Arabic fragments INSIDE it (the switcher label, and the one Arabic word in
   the how-it-works H2).

   assets/tokens/*.css is copied verbatim from the app design system and is
   never edited — so every override here lives in this file, which loads after
   tokens/index.css and therefore wins on cascade order alone.

   The five physical transforms are NOT here: they are fixed in place at their
   own rules with var(--dir). Search for `var(--dir)` to find all five.
============================================================================= */

/* ---------------------------------------------------- direction isolation -- */
/* Layer 1 of two. These containers hold BIDI-NEUTRAL characters — / - + % : —
   between digits, and the bidi algorithm reorders neutrals according to the
   surrounding paragraph. Inside an RTL paragraph `1-3+` renders as `+3-1` and
   `24/7` as `7/24`. Isolating the container fixes all of them without putting a
   tag inside a translated string.

   The one EXCEPTION is directly below. `1-3+` is the example above because it
   was the worst case on this page; it is gone — that KPI is a word now — but
   `24/7` and `100%` are still here and still reorder, so the rule stays.

   Applied UNCONDITIONALLY rather than under [dir="rtl"]: it is a no-op in LTR,
   and an unconditional rule cannot fall out of sync with a locale selector.
   This is also what allowed the hand-written dir="ltr" to be removed from the
   chat timestamp in the markup — the direction now lives in exactly one place.

   Layer 2 is <bdi> inside the copy itself, authored by the Arabic Translation
   Supervisor and tag-checked by i18n/check.mjs. */
.proof-kpi,
.msg-time,
.step-num,
.capacity-opt {
  unicode-bidi: isolate;
  direction: ltr;
}

/* …except the KPI that holds a word. `direction: ltr` exists to hold a run of
   digits and neutrals in Latin order; imposing it on TRANSLATED copy is how a
   two-word Arabic value would ship with its words reversed. The isolation is
   kept — it is what stops the word from being reordered by the bar around it —
   and only the forced direction is handed back to the document. */
.proof-kpi-word { direction: inherit; }

/* ------------------------------------------------------ directional icons -- */
/* Mirrored: these two mean "forward" / "hands over to", and an unmirrored arrow
   points backwards in RTL. Applied to the inner <svg>, never to the parent —
   the parent carries the hover translateX, and mirroring it would fight T3/T4.

   NOT mirrored, and this list is deliberate: the WhatsApp mark (a registered
   brand glyph — mirroring it is a trademark-usage error, not a design choice),
   the envelope (horizontally symmetric), the brand "C" and the C-Link
   wordmark, the sun / moon / hamburger / dialog-close icons, every
   non-directional pictogram, the security-list checkmark, and ALL NUMERALS —
   Western digits 0-9 throughout, never Eastern Arabic-Indic, matching
   Frontend/src/lib/locale.ts. */
[dir="rtl"] .contact-go svg,
[dir="rtl"] .step-icon-directional svg,
/* The chevron on the four free-trial buttons: same reasoning — it means
   "onward", and an unmirrored chevron points backwards in RTL. */
[dir="rtl"] .plan-trial .btn-go {
  transform: scaleX(-1);
}

/* ------------------------------------------------------ Arabic typography -- */
/* Hanken Grotesk ships no Arabic glyphs, so without this the Arabic page falls
   through to system-ui / Segoe UI — NOT to IBM Plex Sans Arabic, which the page
   already loads.

   Both selectors REDECLARE font-family, not just the custom property. That is
   the whole point: redefining --font-sans changes nothing on an element that
   does not itself declare `font-family: var(--font-sans)`, because an inline
   element inherits its parent's already-RESOLVED family. On <html> the
   redefinition works because `body` declares the family; on the two inline
   cases it would silently do nothing. (.from-patient[lang="ar"] above already
   declares font-family directly, which is why it works today and is the
   precedent for this.)

   Both inline cases are on the ENGLISH page: the switcher's `العربية` label —
   the single most visible Arabic word on / — and the one Arabic word in the
   how-it-works H2. Both now get correct Arabic type, which is a genuine
   improvement on today.

   Fallbacks: Segoe UI Arabic is the right first fallback on Windows, this
   audience's dominant platform; system-ui reaches SF Arabic on Apple; Tahoma is
   a mediocre Arabic face but metrically close and universally present, which is
   what a third fallback is for. The stack is chosen to bound the display=swap
   reflow, which is reduced, not eliminated. */
[lang="ar"],
.lang-switch[lang="ar"] {
  --font-sans: 'IBM Plex Sans Arabic', 'Segoe UI', Tahoma, system-ui, sans-serif;
  font-family: var(--font-sans);
}

/* Arabic sets denser than Latin and carries marks ABOVE and BELOW the baseline
   — dots, hamza, shadda. At 1.08 and 1.15 the descenders and the superscript
   hamza genuinely collide. */
[lang="ar"] body { line-height: 1.75; }
[lang="ar"] .hero h1 { line-height: 1.28; }
[lang="ar"] .section h2 { line-height: 1.25; }
[lang="ar"] .chat-msg { line-height: 1.5; }

/* All eight letter-spacing declarations, reset. This is not a stylistic
   preference. Arabic is CURSIVE: letters join along a baseline stroke and the
   join is part of the letterform, not a gap between letterforms. CSS
   letter-spacing inserts space after EVERY glyph, including mid-word joined
   ones, interrupting a stroke that must be continuous — the Arabic equivalent
   of spacing the stem away from the bowl of a Latin `b`. The five POSITIVE
   declarations (0.04-0.09em) land on exactly the short, chrome-like labels
   where the damage is most visible. The two NEGATIVE ones (-0.015em on the H1,
   -0.022em on the H2) crowd the above/below-baseline marks into neighbouring
   letterforms at display sizes.

   text-transform: uppercase is left alone on purpose: Arabic is unicameral, so
   it is a genuine no-op rather than a silent transformation, and removing it
   would add a rule for no benefit. Its CONSEQUENCE — the eyebrows lose both of
   their English differentiation signals at once — is carried instead by weight,
   colour and size, and is on the Supervisor's rendered-review list (TR-10). */
[lang="ar"] .wordmark-word,
[lang="ar"] .eyebrow,
[lang="ar"] .hero h1,
[lang="ar"] .section h2,
[lang="ar"] .capacity-label,
[lang="ar"] .persona-tag,
[lang="ar"] .roadmap-status,
[lang="ar"] .chip-soon,
/* Same rule, same reason, for the pricing chrome: the label above each card
   price and the uppercase-styled dialog headings are short chrome labels
   carrying positive tracking. */
[lang="ar"] .price-label,
[lang="ar"] .price-block h3 {
  letter-spacing: normal;
}

/* The price region's reserve, in Arabic. Its three rows are set at
   line-height 1.75 rather than 1.62 (see the Arabic typography block above),
   so the same three rows are ~8px taller. The reserve has to follow, or the
   guarantee it exists to give — four buttons on one line — would hold in
   English and quietly fail on /ar/. */
[lang="ar"] .plan-price { min-block-size: 8.25rem; }

/* ------------------------------------------------------------ price bidi --
   "$60" is a Latin/numeric run, and in Arabic prose it is always followed by an
   Arabic unit word ("شهريًا", "لكل طبيب شهريًا"). Two mechanisms keep that
   correct, and neither touches the amount itself:

   1. <bdi> around every amount in the MARKUP (not in the dictionaries — the
      amount is not copy). It isolates "$1,098.90" so the "$" and the digits
      cannot be reordered by the surrounding RTL paragraph.
   2. The unit spacing below. English writes "$60/month" with the slash tight
      against the amount; Arabic writes "$60 شهريًا" with a word space. That is
      a typographic difference, not a content one, so it is CSS — the two
      locales keep one markup path and one dictionary key.

   The row order needs nothing: .price-line is a flex row in logical order
   [comparison] [current] [unit], which RTL lays out right-to-left by itself. */
[lang="ar"] .price-unit { margin-inline-start: 0; }

/* The strikethrough is drawn by the text decoration of the LATIN glyphs it sits
   on, so it needs no RTL handling — but IBM Plex Sans Arabic renders the line
   slightly heavier, and at --fs-body it starts to read as a deletion mark. */
[lang="ar"] .price-was { text-decoration-thickness: 1.5px; }

/* The one real conflict between "preserve the visual design" and "correct
   Arabic typography", resolved in favour of correctness by the Supervisor.

   .hero h1 em is Fraunces ITALIC SERIF — a Latin-only device. Fraunces has no
   Arabic coverage, so the Arabic emphasis fragment would render as generic
   serif with SYNTHETIC OBLIQUE. Arabic has no italic tradition: there is no
   reader for whom a slanted Arabic word means emphasis, and on the largest
   piece of type on the page it reads as a font that failed to load rather than
   as a typographic device.

   The emphasis ROLE survives; only the DEVICE changes. Weight and colour are
   the native Arabic emphasis devices, so brand blue at the heading's own weight
   is not a downgrade — for Arabic it is the correct device, and the English
   page merely gets a different one.

   font-weight is --fw-bold (700), matching .hero h1. --fw-semibold (600) would
   render the emphasised fragment LIGHTER than the text it must stand out from,
   which is an active de-emphasis. This depends on IBM Plex Sans Arabic actually
   being FETCHED at 700 (see the font <link> in i18n/page.template.html);
   without that we would have traded synthetic oblique for synthetic bold on the
   same three words. The two must ship together or neither ships. */
[lang="ar"] .hero h1 em {
  font-family: var(--font-sans);
  font-style: normal;
  font-weight: var(--fw-bold);
  /* colour unchanged: --color-primary-ink still carries the accent */
}

/* Plan names stay Latin in both locales (brand-level, inventory: DNT), so
   Fraunces italic remains correct for them and .plan h3 needs no override. Same
   for .wordmark, which is the brand: Latin, LTR and set in the brand sans in
   both locales. */

/* ------------------------------------------------------ language switcher -- */
/* Built entirely from patterns and tokens already on the page: the .icon-btn
   height / border / radius / background / hover pair, and the .site-nav a type
   ramp. No new visual language and no new token.

   :focus-visible needs nothing here — the global rule near the top of this file
   applies to any focusable element and is not scoped to a component. */
.lang-switch {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 38px;            /* matches .icon-btn exactly, so the header row's
                              vertical rhythm is untouched */
  padding-inline: var(--sp-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--surface-primary);
  color: var(--text-secondary);
  font-size: var(--fs-label);
  font-weight: var(--fw-medium);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.lang-switch:hover,
.lang-switch:focus-visible {
  background: var(--surface-hover);
  color: var(--text-primary);
}

/* OQ-13 — ONE cta.book_demo string everywhere, with the header button widened
   to hold the full Arabic form rather than a truncated one. Below 760px
   .header-cta is display:none and the action relocates into the drawer at
   width:100%, so this only ever applies above 760px, where horizontal space is
   least scarce. No ellipsis truncation, in any language, ever. */
.header-cta { white-space: nowrap; }

/* The narrow-desktop band, 761-900px: the hamburger has not appeared yet
   (the breakpoint is 760px) so the full desktop header row is on screen —
   wordmark + seven nav links + the switcher + theme toggle + CTA — in the least
   horizontal space that layout ever gets.

   MEASURED, not guessed. Adding the switcher pushed the row 30px over the
   viewport at 768px in English and 5px in Arabic; the pre-switcher page fitted
   exactly. The switcher is a new element, so absorbing its width is its own
   responsibility — tightening the inter-item gaps here reclaims ~40px, which
   clears both locales with room to spare, and it applies ONLY inside this band.
   Above 900px nothing changes; below 761px the nav is in the drawer.

   This is the CSS accommodation that R3/TR-7 require. The alternatives were
   moving the mobile breakpoint (which would change existing responsive
   behaviour) or shortening an approved Arabic string (which is forbidden, and
   would not have helped: the English overflow was the larger of the two).

   The .header-row gap is reduced for a SECOND, separate reason found in cycle 4.
   With only .site-nav tightened, the row still fitted at 768px but the English
   label "How it works" wrapped INSIDE its own link, silently making the header
   80px instead of 64px — the mirror of the Arabic wrap the Supervisor reported
   as TR-14. Reclaiming the row gap as well removes it: measured across
   761/768/780/800/830/860/890/900/901/980 the header was 64px in BOTH locales
   with zero overflow, except English at exactly 761px, which still wrapped to
   80px — cosmetic, overflow-free and left documented rather than chased.

   RE-DERIVED after a SEVENTH nav link ("Sign in", the seam to /app) joined the
   six that derivation was measured against. That link put the row back over the
   edge: 795px of content in a 768px viewport — 34px of real horizontal
   scrolling at 761px and 27px at 768px, which is iPad portrait. The reclaim
   below is the same mechanism, taken one step further: --sp-3 → --sp-2 on the
   row, and the nav's own gap to zero.

   The nav gap goes to zero rather than to --sp-2 because .site-nav a already
   carries --sp-1 of inline padding on each side, so neighbouring links keep 8px
   of separation and — this is the point — every link keeps the full width of
   its touch target. Reclaiming from the gap costs nothing a finger can feel;
   reclaiming from the padding would have cost 8px off every link.

   MEASURED at 761/768/780/800/830/860/890/900/901/940 in BOTH locales: zero
   overflow, and a 64px header at every one of them. That also retires the one
   defect the previous derivation documented and left — English wrapping "How it
   works" inside its own link at exactly 761px, which silently made the header
   80px. There is now no width in this band where it wraps.

   Still rejected, for the reason found in cycle 4: `white-space: nowrap` on
   .site-nav a. Re-measured against the seven links it is worse than it was —
   +54px English and +124px Arabic at 761px. A 64px header bought with a
   horizontal scrollbar is not a fix. */
@media (min-width: 761px) and (max-width: 900px) {
  .header-row { gap: var(--sp-2); }
  .site-nav { gap: 0; }
  .lang-switch { padding-inline: var(--sp-2); }
}

@media (max-width: 760px) {
  /* In the drawer the switcher becomes a full-width row like the CTA beneath
     it. ONE element serves both breakpoints, because #nav-panel is
     `display: contents` above 760px and the dropdown below it. */
  .lang-switch {
    width: 100%;
    min-height: 44px;
    height: auto;
    margin-block-start: var(--sp-2);
  }
}

/* ===================== THE ALWAYS-THERE LAUNCHER =========================
   The assistant used to be reachable only from a "Book a demo" button, which
   made it reachable only by somebody already sold enough to go looking. This
   is the persistent way in: a visitor deep in the pricing section with a
   question can ask it where they are.

   `inset-inline-end`, not `right`. On the Arabic page the whole document is
   RTL, and a launcher pinned to the physical right would sit on the side the
   reader's eye leaves rather than the side it returns to — the logical
   property puts it in the same relative place in both locales, for free.

   The target is 52px, comfortably past the 24px W3C floor and in the range
   real touch targets want; the visible label sits inside the same button, so
   the whole pill is the hit area rather than the icon alone.

   z-index sits BELOW the dialog, deliberately. Once the assistant is open, the
   launcher that opened it is noise — and a floating pill over a modal backdrop
   is the kind of thing that ends up covering the send button on a small
   screen. `<dialog>` renders in the top layer, above any z-index, so this is
   handled by the platform rather than by a number we would have to maintain. */
.ask-ai {
  position: fixed;
  inset-block-end: clamp(16px, 4vw, 28px);
  inset-inline-end: clamp(16px, 4vw, 28px);
  z-index: 60;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 52px;
  padding-inline: var(--sp-4);
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--brand-600, #1a56a8);
  color: #fff;
  font: inherit;
  font-size: var(--fs-body);
  font-weight: 600;
  cursor: pointer;
  box-shadow: var(--shadow-modal, 0 10px 30px rgb(0 0 0 / 0.22));
  transition: transform 140ms ease, box-shadow 140ms ease, background-color 140ms ease;
}
.ask-ai:hover {
  transform: translateY(-2px);
  background: var(--brand-700, #14458a);
}
.ask-ai:active { transform: translateY(0); }
/* The page's own focus treatment, not the browser's default ring on a pill. */
.ask-ai:focus-visible {
  outline: 3px solid var(--focus-ring, #ffb020);
  outline-offset: 3px;
}
.ask-ai-icon { width: 22px; height: 22px; flex: none; }

/* Below 480px the label is dropped and the pill becomes a circle: at that
   width the words crowd the thumb zone and overlap content at the page edge.
   The aria-label carries the meaning either way, so nothing is lost to anyone
   using a screen reader — only to the space the label was taking. */
@media (max-width: 480px) {
  .ask-ai { padding-inline: 0; width: 52px; justify-content: center; }
  .ask-ai-label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .ask-ai { transition: none; }
  .ask-ai:hover { transform: none; }
}
