/* ==========================================================================
   Applied InTouch — Design tokens
   --------------------------------------------------------------------------
   Every visual decision on the site resolves to one of these custom
   properties. The six brand colours are additionally mirrored in the
   `site_settings` table (group: branding) and re-emitted as an inline
   :root override in the page <head>, so the palette can be swapped from the
   admin panel without touching a stylesheet.

   PALETTE: "Sapphire & Ice".

   The medical blue, taken deep enough to stop being the default. Where the
   original palette used a bright mid-tone cyan — the exact blue on a thousand
   B2B pages — this sits at sapphire depth, which is what reads as considered
   rather than picked from a framework. Surfaces are crisp and cool: this
   palette commits to the clinical register instead of softening it, and the
   ice-blue tints are the "clean" cue a dental audience already associates
   with the chair.

   Contrast (verified, WCAG 2.1 — every pair below was measured, not estimated):
     primary (#1A3D6D) on white ............ 10.87:1  AAA
     primary-dark (#102748) on white ....... 14.94:1  AAA
     ink (#101620) on white ................ 18.14:1  AAA
     ink-muted (#46515F) on white ..........  8.07:1  AAA
     ink-subtle (#5E6A79) on white .........  5.51:1  AA
     ink-subtle on surface-sunken ..........  4.62:1  AA   (tightest text pair)
     accent-dark (#1F6493) on white ........  6.36:1  AA
     ink-inverse-muted on surface-dark-2 ...  5.97:1  AA
     white on gradient-brand, far end ......  6.36:1  AA   (tightest gradient pt)
     border-strong (#7D8691) on white ......  3.69:1  AA   (WCAG 1.4.11)
     border-strong on surface-sunken .......  3.28:1  AA   (tightest UI pair)

   TWO RULES THIS PALETTE DEPENDS ON:

   1. --ai-color-accent is DECORATIVE ONLY. White on the bright sapphire is
      3.39:1 — fine for a graphic under 1.4.11, nowhere near enough for text.
      For accent-coloured TEXT use --ai-color-accent-dark. This is also why
      --ai-gradient-brand ends at accent-dark rather than accent: four
      components (pricing badge, timeline icons, solid icon tiles, the
      announcement bar) paint white text straight onto that gradient.

   2. On dark grounds use --ai-color-ink-inverse / -muted, never the accent:
      accent on surface-dark-2 measures 3.20:1, which carries a graphic but
      not a word. Nothing in the stylesheets does this today; the number is
      recorded so it stays that way.

   --ai-color-border-strong draws input boundaries, so it is held at 3:1;
   --ai-color-border is a divider and is deliberately lighter.
   ========================================================================== */

:root {
    /* ---- Brand (admin-overridable) ------------------------------------ */
    --ai-color-primary: #1a3d6d;
    --ai-color-primary-dark: #102748;
    --ai-color-primary-light: #e2eaf4;
    --ai-color-accent: #3e92cc;
    --ai-color-ink: #101620;
    --ai-color-surface: #ffffff;

    /* ---- Derived / fixed palette -------------------------------------- */
    --ai-color-primary-tint: #f6f9fc;
    --ai-color-accent-dark: #1f6493;
    --ai-color-accent-light: #e4f0f8;
    /* Brick red rather than amber: against an all-cool palette a red error
       state is the one that reads instantly as "stop" rather than "note". */
    --ai-color-warning: #b3261e;
    --ai-color-warning-light: #fbedeb;
    --ai-color-success: #15704b;
    --ai-color-success-light: #e8f2ec;

    --ai-color-ink-muted: #46515f;
    --ai-color-ink-subtle: #5e6a79;
    --ai-color-ink-inverse: #ffffff;
    --ai-color-ink-inverse-muted: #afc2d8;

    /* Cool and crisp — this palette commits to the clinical register.
       These three are the light SECTION BANDS, and they are spaced so each
       step is actually visible: 1.09 / 1.09 / 1.10 against the one before it,
       1.31 from white to tint. The previous muted sat at 1.04 against white,
       which is below the point a large flat area reads as a different colour
       at all — that is why the page looked like one long white scroll. */
    --ai-color-surface-muted: #f1f5fa;
    --ai-color-surface-sunken: #e4ecf5;
    /* The band with real colour in it. Deep enough that two tokens need a
       stronger step on it — see .ai-section--tint in app.css. */
    --ai-color-surface-tint: #d3e3f4;
    /* The dark band deepens from near-black navy to the brand navy itself. */
    --ai-color-surface-dark: #0b1b33;
    --ai-color-surface-dark-2: #1a3d6d;

    --ai-color-border: #dfe4ea;
    --ai-color-border-strong: #7d8691;
    --ai-color-border-inverse: rgba(255, 255, 255, 0.18);

    --ai-color-focus: #1a3d6d;
    --ai-color-focus-ring: rgba(26, 61, 109, 0.32);

    /* ---- Gradients ----------------------------------------------------- */
    --ai-gradient-hero: linear-gradient(168deg, #f6f9fc 0%, #e2eaf4 46%, #ffffff 100%);
    /* Ends at accent-dark, not accent — see rule 1 in the header comment.
       Sampled at 0/25/50/75/100%, white on this gradient never drops below
       6.36:1. */
    --ai-gradient-brand: linear-gradient(135deg, var(--ai-color-primary) 0%, var(--ai-color-accent-dark) 100%);
    --ai-gradient-dark: linear-gradient(150deg, var(--ai-color-surface-dark) 0%, var(--ai-color-surface-dark-2) 100%);
    --ai-gradient-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(255, 255, 255, 0) 100%);

    /* ---- Typography ---------------------------------------------------- */
    --ai-font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif,
        "Apple Color Emoji", "Segoe UI Emoji";
    --ai-font-mono: ui-monospace, SFMono-Regular, "SF Mono", Consolas,
        "Liberation Mono", Menlo, monospace;

    --ai-text-xs: 0.75rem;
    --ai-text-sm: 0.875rem;
    --ai-text-base: 1rem;
    --ai-text-lg: clamp(1.0625rem, 0.35vw + 1rem, 1.125rem);
    --ai-text-xl: clamp(1.1875rem, 0.6vw + 1.05rem, 1.375rem);
    --ai-text-2xl: clamp(1.375rem, 1.1vw + 1.1rem, 1.75rem);
    --ai-text-3xl: clamp(1.625rem, 2vw + 1.15rem, 2.25rem);
    --ai-text-4xl: clamp(1.875rem, 3.2vw + 1.05rem, 2.875rem);
    --ai-text-5xl: clamp(2.125rem, 4.4vw + 1rem, 3.4rem);

    --ai-leading-tight: 1.15;
    --ai-leading-snug: 1.3;
    --ai-leading-normal: 1.55;
    --ai-leading-relaxed: 1.7;

    --ai-weight-normal: 400;
    --ai-weight-medium: 500;
    --ai-weight-semibold: 600;
    --ai-weight-bold: 700;

    --ai-tracking-tight: -0.02em;
    --ai-tracking-normal: 0;
    --ai-tracking-wide: 0.06em;

    /* ---- Spacing scale (4px base) --------------------------------------- */
    --ai-space-1: 0.25rem;
    --ai-space-2: 0.5rem;
    --ai-space-3: 0.75rem;
    --ai-space-4: 1rem;
    --ai-space-5: 1.25rem;
    --ai-space-6: 1.5rem;
    --ai-space-8: 2rem;
    --ai-space-10: 2.5rem;
    --ai-space-12: 3rem;
    --ai-space-16: 4rem;
    --ai-space-20: 5rem;
    --ai-space-24: 6rem;
    --ai-space-32: 8rem;

    /* Fluid vertical rhythm for full-bleed sections (mobile-first). */
    --ai-section-padding: clamp(3rem, 7vw, 6rem);
    --ai-section-padding-lg: clamp(3.5rem, 9vw, 7.5rem);

    /* ---- Layout ---------------------------------------------------------- */
    --ai-container-max: 74rem; /* 1184px */
    --ai-container-narrow: 46rem; /* 736px */
    --ai-container-gutter: clamp(1.125rem, 4vw, 2.5rem);
    /* The header is a min-height, and .ai-container adds no vertical padding,
       so this value IS the bar height until the content outgrows it. Its
       tallest contents are the nav toggle (2.75rem) on mobile and the small
       CTA button (2.5rem) from the desktop breakpoint up -- setting this below
       those turns the bar over to the content and the variable stops meaning
       anything. 3.5rem leaves 6px of air around the toggle. */
    --ai-header-height: 3.5rem;

    /* ---- Radii ------------------------------------------------------------ */
    --ai-radius-xs: 0.25rem;
    --ai-radius-sm: 0.375rem;
    --ai-radius-md: 0.625rem;
    --ai-radius-lg: 1rem;
    --ai-radius-xl: 1.5rem;
    --ai-radius-2xl: 2rem;
    --ai-radius-pill: 62.4375rem;

    /* ---- Elevation --------------------------------------------------------- */
    /* Tinted with the surface-dark navy rather than neutral black: a coloured
       shadow sits in the palette instead of greying it out. */
    --ai-shadow-xs: 0 1px 2px rgba(11, 27, 51, 0.06);
    --ai-shadow-sm: 0 1px 3px rgba(11, 27, 51, 0.08), 0 1px 2px rgba(11, 27, 51, 0.04);
    --ai-shadow-md: 0 4px 12px rgba(11, 27, 51, 0.08), 0 2px 4px rgba(11, 27, 51, 0.04);
    --ai-shadow-lg: 0 12px 32px rgba(11, 27, 51, 0.1), 0 4px 8px rgba(11, 27, 51, 0.04);
    --ai-shadow-xl: 0 24px 56px rgba(11, 27, 51, 0.14), 0 8px 16px rgba(11, 27, 51, 0.05);
    --ai-shadow-header: 0 1px 0 var(--ai-color-border), 0 6px 20px rgba(11, 27, 51, 0.06);

    /* ---- Motion ------------------------------------------------------------ */
    --ai-ease: cubic-bezier(0.2, 0.6, 0.25, 1);
    --ai-duration-fast: 120ms;
    --ai-duration: 220ms;
    --ai-duration-slow: 420ms;

    /* ---- Z-index scale ------------------------------------------------------ */
    --ai-z-base: 1;
    --ai-z-sticky: 100;
    --ai-z-header: 200;
    --ai-z-nav-panel: 300;
    --ai-z-modal: 400;
    --ai-z-skip-link: 500;
}

/* Desktop swaps the 2.75rem toggle for the 2.5rem CTA button, so the bar can
   sit tighter than it does on mobile while keeping 10px of air around the
   button. The old 4.5rem left 16px above and below it, which read as a gap
   rather than as breathing room. */
@media (min-width: 60em) {
    :root {
        --ai-header-height: 3.75rem;
    }
}

/* Honour the OS "reduce motion" setting everywhere. */
@media (prefers-reduced-motion: reduce) {
    :root {
        --ai-duration-fast: 1ms;
        --ai-duration: 1ms;
        --ai-duration-slow: 1ms;
    }
}
