/* Fallback font load. @import must be the first statement in the file or the
   browser drops it. This blocks rendering and cannot preconnect, unlike a
   <link> tag in <head>, so it is the fallback until the generator adds the
   real link tags (build.mjs is not owned by this file). Once those land in
   every generated page's <head>, this line can be deleted. */
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400..800;1,9..144,400..800&family=Inter:wght@400..750&family=JetBrains+Mono:wght@400;600&display=swap');

/* ==========================================================================
   Tokens

   Composition strategy, read this before touching a colour value.

   1. CITY HUE RAMPS are defined once per city, on a selector pair:
      `:root:has(body[data-city="..."]), [data-city="..."]`. They are hues,
      not grounds: a --brand-* ramp (the colour that carries 30 to 60 percent
      of that city's surface) and an --accent2-* ramp (a second, sparingly
      used hue). The :has() half themes the whole page from <body>; custom
      properties only cascade downward, so a plain `body[data-city]` rule
      could not feed :root's neutral ramp, which is why :root reaches down
      with :has() instead. The plain `[data-city]` half is what makes SECTION
      level theming work: Selections, Checklist and Itinerary each mix
      cities on one page, so any element (a <section>, not just <body>) that
      carries data-city gets its own brand and accent ramps directly, no
      JavaScript either way.

   2. GROUND AND INK are a single neutral ramp, --n-025 through --n-950,
      defined once as a formula on `:root, [data-city]`, not once per city
      and not once per theme. Each step is computed with CSS relative
      colour syntax, oklch(from var(--brand-600) L C h), which keeps the
      lightness and chroma formula fixed while pulling its hue live from
      whichever --brand-600 is in scope. On <body> or on :root that is the
      page's city; on a nested `<section data-city="doha">` inside a
      Barcelona page it is that section's own --brand-600, because a custom
      property's value on a given element always comes from the most
      specific rule matching that element, not from an ancestor. One
      formula, correct at any nesting depth.

   3. SEMANTIC TOKENS (--bg, --fg, --accent, --rule, and friends) compose the
      two above, on the same `:root, [data-city]` pairing. The complete
      light palette is the default. Dark mode, whether from the system
      setting or the explicit data-theme="dark" override on the root
      element, only ever reassigns which --n-* or --brand-* step a semantic
      token points to, and does so at both :root and `[data-city]` scope, so
      a themed section stays correctly dark even though data-theme itself is
      only ever set on the root element. No semantic token's only definition
      lives inside a media query; every literal colour it can resolve to
      already exists in bare :root or in a city block.

   Do not add a fourth city block without adding its --brand-* and
   --accent2-* ramp to both halves of the selector pair. Do not give --bg or
   --fg a literal colour inside a dark block. Point it at an existing --n-*
   step instead, the same way the light default does.
   ========================================================================== */

:root {
  /* --- City fallback, applied before a city block can win the cascade.
     Stone grey, matching Brussels: quiet and legible if data-city is ever
     missing, as it is on the login screen. ------------------------------- */
  --brand-900: oklch(0.24 0.014 254);
  --brand-800: oklch(0.31 0.016 253);
  --brand-700: oklch(0.39 0.018 252);
  --brand-600: oklch(0.47 0.020 251);
  --brand-500: oklch(0.55 0.020 250);
  --brand-400: oklch(0.64 0.018 249);
  --brand-300: oklch(0.74 0.014 248);
  --brand-100: oklch(0.90 0.008 246);
  --brand-050: oklch(0.955 0.005 245);
  --accent2-700: var(--brand-700);
  --accent2-600: var(--brand-600);
  --accent2-500: var(--brand-500);
  --accent2-400: var(--brand-400);
  --accent2-100: var(--brand-100);

  /* --- Type ------------------------------------------------------------- */
  --font-display: 'Fraunces', 'Iowan Old Style', 'Palatino Linotype',
    'Times New Roman', serif;
  --font-text: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* 1.28 ratio, steep at the top for display contrast. Prices and times
     both matter here, so pair these with the .num utility in base.css. */
  --step--2: clamp(0.70rem, 0.68rem + 0.10vw, 0.76rem);
  --step--1: clamp(0.82rem, 0.79rem + 0.14vw, 0.89rem);
  --step-0: clamp(0.97rem, 0.94rem + 0.16vw, 1.06rem);
  --step-1: clamp(1.16rem, 1.10rem + 0.30vw, 1.36rem);
  --step-2: clamp(1.42rem, 1.31rem + 0.55vw, 1.80rem);
  --step-3: clamp(1.78rem, 1.56rem + 1.10vw, 2.55rem);
  --step-4: clamp(2.25rem, 1.82rem + 2.15vw, 3.80rem);
  --step-5: clamp(2.85rem, 2.00rem + 4.25vw, 6.00rem);
  --step-6: clamp(3.40rem, 1.90rem + 7.50vw, 9.00rem);
  /* Deliberately outside the 1.28 ratio. A city arrival is the one place on
     the site permitted to break the scale, because it is the only thing on
     its screen. City masthead titles only, never twice on a page. */
  --step-masthead: clamp(4.5rem, 15vw, 13.5rem);

  /* --- Space: a deliberately uneven scale, not a flat 8px grid. Numbered,
     not named, to match what the login screen already consumes. --------- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2.25rem;
  --sp-7: 3.5rem;
  --sp-8: 5.5rem;
  --sp-9: 8rem;

  --measure: 68ch;
  --shell: 82rem;
  --shell-wide: 96rem;

  --radius-s: 3px;
  --radius-m: 5px;

  /* --- Motion: ease-out exponential only. No bounce, no elastic. This site
     should barely move. ---------------------------------------------------- */
  --ease-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 130ms;
  --dur-mid: 260ms;
  --dur-slow: 480ms;
}

@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 1ms;
    --dur-mid: 1ms;
    --dur-slow: 1ms;
  }

  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   City hue ramps. Defined once each, on a page-level selector (:has, for
   theming from <body>) and a section-level selector ([data-city], for any
   element theming itself and its descendants). Selections, Checklist and
   Itinerary rely on the second half: a <section data-city="doha"> inside a
   Barcelona page gets sand-tinted tokens, the rest of the page stays green.
   ========================================================================== */

/* --- Doha: sand, gold, desert rose. --brand is the desert-rose form of the
   National Museum; --accent2 is Katara Towers lit gold at night. --------- */
:root:has(body[data-city="doha"]),
[data-city="doha"] {
  --brand-900: oklch(0.24 0.075 26);
  --brand-800: oklch(0.31 0.095 27);
  --brand-700: oklch(0.38 0.115 28);
  --brand-600: oklch(0.46 0.130 29);
  --brand-500: oklch(0.54 0.140 30);
  --brand-400: oklch(0.63 0.135 31);
  --brand-300: oklch(0.73 0.100 32);
  --brand-100: oklch(0.90 0.045 34);
  --brand-050: oklch(0.955 0.022 35);

  --accent2-700: oklch(0.56 0.125 80);
  --accent2-600: oklch(0.65 0.140 82);
  --accent2-500: oklch(0.74 0.150 84);
  --accent2-400: oklch(0.82 0.135 86);
  --accent2-100: oklch(0.94 0.055 88);
}

/* --- Brussels: stone grey, deliberately plain. Three working days, no
   holiday to sell, so there is no second hue: --accent2 aliases --brand. - */
:root:has(body[data-city="brussels"]),
[data-city="brussels"] {
  --brand-900: oklch(0.24 0.014 254);
  --brand-800: oklch(0.31 0.016 253);
  --brand-700: oklch(0.39 0.018 252);
  --brand-600: oklch(0.47 0.020 251);
  --brand-500: oklch(0.55 0.020 250);
  --brand-400: oklch(0.64 0.018 249);
  --brand-300: oklch(0.74 0.014 248);
  --brand-100: oklch(0.90 0.008 246);
  --brand-050: oklch(0.955 0.005 245);

  --accent2-700: var(--brand-700);
  --accent2-600: var(--brand-600);
  --accent2-500: var(--brand-500);
  --accent2-400: var(--brand-400);
  --accent2-100: var(--brand-100);
}

/* --- Barcelona: Gaudi green, gold, trencadis. --brand is the Passion
   facade glass; --accent2 is the gold of the Park Guell tilework. -------- */
:root:has(body[data-city="barcelona"]),
[data-city="barcelona"] {
  --brand-900: oklch(0.26 0.068 152);
  --brand-800: oklch(0.33 0.088 151);
  --brand-700: oklch(0.41 0.105 150);
  --brand-600: oklch(0.49 0.115 149);
  --brand-500: oklch(0.57 0.120 148);
  --brand-400: oklch(0.66 0.110 147);
  --brand-300: oklch(0.76 0.080 146);
  --brand-100: oklch(0.91 0.038 145);
  --brand-050: oklch(0.958 0.019 144);

  --accent2-700: oklch(0.54 0.130 83);
  --accent2-600: oklch(0.63 0.140 84);
  --accent2-500: oklch(0.72 0.150 86);
  --accent2-400: oklch(0.81 0.135 88);
  --accent2-100: oklch(0.93 0.055 90);
}

/* ==========================================================================
   Neutral ramp. Ground and ink, defined as one formula on :root and on any
   [data-city] element, so it recomputes wherever --brand-600 is redefined
   instead of only once at the page root. Chroma stays inside 0.005 to 0.01
   across every step and peaks at mid lightness, tapering back down as L
   approaches 0 or 100, per the never-flat-neutral rule. The hue at every
   step is inherited live from whichever --brand-600 is in scope for THIS
   element, so the same twelve lines produce a sand-tinted grey inside a
   Doha section, a stone-tinted grey inside a Brussels one, and a
   green-tinted grey inside a Barcelona one, all on the same page.
   ========================================================================== */

:root, [data-city] {
  --n-950: oklch(from var(--brand-600) 0.12 0.0055 h);
  --n-900: oklch(from var(--brand-600) 0.17 0.0065 h);
  --n-800: oklch(from var(--brand-600) 0.25 0.0080 h);
  --n-700: oklch(from var(--brand-600) 0.34 0.0090 h);
  --n-600: oklch(from var(--brand-600) 0.44 0.0100 h);
  --n-500: oklch(from var(--brand-600) 0.54 0.0095 h);
  --n-400: oklch(from var(--brand-600) 0.65 0.0085 h);
  --n-300: oklch(from var(--brand-600) 0.76 0.0075 h);
  --n-200: oklch(from var(--brand-600) 0.86 0.0065 h);
  --n-100: oklch(from var(--brand-600) 0.93 0.0058 h);
  --n-050: oklch(from var(--brand-600) 0.975 0.0052 h);
  --n-025: oklch(from var(--brand-600) 0.992 0.0050 h);

  /* --- Semantic, light default. Complete on its own: nothing below this
     line depends on a media query to be legible. Contrast is pushed well
     past AA on purpose, --fg sits at L 0.12 against a --bg at L 0.975,
     because this has to read on a phone in direct Doha sun. ------------- */
  --bg: var(--n-050);
  --bg-raised: var(--n-025);
  --bg-sunken: var(--n-100);
  --fg: var(--n-950);
  --fg-muted: var(--n-700);
  --fg-subtle: var(--n-600);
  --rule: var(--n-200);
  --rule-strong: var(--n-300);

  --accent: var(--brand-600);
  --accent-strong: var(--brand-700);
  --accent-ink: var(--n-025);
  --accent-soft: color-mix(in oklch, var(--accent) 14%, var(--bg));

  --highlight: var(--accent2-700);
  --highlight-ink: var(--n-025);

  --focus: var(--brand-600);
  --selection-bg: var(--brand-100);
  --selection-fg: var(--n-950);
}

/* --- Dark, via the system setting. Guarded so an explicit data-theme=
   "light" on the root element always wins over prefers-color-scheme. data-
   theme itself only ever sits on the root element, so the second selector
   below reaches down to any themed section: any [data-city] descendant of
   an unlocked root goes dark too, at whatever specificity is needed to beat
   its own light default (that rule is only [data-city], specificity 0-1-0;
   this one is 0-3-0). Every token here points at a step already defined
   above; none is a new literal colour, so nothing's only definition lives
   inside this media query. ------------------------------------------------ */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]),
  :root:not([data-theme="light"]) [data-city] {
    --bg: var(--n-900);
    --bg-raised: var(--n-800);
    --bg-sunken: var(--n-950);
    --fg: var(--n-050);
    --fg-muted: var(--n-300);
    --fg-subtle: var(--n-400);
    --rule: var(--n-800);
    --rule-strong: var(--n-700);

    --accent: var(--brand-400);
    --accent-strong: var(--brand-300);
    --accent-ink: var(--n-950);

    --highlight: var(--accent2-400);
    --highlight-ink: var(--n-950);

    --focus: var(--brand-400);
    --selection-bg: var(--brand-800);
    --selection-fg: var(--n-050);
  }
}

/* --- Dark, via explicit toggle. Same values as above, so a UI control that
   sets data-theme="dark" wins regardless of the system setting, and reaches
   the same [data-city] descendants the media-query block does. --------- */
:root[data-theme="dark"],
:root[data-theme="dark"] [data-city] {
  --bg: var(--n-900);
  --bg-raised: var(--n-800);
  --bg-sunken: var(--n-950);
  --fg: var(--n-050);
  --fg-muted: var(--n-300);
  --fg-subtle: var(--n-400);
  --rule: var(--n-800);
  --rule-strong: var(--n-700);

  --accent: var(--brand-400);
  --accent-strong: var(--brand-300);
  --accent-ink: var(--n-950);

  --highlight: var(--accent2-400);
  --highlight-ink: var(--n-950);

  --focus: var(--brand-400);
  --selection-bg: var(--brand-800);
  --selection-fg: var(--n-050);
}
