/* ============================================================
   NOMARA VOYAGES — styles.css
   ONE file. Order: tokens (canonical paste) → foundation →
   global RTL base → kill switches → components → site chrome →
   section helpers → mode rules.

   RULES (PLAN.md §1):
   - Tokens only. Zero raw hex outside the token paste below, except
     the documented photo-overlay legibility locks (Alliance S5).
   - Logical properties ONLY. Zero left/right/margin-left/etc.
     Document default is RTL (lang="ar" dir="rtl").
   - Mobile-first. Touch ≥48px. Contrast ≥4.5:1.
   ============================================================ */

/* ============================================================
   ░░ LAYER 1–3 · CANONICAL TOKEN PASTE ░░
   Copied VERBATIM from docs/design-system.html <style> head.
   Do not edit — this is the canonical source of truth.
   ============================================================ */
/* ============================================================
   NOMARA DESIGN SYSTEM — «الرفيق» (Al-Rafiq / The Companion)
   v1.0 — Token architecture: primitive → semantic → component
   ============================================================ */

:root {
  /* ---------- LAYER 1 · PRIMITIVES ---------- */
  /* Teal — extracted from the Nomara logo badge */
  --teal-50:  #EFF7F5;
  --teal-100: #D9EDE8;
  --teal-200: #B0DACF;
  --teal-300: #7DC0B0;
  --teal-400: #4BA391;
  --teal-500: #2B927E;
  --teal-600: #1F8A7A;   /* ← brand anchor */
  --teal-700: #176C60;
  --teal-800: #11514A;
  --teal-900: #0C3A35;
  --teal-950: #07211E;

  /* Coral — the "o" accent in the wordmark · Rihla energy */
  --coral-300: #F5A37E;
  --coral-400: #F07B4A;
  --coral-500: #E8602C;
  --coral-600: #CE4D1D;
  --coral-700: #A83E18;

  /* Gold — Sakina reverence · lantern light, not luxury bling */
  --gold-200: #EFDFA8;
  --gold-300: #E2C972;
  --gold-400: #CFAC3F;
  --gold-500: #B8932B;
  --gold-600: #95761F;

  /* Sand neutrals — warm, paper-like, North-African light */
  --sand-0:   #FCFAF6;
  --sand-50:  #FAF6EF;
  --sand-100: #F3ECDF;
  --sand-200: #E8DDC9;
  --sand-300: #D5C6AB;

  /* Ink — teal-tinted darks (never pure black) */
  --ink-900: #14211E;
  --ink-700: #2C3D39;
  --ink-500: #51635E;
  --ink-300: #8A9894;

  /* Night — Sakina deep surfaces */
  --night-800: #0A2D28;
  --night-900: #071F1B;

  /* Functional */
  --whatsapp: #25D366;
  --whatsapp-deep: #128C5E;
  --danger: #C73E1D;
  --success: var(--teal-500);

  /* ---------- TYPE PRIMITIVES ---------- */
  --font-display: 'Changa', sans-serif;        /* AR+LAT condensed — offers, prices, destinations */
  --font-body: 'Readex Pro', sans-serif;       /* AR+LAT, engineered for Arabic readability */
  --font-reverent: 'Amiri', serif;             /* Naskh serif — Sakina headings & du'a quotes */
  --font-mono: 'Space Mono', monospace;        /* doc chrome only, not product UI */

  /* Fluid type scale (mobile-first, 1.25 ratio desktop) */
  --text-xs:   0.8125rem;
  --text-sm:   0.9375rem;
  --text-base: 1.0625rem;                      /* 17px base — Arabic needs the extra px */
  --text-lg:   clamp(1.125rem, 1rem + 0.6vw, 1.375rem);
  --text-xl:   clamp(1.375rem, 1.15rem + 1vw, 1.75rem);
  --text-2xl:  clamp(1.75rem, 1.4rem + 1.6vw, 2.5rem);
  --text-3xl:  clamp(2.25rem, 1.6rem + 2.8vw, 3.5rem);
  --text-hero: clamp(2.75rem, 1.8rem + 4.5vw, 5rem);

  --leading-tight: 1.15;
  --leading-arabic: 1.7;                       /* Arabic body NEVER below 1.65 */
  --leading-latin: 1.55;

  /* ---------- SPACE / RADIUS / ELEVATION ---------- */
  --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem;
  --space-4: 1rem;    --space-5: 1.5rem; --space-6: 2rem;
  --space-7: 3rem;    --space-8: 4rem;   --space-9: 6rem;

  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;
  --radius-arch: 50% 50% 0 0 / 38% 38% 0 0;    /* the Sakina arch mask */

  --shadow-sm: 0 1px 3px rgba(12,58,53,.08);
  --shadow-md: 0 6px 20px rgba(12,58,53,.10);
  --shadow-lg: 0 18px 44px rgba(12,58,53,.16);

  --ease-out: cubic-bezier(.22,.8,.36,1);
  --ease-spring: cubic-bezier(.34,1.4,.5,1);  /* gentle overshoot for scroll-reveal entrances */
  --dur-fast: 160ms; --dur-base: 240ms;
}

/* ---------- LAYER 2 · SEMANTIC — RIHLA MODE (default) ----------
   رحلة — leisure, discovery, energy. Coral accent, bright sand. */
:root, [data-mode="rihla"] {
  --color-primary: var(--teal-600);
  --color-primary-strong: var(--teal-700);
  --color-primary-soft: var(--teal-100);
  --color-accent: var(--coral-500);
  --color-accent-strong: var(--coral-600);
  --color-accent-soft: #FBE9DF;
  --surface-page: var(--sand-0);
  --surface-raised: #FFFFFF;
  --surface-inverse: var(--teal-900);
  --text-strong: var(--ink-900);
  --text-body: var(--ink-700);
  --text-muted: var(--ink-500);
  --text-on-primary: #FFFFFF;
  --text-on-accent: #FFFFFF;
  --border-soft: var(--sand-200);
  --heading-font: var(--font-display);
  --price-color: var(--color-accent-strong);
  --chip-bg: var(--teal-50);
}

/* ---------- LAYER 2 · SEMANTIC — SAKINA MODE ----------
   سكينة — Umrah & Hajj. Same teal spine; gold replaces coral,
   Amiri enters for headings, surfaces deepen and calm down. */
[data-mode="sakina"] {
  --color-primary: var(--teal-700);
  --color-primary-strong: var(--teal-800);
  --color-primary-soft: var(--teal-100);
  --color-accent: var(--gold-400);
  --color-accent-strong: var(--gold-500);
  --color-accent-soft: #F6EFD8;
  --surface-page: var(--sand-50);
  --surface-raised: #FFFFFF;
  --surface-inverse: var(--night-900);
  --text-strong: var(--ink-900);
  --text-body: var(--ink-700);
  --text-muted: var(--ink-500);
  --text-on-primary: #FFFFFF;
  --text-on-accent: var(--ink-900);
  --border-soft: var(--sand-200);
  --heading-font: var(--font-reverent);
  --price-color: var(--gold-600);
  --chip-bg: var(--color-accent-soft);
}

/* ---------- LAYER 3 · COMPONENT TOKENS ---------- */
:root, [data-mode] {
  --btn-radius: var(--radius-pill);
  --btn-pad: 0.875rem 1.75rem;
  --btn-primary-bg: var(--color-primary);
  --btn-primary-bg-hover: var(--color-primary-strong);
  --btn-accent-bg: var(--color-accent);
  --btn-accent-bg-hover: var(--color-accent-strong);
  --card-radius: var(--radius-lg);
  --card-bg: var(--surface-raised);
  --card-shadow: var(--shadow-md);
  --card-shadow-hover: var(--shadow-lg);
  --input-radius: var(--radius-md);
  --input-border: var(--border-soft);
  --badge-promo-bg: var(--color-accent);
  --pricegrid-head-bg: var(--color-primary);
  --stickybar-bg: var(--surface-raised);
}
/* ░░ END CANONICAL TOKEN PASTE ░░ */


/* ============================================================
   ░░ FOUNDATION LAYER (Alliance S4) ░░
   Z-index ladder, chrome heights, touch + container tokens.
   Added on top of the canonical paste — not part of it.
   ============================================================ */
:root {
  /* Pure white — for white text/icons on dark chrome (footer inverse
     surface, hero photo scrim, WhatsApp green). Kept as a named token so
     no raw #fff lives outside the canonical paste or the legibility locks. */
  --white: #FFFFFF;

  /* Z-index ladder — 10 named layers */
  --z-base:    0;
  --z-raised:  10;
  --z-overlay:  100;
  --z-nav:      500;
  --z-backdrop: 590;  /* #18: above the fixed top bar (500) so the scrim dims it, below the drawer */
  --z-drawer:   600;
  --z-sticky:  640;
  --z-fab:     650;
  --z-toast:   700;

  /* Chrome heights */
  --nav-h:        64px;
  --nav-h-desktop: 76px;
  --sticky-bar-h: 72px;
  --fab-size:     56px;

  /* Touch + layout */
  --touch-min:   48px;
  --container-x: clamp(16px, 5vw, 40px);
  --container-max: 1180px;
}


/* ============================================================
   ░░ DARK THEME · brand night-mode ░░
   State lives on <html data-theme="dark">. Strategy: re-point the
   SEMANTIC + a few PRIMITIVE tokens so every component that already
   consumes them inverts cohesively — zero per-component dark rules
   needed downstream. Backgrounds = teal-tinted "ink/night" darks
   (never pure black); body text = sand/near-white; surfaces lift with
   a subtly lighter teal; borders soften; accents brighten just enough
   for WCAG AA on the dark ground. Reads as deliberate in BOTH
   data-mode="rihla" AND data-mode="sakina" (gold + Amiri reverence
   preserved on /omra/). Photo-overlay legibility locks are untouched
   (they hard-code their own dark glass + white text).

   The raw hex values below are the ONLY sanctioned hex outside the
   canonical paste besides the documented photo-overlay locks: they
   define the dark palette anchors that the semantic tokens reference.
   ============================================================ */
:root[data-theme="dark"] {
  /* --- dark palette anchors (teal-tinted, never #000) --- */
  --dk-bg:           #061A17;   /* page ground — deepest night-teal */
  --dk-surface:      #0C2823;   /* raised cards / nav / drawer */
  --dk-surface-2:    #123A33;   /* hover / elevated surface lift */
  --dk-border:       #1E4A42;   /* soft hairline on dark */
  --dk-text-strong:  #F6F1E6;   /* near-white sand for headings */
  --dk-text-body:    #DCD7C9;   /* sand body — AA on --dk-bg */
  --dk-text-muted:   #9FB0AB;   /* muted teal-sand — AA on --dk-bg */

  /* brightened accents (AA-tuned for the dark ground) */
  --dk-teal:         var(--teal-300);   /* #7DC0B0 link/primary on dark */
  --dk-teal-strong:  var(--teal-200);   /* #B0DACF hover */
  --dk-teal-soft:    #12342F;           /* faint teal chip/hover wash */
  --dk-coral:        var(--coral-400);  /* #F07B4A — brighter Rihla accent */
  --dk-coral-strong: var(--coral-300);  /* #F5A37E hover */
  --dk-gold:         var(--gold-300);   /* #E2C972 — Sakina lantern light */
  --dk-gold-strong:  var(--gold-200);   /* #EFDFA8 hover */
  /* Keep WhatsApp at its CANONICAL brand green in dark too: the
     white-icon-on-green pairing is the recognizable brand button (a
     graphical/brand element, exempt from the 4.5:1 text floor). Literal
     hex (not var(--whatsapp)) to avoid a custom-property cycle with the
     --whatsapp re-point in the theme block below. */
  --dk-whatsapp:     #25D366;           /* brand green held in dark */
}

/* RIHLA (default) — coral accent on the dark ground */
:root[data-theme="dark"], :root[data-theme="dark"] [data-mode="rihla"] {
  --color-primary: var(--dk-teal);
  --color-primary-strong: var(--dk-teal-strong);
  --color-primary-soft: var(--dk-teal-soft);
  --color-accent: var(--dk-coral);
  --color-accent-strong: var(--dk-coral-strong);
  --color-accent-soft: #3A241A;
  --surface-page: var(--dk-bg);
  --surface-raised: var(--dk-surface);
  --surface-inverse: #04110F;
  --text-strong: var(--dk-text-strong);
  --text-body: var(--dk-text-body);
  --text-muted: var(--dk-text-muted);
  --text-on-primary: var(--ink-900);   /* dark ink ON the light teal accent = AA */
  --text-on-accent: var(--ink-900);    /* dark ink ON bright coral = AA */
  --border-soft: var(--dk-border);
  --price-color: var(--dk-coral-strong);
  --chip-bg: var(--dk-teal-soft);

  /* primitives consumed directly by chrome (footer/fab/whatsapp).
     WhatsApp keeps brand green; --whatsapp-deep is left inheriting the
     canonical #128C5E so the FAB hover still reads as WhatsApp on dark. */
  --whatsapp: var(--dk-whatsapp);
  --sand-100: var(--dk-text-body);     /* footer copy → readable on dark */
  --sand-200: var(--dk-border);        /* disabled-btn bg lift */
  --ink-300: var(--dk-text-muted);     /* #26: footer legal + disabled labels invert with the chrome */
}

/* SAKINA (/omra/) — gold + Amiri reverence held on the dark ground */
:root[data-theme="dark"] [data-mode="sakina"] {
  --color-primary: var(--dk-teal);
  --color-primary-strong: var(--dk-teal-strong);
  --color-primary-soft: var(--dk-teal-soft);
  --color-accent: var(--dk-gold);
  --color-accent-strong: var(--dk-gold-strong);
  --color-accent-soft: #2E2716;
  --surface-page: var(--night-900);    /* #071F1B — keeps the Sakina deep */
  --surface-raised: #0D2A25;
  --surface-inverse: #04110F;
  --text-strong: var(--dk-text-strong);
  --text-body: var(--dk-text-body);
  --text-muted: var(--dk-text-muted);
  --text-on-primary: var(--ink-900);
  --text-on-accent: var(--ink-900);    /* dark ink ON gold = AA */
  --border-soft: #244C42;
  --price-color: var(--dk-gold-strong);
  --chip-bg: var(--color-accent-soft);
}

/* Gentle theme transition on color/background only (mode transition
   already exists below). Disabled under reduced-motion by the global
   kill switch. Scoped to chrome + surfaces so it never animates text
   reflow. */
html, body, .site-nav, .nav-drawer, .n-card, .service-card,
.n-grid, .n-trust .t, .n-sticky, .n-sticky-bar, .site-footer,
.lang-switcher, .theme-toggle {
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}


/* ============================================================
   ░░ GLOBAL RTL BASE ░░
   Document default is RTL. Logical properties only.
   ============================================================ */
* { margin: 0; padding: 0; box-sizing: border-box; }

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

body {
  font-family: var(--font-body);
  background: var(--surface-page);
  color: var(--text-body);
  font-size: var(--text-base);
  line-height: var(--leading-arabic);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

/* Headings inherit the mode-aware face via the semantic token */
h1, h2, h3, h4, h5, h6 {
  font-family: var(--heading-font);
  color: var(--text-strong);
  line-height: var(--leading-tight);
  font-weight: 700;
}

a { color: var(--color-primary); text-decoration: none; }
a:hover { color: var(--color-primary-strong); }

img, picture, svg, video { max-width: 100%; display: block; }

ul, ol { list-style: none; }

button, input, select, textarea {
  font: inherit;
  color: inherit;
}

/* Visible focus everywhere — accessibility floor.
   P1-11: DUAL ring. A single accent-colored outline vanishes on accent
   (coral/gold) CTAs — exactly the highest-priority controls. We pair a
   white/surface inner outline with a dark ink box-shadow halo so the
   indicator stays visible on ANY background, light or accent. */
:focus-visible {
  outline: 3px solid var(--surface-raised);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px var(--text-strong);
  border-radius: var(--radius-sm);
}

/* Skip link — keyboard accessibility */
.skip-link {
  position: absolute;
  inset-block-start: -100%;
  inset-inline-start: var(--space-3);
  z-index: var(--z-toast);
  background: var(--color-primary);
  color: var(--text-on-primary);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-family: var(--font-display);
  font-weight: 600;
}
.skip-link:focus {
  inset-block-start: var(--space-3);
  color: var(--text-on-primary);
}
/* #9: <main tabindex="-1"> now receives focus from the skip link so AT/keyboard
   land in content — but it's a programmatic target, not a tab stop, so suppress
   the focus ring (visible focus styles stay on real interactive elements). */
main:focus { outline: none; }

/* Latin runs inside Arabic — phone numbers, prices, destination names */
[dir="ltr"], bdi { unicode-bidi: isolate; }

/* Body scroll lock when drawer / lightbox open */
body.nav-scroll-lock { overflow: hidden; }


/* ============================================================
   ░░ REDUCED-MOTION KILL SWITCH (Alliance lesson 5) ░░
   Load-bearing. WCAG 2.3.3. The !important is intentional.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}


/* ============================================================
   ░░ TOUCH ENFORCEMENT (Alliance lesson 6) ░░
   Single source of truth via @media (pointer: coarse).
   ============================================================ */
@media (pointer: coarse) {
  .n-btn,
  a.n-btn,
  button,
  .nav-toggle,
  .filter-chips a,
  .lang-switcher button {
    min-height: var(--touch-min);
  }
}


/* ============================================================
   ░░ COMPONENTS (ported from design-system §06 — token-only) ░░
   ============================================================ */

/* ---- Buttons ---- */
.n-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  line-height: 1;
  border: 0;
  cursor: pointer;
  padding: var(--btn-pad);
  border-radius: var(--btn-radius);
  transition: background var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
  min-height: 48px;
  text-align: center;
}
/* P1-11: buttons use the same dual ring (white inner + ink halo) so the
   focus indicator is visible even on a coral/gold/whatsapp-green button. */
.n-btn:focus-visible {
  outline: 3px solid var(--surface-raised);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px var(--text-strong);
}
/* P1-3: real press feedback. Scale-DOWN is the one place a button SHOULD
   scale (§07 "never scale buttons" targets hover/grow, not the press) — it
   reads as a positive tap response, especially on touch where no hover
   exists. Reduced-motion is covered by the global kill switch. */
.n-btn:active { transform: translateY(0) scale(.98); transition-duration: 90ms; }

.n-btn-primary { background: var(--btn-primary-bg); color: var(--text-on-primary); }
.n-btn-primary:hover { background: var(--btn-primary-bg-hover); color: var(--text-on-primary); transform: translateY(-1px); box-shadow: var(--shadow-md); }

.n-btn-accent { background: var(--btn-accent-bg); color: var(--text-on-accent); }
.n-btn-accent:hover { background: var(--btn-accent-bg-hover); color: var(--text-on-accent); transform: translateY(-1px); }

.n-btn-wa { background: var(--whatsapp); color: var(--white); }
.n-btn-wa:hover { background: var(--whatsapp-deep); color: var(--white); }

.n-btn-ghost { background: transparent; color: var(--color-primary); border: 2px solid var(--color-primary); }
.n-btn-ghost:hover { background: var(--color-primary-soft); color: var(--color-primary-strong); }

.n-btn[disabled],
.n-btn:disabled {
  background: var(--sand-200);
  color: var(--ink-300);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
  border-color: transparent;
}

/* ---- Chips & badges ---- */
.n-chip {
  display: inline-flex;
  align-items: center;
  gap: .4rem;
  background: var(--chip-bg);
  color: var(--color-primary-strong);
  font-weight: 600;
  font-size: var(--text-xs);
  padding: .4rem .9rem;
  border-radius: var(--radius-pill);
}

.n-badge-promo {
  display: inline-block;
  background: var(--badge-promo-bg);
  color: var(--text-on-accent);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: .85rem;
  padding: .35rem 1rem;
  border-radius: var(--radius-pill);
}

.n-seats {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  background: var(--surface-raised);
  border: 1.5px solid var(--color-accent);
  color: var(--color-accent-strong);
  font-weight: 600;
  font-size: var(--text-xs);
  padding: .35rem .9rem;
  border-radius: var(--radius-pill);
}

/* ---- Card ---- */
.n-card {
  background: var(--card-bg);
  border-radius: var(--card-radius);
  box-shadow: var(--card-shadow);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.n-card:hover { box-shadow: var(--card-shadow-hover); transform: translateY(-3px); }
.n-card .img {
  /* P0-1: the wrapper is an <a> (inline by default), so the previous
     height:180px + overflow:hidden were IGNORED and images ballooned to
     their intrinsic aspect-ratio, pushing price/CTA off-card. Force block
     + aspect-ratio so the frame scales with card width and clips. */
  display: block;
  flex: none;
  aspect-ratio: 16 / 10;
  background: linear-gradient(160deg, var(--color-primary), var(--surface-inverse));
  position: relative;
  overflow: hidden;
  transition: border-radius var(--dur-base) var(--ease-out);
}
.n-card .img > img,
.n-card .img > picture { width: 100%; height: 100%; }
.n-card .img > img,
.n-card .img > picture img { object-fit: cover; width: 100%; height: 100%; }

/* P0-3: lazy-image fade-in (kills pop-in). Card/gallery imgs start
   transparent and fade once decoded; reveal.js/enhance.js sets .is-loaded
   on `load` and immediately for already-`complete` (cached) images. The LCP
   hero <img> is EXCLUDED (constraint #2 — it must paint immediately).
   P1-6: image zoom on hover — clipped by the wrapper's overflow:hidden. */
.n-card .img > img,
.n-card .img > picture img {
  opacity: 0;
  transition: opacity 400ms var(--ease-out), transform 600ms var(--ease-out);
}
.n-card .img > img.is-loaded,
.n-card .img > picture img.is-loaded { opacity: 1; }
@media (hover: hover) {
  .n-card:hover .img > img,
  .n-card:hover .img > picture img { transform: scale(1.04); }
}

[data-mode="sakina"] .n-card .img {
  border-radius: var(--radius-arch);
  margin: 10px 10px 0;
  aspect-ratio: 17 / 10;
  filter: saturate(.85);   /* Sakina −15% saturation per §05 */
}
/* Sakina stillness: no image-load fade and no hover zoom on /omra/ cards. */
[data-mode="sakina"] .n-card .img > img,
[data-mode="sakina"] .n-card .img > picture img {
  opacity: 1 !important;
  transition: none !important;
  transform: none !important;
}
.n-card .img .n-badge-promo { position: absolute; inset-block-start: 14px; inset-inline-start: 14px; }
/* #22: the Omra cross-sell card has no photo asset; instead of a blank gradient
   tile next to real photos, render a deliberate branded icon on the gradient. */
.n-card .img--icon { display: flex; align-items: center; justify-content: center; color: rgba(255,255,255,.92); }
.n-card .img--icon svg { inline-size: 56px; block-size: 56px; }
.n-card .img--logo > img {
  object-fit: contain;
  box-sizing: border-box;
  padding: var(--space-6);
}
.n-card .pad { padding: var(--space-5); display: flex; flex-direction: column; flex: 1 1 auto; }
/* #23: card titles are now <h3> (was <h4>, which skipped a level under the
   section <h2>). Style both so any un-migrated card keeps its look. */
.n-card h3, .n-card h4 { font-family: var(--heading-font); font-size: var(--text-xl); color: var(--text-strong); line-height: 1.3; }
/* #45: card titles are clickable to the destination (matches the whole-card
   hover affordance) — keep them visually a heading, not a blue link. */
.n-card h3 a, .n-card h4 a { color: inherit; text-decoration: none; }
.n-card h3 a:hover, .n-card h4 a:hover { color: var(--color-primary-strong); }
.n-card .hotel { color: var(--text-muted); font-size: var(--text-sm); margin-block-start: .3rem; }
.n-stars { color: var(--color-accent); letter-spacing: .1em; font-size: .9rem; }
.n-meta-row { display: flex; flex-wrap: wrap; gap: .5rem; margin-block-start: var(--space-3); }
.n-card .cta-row { display: flex; flex-wrap: wrap; gap: .6rem; margin-block-start: var(--space-4); }
.n-card .cta-row .n-btn { flex: 1; justify-content: center; padding: .75rem 1rem; font-size: .95rem; }
/* UX #14: WhatsApp is the stated checkout — give it visual dominance over the
   secondary "details" link instead of two equal-weight buttons. Demote the
   in-card primary to an outline so the filled green WA button wins attention,
   and let it take slightly more width. */
.n-card .cta-row .n-btn-primary {
  flex: 0 1 auto;
  background: transparent;
  color: var(--color-primary-strong);
  border: 1.5px solid var(--card-border, var(--color-primary));
}
.n-card .cta-row .n-btn-primary:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-strong);
  box-shadow: none;
}
.n-card .cta-row .n-btn-wa { flex: 1.6 1 auto; box-shadow: var(--shadow-sm); }

/* ---- Price (Changa 700 in BOTH modes) ---- */
/* Price block — a clean vertical unit: the Changa amount + دج never break
   (#price-split fix: the long caption used to squeeze the amount until it
   wrapped at its thousands-space → "119" / "000"). The caption drops to its
   own line; the whole block is pushed to the card's bottom (margin-auto) so
   prices align across cards and the eye flows down them while comparing. */
.n-price { display: flex; flex-wrap: wrap; align-items: baseline; gap: .1rem .45rem; margin-block-start: auto; padding-block-start: var(--space-4); }
.n-price .amount { font-family: var(--font-display); font-weight: 700; font-size: var(--text-2xl); color: var(--price-color); line-height: 1.05; white-space: nowrap; }
.n-price .cur { font-weight: 600; color: var(--text-muted); font-size: var(--text-sm); align-self: baseline; }
.n-price .per { flex-basis: 100%; color: var(--text-muted); font-size: var(--text-xs); margin-block-start: .15rem; }

/* P0-6: price-on-request — a DELIBERATE teal chip (not body-weight error
   text) sized at the price baseline, for cards whose price is genuinely
   unconfirmed (Égypte). No number is fabricated. */
.n-price--on-request {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  background: var(--chip-bg);
  color: var(--color-primary-strong);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-base);
  line-height: 1.25;
  padding: .5rem .9rem;
  border-radius: var(--radius-pill);
}
.n-price--on-request svg { flex: none; }

/* ---- Tiered price grid ---- */
.n-grid {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface-raised);
  border-radius: var(--radius-md);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  margin-block-start: var(--space-4);
  font-size: var(--text-sm);
}
.n-grid th {
  background: var(--pricegrid-head-bg);
  color: var(--text-on-primary);
  font-family: var(--font-display);
  font-weight: 600;
  padding: .8rem 1rem;
  text-align: start;
}
.n-grid td { padding: .8rem 1rem; border-block-start: 1px solid var(--border-soft); color: var(--text-body); }
.n-grid td.p { font-family: var(--font-display); font-weight: 700; color: var(--price-color); font-size: 1.15rem; white-space: nowrap; }

/* P1-9: Sakina prices are the brand's #1 signal but gold-600 reads muted at
   price size. Keep the gold (reverence constraint — do NOT switch to coral)
   but lift the price with a heavier weight, a touch more size, and a
   hairline gold rule so it reads as deliberate emphasis. */
[data-mode="sakina"] .n-grid td.p {
  font-weight: 800;
  font-size: 1.3rem;
}
[data-mode="sakina"] .n-grid td.p span[dir="ltr"] {
  border-block-end: 2px solid var(--gold-400);
  padding-block-end: 2px;
}
[data-mode="sakina"] .n-price .amount {
  font-weight: 800;
  text-decoration: underline;
  text-decoration-color: var(--gold-400);
  text-decoration-thickness: 2px;
  text-underline-offset: 5px;
}

/* ---- Trust strip ---- */
.n-trust { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-block-start: var(--space-4); }
.n-trust .t {
  display: flex;
  align-items: center;
  gap: .6rem;
  background: var(--surface-raised);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: .7rem 1.1rem;
  font-size: var(--text-sm);
  color: var(--text-body);
}
/* P2-6: faint primary-soft chip behind each trust icon — lifts the strip
   from "list" to "component", echoing .service-card__icon. */
.n-trust .t svg {
  flex: none;
  box-sizing: content-box;
  inline-size: 22px;
  block-size: 22px;
  padding: 7px;
  background: var(--color-primary-soft);
  border-radius: var(--radius-pill);
}

/* ---- Sticky conversion strip (the inner control) ---- */
.n-sticky {
  display: flex;
  gap: .6rem;
  background: var(--stickybar-bg);
  border: 1px solid var(--border-soft);
  box-shadow: var(--shadow-lg);
  border-radius: var(--radius-pill);
  padding: .5rem;
  max-width: 420px;
  margin-inline: auto;
}
.n-sticky .n-btn { flex: 1; justify-content: center; padding: .7rem .9rem; font-size: .95rem; }


/* ============================================================
   ░░ SITE CHROME ░░  (on-brand; not in the design doc)
   ============================================================ */

/* ---------- .site-nav — fixed top bar ---------- */
.site-nav {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: var(--nav-h);
  padding-inline: var(--container-x);
  padding-block: var(--space-2);
  /* P0-2: NO backdrop-filter. A non-`none` backdrop-filter establishes a
     containing block for position:fixed descendants, which trapped the
     enhance.js-injected .nav-drawer (a fixed child of .site-nav) inside the
     65px-tall bar instead of the viewport. The drawer MUST stay a child of
     .site-nav for the desktop display:contents architecture, so we drop the
     frosted effect and use a near-opaque token surface instead — the nav
     still reads cleanly over scrolling content. */
  background: color-mix(in srgb, var(--surface-raised) 92%, transparent);
  border-block-end: 1px solid transparent;
  transition: box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              background var(--dur-base) var(--ease-out);
}
/* P2-2: scrolled nav gains presence — 1px hairline + an opacity step. */
.site-nav.is-scrolled {
  box-shadow: var(--shadow-sm);
  border-block-end-color: var(--border-soft);
  background: color-mix(in srgb, var(--surface-raised) 98%, transparent);
}

.nav-logo { display: inline-flex; align-items: center; flex: none; }
.nav-logo img,
.nav-logo svg {
  height: 40px;
  width: auto;
  max-inline-size: min(46vw, 220px);
  object-fit: contain;
}
:root[data-theme="dark"] .nav-logo img { content: url("/assets/images/logo-light.png"); }

/* Right-side cluster (links + lang + cta) — moved into the drawer on mobile */
.nav-links {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}
.nav-links a {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--text-strong);
  padding: .5rem .7rem;
  border-radius: var(--radius-pill);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.nav-links a:hover,
.nav-links a[aria-current="page"] {
  background: var(--color-primary-soft);
  color: var(--color-primary-strong);
}

.nav-cta { flex: none; }
.nav-cta .n-btn { padding: .6rem 1.1rem; font-size: .95rem; }

/* Hamburger toggle — visible ≤900px only */
.nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  inline-size: var(--touch-min);
  block-size: var(--touch-min);
  border: 0;
  background: transparent;
  color: var(--text-strong);
  cursor: pointer;
  border-radius: var(--radius-md);
  flex: none;
}
.nav-toggle:hover { background: var(--color-primary-soft); }
.nav-toggle .icon-close { display: none; }
.site-nav.nav-open .nav-toggle .icon-menu { display: none; }
.site-nav.nav-open .nav-toggle .icon-close { display: block; }

/* ---------- .theme-toggle (light/dark) ----------
   Direct child of .site-nav, immediately before .nav-toggle. Stays in
   the TOP BAR at every width (never moved into the drawer). Compact
   circular icon button; ≥48px touch target on coarse pointers. Shows
   the SUN in light theme, the MOON in dark theme — driven by the
   [data-theme] selectors below. RTL-safe (no physical offsets). */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: 42px;
  block-size: 42px;
  padding: 0;
  border: 1px solid var(--border-soft);
  background: transparent;
  color: var(--text-strong);
  cursor: pointer;
  border-radius: var(--radius-pill);
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              transform var(--dur-fast) var(--ease-out);
}
.theme-toggle:hover {
  background: var(--color-primary-soft);
  color: var(--color-primary-strong);
  border-color: var(--color-primary-soft);
}
.theme-toggle:active { transform: scale(.94); }
.theme-toggle:focus-visible { outline: 3px solid var(--surface-raised); outline-offset: 2px; box-shadow: 0 0 0 5px var(--text-strong); }
.theme-toggle svg {
  inline-size: 22px;
  block-size: 22px;
  pointer-events: none;
}
/* Coarse pointers (touch): meet the 48px floor without bloating desktop */
@media (pointer: coarse) {
  .theme-toggle { inline-size: var(--touch-min); block-size: var(--touch-min); }
}
/* Icon swap: SUN in light (default), MOON in dark */
.theme-toggle .icon-moon { display: none; }
.theme-toggle .icon-sun  { display: block; }
:root[data-theme="dark"] .theme-toggle .icon-sun  { display: none; }
:root[data-theme="dark"] .theme-toggle .icon-moon { display: block; }

/* ---------- .nav-drawer (Alliance S3 + S8) ---------- */
/* Desktop ≥901px: wrapper is transparent to layout. Children flow into nav. */
.nav-drawer {
  display: contents;
}

/* Backdrop — only rendered/active on mobile */
.nav-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-backdrop);
  background: rgba(12, 58, 53, .5);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-base) var(--ease-out), visibility var(--dur-base) var(--ease-out);
}
.nav-backdrop.is-visible { opacity: 1; visibility: visible; }

/* Hamburger drawer up to 1024px (was 900px): 7 long Arabic links + WhatsApp
   CTA + lang pill + theme toggle do not fit inline on tablet widths, so the
   drawer now also covers iPad-class portrait/landscape. Inline nav only ≥1025px,
   matching the sticky-bar handoff at 1024/1025 — no cramped middle zone. */
@media (max-width: 1024px) {
  .nav-toggle { display: inline-flex; }

  /* TOP-BAR DECLUTTER (contract §B) — at ≤900px only [logo] +
     [theme-toggle] + [hamburger] may sit in the bar. We hide the three
     control clusters ONLY while they are STILL DIRECT CHILDREN of
     .site-nav (i.e. their top-bar home, before enhance.js moves them).
     This is the empty-drawer-leak guard: the moment enhance.js relocates
     them into .nav-drawer they are no longer `> children` of .site-nav,
     so this rule stops applying and the drawer rules below show them.
     Never a blanket `.nav-links{display:none}` that would leak inside. */
  .site-nav > .nav-links,
  .site-nav > .lang-switcher,
  .site-nav > .nav-cta {
    display: none;
  }
  /* Empty-drawer-leak guard (explicit): once the controls live inside
     .nav-drawer they MUST be visible when the drawer is open. We restate
     their display here so the `> child` hide above can never bleed in via
     specificity. Each gets back its natural display. */
  .nav-drawer .nav-links    { display: flex; }
  .nav-drawer .lang-switcher { display: inline-flex; }
  .nav-drawer .nav-cta      { display: block; }

  /* Drawer becomes a fixed slide-in panel anchored to the inline-end edge.
     RTL-aware: translateX(100%) hides off the inline-end side. We flip the
     sign for LTR so the panel always slides from the correct edge. */
  .nav-drawer {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-4);
    position: fixed;
    inset-block: 0;
    inset-inline-end: 0;
    inline-size: min(84vw, 340px);
    z-index: var(--z-drawer);
    background: var(--surface-raised);
    /* P1-4: faint inline-start shadow so the panel reads as lifted over the
       page as it slides; slightly longer eased slide than --dur-base. */
    box-shadow: -18px 0 44px rgba(12, 58, 53, .18);
    padding: calc(var(--nav-h) + var(--space-4)) var(--space-5) var(--space-6);
    overflow-y: auto;
    transform: translateX(100%);
    transition: transform 320ms var(--ease-out);
  }
  /* LTR: drawer sits on the right, still slides off the right edge */
  [dir="ltr"] .nav-drawer { transform: translateX(100%); }
  /* RTL: drawer sits on the left edge visually (inline-end), slides off it */
  [dir="rtl"] .nav-drawer { transform: translateX(-100%); }

  /* OPEN STATE — direction-explicit so it unambiguously beats the [dir]
     closed rules above (a plain `.site-nav.nav-open .nav-drawer` tied at
     specificity with `[dir="rtl"] .nav-drawer` in some engines and the
     drawer never slid in RTL). Both resolve to translateX(0) = on-screen. */
  [dir="ltr"] .site-nav.nav-open .nav-drawer,
  [dir="rtl"] .site-nav.nav-open .nav-drawer { transform: translateX(0); }

  .nav-drawer .nav-links {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-1);
  }
  .nav-drawer .nav-links a {
    display: block;
    padding: .85rem 1rem;
    font-size: var(--text-base);
  }
  .nav-drawer .lang-switcher,
  .nav-drawer .nav-cta { align-self: stretch; }
  .nav-drawer .nav-cta .n-btn { width: 100%; }

  /* P1-4: links settle in with a small staggered rise once the drawer is
     open (panel slide is the lead motion; this is the follow-through).
     Pure CSS — keyed off .nav-open. Reduced-motion neutralizes it via the
     global kill switch. */
  .site-nav .nav-drawer .nav-links a {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity 240ms var(--ease-out), transform 240ms var(--ease-out);
  }
  .site-nav.nav-open .nav-drawer .nav-links a { opacity: 1; transform: none; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(1) { transition-delay: 80ms; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(2) { transition-delay: 110ms; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(3) { transition-delay: 140ms; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(4) { transition-delay: 170ms; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(5) { transition-delay: 200ms; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(6) { transition-delay: 230ms; }
  .site-nav.nav-open .nav-drawer .nav-links a:nth-child(7) { transition-delay: 260ms; }
}

/* ---------- .lang-switcher (AR | FR pill toggle) ---------- */
.lang-switcher {
  display: inline-flex;
  background: var(--color-primary-soft);
  border-radius: var(--radius-pill);
  padding: 3px;
  flex: none;
}
.lang-switcher button {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-xs);
  border: 0;
  cursor: pointer;
  padding: .4rem .8rem;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-primary-strong);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.lang-switcher button.is-active {
  background: var(--color-primary);
  color: var(--text-on-primary);
}

/* ---------- .fab-whatsapp (Alliance S7) ---------- */
.fab-whatsapp {
  position: fixed;
  inset-block-end: calc(var(--space-4) + env(safe-area-inset-bottom));
  inset-inline-end: var(--space-4);
  z-index: var(--z-fab);
  inline-size: var(--fab-size);
  block-size: var(--fab-size);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--whatsapp);
  color: var(--white);
  border-radius: var(--radius-pill);
  box-shadow: var(--shadow-lg);
  transition: transform var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
/* P1-5: drop the hover scale (§07 "never scale buttons"). Lift + shadow
   bloom only. */
.fab-whatsapp:hover {
  background: var(--whatsapp-deep);
  color: var(--white);
  transform: translateY(-2px);
  box-shadow: 0 22px 50px rgba(12, 58, 53, .28);
}
.fab-whatsapp svg { inline-size: 28px; block-size: 28px; }

/* P1-5: one-shot FAB entrance — fade + rise, fired on .chrome-ready (set by
   enhance.js on load). No-op under Sakina + reduced-motion (guards below). */
.fab-whatsapp {
  opacity: 0;
  transform: translateY(12px);
}
.chrome-ready .fab-whatsapp {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 420ms var(--ease-out) 200ms,
              transform 420ms var(--ease-out) 200ms,
              background var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
.chrome-ready .fab-whatsapp:hover { transform: translateY(-2px); }

/* ---------- .n-sticky-bar (mobile conversion bar wrapper) ---------- */
.n-sticky-bar {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--z-sticky);
  padding: var(--space-3) var(--container-x);
  padding-block-end: calc(var(--space-3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--surface-page) 94%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-block-start: 1px solid var(--border-soft);
  /* P1-5: one-shot slide-up entrance from below the fold. Fires on
     .chrome-ready (enhance.js, on load). No fixed children here so the
     transform creates no containing-block trap. */
  transform: translateY(100%);
}
.chrome-ready .n-sticky-bar {
  transform: translateY(0);
  transition: transform 480ms var(--ease-out) 260ms;
}
.n-sticky-bar .n-sticky { box-shadow: none; border: 0; padding: 0; }

/* Show the sticky bar only ≤1024px; hide above */
@media (min-width: 1025px) {
  .n-sticky-bar { display: none; }
}

/* Alliance S7: only consume bottom padding + lift FAB on pages that
   ACTUALLY have a sticky bar. Homepage pays zero layout cost. */
@media (max-width: 1024px) {
  body:has(.n-sticky-bar) {
    padding-block-end: calc(var(--sticky-bar-h) + env(safe-area-inset-bottom));
  }
  /* #46: the sticky bar already exposes a WhatsApp button, so a lifted FAB in
     the same corner is a redundant second WhatsApp tap-target on phones. Hide
     it where the bar shows; the FAB still serves pages without a sticky bar
     and all desktop widths (where the bar is hidden). */
  body:has(.n-sticky-bar) .fab-whatsapp { display: none; }
}

/* ---------- .site-footer (full contact / trust block) ---------- */
.site-footer {
  background: var(--surface-inverse);
  color: var(--sand-100);
  padding-block: var(--space-8);
  padding-inline: var(--container-x);
  margin-block-start: var(--space-8);
}
.site-footer .wrap { max-width: var(--container-max); margin-inline: auto; }
.site-footer__grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1fr;
}
@media (min-width: 720px) {
  .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr; }
}
.site-footer__brand img,
.site-footer__brand svg { height: 44px; width: auto; margin-block-end: var(--space-3); }
.site-footer h3,
.site-footer h4 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--text-base);
  color: var(--white);
  margin-block-end: var(--space-3);
}
.site-footer a { color: var(--teal-200); }
.site-footer a:hover { color: var(--white); }
.site-footer__list li { margin-block-end: var(--space-2); font-size: var(--text-sm); }
.site-footer__contact { font-size: var(--text-sm); line-height: var(--leading-arabic); }
.site-footer__contact .tel { font-family: var(--font-display); font-weight: 700; color: #fff; font-size: var(--text-lg); }
.site-footer__socials { display: flex; gap: var(--space-3); margin-block-start: var(--space-3); }
.site-footer__socials a {
  display: inline-flex; align-items: center; justify-content: center;
  inline-size: var(--touch-min); block-size: var(--touch-min);
  border: 1px solid var(--teal-700); border-radius: var(--radius-md);
}
.site-footer__legal {
  margin-block-start: var(--space-6);
  padding-block-start: var(--space-4);
  border-block-start: 1px solid var(--teal-800);
  font-size: var(--text-xs);
  color: var(--ink-300);
  line-height: var(--leading-arabic);
}


/* ============================================================
   ░░ PAGE-SECTION HELPERS ░░
   ============================================================ */
.section { padding-block: var(--space-8); }
@media (min-width: 901px) { .section { padding-block: var(--space-9); } }

.wrap {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-x);
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: .75rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  /* A11y #25: teal-600 at .75rem failed 4.5:1 on sand; teal-700 clears AA. */
  color: var(--color-primary-strong);
  display: block;
  margin-block-end: var(--space-3);
}

.sec-head { margin-block-end: var(--space-6); max-width: 64ch; }
.sec-head h2 { font-size: var(--text-2xl); margin-block-end: var(--space-3); }
.sec-head p { color: var(--text-muted); font-size: var(--text-lg); }

/* Generic hero block */
.hero {
  position: relative;
  display: grid;
  align-items: center;
  min-block-size: clamp(420px, 70vh, 620px);
  /* dvh = dynamic viewport height: avoids the hero resize-jump when mobile
     browser toolbars collapse/expand. Falls back to vh on older engines. */
  min-block-size: clamp(420px, 70dvh, 620px);
  padding-block: calc(var(--nav-h) + var(--space-7)) var(--space-8);
  padding-inline: var(--container-x);
  overflow: hidden;
  color: #fff;
  background: var(--surface-inverse);
}
/* #28: short viewports (any orientation) — the 420px floor can push the CTA
   row below the fold on short PORTRAIT phones, which the landscape-only relief
   below missed. Relax the floor whenever the viewport is genuinely short. */
@media (max-height: 600px) {
  .hero { min-block-size: auto; padding-block: calc(var(--nav-h) + var(--space-5)) var(--space-6); }
}
.hero__media {
  position: absolute;
  inset: 0;
  z-index: var(--z-base);
}
.hero__media img,
.hero__media picture,
.hero__media picture img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  /* P1-1: directional, bottom + text-side-weighted scrim — a documented
     legibility lock (raw rgba allowed). The bottom-weight keeps the upper
     photo vivid; the side-weight (flipped per direction below) concentrates
     darkening under the copy column so text stays legible without muting the
     whole image. CSS gradient keywords are physical, so the side layer is
     set per [dir] just under this rule. */
  background:
    linear-gradient(to bottom,
      rgba(7,33,30,.12) 0%,
      rgba(7,33,30,0) 32%,
      rgba(7,33,30,.55) 72%,
      rgba(7,33,30,.84) 100%),
    linear-gradient(to right,
      rgba(7,33,30,.45) 0%,
      rgba(7,33,30,0) 55%);
}
/* RTL (default): copy sits on the visual right → darken from the right.
   LTR: copy sits on the visual left → darken from the left. */
[dir="ltr"] .hero__media::after {
  background:
    linear-gradient(to bottom,
      rgba(7,33,30,.12) 0%,
      rgba(7,33,30,0) 32%,
      rgba(7,33,30,.55) 72%,
      rgba(7,33,30,.84) 100%),
    linear-gradient(to left,
      rgba(7,33,30,.45) 0%,
      rgba(7,33,30,0) 55%);
}
.hero__inner {
  position: relative;
  z-index: var(--z-raised);
  max-width: var(--container-max);
  margin-inline: auto;
  width: 100%;
}
.hero h1 { color: #fff; font-size: var(--text-hero); line-height: 1.05; }
[data-mode="sakina"] .hero h1 { line-height: 1.4; }
.hero p { color: var(--sand-100); font-size: var(--text-lg); max-width: 52ch; margin-block-start: var(--space-4); }
.hero .cta-row { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-block-start: var(--space-5); }

/* ============================================================
   P0-4: HERO TEXT-OVERLAY LOAD-IN ENTRANCE
   The LCP hero IMAGE paints immediately (never animated). Only the
   .hero__inner CHILDREN (chip → h1 → p → cta-row) fade + rise, ONCE, on
   .hero-ready (set by enhance.js on DOMContentLoaded — not on scroll).
   No-op under Sakina (stillness) and reduced-motion (kill switch).
   ============================================================ */
.hero-ready .hero:not(.hero--sakina) .hero__inner > * {
  opacity: 0;
  transform: translateY(14px);
  animation: hero-rise 480ms var(--ease-out) forwards;
}
.hero-ready .hero:not(.hero--sakina) .hero__inner > *:nth-child(1) { animation-delay: 60ms; }
.hero-ready .hero:not(.hero--sakina) .hero__inner > *:nth-child(2) { animation-delay: 130ms; }
.hero-ready .hero:not(.hero--sakina) .hero__inner > *:nth-child(3) { animation-delay: 200ms; }
.hero-ready .hero:not(.hero--sakina) .hero__inner > *:nth-child(4) { animation-delay: 270ms; }
@keyframes hero-rise {
  to { opacity: 1; transform: none; }
}
/* Hard stillness guard: Sakina hero never animates its overlay. */
[data-mode="sakina"] .hero__inner > * {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
}

/* Sakina arch hero (gradient + arch, no photo) */
.hero--sakina {
  background: linear-gradient(165deg, var(--night-900) 0%, var(--teal-800) 60%, var(--teal-700) 130%);
}
.hero--sakina .hero__media::after { background: none; }

/* Responsive trip / card grids */
.trip-grid,
.card-grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: 1fr;
}
@media (min-width: 560px) {
  .trip-grid, .card-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 920px) {
  .trip-grid, .card-grid { grid-template-columns: repeat(3, 1fr); }
}

/* ============================================================
   P1-8: /voyages/ PURE-CSS :target FILTER
   The destination chips were styled like filter pills but only anchor-jumped.
   Now they actually filter: scoping a .trip-grid--filter so that when ANY
   card is :target, all NON-targeted cards hide. The "كل الوجهات" chip points
   at #all (a wrapper outside the grid) which matches no card → everything
   shows (default + reset). Each filter chip also gets an active/:focus state.
   No JS. Only applies on the voyages grid (opt-in class). RTL-safe.
   ============================================================ */
.trip-grid--filter:has(.n-card:target) .n-card { display: none; }
.trip-grid--filter:has(.n-card:target) .n-card:target { display: block; }

/* Filter chip active + focus state (P1-8). The targeted chip reads as
   selected; :focus-visible already gets the dual ring. */
.filter-chips .n-chip {
  border: 1.5px solid transparent;
  transition: background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.filter-chips .n-chip:hover,
.filter-chips .n-chip:focus-visible {
  background: var(--color-primary-soft);
  color: var(--color-primary-strong);
  border-color: var(--color-primary);
}

/* Service card (the 6 parallel offerings) */
.service-card {
  background: var(--surface-raised);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  box-shadow: var(--card-shadow);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  transition: box-shadow var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.service-card:hover { box-shadow: var(--card-shadow-hover); transform: translateY(-3px); }
.service-card__icon {
  inline-size: 48px; block-size: 48px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--color-primary-soft);
  color: var(--color-primary-strong);
  border-radius: var(--radius-md);
}
.service-card h3 { font-size: var(--text-xl); }
.service-card p { color: var(--text-muted); font-size: var(--text-sm); flex: 1; }

/* P1-10: long-line guard. Body paragraphs in cards/blocks have no inherent
   measure cap, so on wide desktop a card paragraph could exceed the ≤65ch
   readability rule. Cap them (heroes/sec-head already cap their own). */
.service-card p,
.n-card .pad > p,
.n-card > p {
  max-inline-size: 62ch;
}

/* ============================================================
   P1-2 / §05 var.3: THE ARCH AS A STILL OUTLINED FRAME
   The brand's signature device, rendered as a calm reverent frame (NOT an
   image mask): arch radius, 2px gold border, sand fill. For Sakina /omra/
   only — one per viewport. No motion (Sakina stillness). RTL-safe.
   ============================================================ */
.arch-frame {
  border: 2px solid var(--color-accent-strong);
  border-radius: var(--radius-arch);
  background: var(--sand-50);
  padding: var(--space-7) var(--space-6) var(--space-6);
}
[data-theme="dark"] .arch-frame { background: var(--surface-raised); }

/* ============================================================
   P2-4: DEPARTURE-FROM-CONSTANTINE MOTIF
   A tiny inline "✈ قسنطينة" route glyph (decorative SVG + label), no
   motion. Reinforces the one geographic differentiator on cards/heroes.
   ============================================================ */
.n-depart {
  display: inline-flex;
  align-items: center;
  gap: .35rem;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--text-xs);
  color: var(--color-primary-strong);
}
.n-depart svg { flex: none; inline-size: 14px; block-size: 14px; }
.hero .n-depart { color: var(--sand-100); }

/* ============================================================
   P2-3: GUIDE PORTRAIT PLACEHOLDER (companion-not-vendor)
   No real guide photo yet → a tasteful circular monogram in brand teal.
   TODO-CLIENT: swap for a real guide photo (same dimensions, object-fit
   cover) when supplied. Pairs the founder/guide beside trust + CTAs.
   ============================================================ */
.guide-portrait {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: 64px;
  block-size: 64px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
  color: var(--text-on-primary);
  font-family: var(--heading-font);
  font-weight: 700;
  font-size: var(--text-lg);
  border: 2px solid var(--surface-raised);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.guide-portrait img { inline-size: 100%; block-size: 100%; object-fit: cover; }
.guide-row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.guide-row .guide-meta { line-height: var(--leading-latin); }
.guide-row .guide-meta strong { color: var(--text-strong); font-family: var(--heading-font); display: block; }
.guide-row .guide-meta span { color: var(--text-muted); font-size: var(--text-sm); }


/* ============================================================
   ░░ SCROLL-REVEAL CSS (driven by reveal.js) ░░
   P0-5: fade + 24px rise (was 16px); transform 600ms / opacity 420ms so the
   motion reads as intentional (240ms flickered). will-change is added here
   on un-revealed nodes and REMOVED in reveal.js on the same unobserve.
   ============================================================ */
[data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 420ms var(--ease-out), transform 600ms var(--ease-out);
}
/* P-perf: promote ONLY while still hidden (JIT layer); the .is-revealed
   end-state drops the hint so we never leave dozens of permanent compositor
   layers alive on reduced-motion / Sakina pages where nothing reveals. */
[data-reveal]:not(.is-revealed) { will-change: opacity, transform; }
[data-reveal].is-revealed { opacity: 1; transform: none; }

/* ── AOS-style directional / zoom variants ───────────────────────────
   The engine (reveal.js) just toggles .is-revealed; the *direction* lives
   here, keyed off the data-reveal value — so we get AOS's effect catalog
   (fade-up/down/left/right, zoom-in, flip) with zero extra JS or library
   weight, and every variant inherits the reduced-motion / Sakina /
   scripting:none safety nets below that target [data-reveal] generically.
   A softer spring easing gives the entrances more life than plain ease-out. */
[data-reveal="fade-up"]    { transform: translateY(28px); }
[data-reveal="fade-down"]  { transform: translateY(-28px); }
/* RTL-aware: "left/right" follow the writing direction via logical sign. */
[data-reveal="fade-left"]  { transform: translateX(32px); }
[data-reveal="fade-right"] { transform: translateX(-32px); }
[dir="rtl"] [data-reveal="fade-left"]  { transform: translateX(-32px); }
[dir="rtl"] [data-reveal="fade-right"] { transform: translateX(32px); }
[data-reveal="zoom-in"]    { transform: scale(.92); }
[data-reveal="zoom-out"]   { transform: scale(1.06); }
[data-reveal="flip-up"]    { transform: perspective(900px) rotateX(12deg) translateY(20px); transform-origin: bottom; }
[data-reveal^="fade-"],
[data-reveal="zoom-in"],
[data-reveal="zoom-out"],
[data-reveal="flip-up"] {
  transition: opacity 480ms var(--ease-out), transform 700ms var(--ease-spring);
}
[data-reveal].is-revealed { transform: none; }

/* ============================================================
   PROGRESSIVE-ENHANCEMENT SAFETY NET
   The motion layer hides several elements by default and reveals them
   via JS-set classes (.is-loaded / .hero-ready / .chrome-ready /
   .is-revealed). If JS never runs (script load failure on flaky 3G, or
   scripting disabled) force the visible end-state so no content — hero
   text, prices, card images, the WhatsApp FAB, or the sticky conversion
   bar ("WhatsApp is checkout") — is ever stranded hidden. Inert whenever
   scripting is active, so it never fights the JS-driven motion path.
   (Covers modern engines; very old browsers without the `scripting`
   media feature fall back to the JS path, which is the common case.)
   ============================================================ */
@media (scripting: none) {
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  .n-card .img > img,
  .n-card .img > picture img { opacity: 1 !important; }
  .hero__inner > * { opacity: 1 !important; transform: none !important; animation: none !important; }
  .fab-whatsapp { opacity: 1 !important; transform: none !important; }
  .n-sticky-bar { transform: none !important; }
}


/* ============================================================
   ░░ MODE RULES ░░
   ============================================================ */
/* Sakina never scroll-animates — "stillness is the design" (§07).
   Force any reveal target to its final state, regardless of JS state. */
[data-mode="sakina"] [data-reveal] {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
  will-change: auto !important;
}

/* Smooth mode/token transition on the page surface (matches the demo) */
[data-mode] { transition: background var(--dur-base) var(--ease-out), color var(--dur-base) var(--ease-out); }


/* ============================================================
   ░░ RESPONSIVE HARDENING ░░
   Tighten the EXISTING layout for 320px → 1440px+ (no redesign):
   overflow-x guards where warranted, sane chrome at tiny + huge
   widths, tap-target floors, landscape-phone safety. Token-driven.
   ============================================================ */

/* Global overflow + wrapping safety nets. body already has overflow-x
   hidden; reinforce that long unbroken strings (URLs, emails, long AR
   words) wrap instead of forcing a horizontal scrollbar. */
html { overflow-x: hidden; }
body { max-inline-size: 100%; }
p, li, a, h1, h2, h3, h4, h5, h6, td, th, figcaption {
  overflow-wrap: break-word;
}

/* Top bar: never overflow from 320px up. Let the logo shrink before the
   bar does, and tighten the inter-item gap on the narrowest phones so
   [logo] + [theme-toggle] + [hamburger] always fit. */
.site-nav { gap: var(--space-3); }
.nav-logo { min-inline-size: 0; }
.nav-logo img,
.nav-logo svg {
  height: clamp(30px, 7vw, 40px);
  max-inline-size: min(46vw, 220px);
}
@media (max-width: 360px) {
  .site-nav { gap: var(--space-2); }
}

/* Hero: keep vertical rhythm sane on very small AND very large screens,
   and survive landscape phones (short viewports) without locking a tall
   min-height that pushes the CTA off-screen. */
.hero { padding-inline: var(--container-x); }
@media (max-width: 380px) {
  .hero h1 { font-size: clamp(2.25rem, 9vw, 2.75rem); }
}
@media (max-height: 520px) and (orientation: landscape) {
  .hero { min-block-size: auto; padding-block: calc(var(--nav-h) + var(--space-5)) var(--space-6); }
}

/* Sticky conversion strip: at 320px the 420px max-width would overflow
   its padded container. Cap it to the viewport. */
.n-sticky { max-width: min(420px, 100%); }

/* Price grid on phones: keep it a real table (column alignment intact)
   but make sure it can never push the page wider than the viewport.
   table-layout:fixed + wrapping prices means it always fits 320px up;
   we don't force display:block (that would destroy the columns) nor add
   a scroll wrapper (that markup belongs to the HTML lane). */
.n-grid { table-layout: fixed; max-inline-size: 100%; }
@media (max-width: 480px) {
  .n-grid td.p { white-space: normal; }   /* let tight prices wrap, not overflow */
  .n-grid th, .n-grid td { padding-inline: .6rem; }
}

/* Tap-target floor for inline chrome links that aren't buttons but are
   primary touch targets. Footer links get a comfortable touch height.
   Top-bar nav links (direct children of .site-nav, shown 901–1024px on
   coarse tablets) also get the floor — but we deliberately DON'T touch
   .nav-drawer links, which already pad to ≥48px and must stay block. */
@media (pointer: coarse) {
  .site-footer__list a {
    min-block-size: var(--touch-min);
    display: inline-flex;
    align-items: center;
  }
  .site-nav > .nav-links a { min-block-size: var(--touch-min); }
}

/* Fluid section padding floor on tiny screens (keeps content off the
   edges without over-padding 320px). Large screens already get space-9. */
@media (max-width: 400px) {
  .section { padding-block: var(--space-7); }
}


/* ============================================================
   ░░ PHOTO-OVERLAY LEGIBILITY LOCKS (Alliance S5) ░░
   Any price / badge sitting OVER a photograph gets a dark glass
   + white text, theme-independent. The !important is load-bearing.
   /* legibility lock — do not "clean up" */
   ============================================================ */
.over-photo,
.n-card .img .n-price,
.n-card .img .n-badge-promo,
.hero .n-price,
.hero .over-photo {
  background: rgba(0, 0, 0, .6) !important;   /* legibility lock — do not "clean up" */
  color: #fff !important;                      /* legibility lock — do not "clean up" */
  border-radius: var(--radius-md);
  padding: .35rem .7rem;
}
.over-photo .amount,
.over-photo .cur,
.over-photo .per,
.n-card .img .n-price .amount,
.n-card .img .n-price .cur,
.hero .n-price .amount,
.hero .n-price .cur {
  color: #fff !important;                      /* legibility lock — do not "clean up" */
}

/* ---- Google Maps location link (footer + contact) ---- */
.map-link{display:inline-flex;align-items:center;gap:.5rem;font-family:var(--font-body);font-weight:600;font-size:var(--text-sm);line-height:1;color:var(--color-primary-strong);background:var(--chip-bg);border:1px solid var(--border-soft);border-radius:var(--radius-pill);padding:.6rem 1.1rem;min-height:44px;text-decoration:none;transition:background var(--dur-fast) var(--ease-out),transform var(--dur-fast) var(--ease-out),box-shadow var(--dur-fast) var(--ease-out)}
.map-link:hover{background:var(--color-primary-soft);transform:translateY(-1px);box-shadow:var(--shadow-sm)}
.map-link:active{transform:translateY(0)}
.map-link svg{flex:none;color:var(--color-accent)}
.map-link--lg{font-size:var(--text-base);padding:.7rem 1.3rem;min-height:48px}
.site-footer .map-link{color:#fff;background:rgba(255,255,255,.12);border-color:rgba(255,255,255,.24)}
.site-footer .map-link:hover{background:rgba(255,255,255,.2)}
.site-footer .map-link svg{color:var(--color-accent)}


/* ============================================================
   SIGNATURE BRAND LAYER  (anti-slop pass · Wave 1)
   North-African geometric soul. Faint girih texture lives ONLY on the quiet
   dark bands (footer + Sakina hero); light sections stay clean. A khatam
   (8-point star) marks every section eyebrow, tinted by the mode via
   currentColor (teal Rihla / deeper Sakina). The Arch stays Sakina-only (§05).
   ============================================================ */
:root {
  --khatam: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='%23000'%3E%3Crect x='4' y='4' width='16' height='16'/%3E%3Crect x='4' y='4' width='16' height='16' transform='rotate(45,12,12)'/%3E%3C/g%3E%3C/svg%3E");
  --girih-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='44' height='44' viewBox='0 0 44 44'%3E%3Cg fill='none' stroke='%23ffffff' stroke-width='1' opacity='0.05'%3E%3Crect x='8' y='8' width='28' height='28'/%3E%3Crect x='8' y='8' width='28' height='28' transform='rotate(45,22,22)'/%3E%3C/g%3E%3C/svg%3E");
}

/* Khatam star before every section eyebrow (masked → inherits eyebrow color) */
.eyebrow { display: flex; align-items: center; gap: .5rem; inline-size: max-content; max-inline-size: 100%; }
.eyebrow::before {
  content: ""; flex: none; inline-size: 13px; block-size: 13px;
  background: currentColor;
  -webkit-mask: var(--khatam) center / contain no-repeat;
          mask: var(--khatam) center / contain no-repeat;
}

/* Faint girih texture on the quiet dark bands */
.site-footer { background-image: var(--girih-dark); background-size: 44px 44px; }
.hero--sakina {
  background-image: var(--girih-dark),
    linear-gradient(165deg, var(--night-900) 0%, var(--teal-800) 60%, var(--teal-700) 130%);
  background-size: 44px 44px, cover;
}


/* ============================================================
   ANTI-SLOP · Wave 2 — editorial hierarchy & card craft
   Breaks the uniform six-equal-card rhythm without leaving the system.
   ============================================================ */

/* Bento: the flagship featured trip leads the grid, image-forward, on desktop.
   Home featured only — the /voyages/ catalog stays an even grid by design. */
@media (min-width: 920px) {
  #featured-trips .trip-grid > article:first-child { grid-column: span 2; }
  #featured-trips .trip-grid > article:first-child .img { aspect-ratio: 2 / 1; }
}

/* Service cards gain a small teal brand-tab at the top edge that extends on
   hover — a quiet crafted detail instead of a plain box. */
.service-card { position: relative; }
.service-card::before {
  content: ""; position: absolute;
  inset-block-start: 0; inset-inline-start: var(--space-5);
  inline-size: 34px; block-size: 3px;
  border-end-start-radius: 3px; border-end-end-radius: 3px;
  background: var(--color-primary);
  transition: inline-size var(--dur-base) var(--ease-out);
}
.service-card:hover::before { inline-size: 60px; }


/* The "see all / كل الوجهات" end-cap cells: a teal-tinted navigational tile,
   visually distinct from white content cards — reads as deliberate, not filler.
   Matched by their centered inline-style signature (no markup change). */
#featured-trips .trip-grid > .n-card[style*="min-height"],
.card-grid > .service-card[style*="justify-content:center"] {
  background: var(--color-primary-soft);
  border-color: transparent;
  box-shadow: none;
}
.card-grid > .service-card[style*="justify-content:center"]::before { content: none; }
.card-grid > .service-card[style*="justify-content:center"] h3 { color: var(--color-primary-strong); }


/* ============================================================
   FRONTEND SANITY — balanced short grids (no lonely/empty cells)
   Caps 3-col grids to 2-col on desktop for short card sets:
   4 cards -> 2x2, 2 cards -> 1x2. Centered + width-capped so cards
   don't over-stretch. Below 920px the base grid already reflows.
   ============================================================ */
@media (min-width: 920px) {
  .card-grid.grid-2, .trip-grid.grid-2 {
    grid-template-columns: repeat(2, 1fr);
    max-inline-size: 880px;
    margin-inline: auto;
  }
}


/* ---- Brand quote band (Amiri reverent beat; Rihla — no arch, girih texture) ---- */
.quote-band { background: var(--surface-inverse); background-image: var(--girih-dark); background-size: 44px 44px; color: #fff; text-align: center; }
.brand-quote { max-inline-size: 32ch; margin-inline: auto; }
.brand-quote__mark { display: inline-block; inline-size: 26px; block-size: 26px; margin-block-end: var(--space-4); background: var(--gold-300); -webkit-mask: var(--khatam) center/contain no-repeat; mask: var(--khatam) center/contain no-repeat; }
.brand-quote p { font-family: var(--font-reverent); font-weight: 700; font-size: var(--text-3xl); line-height: 1.5; color: #fff; }
.brand-quote cite { display: block; margin-block-start: var(--space-4); font-style: normal; color: var(--gold-300); font-size: var(--text-lg); }


/* ---- Itinerary timeline (RTL-aware connected vertical steps) ---- */
.timeline { counter-reset: t; position: relative; display: grid; gap: var(--space-5); max-inline-size: 56ch; margin-inline: auto; }
.timeline::before { content: ""; position: absolute; inset-block: 16px 16px; inset-inline-start: 11px; inline-size: 2px; background: var(--border-soft); border-radius: 2px; }
.timeline__item { position: relative; padding-inline-start: var(--space-6); }
.timeline__item::before { counter-increment: t; content: counter(t); position: absolute; inset-inline-start: 0; inset-block-start: 0; inline-size: 24px; block-size: 24px; border-radius: 50%; background: var(--color-primary); color: var(--text-on-primary); font-family: var(--font-display); font-weight: 700; font-size: .85rem; display: grid; place-items: center; box-shadow: 0 0 0 4px var(--surface-raised); }
.timeline__item h3 { font-size: var(--text-lg); color: var(--text-strong); line-height: 1.3; }
.timeline__item p { color: var(--text-muted); margin-block-start: var(--space-2); }
