:root {
  --actionable-item: rgba(26, 114, 172, 1);
  --backgrounds-groupedsecondary: rgba(255, 255, 255, 1);
  --body-p-small-font-family: "Chivo", Helvetica;
  --body-p-small-font-size: 14px;
  --body-p-small-font-style: normal;
  --body-p-small-font-weight: 400;
  --body-p-small-letter-spacing: -0.21px;
  --body-p-small-line-height: 22px;
  --slate-300: rgba(203, 213, 225, 1);
  --subtle-medium-font-family: "Chivo", Helvetica;
  --subtle-medium-font-size: 14px;
  --subtle-medium-font-style: normal;
  --subtle-medium-font-weight: 500;
  --subtle-medium-letter-spacing: 0px;
  --subtle-medium-line-height: 20px;

  /* ──────────────────────────────────────────────────────────────────
   *  Elevation tokens — Apple-grade soft-and-long shadow system.
   *  Three tiers chosen so the B2B trip-creation chrome (chips, day
   *  cards, popovers, dock, sheets) all read as "cut from the same
   *  fabric" instead of a stack of bespoke `shadow-[Npx_Npx_...]`
   *  inline rules. Use via the `shadow-pill | shadow-card |
   *  shadow-floating` Tailwind utilities — DO NOT inline pixel values
   *  in new components.
   *
   *    --shadow-pill     → small floating UI (suggestion chips, mini
   *                        pills). One-layer hint of lift, ~3px blur.
   *    --shadow-card     → surface cards (day cards, sidebar panels,
   *                        detail cards). Two-layer soft elevation
   *                        with longer blur than Tailwind's `shadow-md`.
   *    --shadow-floating → popovers, modals, bottom-sheets, the
   *                        Smart-Suggest dock. Long + soft so the
   *                        surface feels lifted off the canvas without
   *                        a hard dark band underneath.
   *
   *  Reference: Apple HIG uses softer, longer-falloff shadows than
   *  Tailwind's defaults. Light-mode only for now; dark-mode tokens
   *  land in a follow-up if/when the planner gets a dark surface.
   *  ────────────────────────────────────────────────────────────── */
  --shadow-pill:
    0 1px 2px 0 rgb(0 0 0 / 0.04),
    0 1px 3px 0 rgb(0 0 0 / 0.06);
  --shadow-card:
    0 4px 12px -2px rgb(0 0 0 / 0.06),
    0 2px 6px -1px rgb(0 0 0 / 0.04);
  --shadow-floating:
    0 16px 44px -22px rgb(0 0 0 / 0.18),
    0 6px 18px -8px rgb(0 0 0 / 0.10);

  /* ──────────────────────────────────────────────────────────────────
   *  Chrome tokens — Border-margin token cure pass (2026-05-19).
   *  Sibling system to the elevation tokens above so every B2B
   *  trip-creation surface (popover, day cards, sheets, modals, chips,
   *  map-top pills) can share one radii + border vocabulary instead of
   *  17 distinct ad-hoc `rounded-[Npx]` / `border-[rgba(...)]` recipes.
   *
   *  Exposed as Tailwind utilities (see `tailwind.config.js`):
   *      rounded-pill | rounded-tile | rounded-card | rounded-surface
   *      border-default | border-strong | border-onmap
   *
   *  Tier picks: tight 4px corners across the board for the
   *  rectilinear "documents" feel the Alma brand uses today on its
   *  agency-CTA (rounded-[4px]). Pills stay fully round (chip dock,
   *  avatars). Only genuine pill / circle surfaces use `pill`.
   *  ────────────────────────────────────────────────────────────── */
  --radius-pill: 9999px;
  /* B.UX.18 (2026-05-24) — canonical 10px radius across all
   * rectangular surfaces (pills, chips, buttons, modals, map).
   * Matches the comment in design-system/tokens/radii.ts. Previous
   * 4px-everywhere was off-token. */
  --radius-tile: 10px;
  --radius-card: 10px;
  --radius-surface: 10px;

  /* Border colour — single semantic scale, glassmorphism-friendly.
   * `--chrome-border-*` names so we don't clash with the legacy
   * agency-survey `--border-default` HSL token in @layer base below. */
  --chrome-border-default: rgba(15, 23, 42, 0.08); /* cream-on-cream hairline */
  --chrome-border-strong:  rgba(15, 23, 42, 0.14); /* selected / hovered */
  --chrome-border-onmap:   rgba(255, 255, 255, 0.30); /* over imagery */

  /* Spacing (semantic) — for callers that want to opt into the system
   * via inline style or arbitrary CSS, not consumed by Tailwind utilities
   * directly. Existing `p-*` Tailwind classes stay valid; this is purely
   * additive for future surfaces that want to subscribe by name. */
  --space-card-x: 1.25rem;  /* 20px — horizontal card padding */
  --space-card-y: 1rem;     /* 16px — vertical card padding */
  --space-pill-x: 0.75rem;  /* 12px */
  --space-pill-y: 0.375rem; /* 6px */

  /* ──────────────────────────────────────────────────────────────────
   *  Typography tokens — Final standardization wave (2026-05-19).
   *  Closes out the chrome harmony pass that started with shadows
   *  (PR #230), radii (PRs #241, #244), and chrome borders (PR #241).
   *
   *  An editorial six-step scale tuned to the B2B trip-creation chrome:
   *    h1      → 24px — trip title, modal heros
   *    h2      → 18px — section headers, popover titles, sheet titles
   *    h3      → 15px — card titles
   *    body    → 14px — default body / action labels
   *    caption → 12px — metadata, secondary labels
   *    micro   → 11px — day badges, eyebrow text
   *
   *  Plus a small leading + tracking scale and four named weights.
   *  Exposed as Tailwind utilities (see `tailwind.config.js`):
   *      text-h1 | text-h2 | text-h3 | text-body | text-caption | text-micro
   *      leading-tight | leading-snug | leading-body
   *      tracking-tight | tracking-base | tracking-wide
   *
   *  Use these on every new surface. Replace ad-hoc `text-[Npx]` and
   *  `text-sm/base/xs/lg/xl/2xl` where the mapping is obvious; leave
   *  alone where the surface needs an off-scale value on purpose.
   *  ────────────────────────────────────────────────────────────── */
  --text-h1:      24px;
  --text-h2:      18px;
  --text-h3:      15px;
  --text-body:    14px;
  --text-caption: 12px;
  --text-micro:   11px;

  --leading-tight: 1.15;
  --leading-snug:  1.25;
  --leading-body:  1.45;

  --tracking-tight: -0.01em;
  --tracking-base:  0;
  --tracking-wide:  0.02em;

  --font-weight-regular:  400;
  --font-weight-medium:   500;
  --font-weight-semibold: 600;
  --font-weight-bold:     700;

  /* Spacing tokens — the existing Tailwind default `spacing` scale
   * already covers `p-1 (4px) … p-8 (32px)` and the project doesn't
   * override `theme.spacing`, so these tokens are reference-only for
   * surfaces that want to subscribe via inline style / arbitrary CSS.
   * Tailwind's `p-1`, `gap-2`, `m-4`, etc. stay valid for everyone else. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;

  /* ──────────────────────────────────────────────────────────────────
   *  WJ-P0.a — SMGD named-easing tokens (widget_WBS §0.5.1).
   *  Three named curves. Every motion uses one of these — no
   *  `transition-all`, no bespoke `cubic-bezier()` in components.
   *  Curve numerics MUST match the §0.5.1 table verbatim.
   *
   *    --ease-snap   → iOS-spring-like. Drag drops, sheet snaps,
   *                    pickup, slot settle, Apply commit.
   *                    Duration window 180–260 ms (default 220 ms).
   *    --ease-flow   → Hero reveals, ring fills, Optimise re-flow
   *                    staggers, cinematic moments.
   *                    Duration window 320–680 ms.
   *    --ease-linear → Skeletons, fades, marker opacity, type
   *                    crossfades. Name is "linear" by SMGD vocabulary
   *                    (= the feel at short durations); the curve is a
   *                    gentle Material-standard bezier, NOT the CSS
   *                    `linear` keyword.
   *                    Duration window 120–200 ms (default 240 ms).
   *
   *  Reduced-motion: `prefers-reduced-motion: reduce` collapses every
   *  motion to a 60 ms opacity crossfade (handled at consumer sites;
   *  not encoded in the tokens themselves).
   *
   *  Tailwind utilities expose these as `ease-snap` / `ease-flow` /
   *  `ease-linear-smgd` (preset suffix `-smgd` on `linear` to avoid
   *  colliding with Tailwind's built-in `ease-linear` keyword utility).
   *  JS/TS callers (framer-motion) read the same curves as the
   *  `SMGD_EASE_*` constants exported from
   *  `src/shared/design-system/motion/easings.ts`.
   *  ────────────────────────────────────────────────────────────── */
  --ease-snap:   cubic-bezier(0.32, 0.72, 0, 1);
  --ease-flow:   cubic-bezier(0.16, 1, 0.3, 1);
  --ease-linear: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Mobile viewport height fixes to prevent cropping */
@supports (height: 100dvh) {
  /* Use dynamic viewport height when available */
  .min-h-screen {
    min-height: 100dvh;
  }
  .h-screen {
    height: 100dvh;
  }
}

/* Fallback for browsers that don't support dvh */
@media screen and (max-width: 768px) {
  .min-h-screen {
    min-height: 100vh;
    min-height: calc(var(--vh, 1vh) * 100);
  }
  .h-screen {
    height: 100vh;
    height: calc(var(--vh, 1vh) * 100);
  }
}

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 222.2 47.4% 11.2%;

    /* ----------------------------------------------------------------------
     * Shared-UI-B2B primitive tokens (P1-13 lift, 2026-05-25).
     * Drive the visual identity of `@tc/shared-ui-b2b` Button / Card /
     * Input / ConfirmDialog without per-app forks. Widget keeps the
     * legacy 10px / no-border / no-shadow / p-6 look.
     * -------------------------------------------------------------------- */
    --button-radius: 10px;
    --card-radius: 0;
    --card-border: none;
    --card-shadow: none;
    --card-header-padding: 1.5rem;     /* p-6 */
    --card-content-padding: 0;         /* widget Card content has no built-in pad */
    --input-radius: 10px;
    --input-border: 1px solid hsl(var(--input));

    /* Additive semantic tokens (used by agency survey wrappers; inert until referenced) */
    --surface-primary: 0 0% 100%;
    --surface-secondary: 0 0% 98%;
    --surface-elevated: 210 29% 24%;
    --surface-accent: 233 40% 42%;
    --text-primary: 222.2 47.4% 11.2%;
    --text-secondary: 215.4 16.3% 46.9%;
    --text-tertiary: 215 16% 65%;

    /*
     * W1-Q3 (widget_WBS, 2026-05-29) — accent text token for the
     * "italic gold tagline" pattern (e.g., "yours to refine") and the
     * eyebrow-on-cream uses ("Draft itinerary"). The previous pattern
     * was `text-[hsl(var(--secondary-dark))]/{60-80}` — opacity on the
     * brand gold over cream, which compounded to ~3.74:1 vs cream
     * (WCAG AA needs ≥ 4.5:1 for body text). This token is a darker
     * saturated gold (`rgb(120,80,15)` ≈ HSL `36 75% 26%`) that hits
     * ~7.5:1 against the cream popover/header backdrop.
     *
     * Naming: `accent-strong` (vs `accent`) leaves room for a future
     * `--text-accent-weak` for the secondary-dark gold's larger-text
     * decorative uses (WCAG AAA-tier of ≥ 3:1 for 18pt+).
     */
    --text-accent-strong: 36 75% 26%;
    --border-default: 214.3 31.8% 91.4%;

    --muted: 210 40% 96.1%;
    --muted-foreground: 215.4 16.3% 46.9%;

    --popover: 0 0% 100%;
    --popover-foreground: 222.2 47.4% 11.2%;

    --border: 214.3 31.8% 91.4%;
    --input: 214.3 31.8% 91.4%;

    --card: transparent;
    --card-foreground: 222.2 47.4% 11.2%;

    /* Dynamic primary colors - will be set via JavaScript */
    --primary-h: 222.2;
    --primary-s: 47.4%;
    --primary-l: 11.2%;
    --primary-text-h: 210;
    --primary-text-s: 40%;
    --primary-text-l: 98%;
    --primary-text-light-h: 0;
    --primary-text-light-s: 0%;
    --primary-text-light-l: 0%;
    --primary-text-dark-h: 210;
    --primary-text-dark-s: 40%;
    --primary-text-dark-l: 98%;
    --primary-text-opposite-h: 0;
    --primary-text-opposite-s: 0%;
    --primary-text-opposite-l: 0%;
    --primary-dark-h: 222.2;
    --primary-dark-s: 47.4%;
    --primary-dark-l: 6%;
    --primary-light-h: 222.2;
    --primary-light-s: 47.4%;
    --primary-light-l: 20%;
    --primary-contrast-h: 222.2;
    --primary-contrast-s: 47.4%;
    --primary-contrast-l: 11.2%;
    --primary: var(--primary-h) var(--primary-s) var(--primary-l);
    --primary-text: var(--primary-text-h) var(--primary-text-s)
      var(--primary-text-l);
    --primary-text-light: var(--primary-text-light-h)
      var(--primary-text-light-s) var(--primary-text-light-l);
    --primary-text-dark: var(--primary-text-dark-h) var(--primary-text-dark-s)
      var(--primary-text-dark-l);
    --primary-text-opposite: var(--primary-text-opposite-h)
      var(--primary-text-opposite-s) var(--primary-text-opposite-l);
    --primary-contrast: var(--primary-contrast-h) var(--primary-contrast-s)
      var(--primary-contrast-l);
    --primary-dark: var(--primary-dark-h) var(--primary-dark-s)
      var(--primary-dark-l);
    --primary-light: var(--primary-light-h) var(--primary-light-s)
      var(--primary-light-l);

    /* Dynamic secondary colors - will be set via JavaScript */
    --secondary-h: 210;
    --secondary-s: 40%;
    --secondary-l: 96.1%;
    --secondary-text-h: 222.2;
    --secondary-text-s: 47.4%;
    --secondary-text-l: 11.2%;
    --secondary-text-light-h: 0;
    --secondary-text-light-s: 0%;
    --secondary-text-light-l: 0%;
    --secondary-text-dark-h: 222.2;
    --secondary-text-dark-s: 47.4%;
    --secondary-text-dark-l: 11.2%;
    --secondary-dark-h: 210;
    --secondary-dark-s: 40%;
    --secondary-dark-l: 75%;
    --secondary-light-h: 210;
    --secondary-light-s: 40%;
    --secondary-light-l: 85%;
    --secondary-contrast-h: 210;
    --secondary-contrast-s: 40%;
    --secondary-contrast-l: 43%;
    --secondary: var(--secondary-h) var(--secondary-s) var(--secondary-l);
    --secondary-text: var(--secondary-text-h) var(--secondary-text-s)
      var(--secondary-text-l);
    --secondary-text-light: var(--secondary-text-light-h)
      var(--secondary-text-light-s) var(--secondary-text-light-l);
    --secondary-text-dark: var(--secondary-text-dark-h)
      var(--secondary-text-dark-s) var(--secondary-text-dark-l);
    --secondary-dark: var(--secondary-dark-h) var(--secondary-dark-s)
      var(--secondary-dark-l);
    --secondary-light: var(--secondary-light-h) var(--secondary-light-s)
      var(--secondary-light-l);
    --secondary-contrast: var(--secondary-contrast-h)
      var(--secondary-contrast-s) var(--secondary-contrast-l);

    --accent: 210 40% 96.1%;
    --accent-foreground: 222.2 47.4% 11.2%;

    /* Destructive surface — tokenised so the three EntityBottomSheet
       footers (DaySheet, CitySheet, ActivitySheet) and any future
       Remove/Delete affordance share one palette. `--destructive` is the
       text + focus-ring base; `--destructive-soft` is the muted hover
       wash. Audit reference: planner-ux/apple-senior-design-audit-2026-05-19.md §1.10. */
    --destructive: 353 81% 38%;
    --destructive-soft: 353 81% 56%;
    --destructive-foreground: 210 40% 98%;

    --ring: 215 20.2% 65.1%;

    --radius: 1rem;

    /* Form page background - will be set via JavaScript */
    --form-page-bg: linear-gradient(
      to bottom right,
      white,
      hsl(var(--primary) / 0.2),
      hsl(var(--primary))
    );
  }

  .dark {
    --background: 224 71% 4%;
    --foreground: 213 31% 91%;

    --surface-primary: 224 71% 6%;
    --surface-secondary: 223 47% 11%;
    --surface-elevated: 210 29% 24%;
    --surface-accent: 233 40% 42%;
    --text-primary: 213 31% 91%;
    --text-secondary: 215.4 16.3% 56.9%;
    --text-tertiary: 215 16% 45%;

    /*
     * W1-Q3 dark-mode counterpart of `--text-accent-strong`. The
     * widget itself is light-only today (alma-italia + the static-LB
     * brands), but the token is kept symmetric with the rest of the
     * `--text-*` set so a future dark-mode flip doesn't silently
     * collapse the accent surface back below AA. Lifted lightness +
     * trimmed saturation so the gold reads on a dark navy backdrop
     * (~7:1 against `--background` in the dark theme).
     */
    --text-accent-strong: 38 80% 70%;
    --border-default: 216 34% 17%;

    --muted: 223 47% 11%;
    --muted-foreground: 215.4 16.3% 56.9%;

    --accent: 216 34% 17%;
    --accent-foreground: 210 40% 98%;

    --popover: 224 71% 4%;
    --popover-foreground: 215 20.2% 65.1%;

    --border: 216 34% 17%;
    --input: 216 34% 17%;

    --card: transparent;
    --card-foreground: 213 31% 91%;

    --primary: 210 40% 98%;
    --primary-text: 222.2 47.4% 1.2%;
    --primary-dark-h: 210;
    --primary-dark-s: 40%;
    --primary-dark-l: 90%;
    --primary-dark: var(--primary-dark-h) var(--primary-dark-s)
      var(--primary-dark-l);
    --primary-contrast-h: 210;
    --primary-contrast-s: 40%;
    --primary-contrast-l: 95%;
    --primary-contrast: var(--primary-contrast-h) var(--primary-contrast-s)
      var(--primary-contrast-l);

    --secondary: 222.2 47.4% 11.2%;
    --secondary-text: 210 40% 98%;
    --secondary-dark-h: 222.2;
    --secondary-dark-s: 47.4%;
    --secondary-dark-l: 5%;
    --secondary-dark: var(--secondary-dark-h) var(--secondary-dark-s)
      var(--secondary-dark-l);
    --secondary-contrast-h: 222.2;
    --secondary-contrast-s: 47.4%;
    --secondary-contrast-l: 20%;
    --secondary-contrast: var(--secondary-contrast-h)
      var(--secondary-contrast-s) var(--secondary-contrast-l);

    --destructive: 353 81% 38%;
    --destructive-soft: 353 81% 56%;
    --destructive-foreground: 210 40% 98%;

    --ring: 216 34% 17%;

    --radius: 1rem;
  }
}

/* Animated gradient border for command input */
.animated-gradient-border {
  --angle: 0deg;
  background: conic-gradient(
    from var(--angle),
    hsl(var(--primary, 210 10% 20%)),
    hsl(var(--secondary, 0 0% 70%)),
    hsl(var(--primary, 210 10% 20%))
  );
  animation: gradient-rotate 5s linear infinite;
}

@keyframes gradient-rotate {
  to {
    --angle: 360deg;
  }
}

@property --angle {
  syntax: "<angle>";
  initial-value: 0deg;
  inherits: false;
}

@layer base {
  * {
    @apply border-border;
  }

  /* Touch-hygiene: kill the iOS/Android default tap-highlight flash
     (the blue/grey overlay that briefly paints on every tap). Combined
     with the `hover-hover:` variant in tailwind.config.js, this is the
     "we built this for touch" foundation — see Phase 2 worker γ. */
  html,
  body {
    -webkit-tap-highlight-color: transparent;
  }

  body {
    @apply bg-background text-foreground;
    /* Ensure global font is Chivo (Tailwind sans) — the brand stack
       already falls back to `-apple-system` for browsers without
       Inter loaded (see tailwind.config.js `fontFamily.sans`). */
    @apply font-sans;
    font-feature-settings:
      "rlig" 1,
      "calt" 1;
    /* Mobile performance optimizations */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Prevent zoom on input focus on iOS */
    -webkit-text-size-adjust: 100%;
    /* Smooth scrolling */
    scroll-behavior: smooth;
    /* Optimize for mobile */
    -webkit-overflow-scrolling: touch;
    /* Prevent horizontal scrolling */
    overflow-x: hidden;
    /* Allow vertical scrolling; avoid iOS focus bugs after overlays */
    overflow-y: auto;
    /* Support for dynamic viewport height */
    height: 100vh;
    height: 100dvh;
    /* Fallback cursor - will be overridden by custom cursor if available */
    cursor: auto;
  }

  /* 2026-05-21 — Curated-magazine aesthetic. Inline SVG noise filter
     applied as a fixed pseudo-element so every cream-paper surface
     (header, sidebar, popovers) reads as gently grained paper instead
     of flat fill. ~3% opacity is below conscious notice but adds the
     tactile cue that makes Indagare/Black Tomato sites feel pressed.
     Pinned `position: fixed` + `pointer-events: none` so it floats
     above the canvas without intercepting any input. */
  .planner-paper-grain::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: 0.035;
    mix-blend-mode: multiply;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 0.65 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
    background-size: 240px 240px;
  }

  html {
    overflow-x: hidden;
    /* Support for dynamic viewport units */
    height: 100vh;
    height: 100dvh;
  }

  /* Prevent Swiper from causing horizontal overflow */
  .swiper {
    overflow: hidden !important;
    touch-action: pan-y !important;
  }

  .swiper-wrapper {
    overflow: hidden !important;
  }

  /* Prevent Swiper from causing page-level horizontal scroll */
  .swiper-container {
    overflow: hidden !important;
    touch-action: pan-y !important;
  }

  /* Touch-friendly tap targets */
  button,
  [role="button"],
  input[type="button"],
  input[type="submit"],
  input[type="reset"] {
    /* Prevent text selection on tap */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    /* Optimize for touch */
    -webkit-tap-highlight-color: transparent;
  }

  /* GPU acceleration for animations */
  .gpu-accelerated {
    transform: translateZ(0);
    backface-visibility: hidden;
    /* no will-change here */
  }

  /* only apply will-change when animating */
  .gpu-accelerated.animating {
    will-change: transform;
  }

  /* Smooth transitions */
  .smooth-transition {
    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  }

  /* Mobile-optimized scrolling */
  .mobile-scroll {
    -webkit-overflow-scrolling: touch;
    scroll-behavior: smooth;
    overscroll-behavior: contain;
  }

  /* Prevent horizontal scroll on mobile */
  .prevent-horizontal-scroll {
    overflow-x: hidden;
    width: 100%;
  }

  /* Touch-friendly input styles */
  input,
  textarea,
  select,
  [contenteditable="true"] {
    font-size: 16px !important; /* Prevents zoom on iOS */
    -webkit-appearance: none;
    border-radius: 0;
    /* Ensure selectable text even if parent disables selection */
    -webkit-user-select: text !important;
    user-select: text !important;
  }

  /* iOS Safari input focus reliability */
  @supports (-webkit-touch-callout: none) {
    input,
    textarea,
    select,
    [contenteditable="true"] {
      -webkit-user-select: text !important;
      user-select: text !important;
      -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
      touch-action: manipulation;
      font-size: 16px !important; /* enforce on iOS */
    }
  }

  /* Mobile-optimized focus states */
  .mobile-focus:focus {
    outline: 2px solid hsl(var(--primary));
    outline-offset: 2px;
  }

  /* Haptic feedback simulation */
  .haptic-feedback {
    transition: transform 0.1s ease-out;
  }

  .haptic-feedback:active {
    transform: scale(0.95);
  }

  /* Progressive loading skeleton */
  .skeleton {
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: skeleton-loading 1.5s infinite;
  }

  @keyframes skeleton-loading {
    0% {
      background-position: 200% 0;
    }
    100% {
      background-position: -200% 0;
    }
  }

  /* Mobile-optimized glass effect */
  .glass {
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    background: rgba(255, 255, 255, 0.8);
  }

  /* Touch-friendly spacing */
  .touch-spacing > * + * {
    margin-top: 1rem;
  }

  /* Mobile-optimized button states */
  .mobile-button {
    position: relative;
    overflow: hidden;
  }

  .mobile-button::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 0;
    height: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.3);
    transform: translate(-50%, -50%);
    transition:
      width 0.3s,
      height 0.3s;
  }

  .mobile-button:active::before {
    width: 200px;
    height: 200px;
  }
}

/* Hide scrollbar utility */
.scrollbar-hide::-webkit-scrollbar {
  display: none;
}
.scrollbar-hide {
  -ms-overflow-style: none;
  scrollbar-width: none;
}

/* Modern scrollbar styles using primary color */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: transparent;
  border-radius: 4px;
}

::-webkit-scrollbar-thumb {
  background: hsl(var(--primary) / 0.6);
  border-radius: 4px;
  transition: background-color 0.2s ease;
}

::-webkit-scrollbar-thumb:hover {
  background: hsl(var(--primary) / 0.8);
}

::-webkit-scrollbar-thumb:active {
  background: hsl(var(--primary));
}

::-webkit-scrollbar-corner {
  background: white;
}

/* Firefox scrollbar styles */
* {
  scrollbar-width: thin;
  scrollbar-color: hsl(var(--primary) / 0.6) transparent;
}

/* Custom scrollbar for specific containers */
.custom-scrollbar::-webkit-scrollbar {
  width: 6px;
  height: 6px;
}

.custom-scrollbar::-webkit-scrollbar-track {
  background: transparent;
  border-radius: 3px;
}

.custom-scrollbar::-webkit-scrollbar-thumb {
  background: hsl(var(--primary) / 0.4);
  border-radius: 3px;
  transition: background-color 0.2s ease;
}

.custom-scrollbar::-webkit-scrollbar-thumb:hover {
  background: hsl(var(--primary) / 0.6);
}

.custom-scrollbar::-webkit-scrollbar-thumb:active {
  background: hsl(var(--primary) / 0.8);
}

/* Thin scrollbar for mobile */
.mobile-scrollbar::-webkit-scrollbar {
  width: 4px;
  height: 4px;
}

.mobile-scrollbar::-webkit-scrollbar-track {
  background: transparent;
  border-radius: 4px;
}

.mobile-scrollbar::-webkit-scrollbar-thumb {
  background: hsl(var(--primary) / 0.3);
  border-radius: 4px;
  transition: background-color 0.2s ease;
  border: none;
}

.mobile-scrollbar::-webkit-scrollbar-thumb:hover {
  background: hsl(var(--primary) / 0.5);
}

.mobile-scrollbar::-webkit-scrollbar-corner {
  background: transparent;
  border-radius: 4px;
}

/* Mobile scrollbars with rounded corners */
@media (max-width: 768px) {
  /* Set size */
  *::-webkit-scrollbar {
    width: 6px !important;
    height: 6px !important;
  }

  /* The "track" and its reusable pieces */
  *::-webkit-scrollbar-track,
  *::-webkit-scrollbar-track-piece {
    background: transparent !important;
    border-radius: 8px !important;
    margin: 2px 0 !important;
    border: 1px solid rgba(0, 0, 0, 0.06) !important;
    padding: 1px !important;
  }

  /* The draggable thumb */
  *::-webkit-scrollbar-thumb {
    background: hsl(var(--primary) / 0.4) !important;
    border-radius: 8px !important;
    border: none !important;
    transition: all 0.2s ease !important;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1) !important;
  }

  *::-webkit-scrollbar-thumb:hover {
    background: hsl(var(--primary) / 0.6) !important;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15) !important;
  }

  *::-webkit-scrollbar-thumb:active {
    background: hsl(var(--primary) / 0.8) !important;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) !important;
  }

  /* The corner where two scrollbars meet */
  *::-webkit-scrollbar-corner {
    background: transparent !important;
    border-radius: 8px !important;
  }
}

/* Mobile performance utilities */
@layer utilities {
  .touch-manipulation {
    touch-action: manipulation;
  }

  .touch-pan-x {
    touch-action: pan-x;
  }

  .touch-pan-y {
    touch-action: pan-y;
  }

  .touch-none {
    touch-action: none;
  }

  .will-change-transform {
    will-change: transform;
  }

  .will-change-opacity {
    will-change: opacity;
  }

  .will-change-scroll {
    will-change: scroll-position;
  }

  /* Reserve space for scrollbars to prevent content overlap */
  .scrollbar-gutter-stable {
    scrollbar-gutter: stable;
  }

  .scrollbar-gutter-both {
    scrollbar-gutter: stable both-edges;
  }

  /* Mobile-optimized animations */
  .animate-slide-up {
    animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  }

  .animate-slide-down {
    animation: slideDown 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  }

  .animate-fade-in {
    animation: fadeIn 0.2s ease-out;
  }

  .animate-scale-in {
    animation: scaleIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  }

  @keyframes slideUp {
    from {
      transform: translateY(20px);
      opacity: 0;
    }
    to {
      transform: translateY(0);
      opacity: 1;
    }
  }

  @keyframes slideDown {
    from {
      transform: translateY(-20px);
      opacity: 0;
    }
    to {
      transform: translateY(0);
      opacity: 1;
    }
  }

  @keyframes fadeIn {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }

  /* W4-P2 (widget_WBS, 2026-05-29) — `<ProposedCityBadge>` mount
     animation. Small rise + fade so the badge reads as a "moment
     of arrival" not a static label. No exit animation: when the
     parent unmounts (Accept committed → marker promoted to
     permanent style), the chrome should disappear instantly so
     the visual focus moves to the now-committed marker. */
  @keyframes proposedRise {
    from {
      opacity: 0;
      transform: translateY(2px);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }

  /* WJ-P2 (widget_WBS, 2026-05-30) — inter-city Accept pin-drop.
     The destination city marker descends 18 px (`pickup`) then
     lands at its anchor (`snap`). Pairs with the
     `gameFeelHaptics.impactMedium() -> success()` ladder fired by
     `useInterCityCinematic`. `--ease-flow` for both halves so the
     visual motion matches the rest of the §0.5 motion grammar.
     320 ms total. */
  @keyframes interCityPinDrop {
    0% {
      transform: translate3d(0, -18px, 0);
      opacity: 0;
    }
    100% {
      transform: translate3d(0, 0, 0);
      opacity: 1;
    }
  }

  /* WJ-P2 — `<ToBeConfirmedBadge>` fade-in deferred until *after*
     the pin lands. Replaces the existing `proposedRise` keyframe
     when the badge is mounted during an inter-city cinematic.
     `--ease-linear` 240 ms, no Y-translation: the pin already gave
     the audience the "thing arrived" cue; the badge is a quieter
     follow-up. */
  @keyframes interCityBadgeFade {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }

  /* W6-Q1 (widget_WBS, 2026-05-30) — `<DayRhythmRing>` segment fill.
     Apple Fitness Activity-ring sweep timing (~0.9 s). Per-segment
     30 ms top-down stagger is applied via inline `animationDelay`
     in the component. The dasharray-based reveal is done via a
     transform-scale-only animation because animating stroke-
     dashoffset around an arc segment that's already gap-defined
     would re-introduce the percentage-donut read; scaling the
     filled segment up from 0 conveys "this one is appearing" without
     muddying the discrete-segment encoding. */
  @keyframes dayRhythmRingFill {
    from {
      transform: scale(0);
      opacity: 0;
    }
    to {
      transform: scale(1);
      opacity: 1;
    }
  }
  @media (prefers-reduced-motion: reduce) {
    @keyframes dayRhythmRingFill {
      from {
        transform: scale(1);
        opacity: 1;
      }
      to {
        transform: scale(1);
        opacity: 1;
      }
    }
  }

  @keyframes scaleIn {
    from {
      transform: scale(0.95);
      opacity: 0;
    }
    to {
      transform: scale(1);
      opacity: 1;
    }
  }

  /* PR-D (Alma chrome redesign, edit-loader in-place, 2026-06-01) —
     the in-place edit treatment that replaces the blocking navy
     spinner-card (`EditProgressOverlay`). Three motifs, all gold,
     all `prefers-reduced-motion`-aware:

       1. `.edit-goldbar` — a thin gold indeterminate progress bar
          pinned to the top of the itinerary panel/sheet. A 38 %-wide
          gold gradient sweeps left→right on a faint gold track; it's
          the single global "Trippy is working" signal. Reduced-motion
          parks a centered static gold fill (no travel) so the bar
          still reads as "in progress" without animation.

       2. `.edit-day-shimmer` — the gold-tinted skeleton sweep painted
          over the day-card(s) being edited. A diagonal gold highlight
          translates across the card via a `::after` overlay (the card
          itself supplies the gold ring + tint). Reduced-motion drops
          the sweep entirely; the static ring + tint carry the "this
          card is changing" cue.

       3. `editGoldDrop` — the gold marker drop-in for a newly added
          place (D2). Marker descends 16 px + scales up from 0.4 →
          settles, mirroring `interCityPinDrop` timing so the motion
          grammar matches the rest of the map. Consumed inline by
          `B2bActivityMarkers`; gated on `useReducedMotion()` there so
          this CSS is belt-and-braces. */
  @keyframes editGoldBarSweep {
    0% {
      transform: translateX(-60%);
    }
    100% {
      transform: translateX(260%);
    }
  }
  .edit-goldbar {
    position: relative;
    overflow: hidden;
    background: hsl(var(--secondary) / 0.14);
  }
  .edit-goldbar::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    height: 100%;
    width: 38%;
    border-radius: 2px;
    background: linear-gradient(
      90deg,
      transparent,
      hsl(var(--secondary)),
      transparent
    );
    animation: editGoldBarSweep 1.25s ease-in-out infinite;
  }

  @keyframes editDaySweep {
    to {
      transform: translateX(100%);
    }
  }
  .edit-day-shimmer::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(
      90deg,
      transparent,
      hsl(var(--secondary) / 0.16),
      transparent
    );
    transform: translateX(-100%);
    animation: editDaySweep 1.35s ease-in-out infinite;
  }

  @keyframes editGoldDrop {
    0% {
      transform: translate3d(0, -16px, 0) scale(0.4);
      opacity: 0;
    }
    70% {
      transform: translate3d(0, 2px, 0) scale(1.08);
      opacity: 1;
    }
    100% {
      transform: translate3d(0, 0, 0) scale(1);
      opacity: 1;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .edit-goldbar::after {
      animation: none;
      inset: 0 auto 0 50%;
      width: 24%;
      transform: translateX(-50%);
    }
    .edit-day-shimmer::after {
      animation: none;
      display: none;
    }
  }

  /* WJ-P9 (widget_WBS, 2026-05-30) — Apply commit choreography
     telegraph keyframes. Consumed by `<ApplyTelegraphFlash>` in
     `src/b2b/trip-creation/proposalOverlay/ApplyCommitChoreography.tsx`.

     `apply-telegraph` is the ring-outline flash: 80 ms fade-in
     (0 -> peak opacity), held for ~80 ms, 160 ms fade-out -> 0. Total
     envelope 320 ms; the consumer composes with `var(--ease-flow)`
     so the falloff feels organic, not metronomic.

     `apply-counter` is the +1 counter-ticker badge: pops in with a
     small translate-up so it reads as a numerical tally settling
     into place; same 320 ms envelope so both effects retire as a
     unit (no late-lingering counter after the ring fades).

     `prefers-reduced-motion: reduce` -> both keyframes flatten so
     the user lands on a no-op idle state instantly. The consumer
     also gates the mount itself via `useReducedMotion()`, so this
     CSS-level fallback is belt-and-braces. */
  @keyframes apply-telegraph {
    0% {
      opacity: 0;
    }
    25% {
      opacity: 1;
    }
    50% {
      opacity: 1;
    }
    100% {
      opacity: 0;
    }
  }
  @keyframes apply-counter {
    0% {
      opacity: 0;
      transform: translateY(4px) scale(0.92);
    }
    30% {
      opacity: 1;
      transform: translateY(0) scale(1);
    }
    70% {
      opacity: 1;
      transform: translateY(-2px) scale(1);
    }
    100% {
      opacity: 0;
      transform: translateY(-8px) scale(1);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    @keyframes apply-telegraph {
      0%,
      100% {
        opacity: 0;
      }
    }
    @keyframes apply-counter {
      0%,
      100% {
        opacity: 0;
        transform: none;
      }
    }
  }
}

/* DayPicker */

.rdp-day_selected {
  background-color: hsl(var(--primary));
  color: hsl(var(--primary-text));
}

/* Custom utility classes for primary and secondary with automatic text colors */
.bg-primary {
  background-color: hsl(var(--primary));
  color: hsl(var(--primary-text));
}

.bg-secondary {
  background-color: hsl(var(--secondary));
  color: hsl(var(--secondary-text));
}

.bg-primary-light {
  background-color: hsl(var(--primary-light));
  color: hsl(var(--primary-text-light));
}

.bg-secondary-light {
  background-color: hsl(var(--secondary-light));
  color: hsl(var(--secondary-text-light));
}

/* Text color utilities */
.text-primary {
  color: hsl(var(--primary));
}

.text-primary-text {
  color: hsl(var(--primary-text));
}

.text-primary-text-light {
  color: hsl(var(--primary-text-light));
}

.text-primary-text-dark {
  color: hsl(var(--primary-text-dark));
}

.text-primary-text-opposite {
  color: hsl(var(--primary-text-opposite));
}

.text-secondary {
  color: hsl(var(--secondary));
}

.text-secondary-text {
  color: hsl(var(--secondary-text));
}

.text-secondary-text-light {
  color: hsl(var(--secondary-text-light));
}

.text-secondary-text-dark {
  color: hsl(var(--secondary-text-dark));
}

.text-secondary-text-opposite {
  color: hsl(var(--secondary-text-opposite));
}

/* Border utilities */
.border-primary {
  border-color: hsl(var(--primary));
}

.border-secondary {
  border-color: hsl(var(--secondary));
}

/* Form page background utilities */
.bg-form-page {
  background: var(--form-page-bg);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

/* VoiceInputButton soft pulse effect */
/* base pulse */
.soft-pulse {
  position: absolute;
  inset: 0; /* shorthand for top/right/bottom/left = 0 */
  border-radius: 50%;
  background-color: hsl(var(--primary) / 0.3);
  /* simple from‑to keyframe, smooth ease‑out, infinite loop */
  animation: softPulse 1.8s ease-out infinite;
  z-index: 1;
}

/* staggered delays for the 2nd & 3rd blobs */
.soft-pulse2 {
  animation-delay: 0.6s;
}
.soft-pulse3 {
  animation-delay: 1.2s;
}

@keyframes softPulse {
  from {
    transform: scale(1);
    opacity: 0.6;
  }
  to {
    transform: scale(2);
    opacity: 0;
  }
}

/* Custom Google Maps Marker Styles */
.custom-marker {
  position: relative;
  z-index: 1000;
}

.custom-marker:hover {
  z-index: 1001;
}

/* Ensure markers are clickable and properly positioned */
.gm-style-moc {
  cursor: pointer !important;
}

.gm-style-moc:hover {
  transform: scale(1.1);
  transition: transform 0.2s ease;
}

/* Custom InfoBox Styles */
.custom-infobox {
  position: absolute;
  width: 280px;
  border-radius: 20px;
  overflow: hidden;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
  background: white;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  z-index: 1000;
}

.custom-infobox .header {
  height: 160px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  font-size: 24px;
  font-weight: bold;
  padding: 0 16px;
  text-align: center;
}

.custom-infobox .body {
  padding: 16px;
}

.custom-infobox .body h3 {
  margin: 0 0 4px 0;
  font-size: 18px;
  font-weight: bold;
  color: #333;
}

.custom-infobox .body .location {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 8px;
  color: #666;
  font-size: 14px;
}

.custom-infobox .body .location .icon {
  font-size: 12px;
}

.custom-infobox .body p {
  margin: 0;
  font-size: 14px;
  line-height: 1.4;
  color: #666;
}

/* Mobile-specific touch optimizations for drag and drop */
@media (max-width: 768px) {
  /* Prevent text selection during drag operations */
  .touch-manipulation {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
  }

  /* Improve touch feedback for drag handles */
  .drag-handle {
    touch-action: none; /* <-- critical */
    -webkit-user-select: none;
    user-select: none;
    -webkit-user-drag: none;
    cursor: grab; /* <-- visual clarity */
    /* Larger touch target for mobile */
    min-height: 44px;
    min-width: 44px;
    /* Better visual feedback */
    transition: all 0.15s ease;
  }

  /* Better touch area for mobile drag operations */
  .sortable-item {
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;
  }

  /* Prevent zoom on double-tap during drag */
  .dragging {
    touch-action: none;
  }

  /* Additional mobile drag optimizations */
  .drag-handle:active {
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
    cursor: grabbing; /* <-- active state */
    background-color: rgb(229 231 235); /* bg-gray-200 */
    transform: scale(0.95);
  }

  /* Prevent touch conflicts during drag operations */
  .sortable-item.dragging {
    touch-action: none;
    pointer-events: none;
  }

  /* Enable touch events only on drag handle during drag */
  .sortable-item.dragging .drag-handle {
    pointer-events: auto;
    touch-action: none;
  }
}

/* Global sortable item styles (safe defaults) */
.sortable-item {
  touch-action: auto; /* allow inputs inside to work normally */
  -webkit-user-select: auto;
  user-select: auto;
}

/* Global drag handle styles */
.drag-handle {
  touch-action: none; /* suppress gestures only on the handle */
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  cursor: grab;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

.drag-handle:active {
  cursor: grabbing;
}

/*
 * Alma Italia survey: match tripcreatormodel/widget-frontend radius scale
 * (tailwind.config.js there: sm 2px, md 4px, lg 4px). Admin `theme.extend.borderRadius`
 * uses larger values so `rounded-md` was ~12px vs Next’s 4px.
 * Scoped under TripSurveyLayout’s `data-agency-survey="alma"` (not <html>) so global
 * chrome outside the survey is unchanged. Portaled modals: use explicit rounded-[4px]
 * in Alma survey code where needed.
 */
[data-agency-survey="alma"] .rounded-sm {
  border-radius: 2px;
}
[data-agency-survey="alma"] .rounded-md {
  border-radius: 4px;
}
[data-agency-survey="alma"] .rounded-lg {
  border-radius: 4px;
}
[data-agency-survey="alma"] .rounded-xl {
  border-radius: 4px;
}

/*
 * B2B trip-creation page: ~25% larger text for readability.
 * Scoped to the B2B trip shell's existing data attribute on <main>
 * (see B2bTripCreationShell.tsx) so the default (B2C) trip page and
 * other B2B surfaces (e.g. the survey form) are unaffected.
 *
 * Two layers:
 *  1. `font-size: 1.25em` on the wrapper scales rem-based Tailwind
 *     text-* utilities (text-xs, text-sm, ...).
 *  2. Explicit overrides for each `text-[Npx]` size present in the
 *     b2b-trip-creation directory (px is fixed and won't scale via em).
 */
[data-trip-creation-variant="b2b"] {
  font-size: 1.25em;
}

[data-trip-creation-variant="b2b"] .text-\[8px\]  { font-size: 10px; }
[data-trip-creation-variant="b2b"] .text-\[9px\]  { font-size: 11px; }
[data-trip-creation-variant="b2b"] .text-\[10px\] { font-size: 13px; }
[data-trip-creation-variant="b2b"] .text-\[11px\] { font-size: 14px; }
[data-trip-creation-variant="b2b"] .text-\[12px\] { font-size: 15px; }
[data-trip-creation-variant="b2b"] .text-\[13px\] { font-size: 16px; }
[data-trip-creation-variant="b2b"] .text-\[14px\] { font-size: 18px; }
[data-trip-creation-variant="b2b"] .text-\[15px\] { font-size: 19px; }
[data-trip-creation-variant="b2b"] .text-\[16px\] { font-size: 20px; }
[data-trip-creation-variant="b2b"] .text-\[17px\] { font-size: 21px; }
[data-trip-creation-variant="b2b"] .text-\[18px\] { font-size: 23px; }
[data-trip-creation-variant="b2b"] .text-\[20px\] { font-size: 25px; }
[data-trip-creation-variant="b2b"] .text-\[22px\] { font-size: 28px; }

/*
 * B2B typography-token overrides — the new editorial scale (`text-h1`,
 * `text-h2`, …) is defined in absolute px on `:root`, so the wrapper's
 * `font-size: 1.25em` won't scale it. We re-state each token inside the
 * b2b scope so the new utilities feel ~25% larger inside the B2B trip
 * shell, matching the visual targets of the existing `text-[Npx]`
 * mappings above (e.g. body 14px → 18px here, same as `text-[14px]`).
 */
[data-trip-creation-variant="b2b"] {
  --text-h1: 30px;       /* 24 × 1.25 */
  --text-h2: 23px;       /* 18 × 1.25 — same as text-[18px] above */
  --text-h3: 19px;       /* 15 × 1.25 — same as text-[15px] above */
  --text-body: 18px;     /* 14 × 1.25 — same as text-[14px] above */
  --text-caption: 15px;  /* 12 × 1.25 — same as text-[12px] above */
  --text-micro: 14px;    /* 11 × 1.25 — same as text-[11px] above */
}

/*
 * W6-Q4 (widget_WBS, 2026-05-30) — Four-tier type scale, b2b scope.
 *
 * The utilities `.type-display | .type-section | .type-body | .type-micro`
 * are defined in absolute px on the shared preset (see
 * `packages/shared-ui-b2b/tailwind-preset.cjs`). The b2b shell wraps
 * everything in `font-size: 1.25em` (above), which scales rem-based
 * Tailwind utilities but does NOT scale absolute px values. We re-state
 * the four utilities inside the b2b scope at 1.25× their base so the
 * boot-frame keeps its ~25 % visual upsize — same pattern as the
 * `text-[Npx]` and `--text-h1/h2/...` overrides above.
 *
 * Numbers:
 *   display 22 / 28 → 28 / 35 (font-size × 1.25, line-height × 1.25)
 *   section 16 / 22 → 20 / 28
 *   body    14 / 20 → 18 / 25
 *   micro   11 / 14 → 14 / 18
 */
[data-trip-creation-variant="b2b"] .type-display {
  font-size: 28px;
  line-height: 35px;
}
[data-trip-creation-variant="b2b"] .type-section {
  font-size: 20px;
  line-height: 28px;
}
[data-trip-creation-variant="b2b"] .type-body {
  font-size: 18px;
  line-height: 25px;
}
[data-trip-creation-variant="b2b"] .type-micro {
  font-size: 14px;
  line-height: 18px;
}

/*
 * Mapbox canvas / canvas-container inherit the wrapper's border-radius.
 *
 * Mapbox renders to a WebGL <canvas> that doesn't respect a parent's
 * `overflow: hidden` + `border-radius` combo on every platform (Safari
 * desktop + Chromium on macOS were both producing sharp corners on the
 * b2b map frame). Explicit `border-radius: inherit` cascades the
 * wrapper's rounding down to the actual paint surface. Scoped to b2b
 * so the planner / story-view layouts are untouched.
 */
[data-trip-creation-variant="b2b"] .mapboxgl-canvas-container,
[data-trip-creation-variant="b2b"] .mapboxgl-canvas,
[data-trip-creation-variant="b2b"] .mapboxgl-map {
  border-radius: inherit;
}

/*
 * ──────────────────────────────────────────────────────────────────
 *  Nav-stack right-slide transitions (Wave 2 / R-3).
 *
 *  `pushNavStack` / `popNavStack` toggle `.nav-stack-push` or
 *  `.nav-stack-pop` on `<html>` while a View Transition is in
 *  flight. The keyframes below map those root classes to the
 *  ::view-transition-old / ::view-transition-new pseudo-elements so
 *  the takeover modal animates IN from the right edge on push and
 *  OUT to the right edge on pop — the UINavigationController feel.
 *
 *  Reduced-motion users get a `0.001s` transition (effectively
 *  instant) because the JS path (`navStackTransition.ts`) already
 *  short-circuits to render-without-transition. The CSS is here as
 *  a defence-in-depth: if some future caller forgets the JS guard,
 *  the keyframe still respects the system preference.
 *
 *  Why `cubic-bezier(0.32, 0.72, 0, 1)`?
 *  Apple HIG default for navigation push — matches the iOS
 *  navigation-stack feel exactly. Same curve we use on the
 *  bottom-sheet height spring (see `useB2bBottomSheet`).
 *  ──────────────────────────────────────────────────────────────── */
@keyframes nav-stack-slide-in-right {
  from { transform: translateX(100%); }
  to   { transform: translateX(0);    }
}
@keyframes nav-stack-slide-out-right {
  from { transform: translateX(0);    }
  to   { transform: translateX(100%); }
}
@keyframes nav-stack-fade-out {
  from { opacity: 1; }
  to   { opacity: 0; }
}

/* Push: old slides nothing (stays put as a backdrop fade), new
 * slides in from the right. */
html.nav-stack-push::view-transition-old(root) {
  animation: 0.28s cubic-bezier(0.32, 0.72, 0, 1) both nav-stack-fade-out;
}
html.nav-stack-push::view-transition-new(root) {
  animation: 0.28s cubic-bezier(0.32, 0.72, 0, 1) both nav-stack-slide-in-right;
}
/* Pop: new fades in (the underlying screen reappears), old slides
 * out to the right. */
html.nav-stack-pop::view-transition-old(root) {
  animation: 0.28s cubic-bezier(0.32, 0.72, 0, 1) both nav-stack-slide-out-right;
}
html.nav-stack-pop::view-transition-new(root) {
  animation: 0.18s ease-out both nav-stack-fade-out;
  animation-direction: reverse;
}

@media (prefers-reduced-motion: reduce) {
  html.nav-stack-push::view-transition-old(root),
  html.nav-stack-push::view-transition-new(root),
  html.nav-stack-pop::view-transition-old(root),
  html.nav-stack-pop::view-transition-new(root) {
    animation-duration: 0.001s;
  }
}

/*
 * WCAG 2.3.3 — Honour the user's `prefers-reduced-motion: reduce`
 * preference globally. The 2026-05-29 a11y audit caught 170+
 * elements still animating under the reduce-motion media query
 * because individual components opted in case-by-case (motion.div,
 * useReducedMotion hooks, framer-motion + view-transitions blocks).
 * Library defaults that bypass the hook (mapbox-gl marker transitions,
 * <Image>'s fade-in, Tailwind `animate-*` utilities like `animate-pulse`
 * + `animate-spin`, framer-motion's auto-layout transitions) all
 * fired regardless.
 *
 * This block enforces the user's preference at the engine: every
 * animation + transition is collapsed to 1ms + iteration count clamped
 * to 1. Components that need to KEEP animating under reduce (rare —
 * e.g. an essential "loading…" affordance) can opt out via a
 * `data-motion-essential` attribute on the element.
 *
 * The pattern follows the W3C MDN recommendation
 * (https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion)
 * and matches Apple's own approach in WebKit's `prefers-reduced-motion`
 * support — every CSS-driven animation respects the user's preference
 * unless the developer explicitly carves an exception.
 *
 * `scroll-behavior` is also collapsed because smooth-scroll induces
 * vestibular triggers per WCAG 2.3.3 success criterion.
 */
@media (prefers-reduced-motion: reduce) {
  *:not([data-motion-essential]),
  *:not([data-motion-essential])::before,
  *:not([data-motion-essential])::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
