/* ============================================================================
   Cast-A-Booking — the type system. ONE file, loaded first by every surface
   (owner dashboard, admin console, booking widget, marketing site, legal pages)
   so the whole product speaks in one voice across all three themes.

   Loaded BEFORE each page's own stylesheet, deliberately: a page may still
   override a token, but nothing has to redeclare the stack.

   ── The faces ──────────────────────────────────────────────────────────────
   UI: Inter, when the machine has it, falling back to the platform's own
   grotesque. Inter is the right face for a dark, data-dense dashboard — tall
   x-height, open apertures, real tabular figures — and it is NOT loaded over
   the network: this app ships no webfonts, the CSP has no `font-src` (so it
   would be blocked anyway), and a system stack paints on the first frame with
   nothing to download. To adopt Inter everywhere, drop `inter-var.woff2` in
   /public and uncomment the @font-face at the bottom of this file; the stack
   below already prefers it.

   Display serif: kept for BRAND voice only — the wordmark and the marketing
   site's big display type. Product chrome (page headings, card titles, tables)
   is Inter, per the dashboard's clean/sharp/data-dense brief. To put the serif
   back on dashboard headings, set `--font-heading: var(--font-display)`.
   ========================================================================== */

:root {
  /* ---- Families ------------------------------------------------------- */
  --font-ui:
    "Inter", "Inter var", "Avenir Next", "Segoe UI", -apple-system,
    BlinkMacSystemFont, Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-display: "Iowan Old Style", "Palatino Linotype", Georgia, serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  /* Aliases the existing stylesheets already consume. Keeping the names means
     ~40 rules across four files pick the system up without being touched. */
  --font-body: var(--font-ui);
  --font-heading: var(--font-ui);

  /* Text on an INK-filled surface (an inverted chip). Opaque by definition —
     a surface token can be translucent, and a text colour never may be. */
  --on-ink: #ffffff;

  /* ---- Weights ---------------------------------------------------------
     Named, not numeric, at every call site: dark mode wants a different set
     (see the theme block below) and a hardcoded 700 cannot follow. */
  --w-regular: 400;
  --w-medium: 500;
  --w-semibold: 600;
  --w-bold: 700;
  --w-heavy: 800;

  /* ---- The scale -------------------------------------------------------
     Root is 16px, so 1rem = 16px. Sizes are rem so a browser/OS text-size
     preference actually scales the product; everything else (line height,
     tracking) is unitless or em so it scales with the size it belongs to.

     Line heights tighten as size grows — a 30px heading at 1.5 looks like two
     unrelated lines, a 13px list item at 1.2 is unreadable. Tracking moves the
     other way: negative on display sizes (large type looks loose), positive on
     small caps-y labels (small type looks cramped). */

  /* Display — page headings and hero numbers */
  --ts-display-lg: 1.875rem;  /* 30px */
  --lh-display-lg: 1.15;
  --tr-display-lg: -0.022em;

  --ts-display: 1.5rem;       /* 24px — PAGE HEADING */
  --lh-display: 1.2;
  --tr-display: -0.02em;

  /* Headings inside the page */
  --ts-h1: 1.25rem;           /* 20px */
  --lh-h1: 1.25;
  --tr-h1: -0.015em;

  --ts-h2: 1.0625rem;         /* 17px */
  --lh-h2: 1.3;
  --tr-h2: -0.01em;

  --ts-h3: 0.9375rem;         /* 15px */
  --lh-h3: 1.35;
  --tr-h3: -0.005em;

  /* Text */
  --ts-body: 0.875rem;        /* 14px — the base */
  --lh-body: 1.5;
  --tr-body: 0em;

  --ts-body-sm: 0.8125rem;    /* 13px — FEATURE LISTS, dense table rows */
  --lh-body-sm: 1.45;
  --tr-body-sm: 0em;

  --ts-caption: 0.75rem;      /* 12px — hints, sub-labels */
  --lh-caption: 1.4;
  --tr-caption: 0em;

  --ts-micro: 0.6875rem;      /* 11px — timestamps, footnotes */
  --lh-micro: 1.35;
  --tr-micro: 0.005em;

  /* Labels that shout in small caps */
  --ts-overline: 0.75rem;     /* 12px — TIER CARD TITLES */
  --lh-overline: 1.2;
  --tr-overline: 0.08em;

  --ts-eyebrow: 0.6875rem;    /* 11px — section labels (NEEDS ATTENTION) */
  --lh-eyebrow: 1.2;
  --tr-eyebrow: 0.07em;

  --ts-badge: 0.65625rem;     /* 10.5px — CURRENT PLAN, 2 months free */
  --lh-badge: 1;
  --tr-badge: 0.06em;

  /* Numbers. Money and counts are compared down a column, so they are always
     tabular — proportional digits make $1,111 look shorter than $8,888. */
  --ts-price: 1.75rem;        /* 28px — PRICE FIGURES */
  --lh-price: 1.05;
  --tr-price: -0.03em;

  --ts-numeric: 1.125rem;     /* 18px — stat tiles, Today's numbers */
  --lh-numeric: 1.15;
  --tr-numeric: -0.02em;

  /* Controls */
  --ts-nav: 0.84375rem;       /* 13.5px — SIDEBAR NAV LINKS */
  --lh-nav: 1.2;
  --tr-nav: -0.005em;

  --ts-button: 0.8125rem;     /* 13px */
  --lh-button: 1.2;
  --tr-button: 0em;

  --ts-input: 0.875rem;       /* 14px — never below 14: iOS zooms a focused
                                 field whose text is smaller than 16px, and 14
                                 is the floor that still reads as deliberate */
  --lh-input: 1.3;
}

/* ---- Theme compensation -----------------------------------------------
   Light text on a dark ground BLOOMS: the same weight reads roughly half a
   step heavier than it does on white. Two corrections, both cheap:
   1. subpixel smoothing off, so glyphs are not fattened by the renderer;
   2. one step down on the heaviest weights, so a "bold" price on #0F0F0F
      matches the presence it had on #FFF instead of shouting.
   Pure #FFF on near-black also haloes — the dark ink ladder tops out at
   #FAFAFA for that reason (see each stylesheet's theme block). */
:root[data-theme="dark"] {
  --on-ink: #0f0f0f;           /* the dark theme's ink is near-white */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  --w-bold: 650;
  --w-heavy: 750;
}

/* The root carries the family too, so anything mounted outside <body> (portals,
   print sheets, a dialog appended to documentElement) is on the system as well. */
html { font-family: var(--font-ui); }

/* ---- The control reset -------------------------------------------------
   Form controls do NOT inherit font-family from their parent — the UA gives
   button/input/select/textarea/option their own default (Arial here). Without
   this reset every button, field and dropdown in the product renders in a face
   nothing else uses, which is most of the chrome on a dashboard. Family and
   tracking only: each component keeps whatever size it already defines, so this
   changes what the text IS, not how much room it takes. */
button, input, select, textarea, optgroup, option {
  font-family: inherit;
  letter-spacing: inherit;
}
/* Buttons are pointers. The UA default (cursor: default) makes a clickable
   button read as inert text — the marketing CTA was the complaint, but every
   page here treats <button> as an action, so the reset owns it like the font. */
button:not(:disabled) { cursor: pointer; }
/* Controls that never got a size of their own would otherwise sit at the UA's
   13.33px, which is not a step on any scale. */
button:not([class]), input:not([class]), select:not([class]), textarea:not([class]) {
  font-size: var(--ts-body);
}

/* ---- iOS Safari focus-zoom ---------------------------------------------
   Safari on iPhone AND iPad zooms the whole viewport when a focused editable
   control (input / select / textarea) renders its text under 16px — and it does
   not zoom back out, which leaves the app "stuck zoomed" after one tap in a
   form. The dense 13-14px control sizes are a desktop register; on touch they
   trigger the zoom on every popup with a field in it.

   THE fix is 16px controls on touch devices — the only approach with no cost:
   the `maximum-scale=1` viewport hack suppresses the zoom but Android honours
   it literally and pinch-zoom dies there (a WCAG 1.4.4 failure), and iOS only
   ignores it for user gestures. So: pointer:coarse (phones, tablets, any touch
   screen) gets 16px editable controls. Buttons are untouched — they do not
   trigger the zoom.

   !important, deliberately and only here: dozens of component rules set control
   sizes at higher specificity, and this must beat every one of them or the zoom
   comes back through whichever field was missed. One documented hammer beats
   thirty fragile overrides. */
@media (pointer: coarse) {
  input:not([type="checkbox"]):not([type="radio"]), select, textarea {
    font-size: 16px !important;
  }
}

/* ---- Focus, everywhere -------------------------------------------------
   WCAG 2.4.7 (Focus Visible, level A) and 1.4.11 (Non-text Contrast, AA): the
   keyboard indicator must EXIST on every interactive element and carry 3:1
   against what surrounds it. Only two components in the dashboard had a
   :focus-visible rule, and the shared ring token was 22%-alpha (~1.3:1), so a
   keyboard user was effectively navigating blind.

   `:where()` gives this ZERO specificity: it is the floor, and any component
   that wants its own indicator still wins without an !important anywhere.
   Each surface defines --focus-ring; this is the fallback for pages that don't. */
:where(a[href], area[href], button, input, select, textarea, summary,
       [tabindex]:not([tabindex="-1"]), [role="button"], [role="tab"],
       [role="link"], [role="switch"], [contenteditable="true"]):focus-visible {
  outline: 2px solid var(--focus-ring, currentColor);
  outline-offset: 2px;
}
/* Never remove it without replacing it. A control that kills its own outline
   must supply a ring of its own — this is here so the mistake is visible in
   review rather than in an audit. */

/* ---- Reduced motion -----------------------------------------------------
   Three specific animations honoured the preference; the other ~280 transitioned
   elements did not. Short transitions are not a WCAG violation on their own, but
   "respect the setting globally" is the accepted practice — vestibular triggers
   are the user's to judge, not ours. Motion is reduced to a hair rather than
   zeroed: `transition: none` can break code that waits on transitionend. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---- Windows High Contrast / forced-colors ------------------------------
   In forced-colors the OS replaces every colour, and anything drawn with a
   background or a shadow silently disappears. Borders and outlines survive, so
   the indicator is re-expressed in system colours. */
@media (forced-colors: active) {
  :where(a[href], button, input, select, textarea, summary,
         [tabindex]:not([tabindex="-1"])):focus-visible {
    outline: 3px solid Highlight;
    outline-offset: 2px;
  }
  /* Translucent glass panels have no forced-colors equivalent — give every
     panel an explicit edge so the layout does not collapse into one field. */
  * { backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
}

/* ---- Semantic helpers --------------------------------------------------
   Available to every surface. Components below map onto these rather than
   restating numbers. */
.t-display { font: var(--w-semibold) var(--ts-display) / var(--lh-display) var(--font-heading); letter-spacing: var(--tr-display); }
.t-h1      { font: var(--w-semibold) var(--ts-h1) / var(--lh-h1) var(--font-heading); letter-spacing: var(--tr-h1); }
.t-h2      { font: var(--w-semibold) var(--ts-h2) / var(--lh-h2) var(--font-heading); letter-spacing: var(--tr-h2); }
.t-h3      { font: var(--w-semibold) var(--ts-h3) / var(--lh-h3) var(--font-heading); letter-spacing: var(--tr-h3); }
.t-body    { font: var(--w-regular) var(--ts-body) / var(--lh-body) var(--font-ui); }
.t-body-sm { font: var(--w-regular) var(--ts-body-sm) / var(--lh-body-sm) var(--font-ui); }
.t-caption { font: var(--w-regular) var(--ts-caption) / var(--lh-caption) var(--font-ui); }
.t-overline {
  font: var(--w-bold) var(--ts-overline) / var(--lh-overline) var(--font-ui);
  letter-spacing: var(--tr-overline); text-transform: uppercase;
}
.t-eyebrow {
  font: var(--w-bold) var(--ts-eyebrow) / var(--lh-eyebrow) var(--font-ui);
  letter-spacing: var(--tr-eyebrow); text-transform: uppercase;
}
.t-badge {
  font: var(--w-heavy) var(--ts-badge) / var(--lh-badge) var(--font-ui);
  letter-spacing: var(--tr-badge); text-transform: uppercase;
}
.t-price {
  font: var(--w-bold) var(--ts-price) / var(--lh-price) var(--font-ui);
  letter-spacing: var(--tr-price); font-variant-numeric: tabular-nums;
}
.t-numeric {
  font: var(--w-bold) var(--ts-numeric) / var(--lh-numeric) var(--font-ui);
  letter-spacing: var(--tr-numeric); font-variant-numeric: tabular-nums;
}
/* The wordmark keeps the serif — it is the logo, not UI. */
.t-wordmark { font-family: var(--font-display); letter-spacing: -0.02em; }

/* Any figure compared against another figure gets tabular digits, wherever it
   lives. Cheaper than remembering to ask for it per component. */
.tabular, [data-tabular] { font-variant-numeric: tabular-nums; }

/* ---- Self-hosting Inter (optional) -------------------------------------
   The CSP is `default-src 'self'` with no font-src, so a Google Fonts URL is
   blocked by design. Serve the file from this origin instead: drop the
   variable woff2 in /public and uncomment. `swap` keeps first paint on the
   fallback rather than blocking on the file.

@font-face {
  font-family: "Inter";
  src: local("Inter"), local("Inter var"), url("/inter-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
*/
