/* ===== Layout ===== */
.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: var(--sp-6);
  position: relative;
  z-index: var(--z-content);
}

main { position: relative; z-index: var(--z-content); }

/* --- dot grid (Figma/Miro-style) — bottom-most background layer ---
   Pure CSS, no cursor reaction. Absolute, not fixed: it spans the whole
   document and scrolls with the page, so the dots travel past you instead of
   sitting still like a screen overlay. `inset: 0` inside a `position: relative`
   body stretches to the body's full height even though that height is auto —
   a percentage height could not. */
/* FIXED, not absolute: the grid stays put in the viewport and the content
   scrolls over it. It used to be absolute + inset:0 inside a relative body so
   it spanned the whole document and travelled with the page — which meant the
   dots drifted upward as you read. Fixed positions it against the viewport
   instead, so `inset: 0` is now simply the screen and the body's auto height
   stops mattering. */
.dot-grid {
  position: fixed;
  inset: 0;
  z-index: var(--z-dot-grid);
  pointer-events: none;
  background-image: radial-gradient(circle, var(--dot-grid) 1px, transparent 1.5px);
  background-size: 24px 24px;
  transition: background-image var(--theme-dur) var(--ease-out);
}

/* --- nav dock: pill nav + sound toggle + theme toggle, side by side --- */
.nav-dock {
  position: fixed;
  bottom: var(--sp-3);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--z-nav);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  max-width: calc(100vw - 2 * var(--sp-3));
}
/* On a narrow phone the gutter is decoration: the dock needs its full 300px */
@media (max-width: 768px) {
  .nav-dock { max-width: calc(100vw - 2 * var(--space-2)); }
}

/* pill nav — ported from Notion "💊 Pill nav" (tested); colours themed.
   The capsule itself is .pill-bar (css/components/pill-bar.css), shared with
   the Hero controls; only the sliding-highlight machinery lives here. */
.nav {
  position: relative;
  isolation: isolate;
  /* How far the active highlight reaches past the link's own box, per side.
     Lives here rather than on .nav__pill so the capsule can budget for it
     below — both need the same number. */
  --nav-pill-grow: var(--space-1);
}
/* Two classes deep on purpose: .nav alone ties with .pill-bar on specificity,
   and pill-bar.css is linked after this file, so its box-shadow — and its
   `padding` shorthand, which resets padding-inline — would win. */
.nav-dock .nav {
  /* .pill-bar's uniform padding is the gap the highlight should keep from the
     capsule's inner edge, but the highlight starts --nav-pill-grow before the
     first link and ends that much after the last, which ate the whole gap at
     the two ends. Adding the grow back to the inline padding keeps the side
     gap equal to the top/bottom one, whatever either value is set to. */
  padding-inline: calc(var(--pill-bar-pad) + var(--nav-pill-grow));
  /* Heavier than the .pill-bar default: the dock is the one element that
     floats over everything and now that the cards beneath it are veiled
     (--c-surface-veil), it needs the shadow to sell that separation, not just
     the opacity difference. Deliberately its own value rather than
     --shadow-md — that step's 44px blur bands visibly on mobile Safari, which
     is why --shadow-sm is trimmed there in the first place. A tighter, darker
     spread reads as heavier without the gradient stepping. */
  box-shadow: 0 2px 8px rgba(var(--shadow-tint), .10),
              0 10px 24px rgba(var(--shadow-tint), .18);
}
/* highlight pill: a single element for the whole menu */
.nav__pill {
  /* Widened by --nav-pill-grow either side (declared on .nav) so the highlight
     has air around the label instead of hugging it. JS keeps writing the raw
     measured --x/--w (nav-pill.js) and the padding is applied here — the
     module stays a pure measurer, this stays a purely visual choice. */
  position: absolute;
  z-index: 0;
  /* must track .pill-bar's padding, or the highlight sits proud of the links */
  top: var(--pill-bar-pad);
  bottom: var(--pill-bar-pad);
  left: 0;
  width: calc(var(--w, 0px) + 2 * var(--nav-pill-grow));
  transform: translateX(calc(var(--x, 0px) - var(--nav-pill-grow)));
  background: var(--c-bg);
  border-radius: var(--radius-pill);
  /* The .38s is hand-tuned against the overshoot curve — the capsule has to
     arrive, overshoot and settle within one glance, and the pair was set by
     eye on this specific travel distance. It stays a literal rather than
     being snapped to a duration step; only the curve is tokenised.
     See MOTION_AUDIT.md. */
  transition: transform .38s var(--ease-overshoot),
              width .38s var(--ease-overshoot),
              background var(--theme-dur) var(--ease-out);
  pointer-events: none;
}
/* first frame without animation, so the pill doesn't fly in from the corner */
.nav__pill.is-instant { transition: none; }

/* The capsule holds the section tabs and nothing else — the name that used to
   sit to the left of them is now the standalone .logo in the Hero's top-left. */

.nav__link {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  text-decoration: none;
  color: var(--c-ink-soft);
  white-space: nowrap;
  /* was .25s with no curve at all — the odd one out among the page's colour
     fades, which all run at --dur-fast through the house curve */
  transition: color var(--motion-hover);
  -webkit-tap-highlight-color: transparent;
}
.nav__link:hover { color: var(--c-ink); }
.nav__link.is-active { color: var(--c-ink); font-weight: 500; }
/* No focus ring on the tabs: the sliding highlight already follows keyboard
   focus (nav-pill.js moves it on 'focus', not just on hover), so the outline
   was a second indicator drawn on top of a working one. Every other focusable
   control on the page keeps its ring — they have nothing equivalent. */

/* Hidden on the base, the Nav Dock block below turns them on. */
.nav__icon,
.nav__dot { display: none; }

/* ===== Nav Dock — macOS-style icon dock =====
   Frame 1923:3743. The same dock at every width: the capsule becomes the Dock,
   the tabs become tiles, the sliding pill is dropped, and the active tile
   carries the glass fill + dot. All sizes are --dock-* tokens. */
/* the capsule → the Dock (glass, spec §2) */
.nav-dock .nav {
  gap: var(--dock-gap);
  padding: var(--dock-pad-top) var(--dock-pad) var(--dock-bottom-space);
  border-radius: var(--dock-radius);
  background: var(--dock-bg);
  -webkit-backdrop-filter: var(--dock-glass);
          backdrop-filter: var(--dock-glass);
  /* also replaces the floating shadow the base .nav-dock .nav sets for the
     mobile pill */
  box-shadow: var(--dock-shadow);
}
/* the rim: a gradient ring, same technique as the active tile below */
.nav-dock .nav::after {
  content: "";
  position: absolute;
  inset: 0;
  padding: var(--dock-rim-width);
  border-radius: inherit;
  background: var(--dock-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.nav__pill { display: none; }     /* the sliding highlight is gone on desktop */

/* a tab is a 48×48 tile — grid so the icon is centred on BOTH axes (the base
   .nav__link is a flex row, which only centres vertically). */
.nav__link {
  position: relative;
  flex: 0 0 auto;
  width: var(--dock-tile);
  height: var(--dock-tile);
  padding: 0;
  gap: 0;
  display: grid;
  place-items: center;
  border-radius: var(--dock-tile-radius);
  color: #fff;
  font-weight: 400;
  /* IDLE = nothing. No fill, no border, no highlight — just the icon on the
     dock glass. Only the active tile gets a surface. */
  background: none;
  box-shadow: none;
  cursor: pointer;
}
.nav__link:hover,
.nav__link.is-active { color: #fff; }   /* the tile never dims the icon */
.nav__label { display: none; }          /* desktop shows icons, not words */
.nav__icon {
  display: grid;
  place-items: center;
  width: var(--dock-icon);
  height: var(--dock-icon);
}
.nav__icon .ico { grid-area: 1 / 1; width: 100%; height: 100%; display: block; }
/* idle icon: LINE at 85% */
.nav__icon .ico-line { opacity: .85; filter: var(--dock-icon-lift); }
.nav__icon .ico-fill { display: none; }

/* active tile: liquid glass, FILL icon, the dot (spec §3 Active). Figma Site,
   variant 6. No transition — the switch is instant, per the spec. No size
   change either — every tile stays 48×48 in every state (spec §1); the tile,
   icon and dot never move, only the icon swaps line→fill and the dot appears. */
.nav__link.is-active {
  background: var(--dock-tile-active);
  box-shadow: var(--dock-tile-shadow);
}
/* the rim: a gradient border, bright on two corners. Drawn as a masked ring
   because a CSS border cannot be a gradient and keep the radius. */
.nav__link.is-active::before {
  content: "";
  position: absolute;
  inset: 0;
  padding: var(--dock-tile-rim-width);
  border-radius: inherit;
  background: var(--dock-tile-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
/* the lens: a soft highlight over the top half of the tile */
.nav__link.is-active::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  right: 2px;
  height: 22px;
  border-radius: 10px 10px 16px 16px;
  background: var(--dock-tile-lens);
  pointer-events: none;
}
.nav__link.is-active .nav__icon { position: relative; z-index: 1; }
.nav__link.is-active .ico-line { display: none; }
.nav__link.is-active .ico-fill { display: block; opacity: 1; filter: var(--dock-icon-shadow); }

.nav__link.is-active .nav__dot {
  display: block;
  position: absolute;
  left: 50%;
  top: calc(100% + var(--dock-dot-gap));
  width: var(--dock-dot);
  height: var(--dock-dot);
  transform: translateX(-50%);
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.75);
}

/* hover / focus tooltip — the aria-label in a glass capsule above the tile;
   the tile, icon and dot do NOT move (spec §3 Hover / §5).

   The tooltip itself is NOT a pseudo-element here any more. A backdrop-filter
   only blurs the page when no ancestor carries its own backdrop-filter,
   filter or transform — and the dock has both (its Glass and its
   translate/scale transform), so a tooltip nested in it saw the dock's flat
   layer and the Glass did nothing. It lives at body level now as the shared
   .glass-tooltip (styled below, driven by js/modules/tooltip.js), hoisted
   clear of the dock so its backdrop is the real page again. Positioning is
   done in JS from the tile's live rect. */

/* size on scroll (Instagram): JS writes --dock-scale (1 or --dock-scale-min)
   from the scroll direction; always 1 at the top and on hover/focus.
   bottom: the dock rides --dock-bottom-offset above the viewport floor (30%
   of the old 24px), but never closer than the device's safe-area inset, so a
   landscape iPhone's home indicator can't overlap it. The divider and the
   fidget floor are pinned to --sp-3 elsewhere and do NOT follow the dock. */
.nav-dock {
  bottom: max(var(--dock-bottom-offset), env(safe-area-inset-bottom));
  transform: translateX(-50%) scale(var(--dock-scale, 1));
  transform-origin: bottom center;
  transition: transform var(--dock-scale-motion);
}

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

/* ===== Shared glass tooltip =====
   One body-level element reused by every trigger (dock tiles now, fidget tiles
   next). position: fixed with no blur/transform/filter ancestor, so its
   backdrop-filter blurs the actual page behind it — a light/dark seam under the
   tooltip reads as soft Glass, not a hard edge. JS (js/modules/tooltip.js) sets
   left/top from the trigger's rect and writes the shape vars; the reveal is a
   small lift + fade, left free of the positioning. Glass is backdrop-filter
   only: no stroke, border, outline or shadow (spec §7). */
.glass-tooltip {
  position: fixed;
  left: 0;
  top: 0;
  z-index: var(--z-tooltip);
  width: var(--tip-w, max-content);
  height: var(--tip-h, auto);
  box-sizing: border-box;
  /* padding-bottom reserves the tail band so the label centres in the capsule,
     not in the whole figure */
  padding: 0 var(--tooltip-pad-x) calc(var(--tip-h, 0px) - var(--tip-cap-h, 0px));
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--tooltip-bg);
  -webkit-backdrop-filter: var(--tooltip-glass);
          backdrop-filter: var(--tooltip-glass);
  clip-path: var(--tip-path, none);
  color: var(--tip-text);
  font-size: var(--tooltip-text);
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.glass-tooltip.is-visible {
  opacity: 1;
  transform: translateY(0);
}
/* The rim: the same outline, stroked. The clip-path cuts the outer half of the
   stroke away, which leaves a 1px line on the inside edge, tail included. A CSS
   border or box-shadow cannot follow the shape, the clip would remove them. */
.glass-tooltip__rim {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: none;
  stroke-width: 2;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .glass-tooltip { transition: opacity 150ms var(--ease-out); transform: none; }
  .glass-tooltip.is-visible { transform: none; }
}

/* ===== Divider the fidget tiles rest on =====
   A 1px line across the Hero, sitting at the tiles' resting line
   (--dock-divider-gap above where the dock floats, which at rest is
   --sp-3 + --dock-height + --dock-divider-gap up from the Hero's bottom).
   It is ABSOLUTE inside the Hero, NOT fixed to the viewport: it belongs to
   the tiles, so it scrolls up and away with them. Below the fidget canvas in
   z-order so the tiles paint ON it. Desktop only — the mobile nav keeps its
   own geometry and has no divider. */
.hero .dock-divider {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--sp-3) + var(--dock-height) + var(--dock-divider-gap));
  height: 1px;
  background: var(--divider-color);
  z-index: var(--z-content);
  pointer-events: none;
}
@media (max-width: 768px) { .hero .dock-divider { display: none; } }

/* ===== Site header — fixed, and nothing else =====
   Was .hero__topbar: absolutely positioned inside #hero, which meant the
   logo and the controls scrolled away with the Hero and were reachable only
   at the top of the page. It is a page-level header now, fixed to the top of
   the viewport, and the content scrolls underneath it.

   NO SURFACE. There is no background, no blur, no border and no shadow here,
   at the top of the page or anywhere down it. The header is the logo and two
   icons floating over whatever happens to be behind them, and that is the
   whole brief.

   ⚠️ This is a decision that has been made twice and reversed once, so it is
   worth knowing what it costs. A fixed header was tried long ago and reverted
   because a bare logo over a scrolling page runs into section headings that
   pass behind it. The second attempt (this file, an earlier pass) answered
   that with a translucent blurred plate faded in on a .is-stuck class once
   the page scrolled. That plate was removed on request: the header is to stay
   fully transparent. The collision is therefore back and is accepted — if it
   ever reads wrong, the thing to reach for is the plate, and both it and the
   js/modules/site-header.js that drove it are recoverable from git history.

   Its height is --header-h, declared in tokens.css rather than here: the
   Hero's top padding measures against it and is not a descendant. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--sp-3);
  /* The bar spans the viewport but only its two ends are ever drawn, so the
     empty middle must not eat clicks on the content scrolling under it. The
     logo and the controls take their own back below. */
  pointer-events: none;
}
.site-header__logo,
.site-header__controls { pointer-events: auto; }

.site-header__logo { flex-shrink: 0; }

/* Two controls, no capsule. The pill bar is still the right shape for the
   nav dock, which holds five tabs and a sliding highlight; around two icons
   it was a container drawn for its own sake. They sit bare in the corner
   now and the header's own plate is the only surface up there. */
.site-header__controls {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

/* A bare icon button. NOT .pill-bar__btn: that one is "a button inside the
   capsule" and paints a --c-bg wash on hover, because inside a capsule the
   wash has an edge to sit against. Out here there is no capsule and no
   header plate, so a disc appearing under the icon reads as a stray blob on
   the page rather than as a hover state.

   SO NOTHING PAINTS A BACKGROUND — not hover, not :active, not focus. The
   only thing that answers a pointer is the icon itself: it goes from muted
   to full ink and lifts a hair. Keyboard focus is the one exception and it
   is a hairline outline, not a fill: a keyboard user needs to be told where
   they are, and that is the one case where a mouse user sees nothing. */
.icon-btn {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  background: none;
  border: 0;
  padding: 0;
  color: var(--c-ink-soft);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--motion-hover);
}
.icon-btn:hover,
.icon-btn:focus-visible { color: var(--c-ink); }
/* 1px and no offset — a thin ring around the icon, not a second button. */
.icon-btn:focus-visible { outline: 1px solid var(--c-ink); outline-offset: 4px; }
.icon-btn svg {
  width: 20px;
  height: 20px;
  stroke-width: 1.5;
  /* The whole of the feedback: a 6 % lift on hover and a press back under
     the finger. `scale` as its own property rather than inside `transform`,
     so an icon that ever wants a rotate can add one without rewriting this.
     reset.css clamps the duration under reduced motion. */
  scale: 1;
  transition: scale var(--motion-hover);
}
.icon-btn:hover svg  { scale: 1.06; }
.icon-btn:active svg { scale: .94; }

@media (max-width: 768px) {
  /* The token moves with the padding — everything that measures against the
     header (the Hero's padding, the parked headline, the chat band) has to
     follow it down, and they all read --header-h. :root, not .site-header:
     those three are not descendants of the header. */
  :root { --header-h: calc(38px + var(--space-3) * 2); }
  .site-header { padding: var(--space-3) var(--space-4); }
  /* The page dissolves under the logo instead of running into it. Not a colour:
     the same background and the same dots as .dot-grid, which is fixed to the
     screen too, so they line up exactly and only the content seems to fade. */
  .site-header::before {
    /* Fades from the very top on an eased curve: a straight ramp shows its
       edges. Values from the frame's version E, component 170:5241. */
    --header-fade-end: 82px;
    --header-fade: linear-gradient(to bottom,
      #000,
      rgb(0 0 0 / .957) 12.5%,
      rgb(0 0 0 / .844) 25%,
      rgb(0 0 0 / .684) 37.5%,
      rgb(0 0 0 / .5) 50%,
      rgb(0 0 0 / .316) 62.5%,
      rgb(0 0 0 / .156) 75%,
      rgb(0 0 0 / .043) 87.5%,
      transparent);
    content: "";
    position: absolute;
    inset: 0 0 auto;
    z-index: -1;
    height: var(--header-fade-end);
    background: radial-gradient(circle, var(--dot-grid) 1px, transparent 1.5px) 0 0 / 24px 24px,
                var(--c-bg);
    -webkit-mask-image: var(--header-fade);
            mask-image: var(--header-fade);
    pointer-events: none;
  }
}

/* The theme toggle AND the language switcher are HIDDEN (30 Oct): the site
   runs dark + English only. Both keep their markup and their modules intact
   (theme.js FORCE_THEME, lang.js FORCE_LANG) so either can be brought back by
   removing one rule here plus its force. With both gone the header is just the
   logo in the corner. */
[data-theme-toggle] { display: none; }
.lang-picker { display: none; }

/* theme icon swap */
[data-theme-toggle] .icon-sun  { display: none; }
[data-theme-toggle] .icon-moon { display: block; }
:root[data-theme="dark"] [data-theme-toggle] .icon-sun  { display: block; }
:root[data-theme="dark"] [data-theme-toggle] .icon-moon { display: none; }

/* --- language switcher: a text code + a bare list -----------------------
   Was a globe button opening the colour picker's panel (it literally carried
   class="shade-menu lang-menu"). Both halves changed:

   • the BUTTON is the current language's code — EN / RO / RU. A globe only
     says "languages exist here"; the code answers the question a visitor
     actually has, which is which one they are reading. lang.js writes it.
   • the MENU has no panel. No surface, no border, no shadow — just the other
     languages hanging under the button, stacked. Two words behind a card,
     a hairline and a drop shadow is three devices carrying four characters;
     the list is legible without any of them, so none of them earns its place.

   It still opens on click, closes on Escape and on a click outside, and its
   options stay out of the tab order while closed (`inert`) — see lang.js. */
.lang-picker { position: relative; flex-shrink: 0; }

.lang-toggle {
  display: grid;
  place-items: center;
  min-width: 38px;
  height: 38px;
  padding-inline: var(--space-2);
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  /* Same resting mute as the icon beside it, and the same tabular-ish
     steadiness: the three codes are all two capitals, so the button never
     changes width when the language does. */
  color: var(--c-ink-soft);
  font-family: var(--font-body);
  font-size: var(--fs-100);
  font-weight: 500;
  letter-spacing: .04em;
  line-height: 1;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color var(--motion-hover), background var(--motion-hover);
}
/* Same rule as .icon-btn beside it: no background in any state. It never
   had one, and keeping the two in step is why that is written down here. */
.lang-toggle:hover,
.lang-toggle[aria-expanded="true"] { color: var(--c-ink); }
.lang-toggle:focus-visible { outline: 1px solid var(--c-ink); outline-offset: 4px; }

.lang-menu {
  position: absolute;
  top: calc(100% + var(--space-1));
  /* right-aligned under the button: it is the last thing in the top-right
     corner, so left is the only direction with room to grow */
  right: 0;
  display: grid;
  justify-items: stretch;
  gap: 2px;
  width: max-content;
  min-width: 100%;
  opacity: 0;
  translate: 0 -4px;
  pointer-events: none;
  transition: opacity var(--dur-fast) var(--ease-out),
              translate var(--dur-fast) var(--ease-out);
}
.lang-menu.is-open {
  opacity: 1;
  translate: 0 0;
  pointer-events: auto;
}

.lang-option {
  padding: var(--space-1) var(--space-2);
  border: 0;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--c-ink-soft);
  font-family: var(--font-body);
  font-size: var(--fs-100);
  font-weight: 500;
  letter-spacing: .04em;
  line-height: 1.4;
  text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background var(--motion-hover), color var(--motion-hover);
}
.lang-option:hover { color: var(--c-ink); background: var(--c-bg); }
.lang-option:focus-visible { outline: 2px solid var(--c-ink); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .lang-menu { transition: opacity var(--dur-fast) linear; transform: none; }
}

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