/* ==========================================================================
   THE HEADER ISLAND — lifted out of gsap-site.css, which only the homepage
   loads. That is why the two headers looked different: the homepage had this
   glass island that starts flat against the page and fills in as you scroll,
   while the feature pages had a simpler one in site.css that floated from the
   first pixel.

   --nav-p is the fill, 0 at the top of the page and 1 once scrolled. On the
   homepage gsap-site.js drives it alongside the chapter accent; everywhere
   else chrome.js does it from scroll position alone, so the behaviour reads
   the same without importing the narrative.
   ========================================================================== */

.site-header {
  /* --- runtime state (written by JS) ---------------------------------- */
  --nav-p: 0;                       /* header fill progress, 0 at page top   */
  --nav-accent-rgb: 226, 104, 74;   /* current chapter accent (tweened)      */
  --nav-glass-rgb: 255, 255, 255;   /* glass body colour (tweened on theme)  */
  --nav-ink-rgb: 22, 24, 46;        /* text + border colour  (tweened)       */

  /* --- tunables: geometry --------------------------------------------- */
  --nav-top: 15px;                  /* resting distance from viewport top    */
  --nav-settle: 2px;                /* how much it settles up when scrolled  */
  --nav-h: calc(51px + 5px * var(--nav-p));
  --nav-pad-x: calc(13px + 7px * var(--nav-p));
  --nav-pad-y: 5px;
  --nav-gap: calc(13px + 5px * var(--nav-p));
  --nav-radius: calc(17px + 4px * var(--nav-p));

  /* --- tunables: glass ------------------------------------------------- */
  --nav-glass-alpha: 0.62;          /* final translucency (never opaque)     */
  --nav-glass-rest: 0.1;            /* how present the glass is at scrollY 0 */
  --nav-blur: 20px;
  --nav-sat: 116%;

  /* --- tunables: the bottom-up accent fill ----------------------------- */
  --nav-fill: calc(var(--nav-p) * 116%);  /* >100% so it clears the top edge */
  --nav-fill-feather: 30%;          /* softness of the leading edge          */
  --nav-fill-a-low: 0.19;           /* accent alpha at the shell's bottom    */
  --nav-fill-a-mid: 0.105;
  --nav-fill-a-high: 0.03;
  --nav-seed: 0.09;                 /* always-on glow off the status bar     */

  position: fixed;
  inset: 0 0 auto;
  z-index: 50;
  display: flex;
  justify-content: center;

  /* site.css centres its own header with left:50% + translateX(-50%) on a
     fixed width. `inset` above resets left, but a transform and a width
     survive it, and the island came out shifted half its own width off the
     left edge. Both are cleared by name rather than relied on. */
  transform: none;
  width: auto;
  max-width: none;
  padding-top: calc(
    var(--nav-top) + env(safe-area-inset-top, 0px) - var(--nav-settle) * var(--nav-p)
  );
  pointer-events: none;
  transition: opacity 220ms ease;
}

.header-inner {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  width: min(1180px, calc(100% - 36px));
  margin: 0 auto;
}

/* --- the floating island ------------------------------------------------ */

/* Sign in, then the store badge, hard against the right edge. Both sit after
   the nav in DOM order, but .actions carried no `order` and so defaulted to 0 —
   which put the badge in the order-0 run with the brand and the nav, i.e. to
   the LEFT of a Sign in that was already order:2. Stated explicitly here so the
   two are read in the order they are meant to be used: sign in if you have an
   account, install if you do not. 4, not 3, because the burger takes 3 in the
   compact block below. */
.nav-shell > .nav-signin { order: 2; }
.nav-shell > .actions { order: 4; }

.nav-shell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nav-gap);
  width: fit-content;
  max-width: 100%;
  min-height: var(--nav-h);
  padding: var(--nav-pad-y) var(--nav-pad-x);
  border: 1px solid rgba(var(--nav-ink-rgb), calc(0.035 + 0.085 * var(--nav-p)));
  border-radius: var(--nav-radius);
  background: transparent;
  box-shadow:
    0 calc(3px + 11px * var(--nav-p)) calc(14px + 28px * var(--nav-p))
      rgba(var(--nav-ink-rgb), calc(0.02 + 0.06 * var(--nav-p)));
  color: rgba(var(--nav-ink-rgb), 0.86);
  pointer-events: auto;
}

.nav-shell::before,
.nav-shell::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
}

/* Glass base. The blur radius is CONSTANT (cheap); only opacity is animated,
   so the surface reads lighter at the top of the page without re-blurring. */
.nav-shell::before {
  z-index: 0;
  background: rgba(var(--nav-glass-rgb), var(--nav-glass-alpha));
  backdrop-filter: blur(var(--nav-blur)) saturate(var(--nav-sat));
  -webkit-backdrop-filter: blur(var(--nav-blur)) saturate(var(--nav-sat));
  opacity: calc(var(--nav-glass-rest) + (1 - var(--nav-glass-rest)) * var(--nav-p));
}

/* The accent suffusing upward out of the status bar. Two layers: a small
   always-on seed at the very bottom edge, and the progress-driven body whose
   leading edge is feathered over --nav-fill-feather so there is no hard line. */
.nav-shell::after {
  z-index: 1;
  background:
    linear-gradient(
      to top,
      rgba(var(--nav-accent-rgb), var(--nav-seed)) 0,
      rgba(var(--nav-accent-rgb), 0) 7px
    ),
    linear-gradient(
      to top,
      rgba(var(--nav-accent-rgb), calc(var(--nav-fill-a-low) * var(--nav-p))) 0%,
      rgba(var(--nav-accent-rgb), calc(var(--nav-fill-a-mid) * var(--nav-p)))
        calc(var(--nav-fill) * 0.45),
      rgba(var(--nav-accent-rgb), calc(var(--nav-fill-a-high) * var(--nav-p)))
        var(--nav-fill),
      rgba(var(--nav-accent-rgb), 0) calc(var(--nav-fill) + var(--nav-fill-feather))
    );
}

.nav-shell > * {
  position: relative;
  z-index: 2;
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header {
    --nav-glass-alpha: 0.85;
  }
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--ink);
  flex: 0 0 auto;
  font-weight: 860;
  text-decoration: none;
}

.brand-mark {
  display: grid;
  width: 38px;
  height: 38px;
  overflow: hidden;
  place-items: center;
  border-radius: 11px;
}

.brand-mark img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.nav {
  display: flex;
  flex: 1 1 auto;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 0;
}

.nav a {
  border-radius: 999px;
  padding: 9px 11px;
  color: #383750;
  font-size: 0.9rem;
  font-weight: 760;
  text-decoration: none;
}

/* --- the About submenu --------------------------------------------------
   The top-level nav is Home / About / Support, and every page that is not one
   of those three hangs off About. That is a deliberate trade and it is worth
   writing down: the five feature pages were promoted INTO the nav so a crawler
   would read them as primary navigation (see README, "The feature pages"), and
   putting them behind a trigger weakens that signal. What keeps them reachable
   is that the panel is real markup — six ordinary `<a href>`s that are in the
   DOM at all times, `hidden` rather than absent — plus the footer's Features
   column and the homepage's in-body links, both of which are unchanged.

   The pattern is the homepage's own `.rail-group`, which lives in
   gsap-site.css and so is not available here. Same shape, same two lessons:
   `[hidden]` must beat the display rule, and the popover has to escape the
   row's overflow.

   Named `.nav-*` rather than `.rail-*` on purpose. They are two components
   that happen to look alike — one navigates pages, the other jumps to stages
   on one page — and sharing a class name would mean the homepage's rail and
   every sub-page's menu could never diverge again. */

.nav-group {
  position: relative;
  flex: 0 0 auto;
}

/* The trigger is a nav pill that happens to be a <button>, so it has to be
   told all the things a <button> does not inherit: the family, the colour and
   the fact that it is not a form control. `font-size`/`weight` match
   `.site-header .nav a` in site.css, which wins over this file on
   specificity — if that rule moves, this moves with it. */
.site-header .nav .nav-group-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  margin: 0;
  border: 0;
  border-radius: 999px;
  padding: 7px 11px;
  background: transparent;
  color: rgba(var(--nav-ink-rgb), 0.62);
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 760;
  line-height: inherit;
  white-space: nowrap;
  cursor: pointer;
  transition: color 180ms ease, background-color 180ms ease;
}

.site-header .nav .nav-group-trigger:hover,
.site-header .nav .nav-group-trigger.is-open,
.site-header .nav .nav-group-trigger[aria-current] {
  color: rgba(var(--nav-ink-rgb), 0.95);
  background: rgba(47, 127, 120, 0.1);
}

.site-header .nav .nav-group-trigger:focus-visible {
  outline: 2px solid rgba(47, 127, 120, 0.72);
  outline-offset: 3px;
}

.nav-caret {
  width: 6px;
  height: 6px;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  transform: translateY(-1px) rotate(45deg);
  transition: transform 200ms ease;
}

.nav-group-trigger.is-open .nav-caret {
  transform: translateY(1px) rotate(-135deg);
}

/* [hidden] has to win over the display below AND over the compact block's
   `display: grid`, or the UA's `[hidden] { display: none }` loses to a class
   selector and the closed menu is still laid out — invisible only because
   something else clips it, while quietly giving the row scroll overflow it
   has no business having. That is the exact bug the homepage's rail had. */
.site-header .nav .nav-menu[hidden] {
  display: none;
}

.site-header .nav .nav-menu {
  position: absolute;
  top: calc(100% + 10px);
  left: 50%;
  z-index: 3;
  display: grid;
  gap: 2px;
  min-width: 176px;
  padding: 6px;
  transform: translateX(-50%);
  border: 1px solid rgba(var(--nav-ink-rgb), 0.08);
  border-radius: 14px;
  /* Opaque, not the island's glass: a translucent panel over a scrolling page
     makes its own items unreadable the moment an illustration passes under. */
  background: #fff;
  box-shadow:
    0 22px 44px rgba(16, 18, 38, 0.14),
    0 3px 10px rgba(16, 18, 38, 0.06);
  pointer-events: auto;
}

.site-header .nav .nav-menu a {
  width: 100%;
  border-radius: 9px;
  padding: 8px 11px;
  text-align: left;
  white-space: nowrap;
}

/* site.js puts a fade on the row's right edge when it overflows. With three
   items it never does — but the popover must not be masked by it if the rule
   ever applies again, and a mask clips as surely as an overflow does. */
.site-header .nav.is-scrollable:has(.nav-menu:not([hidden])) {
  mask-image: none;
  -webkit-mask-image: none;
  overflow: visible;
}

.actions,
.hero-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.actions {
  flex: 0 0 auto;
  flex-wrap: nowrap;
}

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 42px;
  border: 1px solid transparent;
  border-radius: 999px;
  padding: 0 18px;
  font-weight: 830;
  text-decoration: none;
  white-space: nowrap;
}

.button.primary {
  background: var(--purple);
  color: #fff;
  box-shadow: 0 18px 38px rgba(124, 58, 237, 0.24);
}

.button.secondary,
.menu-button {
  border-color: var(--line);
  background: rgba(255, 255, 255, 0.74);
}

.button.text {
  padding: 0;
  color: var(--teal-deep);
}

/* --- "Get it on Google Play" store badge -------------------------------- */

.store-button {
  display: inline-flex;
  align-items: center;
  line-height: 0;
  text-decoration: none;
}

.store-button img {
  display: block;
  height: 46px;
  width: auto;
}

/* Compact version inside the fixed header. */
.site-header .store-button img {
  height: 36px;
}

/* Sign in sits beside the store badge in the island: quieter than the badge,
   and sized to the same 36px optical height so the pair reads as one group.

   This rule pre-dated the link itself and only ever set the box, so when the
   link was finally added it rendered as a bare underlined <a> at 36px — the
   height of a button with none of the look of one. The border, radius and
   colour below are the rest of that intent. display:inline-flex + centring is
   what actually makes the 36px read as optical height rather than as a tall
   box with small text floating at the top of it. */
.nav-signin {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 0 14px;
  border: 1px solid rgba(var(--nav-ink-rgb), 0.2);
  border-radius: 999px;
  color: rgba(var(--nav-ink-rgb), 0.82);
  font-size: 0.86rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  transition: color 180ms ease, background-color 180ms ease, border-color 180ms ease;
}

.nav-signin:hover {
  border-color: rgba(var(--nav-ink-rgb), 0.38);
  background: rgba(var(--nav-ink-rgb), 0.05);
  color: rgba(var(--nav-ink-rgb), 1);
}

.nav-signin:focus-visible {
  outline: 2px solid rgba(47, 127, 120, 0.72);
  outline-offset: 2px;
}

.menu-button {
  display: none;
  width: 44px;
  height: 44px;
  border-radius: 999px;
}

.menu-button span {
  display: block;
  width: 18px;
  height: 2px;
  margin: 4px auto;
  border-radius: 99px;
  background: currentColor;
}

/* --- chapter rail ------------------------------------------------------- */


/* ==========================================================================
   SITE CHROME — the header's mobile menu, shared by every page.

   This lived in gsap-site.css, which only the homepage loads, so the homepage
   had a hamburger and the six feature pages had none: their nav simply ran off
   the side and scrolled. One file now, loaded by both, so the header behaves
   the same wherever you are.

   The panel rules address `.stage-rail` and `.nav` together — the homepage's
   rail of page stages and the feature pages' list of links are the same thing
   at phone width, a list under the island.

   The two colour tokens are defined by gsap-site.css on the homepage, where
   the chapter accent tweens them as you scroll. The fallbacks below are what
   the feature pages use, so this file never depends on them being set.

   LOAD ORDER MATTERS: this file must come AFTER site.css / gsap-site.css in
   every page's <head>. site.css already stacks `.nav` into a column at phone
   width; linked first, chrome.css lost on equal specificity and the feature
   pages rendered a 549px-tall header with every link in it.
   ========================================================================== */

:root {
  --nav-ink-rgb: 22, 24, 46;
  --nav-accent-rgb: 226, 104, 74;
}

.nav-toggle {
  display: none;
  flex: 0 0 auto;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: transparent;
  color: rgba(var(--nav-ink-rgb), 0.82);
  cursor: pointer;
}

.nav-toggle:hover { background: rgba(var(--nav-ink-rgb), 0.05); }

.nav-toggle:focus-visible {
  outline: 2px solid rgba(var(--nav-accent-rgb), 0.9);
  outline-offset: 2px;
}

.nav-toggle__lines { display: grid; gap: 4px; width: 18px; }

.nav-toggle__lines span {
  display: block;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 200ms ease, opacity 160ms ease;
}

/* The three lines become a cross while the panel is open. */
.site-header.is-nav-open .nav-toggle__lines span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.site-header.is-nav-open .nav-toggle__lines span:nth-child(2) { opacity: 0; }
.site-header.is-nav-open .nav-toggle__lines span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

/* THE HAMBURGER APPEARS WHEN THE MENU STOPS FITTING, AND NOT ONE PIXEL SOONER.
   Measured, not guessed. The island hugs its content, so the full row has a
   fixed natural width: brand + links + Sign in + store badge comes to 671px on
   the homepage and 586px on a feature page, inside a header that gives its
   content the viewport less 36px of gutter. So the homepage needs ~707px, and
   with the tighter gaps and rail font of the 700-1320px block below, ~675px.
   699px is the first width under that with a whole-number breakpoint.

   It used to be 1180px, which hid the store badge and folded four links behind
   a tap on an iPad and on most laptop windows — roughly 500px of viewport in
   which everything fitted and nothing was shown. 1180 was inherited from the
   two-row island that gsap-site.css and site.css drew between 760 and 1180;
   those blocks now stop at 699 too, so there is no width at which the island
   wraps to two rows. It is one row, or it is the panel.

   The short-landscape clause went with it. It existed because the two-row
   header ate a quarter of a landscape handset; one row is ~62px, and a
   landscape phone narrower than 700px is caught by the width anyway. */
@media (max-width: 699px) {
  .site-header .actions { display: none; }

  /* Undo the two-row island from gsap-site.css's own 699px block.

     Full width, not fit-content: with only three items left the island shrank
     to hug them and floated in the middle of the screen while the page content
     beside it ran the full column — it read as a detached pill rather than the
     top of the page. It now spans the same width the content does, brand at
     the left edge and the controls at the right. */
  .nav-shell {
    flex-wrap: nowrap;
    width: 100%;
    justify-content: flex-start;
  }
  .site-header { --nav-h: calc(51px + 5px * var(--nav-p)); --nav-pad-y: 5px; }

  /* Sign in stays OUT of the panel and sits beside the burger: it is the one
     thing a returning visitor came for, and burying it behind a tap on every
     page is a tap too many. Brand is order:1, so these take 2 and 3. */
  .nav-shell > .nav-signin {
    display: inline-flex;
    order: 2;
    margin-left: auto;
    white-space: nowrap;
  }

  .nav-toggle { display: grid; order: 3; margin-left: 2px; }

  .nav-shell { flex-wrap: nowrap; align-items: center; }

  /* The links become the panel: full width under the island, not a row in it. */
  .nav-shell > .stage-rail,
  .nav-shell > .nav {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    z-index: 40;
    display: none;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    overflow: visible;
    padding: 8px;
    border: 1px solid rgba(var(--nav-ink-rgb), 0.1);
    border-radius: 16px;
    /* Opaque, and NOT glass — the same rule the desktop popover follows a few
       hundred lines up, for the same reason: a translucent panel over a
       scrolling page makes its own items unreadable.

       It was glass, and it did not work. `.nav-shell` carries its own
       `backdrop-filter` (site.css, the legacy copy of the island's blur), so
       it is a BACKDROP ROOT — and a backdrop-filtered child of a
       backdrop-filtered parent is filtering the wrong thing. The effect was
       that a panel declared at 0.97 alpha painted at roughly half that, and
       the hero headline read straight through the menu. Measured, not
       guessed: removing the blur from `.nav-shell` fixed the panel and
       changed nothing about the panel's own declarations.

       The blur is gone rather than the parent's, because an opaque sheet
       needs no blur behind it and the island's glass is not this file's to
       take away. */
    background: #fffdf8;
    box-shadow: 0 18px 44px rgba(23, 35, 38, 0.16);
    mask-image: none;
    -webkit-mask-image: none;
  }

  .site-header.is-nav-open .nav-shell > .stage-rail,
  .site-header.is-nav-open .nav-shell > .nav { display: flex; }

  .stage-rail :is(button, a),
  .nav-shell > .nav :is(button, a) {
    /* `display: flex` stated here, not left to the `pointer: coarse` block in
       site.css. The current-page dot below is `margin-left: auto`, which does
       nothing in an inline box — so on a narrow window with a mouse the cue
       simply was not drawn, and which page you are on is not a touch-only
       question. */
    display: flex;
    align-items: center;
    justify-content: flex-start;
    width: 100%;
    min-height: 46px;
    padding-inline: 14px;
    border-radius: 10px;
    font-size: 0.92rem;
    text-align: left;
  }

  /* The panel is already a vertical sheet, so the submenu is an indented list
     inside it rather than a popover — floating it there lays the six pages
     straight over "Support". Still governed by `hidden`: a phone with six
     extra rows permanently unfolded is the long nav this menu exists to end.

     `display: contents` on the group so the trigger and the menu become
     siblings of Home and Support, and the sheet's own `:is(button, a)` rule
     reaches both. */
  .nav-group { display: contents; }

  .site-header .nav .nav-menu {
    position: static;
    display: grid;
    min-width: 0;
    margin: 0 0 2px 12px;
    padding: 2px 0 2px 10px;
    transform: none;
    border: 0;
    border-left: 2px solid rgba(var(--nav-accent-rgb), 0.35);
    border-radius: 0;
    background: none;
    box-shadow: none;
  }

  .site-header .nav .nav-group-trigger {
    justify-content: flex-start;
    width: 100%;
    min-height: 46px;
    padding-inline: 14px;
    border-radius: 10px;
    font-size: 0.92rem;
    text-align: left;
  }

  /* The caret goes to the right-hand end, where a disclosure control belongs
     in a list. In the island it sits against the word. */
  .site-header .nav .nav-group-trigger .nav-caret { margin-left: auto; }

  /* Which page or stage you are on — the cue the inline row gave for free. */
  .stage-rail :is(button, a).is-active::after,
  .nav-shell > .nav a[aria-current="page"]::after {
    content: "";
    width: 7px;
    height: 7px;
    margin-left: auto;
    border-radius: 50%;
    background: rgb(var(--nav-accent-rgb));
  }

}

@media (prefers-reduced-motion: reduce) {
  .nav-toggle__lines span { transition: none; }
}
