/* ============================================================================
   nav.css — the site nav, extracted from home.css.

   ⚠ IT LIVED IN home.css AND ONLY home.css, which is why extracting the
   MARKUP alone would not have been enough: six documents would have injected
   a nav and rendered it unstyled. The rules below are moved verbatim — no
   redesign rode along with the move, so a diff of this file against the block
   that left home.css is empty of intent.

   Depends on base.css for .btn, .wrap and --nav-h, so every document that
   loads this one must load that one first. privacy.html and terms.html gained
   base.css for exactly that reason; their own <style> still follows it and so
   still wins on everything it sets.
   ========================================================================== */

/* ── Nav ──────────────────────────────────────────────────────────────────── */
.nav {
  position: sticky;
  top: 0;
  z-index: 60;
  background: var(--bg);
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
/* ⚠ THE HEADER'S OWN TYPE, NOT THE PAGE'S (QA: one header everywhere). Every
   inherited property is pinned to Home's body values (css/base.css body), so
   no page's body class can leak in: .article-body set IBM Plex Sans (loaded
   to 600 only — the 800 wordmark rendered semibold), .legal-body a 1.75
   line-height, About / Account / legal a secondary body colour. The mobile
   menu panel is part of the header and inherits the same way. */
.nav, #ov-menu {
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  font-style: normal;
  line-height: 1.6;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
.nav.is-scrolled {
  border-bottom-color: var(--line);
  box-shadow: var(--shadow-sm);
}

.nav__inner {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  height: var(--nav-h);
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
}
.brand img { width: 30px; height: auto; }
.brand__word {
  font-size: 1.3rem;
  font-weight: var(--fw-display);
  letter-spacing: var(--tracking-tight);
  color: var(--text);
}

.nav__tabs {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  flex: 0 0 auto;
}
.navtab {
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.navtab:hover { color: var(--text); background: var(--surface-sunken); }
/* A tab that owns a dropdown (nav-menus.js marks it data-menu) says so: a
   chevron, turned while the menu is open. Drawn with a mask so it is the
   tab's own colour in both themes. */
.navtab[data-menu] { display: inline-flex; align-items: center; gap: 4px; }
.navtab[data-menu]::after {
  content: '';
  width: 12px;
  height: 12px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") center / contain no-repeat;
  transition: transform var(--dur-fast) var(--ease);
}
.navtab[data-menu][aria-expanded='true']::after { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .navtab[data-menu]::after { transition: none; } }

/* Search: a real-looking field that opens the overlay. */
.nav__search {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: 1 1 auto;
  min-width: 0;
  max-width: 320px;
  margin-left: auto;
  padding: 9px var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  text-align: left;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.nav__search:hover { border-color: var(--line-strong); background: var(--bg); }
.nav__search svg { flex: 0 0 auto; width: 17px; height: 17px; }
.nav__search span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav__end {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex: 0 0 auto;
}

.nav__iconbtn {
  display: none;
  place-items: center;
  width: 40px;
  height: 40px;
  color: var(--text);
  border-radius: var(--r-pill);
  transition: background var(--dur-fast) var(--ease);
}
.nav__iconbtn:hover { background: var(--surface-sunken); }
.nav__iconbtn svg { width: 21px; height: 21px; }

/* TWO breakpoints, not one, because two different things run out of room.
   
   the UPPER one (1140px today — DERIVED, see the table below) — the TABS
   collapse into the menu overlay and the inline search bar gives way to the
   icon. It has moved three times, always for the same reason and always upward:
   900 -> 1000 when the Download button landed, 1000 -> 1030 when the floor
   itself was derived, 1030 -> 1140 when the Account button landed. The search
   bar is the only flex-grow item in the row, so it absorbs the entire cost of
   anything added to either end — it measured 92px at 901px and 131px at 940px
   back when the number was 900.

   ⚠ The floor those two figures were judged against was 140, and 140 IS
   SUPERSEDED — the table below derives 167 from the field's own content. Do
   not reason from the 140 in this paragraph; it is here as history.

   <=900px — the nav DOWNLOAD button goes too. Below that the row simply does
   not fit: at 390px it needed 439px of 390, a 49px overflow and a horizontal
   scrollbar on every phone. The button is not lost, it moves into the menu
   overlay, and the hero carries one regardless. Nav returns to
   logo - search - Fetch+ - burger, which is the configuration that passed. */
/* ⚠ 1140px, DERIVED FROM THE ROW'S OWN CONTENT — not a device breakpoint,
   and NOT the 1030px this used to be (nor the 1000 before that). Re-measured
   2026-09-09 at 1600px, where nothing is compressed:

     wrap padding                     24 + 24 =  48
     brand                                        93
     tabs (Forecast/Maps/Guides/About)           307
     search field, at its FLOOR                  167
     nav end (theme, download, Fetch+, ACCOUNT,
              sign in)                           464
     three 20px gaps between the four children    60
                                                ----
                                                1139  ->  1140

   THE SEARCH FLOOR IS THE BINDING CONSTRAINT, and it is content too. The
   field is 61px of chrome (16 + 16 padding, 12 gap, 17 icon) plus its
   placeholder. The full placeholder is 231px of text; the smallest
   truncation that still NAMES what it searches is "Search spots…" at 106px,
   so 106 + 61 = 167. Below that it degrades to "Search…", which names
   nothing — and at that point the icon-only button IS the better control,
   which is exactly what the fallback nav uses.

   ⚠ THIS TABLE WENT STALE THE DAY THE ACCOUNT BUTTON LANDED, AND NOTHING
   SAID SO. `nav end` was 356 for four children; it is 464 for five. The
   difference is 108 — the Account button (100) plus its 8px gap, EXACTLY —
   and the breakpoint was never recomputed, so from 1031 to 1138 the tabs
   showed while the search sat under its floor. That is the same defect the
   1000 -> 1030 move fixed, re-created by adding a button. Measured on the
   shipping row, where search width is simply `viewport - 972`:

     1031px ->  59   placeholder shows   0 of 231px  (NO text at all)
     1100px -> 128   placeholder shows  65 of 231px
     1160px -> 188   placeholder shows 125 of 231px  (above the floor)

   The row is now FIVE grown children deep, so the next button added here is
   worth about another 108px of breakpoint. Re-derive rather than nudge: the
   arithmetic above is the whole method, and `tools/download.mjs` pins the
   167 floor across the boundary so a stale table fails loudly next time. */
/* ⚠ SUPERSEDED BY THE FULL-WIDTH HEADER (Stage 5a rev A §8) — the single
   1140px collapse above is replaced by TWO steps, re-derived with the
   Account button gone (§7) and Articles in the tabs:

     side padding                    24 + 24 =  48
     brand                                       93
     tabs (Forecast/Maps/Articles/About)        ~320
     search, at its FLOOR                       167
     nav end (theme, Download, Fetch+, Sign in) 356
     three 20px gaps                             60
                                               ----
                                               1044  ->  search collapses ≤ 1050

   then, with the search ICON in the end group (+48):
     48 + 93 + 320 + 404 + two gaps 40 = 905   ->  tabs fold ≤ 910

   The search field is flex: 1 between 167px (the floor derived above) and
   640px (past that, a single-line search field only gets emptier); the end
   group takes margin-left: auto, so on ultra-wide screens the slack sits
   between the field and the buttons, never inside the field. Nothing wraps:
   both steps happen before the row would overflow. */
/* ⚠ RE-DERIVED FOR THE FAVORITES TAB (Stage 5c §1). The row gained one
   un-shrinkable child: "Favorites" at --fs-sm / --fw-medium is ~62px of text
   + 2 x 12px tab padding + the 4px tab gap = ~90px. Everything else in the
   table above is unchanged, so both steps move by that amount:

     48 + 93 + tabs ~410 + search floor 167 + end 356 + gaps 60
                                               = 1134  ->  search collapses ≤ 1140
     48 + 93 + 410 + end-with-icon 404 + gaps 40
                                               =  995  ->  tabs fold ≤ 1000

   Rev A §3 put Favorites FIRST: same tabs, same widths, same gaps — the
   order changes nothing in the arithmetic, so both steps stay put.

   ⚠ REV A §4 RENAMED IT "Daily Check" — ~76px of text against "Favorites"'
   ~62, so the tab is ~14px wider and both steps move by that:
     search collapses  1134 + 14 = 1148  ->  ≤ 1150
     tabs fold          995 + 14 = 1009  ->  ≤ 1010

   Download still hides at ≤ 900 — below the fold the tabs are gone, so the
   end group alone (~480px) is what the row carries there. tools/download.mjs
   straddles 1140/1141 and 1000/1001. */
@media (max-width: 1150px) {
  .nav__search { display: none; }
  .nav__searchbtn { display: grid; }
}
@media (max-width: 1010px) {
  .nav__tabs,
  .nav__signin { display: none; }
  .nav__menubtn { display: grid; }
}
@media (max-width: 900px) {
  .nav__download { display: none; }
}
@media (max-width: 380px) {
  .brand__word { display: none; }
}

/* ── Theme toggle ─────────────────────────────────────────────────────────
   ⚠ SHOWN AT EVERY WIDTH — it is the ONLY theme control above 1000px, where
   the burger that opens the menu (and its labelled segmented control) is
   display:none. The .nav__iconbtn rule hides icon buttons on desktop, so this
   one opts back in rather than inheriting that. */
.nav__theme { display: grid; }
.nav__theme svg { width: 20px; height: 20px; }
/* Which glyph shows is CSS keyed off the theme: no JS branch to fall out of
   step with the page, and it is correct on the very first paint because the
   inline <head> seed has already set data-theme. */
.nav__theme-moon { display: none; }
:root[data-theme='dark'] .nav__theme-sun { display: none; }
:root[data-theme='dark'] .nav__theme-moon { display: block; }

/* ── Nav hover menus ──────────────────────────────────────────────────────
   The panel is positioned against a host wrapper rather than the tab itself,
   so the tab keeps its place in the flex row and the panel can overhang
   without pushing anything. Trigger and panel are ONE hover region — see the
   grace period in js/nav-menus.js — so there is deliberately no gap between
   them for the pointer to fall through. */
.navtab-host { position: relative; display: inline-flex; }
.navmenu {
  position: absolute; top: 100%; left: 0; z-index: 60;
  width: max-content; min-width: 260px; max-width: min(92vw, 420px);
  /* ⚠ PADDING, NOT MARGIN — the 6px used to be margin-top, which left a
     6px band belonging to NEITHER the trigger nor the panel. Measured:
     trigger bottom 54, panel top 60, and a pointer at y=58 hovering
     nothing at all. Crossing it fired the trigger's leave with no
     matching panel enter, so the menu depended on the grace timer to
     survive an ordinary downward move. As padding the panel's box starts
     at the trigger's edge: the two touch, and the grace period goes back
     to covering only what it was for — diagonal travel. */
  padding: 14px 8px 8px;
  background: var(--bg, #fff);
  border: 1px solid var(--line, #E3E9F0);
  border-radius: var(--r-lg, 12px);
  box-shadow: 0 12px 32px rgba(10, 22, 40, 0.14);
}
:root[data-theme='dark'] .navmenu { box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5); }
.navmenu[hidden] { display: none; }

.navmenu__modes { display: flex; gap: 4px; margin-bottom: 8px; }
.navmenu__mode {
  flex: 1 1 0; padding: 7px 10px; border-radius: 999px;
  border: 1px solid var(--line, #E3E9F0); background: var(--bg, #fff);
  font: 600 13px/1 var(--font-sans, "Inter", system-ui, sans-serif);
  color: var(--text-2, #5B6B7C); cursor: pointer;
}
.navmenu__mode[aria-selected='true'] {
  background: var(--accent, #12A594); border-color: var(--accent, #12A594); color: #fff;
}

/* ⚠ THE SEARCH IS PINNED AND THE RESULTS SCROLL UNDER IT. A deep region list
   that scrolls the whole panel takes the field off screen, and then typing to
   filter means scrolling back up first — the moment a filter stops being
   useful. */
.navmenu__panel-inner { display: flex; flex-direction: column; min-height: 0; }
.navmenu__search {
  flex: 0 0 auto; width: 100%; box-sizing: border-box;
  padding: 8px 12px; margin-bottom: 6px;
  border: 1px solid var(--line, #E3E9F0); border-radius: var(--r-md, 10px);
  background: var(--surface-sunken, #F4F7FA); color: var(--text-1, #16202C);
  font: 500 14px/1.2 var(--font-sans, "Inter", system-ui, sans-serif);
}
.navmenu__search:focus-visible { outline: 2px solid var(--accent, #12A594); outline-offset: 1px; }
.navmenu__results {
  overflow-y: auto; max-height: min(52vh, 420px);
  overscroll-behavior: contain; min-height: 0;
}

.navmenu__row {
  display: flex; flex-direction: column; gap: 1px; width: 100%;
  box-sizing: border-box;
  padding: 8px 10px; border: 0; border-radius: var(--r-md, 10px);
  background: none; text-align: left; text-decoration: none; cursor: pointer;
  color: inherit; font: inherit;
}
.navmenu__row:hover, .navmenu__row:focus-visible { background: var(--surface-sunken, #F4F7FA); }
.navmenu__row:focus-visible { outline: 2px solid var(--accent, #12A594); outline-offset: -2px; }
.navmenu__row[aria-disabled='true'] { opacity: 0.55; cursor: default; }

/* The Favorites destination sits below the region content, set off by a rule
   so it reads as a link out of the menu rather than another region row. */
.navmenu__rule { height: 1px; background: var(--line, #E3E9F0); margin: 6px 8px; }
.navmenu__fav {
  display: flex; flex-direction: column; gap: 2px;
  padding: 8px 12px; border-radius: 8px; text-decoration: none;
}
.navmenu__fav:hover, .navmenu__fav:focus-visible { background: var(--surface-sunken, #F4F7FA); }
.navmenu__fav:focus-visible { outline: 2px solid var(--accent, #12A594); outline-offset: -2px; }
.navmenu__fav-name { font-weight: 600; color: var(--text); }
.navmenu__fav-sub { font-size: var(--fs-sm); color: var(--text-muted); }
.navmenu__row-name {
  font: 600 14px/1.3 var(--font-sans, "Inter", system-ui, sans-serif);
  color: var(--text-1, #16202C);
}
.navmenu__row-sub {
  font: 500 12px/1.3 var(--font-sans, "Inter", system-ui, sans-serif);
  color: var(--text-2, #5B6B7C);
}
.navmenu__empty {
  margin: 0; padding: 10px;
  font: 500 13px/1.4 var(--font-sans, "Inter", system-ui, sans-serif);
  color: var(--text-2, #5B6B7C);
}

/* ── the signed-in profile button (Stage 5a rev A §7) ──────────────────────── */
.nav__profile[hidden] { display: none; }
.nav__avatar {
  width: 36px; height: 36px; padding: 0; border-radius: 50%; border: 2px solid var(--bg, #fff);
  box-shadow: 0 0 0 1px var(--line-strong, #C9D3DD); overflow: hidden; cursor: pointer;
  display: grid; place-items: center; background: var(--brand-teal, #0E7A66); color: #fff;
  font: 700 13px/1 var(--font-sans, "Inter", system-ui, sans-serif); letter-spacing: 0.02em;
}
.nav__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.nav__avatar:focus-visible { outline: 3px solid var(--accent-ring, rgba(18,165,148,.35)); outline-offset: 2px; }
.nav__pmenu { left: auto; right: 0; min-width: 200px; }
:root[data-theme='dark'] .nav__avatar { border-color: var(--surface-raised, #122334); }

/* ── Stage 5a rev A §8: the header spans the full viewport ─────────────────
   Edge to edge with ~24px sides (safe-area aware on phones); page content
   below keeps its own max-width. Left: brand. Then the tabs, the search
   (flex: 1, 167–640px), and the end group pinned right (theme · Download ·
   Fetch+ · Sign in / profile). */
.nav__inner.wrap {
  max-width: none;
  padding-inline: max(24px, env(safe-area-inset-left)) max(24px, env(safe-area-inset-right));
}
@media (max-width: 560px) {
  .nav__inner.wrap { padding-inline: max(16px, env(safe-area-inset-left)) max(16px, env(safe-area-inset-right)); }
}
.nav__search { flex: 1 1 auto; min-width: 167px; max-width: 640px; margin-left: 0; }
.nav__end { margin-left: auto; }

/* ── the header's mobile menu (#ov-menu) ────────────────────────────────────
   Moved verbatim from overlay.css (QA, one header): the menu is part of the
   header, so its rules live with the header's — tools/header_check.py fails
   any header selector outside this file. The id outranks overlay.css's base
   .menu-list rules whatever the load order. */
/* Menu: links were transparent until hover; over the field each becomes a
   solid pill so the tap target reads as a control, not floating text. Hover
   keeps its distinct var(--surface). */
#ov-menu .menu-list__item {
  background: var(--bg);
  border-radius: var(--r-md);
  border: 1px solid var(--line);
}
#ov-menu .menu-list__item + .menu-list__item { margin-top: var(--sp-2); }
#ov-menu .menu-list__item:hover { background: var(--surface); }
#ov-menu .menu-list__rule { background: transparent; }

/* The profile menu's top row: the name, and the username under it */
.navmenu__who {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 12px 10px;
  border-bottom: 1px solid var(--line, rgba(10, 22, 40, 0.08)); margin-bottom: 4px;
}
.navmenu__who[hidden] { display: none; }
.navmenu__whoname { font-weight: 700; font-size: 14px; color: var(--text); overflow-wrap: anywhere; }
.navmenu__whouser { font-size: 12px; color: var(--text-secondary); overflow-wrap: anywhere; }
.navmenu__whouser[hidden] { display: none; }
