/*
  The one standard global nav, one bar for every surface:
  • position: fixed, full-bleed, frosted glass (translucent + backdrop-blur)
  • travels UP with the page as you scroll down — 1:1 with the scroll, no
    animation, so it reads as part of the page — then SLIDES back down as
    soon as you scroll up (the `.is-revealing` transition)
  • light by default, dark under `html.dark` (the toggle flips that class)
  Left: brand mark + "Futures Atlas" (+ project breadcrumb dropdown on a project
  page). Right: uppercase links + circular theme toggle. Self-contained colours
  so it renders identically across host, React and vanilla project apps.
  Consumers offset their content by --fa-nav-h (64px).
*/
/*
 * --fa-nav-h  the bar's height. Constant. Use it to reserve layout space.
 * --fa-nav-now  how many pixels of the bar are ON SCREEN right now: its full
 *   height at rest, 0 once it has travelled away, and every value in between
 *   while it moves. Anything sticky or pinned must offset from THIS one, never
 *   from --fa-nav-h, or it either floats above a dead band when the bar leaves
 *   or gets buried when it returns.
 *
 *   It carries NO transition, deliberately. It used to, to cover a bar that
 *   animated away on a 0.55s curve — which meant every sticky offset on the
 *   site lagged half a second behind the thing it was supposed to sit under,
 *   and the glossary alphabet visibly detached from the bar. The bar now moves
 *   with the scroll instead of animating, so the exact number is available on
 *   every frame and nothing has to be interpolated.
 */
:root { --fa-nav-h: 64px; --fa-nav-now: 64px; }

.fa-shell {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 60;
  height: var(--fa-nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 28px;
  background: rgba(243, 241, 236, 0.82);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  color: #141518;
  border-bottom: 1px solid rgba(20, 21, 24, 0.12);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  /* No transform transition at rest: on the way up the bar is driven directly
     from scrollY, and interpolating would make it lag the page it is meant to
     be part of. The transition is added only for the ride back down. */
  transition: background-color 0.5s, border-color 0.5s, color 0.5s;
  transform: translateY(0);
  will-change: transform;
}
.fa-shell.is-revealing {
  transition: transform 0.26s cubic-bezier(0.4, 0, 0.2, 1),
    background-color 0.5s, border-color 0.5s, color 0.5s;
}
@media (prefers-reduced-motion: reduce) {
  .fa-shell.is-revealing { transition: background-color 0.5s, border-color 0.5s, color 0.5s; }
}

.fa-shell__left { display: flex; align-items: center; gap: 8px; min-width: 0; }
.fa-shell__right { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.fa-shell__home { display: inline-flex; align-items: center; gap: 10px; color: inherit; text-decoration: none; }
.fa-shell__mark { display: inline-flex; }
/*
  One ring roll of the mark on ARRIVING at the homepage from elsewhere on the
  site (atlas-nav.js decides when). Not on a reload, and not on every page: it
  marks the transition, and firing it constantly would make it wallpaper.

  This is the bench's own "Ring rolls" preset (/logo-animator), baked to a
  sprite strip by scripts/bake-mark-roll.mjs. The mark is a 3-D construction —
  a tilted tube ring turning inside a sphere — so a CSS rotate of the flat
  artwork is a different animation altogether, and that is what the first
  attempt was. Shipping the bench's renderer instead would put a few hundred
  lines of 3-D maths on every page of the site for 900ms of motion.

  48 cells, one whole turn eased in and out, so the first and last cells ARE
  the resting pose, which is the flat mark: the roll starts from what is on
  the page and lands back on it. steps(47) rather than steps(48): 48 cells are
  47 intervals, and stepping 48 lands every frame a fraction off its cell.

  The strip is an alpha mask filled with currentColor, so it takes the bar's
  own colour in either theme. Only the ink is opaque: the core's interior is
  transparent, so the page shows through it exactly as it does through the
  flat mark. A 3-D render is never pixel-identical to the artwork, so the
  sprite fades in OVER the flat <img> for the first ~70ms and out over it for
  the last ~70ms, and the flat mark is hidden only while the sprite is fully
  opaque. Not a cross-fade: two half-transparent copies of one shape never add
  up to solid, and the mark would dip grey for a frame. Same 900ms clock.
*/
@keyframes fa-mark-roll {
  from { -webkit-mask-position: 0% 0; mask-position: 0% 0; }
  to   { -webkit-mask-position: 100% 0; mask-position: 100% 0; }
}
@keyframes fa-mark-sprite-fade {
  0% { opacity: 0; }
  8% { opacity: 1; }
  92% { opacity: 1; }
  100% { opacity: 0; }
}
@keyframes fa-mark-flat-hide {
  0%, 8% { visibility: visible; }
  8.01%, 91.99% { visibility: hidden; }
  92%, 100% { visibility: visible; }
}
.fa-shell__mark { position: relative; }
.fa-shell__mark.is-rolling img { animation: fa-mark-flat-hide 900ms step-end both; }
.fa-shell__mark.is-rolling::after {
  content: "";
  position: absolute;
  inset: 0;
  background-color: currentColor;
  -webkit-mask-image: url(/fa-mark-roll.png);
  mask-image: url(/fa-mark-roll.png);
  -webkit-mask-size: 4800% 100%;
  mask-size: 4800% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  animation:
    fa-mark-roll 900ms steps(47, end) both,
    fa-mark-sprite-fade 900ms linear both;
}
@media (prefers-reduced-motion: reduce) {
  .fa-shell__mark.is-rolling img { animation: none; }
  .fa-shell__mark.is-rolling::after { display: none; }
}
.fa-shell__word { font-size: 19px; font-weight: 500; letter-spacing: -0.01em; }
/* The "Futures Atlas" wordmark shows alongside the logo on every page, including
   project pages, where the breadcrumb names the project after it. (Hidden only on
   narrow screens, see the mobile block, so the project name keeps its room.) */

.fa-shell__sep { color: currentColor; opacity: 0.32; padding: 0 4px; font-size: 18px; }

/* project breadcrumb + switcher (project pages only) */
.fa-shell__crumb { position: relative; min-width: 0; }
.fa-shell__current {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 46vw;
  padding: 6px 8px;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: 16px;
  cursor: pointer;
}
.fa-shell__current > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fa-shell__current:hover { background: rgba(20, 21, 24, 0.06); }
.fa-shell__chev { display: inline-flex; opacity: 0.55; }
.fa-shell__menu {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 240px;
  padding: 6px;
  background: #fff;
  border: 1px solid rgba(20, 21, 24, 0.12);
  border-radius: 10px;
  box-shadow: 0 14px 40px rgba(20, 21, 24, 0.16);
  display: flex;
  flex-direction: column;
}
.fa-shell__menu[hidden] { display: none; }
.fa-shell__item { display: block; padding: 9px 11px; border-radius: 6px; color: #141518; text-decoration: none; font-size: 14px; white-space: nowrap; }
.fa-shell__item:hover { background: rgba(20, 21, 24, 0.06); }
.fa-shell__item.is-current { color: rgba(20, 21, 24, 0.45); cursor: default; }
/* The brand blue, written out because this stylesheet is self-contained by
   design — the vanilla project bundles load it WITHOUT the core tokens. This
   file used to accent light mode in a burnt orange (#b4541e) and dark mode in a
   pale blue (#7fb2e6), so the "accent" was not even the same hue from one
   setting to the next; that is fixed and stays fixed.

   Every use of it in this file is small TEXT — a switcher item, an active tab,
   an active sheet link, the share chips — and the brand blue #1e7cbd (matching
   oklch(0.565 0.13 245) in core) is 3.91 on bone and 3.95 on ink, under the 4.5
   that small type owes. One blue cannot clear 4.5 on both grounds at any
   chroma, so the TEXT blue moves with the theme while the fill blue does not.
   Same hue, same chroma, lightness only:

     light  #0067a6 = oklch(0.495 0.13 245) — 5.25 on bone, 6.02 on the menu's white
     dark   #3b93d5 = oklch(0.64  0.13 245) — 5.34 on ink, 5.85 on the menu's #0c0d10

   These mirror --fa-accent-deep / --fa-accent-deep-d in
   futures-atlas-core/src/tokens.css; change one, change the other. */
:root { --fa-nav-accent: #0067a6; }
html.dark { --fa-nav-accent: #3b93d5; }
.fa-shell__item--accent { color: var(--fa-nav-accent); }
/* Editor-only marker in the switcher (drafts are listed for signed-in editors). */
.fa-shell__draft { margin-left: 6px; border-radius: 3px; background: rgba(20, 21, 24, 0.1); padding: 1px 5px; font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; vertical-align: 1px; }
.fa-shell__menusep { height: 1px; margin: 6px 4px; background: rgba(20, 21, 24, 0.1); }

/* primary nav links (right), uppercase, letter-spaced */
.fa-shell__nav { display: flex; align-items: center; gap: 8px; }
.fa-shell__navlink {
  padding: 8px 12px;
  border-radius: 5px;
  color: currentColor;
  opacity: 0.66;
  text-decoration: none;
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  white-space: nowrap;
}
.fa-shell__navlink:hover, .fa-shell__navlink.is-active { opacity: 1; }

/* Instagram and X, quiet icons after the links; in the sheet below 900px */
.fa-shell__socials { display: inline-flex; align-items: center; gap: 2px; margin-left: 4px; }
.fa-shell__social {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 5px;
  color: currentColor; opacity: 0.66;
}
.fa-shell__social:hover { opacity: 1; }

/* circular theme toggle */
.fa-shell__toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  margin-left: 8px;
  border: 1px solid currentColor;
  border-color: rgba(20, 21, 24, 0.28);
  border-radius: 50%;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.fa-shell__toggle:hover { background: rgba(20, 21, 24, 0.06); }

/* the profile control is the toggle's twin: same circle, same border, same hover */
.fa-shell__profile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; margin-left: 8px;
  border: 1px solid rgba(20, 21, 24, 0.28); border-radius: 50%;
  background: transparent; color: inherit; cursor: pointer;
  transition: background 0.15s;
}
.fa-shell__profile:hover { background: rgba(20, 21, 24, 0.06); }

/* ── dark theme ─────────────────────────────────────────────────────────── */
html.dark .fa-shell {
  background: rgba(12, 13, 16, 0.8);
  color: #f3f1ec;
  border-bottom-color: rgba(255, 255, 255, 0.14);
}
html.dark .fa-shell__current:hover,
html.dark .fa-shell__toggle:hover { background: rgba(255, 255, 255, 0.08); }
html.dark .fa-shell__toggle,
html.dark .fa-shell__profile { border-color: rgba(255, 255, 255, 0.3); }
html.dark .fa-shell__profile:hover { background: rgba(255, 255, 255, 0.08); }
html.dark .fa-shell__mark img { filter: invert(1); } /* vanilla <img> marks → white */
html.dark .fa-shell__menu { background: #0c0d10; border-color: rgba(255, 255, 255, 0.14); box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55); }
html.dark .fa-shell__item { color: #f3f1ec; }
html.dark .fa-shell__item:hover { background: rgba(255, 255, 255, 0.08); }
html.dark .fa-shell__item.is-current { color: rgba(243, 241, 236, 0.45); }
html.dark .fa-shell__draft { background: rgba(255, 255, 255, 0.14); }

/* ── Touch hit areas ────────────────────────────────────────────────────────
   The bar's controls are drawn small on purpose (the mark is 20px, the round
   controls 38px, the crumb 30px tall) and that is fine for a cursor. A finger
   needs 44px, so the target is grown with a centred pseudo-element instead of
   with padding: nothing in the bar moves, the drawing is untouched, and only
   the region that answers a tap changes. Guarded on `pointer: coarse` so a
   desktop keeps its exact hover boxes. */
@media (pointer: coarse) {
  .fa-shell__home,
  .fa-shell__current,
  .fa-shell__toggle,
  .fa-shell__profile,
  .fa-share__btn,
  .fa-shell__burger { position: relative; }
  .fa-shell__home::after,
  .fa-shell__current::after,
  .fa-shell__toggle::after,
  .fa-shell__profile::after,
  .fa-share__btn::after,
  .fa-shell__burger::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 100%;
    height: 100%;
    min-width: 44px;
    min-height: 44px;
  }
}

@media (max-width: 680px) {
  /* the bar is tall for a phone; trim the row and let consumers reclaim it */
  :root { --fa-nav-h: 54px; }
  .fa-shell { padding: 0 14px; gap: 8px; }
  .fa-shell__navlink { padding: 8px 7px; letter-spacing: 0.08em; }
  .fa-shell__word { font-size: 17px; }
  /* tight rows: drop the wordmark on project pages so the project name has room */
  .fa-shell:has(.fa-shell__crumb) .fa-shell__word { display: none; }
  .fa-shell__current { max-width: 56vw; }
  .fa-shell__burger { flex: 0 0 auto; }
}

/* ── shared project-page footer, mirrors the Atlas home footer; themes with the light/dark toggle ── */
.fa-foot {
  /* above the full-viewport fixed canvases some project pages use, so the
     footer reads as a solid band instead of text floating over the art */
  position: relative;
  z-index: 5;
  padding: clamp(58px, 9vw, 130px) 0 40px;
  /* Brand blue, in BOTH themes. This is the one band on the site that does not
     follow the light/dark toggle: it is the sign-off, and it reads as a
     deliberate block of brand rather than as more page. The literal value is
     --fa-accent-deep from the core tokens, written out because this stylesheet
     is self-contained by design — the vanilla sub-apps load it without the
     token sheet and must render the same footer. */
  /* a shade of the one brand blue: same hue and chroma as
     oklch(0.565 0.13 245), darker so 12.5px copy still reads on it (7.22).
     Mirrors --fa-accent-band in futures-atlas-core/src/tokens.css. */
  background: oklch(0.42 0.13 245);
  color: #f3f1ec;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  transition: background-color 0.5s, color 0.5s;
}
/* full-bleed, on the same 28px gutter grid as the master nav + every page's
   content (Container / studio layout) so the footer edges line up site-wide */
.fa-foot__inner { width: 100%; padding: 0 28px; }
.fa-foot__lede { margin: 0; max-width: 18ch; font-size: clamp(34px, 6vw, 96px); font-weight: 800; line-height: 0.96; letter-spacing: -0.028em; }
.fa-foot__blurb { margin: 22px 0 0; max-width: 620px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 14px; line-height: 1.7; color: rgba(243, 241, 236, 0.78); }
.fa-foot__row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px; margin-top: clamp(44px, 8vw, 90px); border-top: 1px solid rgba(243, 241, 236, 0.24); padding-top: 28px; }
.fa-foot__brand { font-size: 15px; font-weight: 800; letter-spacing: 0.02em; }
.fa-foot__nav { display: flex; flex-wrap: wrap; gap: 22px; }
/*
  A column heading and a link in this footer used to be the same thing to look
  at: both mono, both 11px, both uppercase, opacity 0.74 against 0.72, and
  0.04em of letter-spacing between them. Nothing said which was a label and
  which you could click.

  The heading keeps the label treatment (small, uppercase, tracked out) because
  that is what a label is across the whole site. The links stop imitating it:
  sentence case, larger, brighter, tracked normally, so they read as the
  content of the column rather than more headings. That also matches
  `.fa-foot__a` ("All projects"), which was already the only thing down here
  that looked like a link.

  Underline on hover and on focus, never at rest: seven of these stacked with
  permanent rules turns the column into a fence.
*/
.fa-foot__link {
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 13px;
  letter-spacing: 0.01em;
  line-height: 1.5;
  color: rgba(243, 241, 236, 0.9);
  text-decoration: none;
}
.fa-foot__link:hover,
.fa-foot__link:focus-visible {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 4px;
}
/*
  THE FLOOR ON THIS BAND IS 0.72, and every quiet tier below runs into it.

  The band is fixed (oklch 0.42 0.13 245, #00508e) and the type on it is one
  colour, #f3f1ec, so opacity is the only lever there is — and at full strength
  that pair is 7.33. Solve backwards and 4.5 lands at alpha 0.711. Everything
  under that was failing: the column headings at 0.55 (3.34), the post dates at
  0.5 (3.02), the meta lines at 0.6 (3.67) and 0.45 (2.69).

  So the quiet end is lifted to 0.72 / 0.74 / 0.76 (4.59 / 4.75 / 4.91), which
  keeps the tiers in their old order but compresses them hard: the footer's
  range used to run 0.45→1.0 and now runs 0.72→1.0. That is a real loss of
  quietness and it is not recoverable by colour, because on a mid-blue ground
  4.5 IS a light value. Anything that wants to read softer than this has to get
  it from size or weight, not from alpha.
*/
.fa-foot__tag { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 10px; text-transform: uppercase; letter-spacing: 0.14em; color: rgba(243, 241, 236, 0.74); /* was 0.55 → 3.34; now 4.75 */ }
.fa-foot__grid { display: grid; grid-template-columns: 1.3fr 0.7fr 1.3fr 1fr; gap: 40px clamp(28px, 4vw, 64px); }
@media (max-width: 900px) { .fa-foot__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .fa-foot__grid { grid-template-columns: 1fr; } }
.fa-foot__h { margin: 0 0 16px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; text-transform: uppercase; letter-spacing: 0.18em; opacity: 0.74; /* was 0.55 → 3.34; now 4.75. Do not dim this to separate it from the links: the contrast here was deliberately raised to clear 4.5:1. The links moved instead. */ }
.fa-foot__body { margin: 0 0 12px; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; line-height: 1.75; opacity: 0.78; }
.fa-foot__body b { font-weight: 600; opacity: 1; }
.fa-foot__list { display: flex; flex-direction: column; gap: 10px; }
.fa-foot__a { color: inherit; text-decoration: underline; text-underline-offset: 4px; }
/* "Cookie settings": a button (it opens the consent banner), dressed as a footer link */
.fa-foot__cookies { background: none; border: 0; padding: 0; margin: 0; cursor: pointer; font: inherit; font-size: 13px; color: rgba(243, 241, 236, 0.74); }
.fa-foot__cookies:hover { color: #f3f1ec; }
.fa-foot__a:hover { opacity: 1; }
.fa-foot__brand { display: block; margin-bottom: 14px; }
.fa-foot__home { display: inline-flex; align-items: center; gap: 14px; margin-bottom: 18px; color: inherit; text-decoration: none; }
/* the brand lockup at 1.5x: mark 20 -> 30px (sized in the markup), word 19 -> 28.5px */
.fa-foot__mark { display: inline-flex; }
.fa-foot__word { font-size: 28.5px; font-weight: 500; letter-spacing: -0.015em; }
/* The ground is blue in both themes now, so the dark mark is always inverted. */
.fa-foot .fa-foot__mark img { filter: invert(1); }

/* No dark variant: the footer band is ONE blue in both themes, the same as
   --brand in the core tokens and the same as the host footer. A toggle-following
   footer was the reason the two did not match. */
@media (max-width: 680px) {
  .fa-foot { padding-top: clamp(40px, 12vw, 70px); }
  .fa-foot__inner { padding: 0 16px; } /* match the page gutter at this breakpoint */
}

/* Touch targets in the footer.

   The footer's link columns are set at 12.5px with a 10px gap: a 19px-tall box
   with a 10px dead band under it. Fine for a cursor, tight for a thumb. The
   padding below grows each link's hit area into that dead band and the equal
   negative margin puts the box back where it was, so on a phone the whole
   column is tappable and nothing moves by a pixel on any screen. Adjacent
   links end up exactly contiguous rather than overlapping, so a tap still
   lands on the link it looks like it landed on. */
@media (pointer: coarse) {
  .fa-foot__list .fa-foot__a,
  .fa-foot__list > a { display: block; padding: 5px 0; margin: -5px 0; }
  .fa-foot__nav .fa-foot__link { display: inline-block; padding: 10px 0; margin: -10px 0; }
}

/* ── Project sub-nav ────────────────────────────────────────────────────────
   A slim row of the CURRENT project's own tabs, sitting directly below the one
   master .fa-shell. No brand / switcher / toggle, the master owns those. Used
   by the zones that have internal pages (Villages, Underground Intelligence,
   The Odds) so every zone reads as: one master bar + a slim tab row. ── */
.fa-subnav {
  position: sticky;
  top: var(--fa-nav-h);
  z-index: 55;
  display: flex;
  align-items: center;
  gap: 2px;
  height: 46px;
  padding: 0 28px;
  background: rgba(243, 241, 236, 0.92);
  -webkit-backdrop-filter: blur(18px);
  backdrop-filter: blur(18px);
  color: #141518;
  border-bottom: 1px solid rgba(20, 21, 24, 0.1);
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
  transition: background-color 0.5s, color 0.5s, border-color 0.5s;
}
.fa-subnav__link {
  padding: 7px 12px;
  border-radius: 5px;
  color: currentColor;
  opacity: 0.6;
  text-decoration: none;
  font-size: 12px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  white-space: nowrap;
  background: transparent;
  border: 0;
  cursor: pointer;
}
.fa-subnav__link:hover { opacity: 0.9; }
.fa-subnav__link.is-active { opacity: 1; color: var(--fa-nav-accent); }
html.dark .fa-subnav { background: rgba(12, 13, 16, 0.9); color: #f3f1ec; border-bottom-color: rgba(255, 255, 255, 0.1); }
/* On pages with a sub-nav, keep the master bar put (it never travels) so the
   slim tab row never floats with a gap above it. atlas-nav.js skips the scroll
   handling entirely on these pages, and this backs it up. */
body:has(.fa-subnav) .fa-shell { transform: none !important; }

/* ── Hamburger + mobile sheet ───────────────────────────────────────────────
   On tablet/mobile the primary links collapse into a hamburger that opens a
   full sheet: primary links, the current project's own pages (under its title),
   and the theme toggle, fading/staggering in. ── */
.fa-shell__burger {
  display: none;
  position: relative;
  width: 40px;
  height: 40px;
  margin-left: 4px;
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.fa-shell__burger span {
  position: absolute;
  left: 9px;
  right: 9px;
  height: 1.6px;
  border-radius: 2px;
  background: currentColor;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.2s ease;
}
.fa-shell__burger span:nth-child(1) { top: 14px; }
.fa-shell__burger span:nth-child(2) { top: 19px; }
.fa-shell__burger span:nth-child(3) { top: 24px; }
.fa-shell__burger.is-open span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.fa-shell__burger.is-open span:nth-child(2) { opacity: 0; }
.fa-shell__burger.is-open span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

.fa-sheet {
  position: fixed;
  /* Full bleed, not inset below the bar. Starting at --fa-nav-h left a strip of
     the page's own frosted bar across the top of an otherwise full-screen
     panel, so the menu read as a box that had failed to reach the top rather
     than as a screen. The bar stays ON TOP of the sheet (see .fa-menu-open
     below) and loses its plate, so its controls still work and the sheet's
     ground runs behind them. */
  inset: 0;
  /* svh, not the default 100% of inset:0. On phones the layout viewport is the
     LARGE viewport, the one measured with the browser chrome retracted, so a
     full-bleed fixed panel ends up taller than what you can actually see and
     the difference becomes dead scrollable space inside the menu. */
  height: 100svh;
  max-height: 100svh;
  z-index: 9990; /* above zone content incl. the Odds stage (z 9000) */
  display: flex;
  /* nothing chains to the page behind, on the rare page whose menu does scroll */
  overscroll-behavior: contain;
  /* Bone with a blue cast pulled towards --brand, rather than the page's plain
     ground: the menu is the one full-screen surface the site owns, and it
     should feel like the brand rather than like a page that lost its content. */
  /* Opaque, not a 0.975 veil over a blur. backdrop-filter is the first thing
     to be dropped (older WebKit, low-power mode, a GPU-blocked context), and
     when it goes the page underneath reads straight through the menu. In light
     especially, body text behind the links made the whole panel look broken.
     The blur stays as an enhancement on the frosted edge, it is no longer what
     makes the panel legible. */
  background: #eef1f8;
  color: #141518;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.4s ease, visibility 0.4s ease;
  overflow-y: auto;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}
/*
  A very quiet interference figure behind the menu: two ring systems from two
  sources, crossing. Static, not animated — this thing is on screen for three
  seconds at a time and a moving backdrop behind navigation is just noise. It
  is two repeating-radial-gradients rather than a canvas or an image because
  this stylesheet is loaded by the vanilla project bundles too and has to stay
  self-contained; drawn in currentColor, so it inverts with the theme for free.
  The mask keeps it off the left edge where the link column sits.
*/
.fa-sheet::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Two sources CLOSE together, not one in each corner. Far apart and at the
     same pitch, two ring systems cross at a constant angle and tile the screen
     with a diamond lattice — technically interference, visually a quilt. Placed
     a couple of wavelengths apart they give the hyperbolic fringes that fan out
     from a two-slit pattern, which is the figure this site is actually about.
     The pitch is in vmin so the fan is the same size relative to any viewport;
     fixed in px it was clean rings on a phone and a halftone screen on a
     desktop. */
  background-image:
    repeating-radial-gradient(circle at 34% 6%, transparent 0, currentColor 4.5vmin, transparent 9vmin),
    repeating-radial-gradient(circle at 58% 6%, transparent 0, currentColor 4.5vmin, transparent 9vmin);
  opacity: 0.055;
  -webkit-mask-image: radial-gradient(120% 88% at 62% 4%, #000 0%, rgba(0,0,0,0.5) 44%, transparent 82%);
  mask-image: radial-gradient(120% 88% at 62% 4%, #000 0%, rgba(0,0,0,0.5) 44%, transparent 82%);
}
html.dark .fa-sheet::before { opacity: 0.075; }
.fa-sheet__inner { position: relative; z-index: 1; }

.fa-sheet.is-open { opacity: 1; visibility: visible; pointer-events: auto; }
html.dark .fa-sheet { background: #0b101c; color: #f3f1ec; }
/* Lock the page while the sheet is open. overflow alone is not enough on
   iOS, where the scroll chains through to the document anyway. */
html.fa-menu-open, html.fa-menu-open body { overflow: hidden; overscroll-behavior: none; touch-action: none; }

/* While the menu is open the bar keeps its position and its controls but gives
   up its surface, so there is no seam between the two. It has to outrank the
   sheet's z-index or the burger — the only way to close the thing — would be
   underneath it. */
html.fa-menu-open .fa-shell {
  z-index: 9991;
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-bottom-color: transparent;
}
html.fa-menu-open .fa-shell.is-clear { color: #141518; }
html.dark.fa-menu-open .fa-shell.is-clear { color: #f3f1ec; }

.fa-sheet__inner {
  width: 100%;
  max-width: 620px;
  /* Centred in the panel rather than stacked at the top. Four links and two
     buttons filled about half the screen and left the rest empty, which read
     as a menu that had lost some of itself. `auto` margins centre it while
     still letting it scroll from the top if a project page adds enough
     sublinks to overflow. */
  margin: auto;
  display: flex;
  flex-direction: column;
  /* the sheet runs under the bar, so the links clear it */
  padding: calc(var(--fa-nav-h) + clamp(16px, 4vw, 28px)) clamp(22px, 7vw, 48px) clamp(24px, 6vw, 40px);
}
.fa-sheet__sec {
  display: flex;
  flex-direction: column;
  padding: 14px 0;
}
.fa-sheet__sec + .fa-sheet__sec { border-top: 1px solid color-mix(in srgb, currentColor 14%, transparent); }
.fa-sheet__link {
  padding: 9px 0;
  color: inherit;
  text-decoration: none;
  font-size: clamp(26px, 7vw, 34px);
  font-weight: 600;
  letter-spacing: -0.02em;
  opacity: 0.62;
}
.fa-sheet__link.is-active { opacity: 1; }
.fa-sheet__title {
  margin: 8px 0 6px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  opacity: 0.5;
}
.fa-sheet__sublink {
  padding: 8px 0;
  color: inherit;
  text-decoration: none;
  font-size: clamp(18px, 5vw, 22px);
  font-weight: 500;
  opacity: 0.6;
}
.fa-sheet__sublink.is-active { opacity: 1; color: var(--fa-nav-accent); }
.fa-sheet__theme {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  align-self: flex-start;
  margin-top: 28px;
  padding: 11px 18px 11px 14px;
  border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font: inherit;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  min-height: 44px; /* a finger's worth, which the pill's padding alone is not */
}
.fa-sheet__themeicon { display: inline-flex; }
.fa-sheet__social { display: flex; gap: 22px; margin: 22px 0 0; font-size: 15px; font-weight: 500; }
.fa-sheet__social a { color: inherit; text-decoration: none; padding: 10px 0; }

/* stagger: each item fades/rises in, delayed by its --i (reverses fast on close) */
.fa-sheet__link,
.fa-sheet__title,
.fa-sheet__sublink,
.fa-sheet__theme,
.fa-sheet__social {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.45s cubic-bezier(0.4, 0, 0.2, 1), transform 0.45s cubic-bezier(0.4, 0, 0.2, 1);
}
.fa-sheet.is-open .fa-sheet__link,
.fa-sheet.is-open .fa-sheet__title,
.fa-sheet.is-open .fa-sheet__sublink,
.fa-sheet.is-open .fa-sheet__theme,
.fa-sheet.is-open .fa-sheet__social {
  opacity: 0.62;
  transform: none;
  transition-delay: calc(var(--i) * 55ms + 90ms);
}
.fa-sheet.is-open .fa-sheet__link.is-active,
.fa-sheet.is-open .fa-sheet__sublink.is-active,
.fa-sheet.is-open .fa-sheet__theme { opacity: 1; }
.fa-sheet.is-open .fa-sheet__title { opacity: 0.5; }
@media (prefers-reduced-motion: reduce) {
  .fa-sheet, .fa-sheet__link, .fa-sheet__title, .fa-sheet__sublink, .fa-sheet__theme, .fa-sheet__social, .fa-shell__burger span { transition: none; }
}

/* A phone held sideways is about 375px tall, and the sheet at full size does
   not fit in that: the theme button and the share row ended up below the fold,
   so the menu became a thing you had to scroll — which nobody expects a menu
   to do. A short viewport has width to spare, so the links go into two columns
   rather than just getting smaller: the panel fits without scrolling AND the
   rows stay tappable. */
@media (max-width: 900px) and (max-height: 620px) {
  .fa-sheet__inner {
    max-width: 760px;
    padding: calc(var(--fa-nav-h) + 6px) clamp(22px, 6vw, 56px) 12px;
  }
  .fa-sheet__sec {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: clamp(20px, 5vw, 44px);
    padding: 6px 0;
  }
  .fa-sheet__title { grid-column: 1 / -1; margin: 2px 0 0; }
  .fa-sheet__link { font-size: clamp(18px, 4.6vh, 26px); padding: 7px 0; }
  .fa-sheet__sublink { font-size: clamp(14px, 3.2vh, 18px); padding: 6px 0; }
  .fa-sheet__theme { margin-top: 10px; padding: 8px 16px; min-height: 0; }
}

/* the swap: links → hamburger at tablet width and below */
@media (max-width: 900px) {
  /* Only the link list collapses into the hamburger. The three round controls
     stay in the bar — they are the reachable ones on a phone, so burying share
     and sign-in behind a sheet is the wrong trade. Slightly smaller and closer
     together so they fit beside the burger. */
  .fa-shell__nav { display: none; }
  /* Theme and sign-in move into the sheet at this width; share stays in the
     bar, immediately left of the burger, because it acts on the page you are
     looking at and a sheet is the wrong place for that. */
  .fa-shell__toggle,
  .fa-shell__profile { display: none; }
  .fa-share { margin-left: 4px; margin-right: 2px; }
  .fa-shell__burger { display: inline-flex; align-items: center; justify-content: center; }
  .fa-subnav { display: none; } /* Villages tabs move into the sheet */
}

/* ── global Share tool, fixed bottom-left on every page, expands on click ── */
.fa-share { position: relative; display: inline-flex; align-items: center; margin-left: 8px; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
.fa-share__btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border: 1px solid rgba(20, 21, 24, 0.28); border-radius: 50%;
  background: transparent; color: inherit; cursor: pointer;
  transition: background 0.15s;
}

.fa-share__btn:hover { background: rgba(20, 21, 24, 0.06); }
.fa-share.is-open .fa-share__btn { background: rgba(20, 21, 24, 0.1); }
html.dark .fa-share__btn { border-color: rgba(255, 255, 255, 0.3); }
html.dark .fa-share__btn:hover,
html.dark .fa-share.is-open .fa-share__btn { background: rgba(255, 255, 255, 0.08); }
.fa-share__panel {
  position: absolute; top: calc(100% + 10px); right: 0;
  display: flex; flex-direction: column; gap: 2px; min-width: 220px; padding: 6px;
  border-radius: 14px; background: rgba(243, 241, 236, 0.96); border: 1px solid rgba(20, 21, 24, 0.12);
  box-shadow: 0 14px 44px rgba(0, 0, 0, 0.22);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  opacity: 0; visibility: hidden; transform: translateY(-8px) scale(0.97); transform-origin: top right;
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s;
}
.fa-share.is-open .fa-share__panel { opacity: 1; visibility: visible; transform: none; pointer-events: auto; }
.fa-share__opt {
  display: block; text-align: left; width: 100%; padding: 10px 13px; border: 0; background: transparent;
  border-radius: 9px; color: #141518; font-size: 13px; font-weight: 500; cursor: pointer; text-decoration: none; white-space: nowrap;
}
.fa-share__opt:hover { background: rgba(20, 21, 24, 0.07); }
.fa-share__opt--accent { color: var(--fa-nav-accent); font-weight: 600; }
.fa-share__sep { height: 1px; margin: 5px 8px; background: rgba(20, 21, 24, 0.1); }
html.dark .fa-share__sep { background: rgba(255, 255, 255, 0.1); }
.fa-share__ig { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; padding: 6px 13px 8px; }
.fa-share__iglbl { color: var(--fa-nav-accent); font-weight: 600; font-size: 13px; margin-right: 2px; }
.fa-share__chip { font-size: 12px; padding: 4px 10px; border-radius: 999px; border: 1px solid rgba(20, 21, 24, 0.18); color: #141518; text-decoration: none; }
.fa-share__chip:hover { border-color: var(--fa-nav-accent); color: var(--fa-nav-accent); }
html.dark .fa-share__iglbl { color: var(--fa-nav-accent); }
html.dark .fa-share__chip { border-color: rgba(255, 255, 255, 0.18); color: #f3f1ec; }
/* the var already flips; these two only restate what :root/html.dark set */
html.dark .fa-share__chip:hover { border-color: var(--fa-nav-accent); color: var(--fa-nav-accent); }
html.dark .fa-share__btn { background: rgba(12, 13, 16, 0.92); color: #f3f1ec; border-color: rgba(255, 255, 255, 0.16); }
html.dark .fa-share.is-open .fa-share__btn { background: #f3f1ec; color: #0c0d10; }
html.dark .fa-share__panel { background: rgba(12, 13, 16, 0.96); border-color: rgba(255, 255, 255, 0.12); }
html.dark .fa-share__opt { color: #f3f1ec; }
html.dark .fa-share__opt:hover { background: rgba(255, 255, 255, 0.08); }

/* ── the share tool as a menu row ──────────────────────────────────────────
   Same node as the bar's circular button, restyled into a pill that matches
   the theme and account rows beside it. The panel flips its anchor because in
   the sheet the trigger is left-aligned, not tucked into the right edge. */
.fa-share__lbl { display: none; }
.fa-share--sheet { display: block; margin: 0; }
.fa-share--sheet .fa-share__btn {
  width: auto; height: auto;
  gap: 12px;
  margin-top: 14px;
  padding: 11px 18px 11px 20px;
  border-radius: 999px;
  border-color: color-mix(in srgb, currentColor 28%, transparent);
  font: inherit;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
}
.fa-share--sheet .fa-share__lbl { display: inline; }
.fa-share--sheet .fa-share__panel {
  left: 0; right: auto;
  transform-origin: top left;
  min-width: min(280px, calc(100vw - 56px));
  /*
    Closed, this panel is only visibility:hidden, and an absolutely positioned
    box still counts towards its scroll container's scrollHeight. Inside the
    mobile menu that added ~100px of scrollable nothing below the last button,
    which is the phantom scroll space in the open menu. Taking it out of layout
    costs its 0.18s fade in the menu (the bar's own copy keeps it) and buys a
    sheet that does not scroll at all.
  */
  display: none;
}
.fa-share--sheet.is-open .fa-share__panel { display: flex; }
html.dark .fa-share--sheet .fa-share__btn { background: transparent; color: inherit; }
html.dark .fa-share--sheet.is-open .fa-share__btn { background: rgba(255, 255, 255, 0.08); color: inherit; }

@media (max-width: 900px) {
  .fa-share__btn { width: 34px; height: 34px; }
  /* the bar's own circle goes with it — the tool is in the menu at this width */
  .fa-share--sheet .fa-share__btn { width: auto; height: auto; }
}

/* Transparent over a full-bleed hero — see atlas-nav.js. The bar keeps its
   text colour (heroes here are dark stages) and loses only its plate, so the
   visualisation can reach the top edge. */
html .fa-shell.is-clear,
.fa-shell.is-clear {
  background: transparent;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  border-bottom-color: transparent;
  color: #f3f1ec;
}
.fa-shell.is-clear .fa-shell__toggle,
.fa-shell.is-clear .fa-shell__profile,
.fa-shell.is-clear .fa-share__btn { border-color: rgba(255, 255, 255, 0.34); }
/* The mark is a black <img>, inverted to white only under html.dark. Over a
   full-bleed dark hero the bar is clear and its type is forced light in BOTH
   themes — but the mark was not, so in LIGHT mode the homepage lost its logo
   entirely: black artwork on a black stage. The bar is now identical in both
   themes on a hero page, which is the point of the clear variant. */
.fa-shell.is-clear .fa-shell__mark img { filter: invert(1); }

/* Left over from when the share tool was a fixed floating button: `top` and
   `right` on what is now a RELATIVE element offset it, which pushed it exactly
   one bar-height down and out of the row. Removed above; these keep the group
   on one line. */
.fa-shell__right { flex-wrap: nowrap; align-items: center; }
.fa-share, .fa-shell__burger { flex: none; }

/* the shared footer's extra pieces — the host used to style these with Tailwind
   utilities, but there is one footer now and one stylesheet for it, loaded by
   host and sub-app alike */
.fa-foot__meta { margin: 16px 0 0; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 11px; letter-spacing: 0.02em; opacity: 0.76; /* was 0.6 → 3.67; now 4.91. .fa-foot__a inherits this one. */ }
.fa-foot__split { display: grid; gap: 32px clamp(28px, 4vw, 64px); margin-top: clamp(32px, 4.5vw, 56px); border-top: 1px solid rgba(243, 241, 236, 0.18); padding-top: clamp(28px, 4vw, 44px); }
@media (min-width: 640px) { .fa-foot__split { grid-template-columns: 1fr 1fr; } }
/* The footer's column count varies with the environment: brand, sections and
   projects always, plus the feed column and the internal column where those
   exist (see scripts/gen-footer.mjs). --4 is the default four-column rule. */
/* The internal column's sign-out is a form button, so it has to be un-styled
   back down to the plain link the rest of that column is set in. */
.fa-foot__signout { margin: 10px 0 0; }
.fa-foot__signout button { background: none; border: 0; padding: 0; font: inherit; cursor: pointer; }

.fa-foot__grid--3 { grid-template-columns: 1.4fr 0.8fr 1.4fr; }
@media (max-width: 900px) { .fa-foot__grid--3 { grid-template-columns: 1fr 1fr; } }
@media (max-width: 560px) { .fa-foot__grid--3 { grid-template-columns: 1fr; } }
@media (min-width: 1180px) { .fa-foot__grid--5 { grid-template-columns: 1.15fr 0.65fr 1.1fr 1fr 0.85fr; } }
/* The internal column is not part of the site — it is the working drawer, and
   only exists on preview and local builds. Boxing it says so: without a border
   it reads as a fifth public section that happens to be full of dead ends on
   production. Dashed rather than solid, because it is scaffolding. */
.fa-foot__internal {
  border: 1px dashed rgba(243, 241, 236, 0.28);
  border-radius: 6px;
  padding: 16px 18px 18px;
  margin-top: -4px;
  align-self: start;
}
.fa-foot__internal .fa-foot__h { opacity: 0.75; }
.fa-foot__internal .fa-foot__meta { opacity: 0.72; } /* was 0.45 → 2.69; now 4.59 */
.fa-foot__post { display: block; text-decoration: none; color: inherit; }
.fa-foot__date { display: block; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.14em; opacity: 0.72; /* was 0.5 → 3.02; now 4.59 */ }
.fa-foot__ptitle { display: block; margin-top: 2px; font-size: 13px; font-weight: 500; line-height: 1.4; opacity: 0.85; }
.fa-foot__post:hover .fa-foot__ptitle { opacity: 1; }
.fa-foot__mark img { display: block; width: 22px; height: auto; }

/* ---------------------------------------------------------------------------
   Arrival, shared by every bundle.

   The host app fades each page up with `.fa-page-in` (src/app/globals.css).
   The project bundles are separate apps that never pass through its layout, so
   until now the Atlas eased in and its projects snapped in, which read as two
   different sites. This is the same 0.42s and the same curve, deliberately, so
   crossing from a listing into a project feels like one place.

   Three choices worth keeping:

   OPACITY ONLY, and starting at 0.35 rather than 0. Fading the whole way up
   from nothing reads as slow even at this duration, and a transform here would
   make body the containing block for the fixed shell bar, which would then
   jump on arrival and snap back. Opacity creates a stacking context but not a
   containing block, so the bar stays put.

   ON BODY, not a wrapper. Twenty-four bundles built by different tools share
   this stylesheet and agree on nothing below <body>.

   PURE CSS, with `both`. It needs no script and cannot leave a page stuck
   invisible if one fails to load, which is the usual way an entrance animation
   turns into a blank screen.
--------------------------------------------------------------------------- */
@keyframes fa-arrive {
  from { opacity: 0.35; }
  to   { opacity: 1; }
}

/*
  The bar and the footer are BUILT BY SCRIPT and inserted once it lands, so
  they cannot fade with the body: by then the body's own animation has long
  finished and they simply appear, which is the "the navbar jumps in" of it.
  They get their own short entrance from the moment they are inserted.

  Deliberately shorter than the body's 0.42s and with no movement in the bar:
  it is fixed to the top of the window, and sliding a fixed element down over
  content that has already painted draws more attention to the lateness than
  the pop did. The footer is below the fold on arrival, so it can afford the
  small lift.
*/
@keyframes fa-foot-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/*
  The bar deliberately does NOT animate. It is injected by script, so a fade
  reads as it arriving late rather than as an entrance, and it made one project
  behave differently from the rest. The fix for a late bar is to stop it being
  late (reserve its height in CSS, load the page's blocking scripts in the
  head), not to decorate the lateness.
*/
.fa-foot  { animation: fa-foot-in 0.5s cubic-bezier(0.2, 0.6, 0.2, 1) both; }

@media (prefers-reduced-motion: reduce) {
  .fa-foot { animation: none; }
}

body {
  animation: fa-arrive 0.42s cubic-bezier(0.2, 0.6, 0.2, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  body { animation: none; }
}
