/* ================================================================
   site-shell.css — the marketing site's header, footer and page chrome,
   copied VERBATIM from /Users/vlados/waypoint (zn-dsgn/waypoint, branch
   homepage-v2 @ dcd597f) css/style.css. Loaded AFTER css/style.css so it
   is the authority on everything it covers. Do not edit here — re-copy.
   Blocks, in source order of the original file:
     · html · body · .page (marketing base)
     · .chip family
     · Header
     · .btn-pill-icon / .pill-icon
     · Footer
     · Easter-egg band
     · Tooltip
     · Responsive
     · Custom circle cursor + media
     · Cross-page transitions
     · Coming-soon modal
     · "Our story" lightbox
   ================================================================ */

/* ---- site-shell: html · body · .page (marketing base) ---- */
html {
  scroll-behavior: smooth;
  /* iOS Safari inflates type inside long text blocks — hardest in landscape,
     where it re-scales body copy AND headings and blows the layout out of the
     column. Never let it: the type here is already fluid. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
html.lock { overflow: hidden; }
/* menu open: the page is dimmed behind the pill and scrolling is blocked in
   js (by swallowing wheel/touchmove) rather than with overflow:hidden —
   toggling overflow on the scroll container re-resolves every position:sticky
   on the page, which made the tech pin and the carousel jump out of view. */
.pill-scrim {
  position: fixed;
  inset: 0;
  z-index: 9000;               /* above the whole page, under .site-header */
  background: rgba(20, 20, 20, 0.5);
  opacity: 0;
  visibility: hidden;
  touch-action: none;
  transition: opacity 0.4s var(--ease), visibility 0s linear 0.4s;
}
html.menu-open .pill-scrim {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.4s var(--ease), visibility 0s linear 0s;
}
body {
  font-family: "Helvetica Neue W", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: 400;
  color: var(--ink);
  /* This is the canvas colour: what a rubber-band overscroll shows, and what
     iOS tints the status bar / dynamic island area with. It has to match the
     page it sits behind or the notch area goes black against a white page.
     The footer underlay is .egg — a fixed sheet with its own dark background
     — so nothing here depends on body being dark. /404 overrides it, because
     there body IS the page (that document has no .page wrapper). */
  background: var(--white);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;   /* clip (not hidden): hidden makes body a scroll container and breaks position: sticky */
}
a { color: inherit; text-decoration: none; }
video { display: block; }
::selection { background: var(--sky); color: var(--ink); }

/* the white page container — contracts near the footer (see main.js) */
.page {
  background: var(--white);
  position: relative;
  z-index: 1;   /* paints above the fixed .egg underlay */
  /* curtain bottom edge over the footer underlay is always rounded */
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* ---------- Layout primitives ---------- */

/* ---- site-shell: .chip family ---- */
.chip {
  position: absolute;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--white);
  background: var(--text-3);
  backdrop-filter: blur(8px);
  padding: 8px 13px;
  border-radius: 999px;
  white-space: nowrap;
}
.chip-top { top: 14px; left: 14px; }
.chip-bottom { bottom: 14px; left: 14px; }
.chip-play { top: 50%; left: 50%; translate: -50% -50%; }

.card-label { font-size: 14px; font-weight: 700; letter-spacing: 0.01em; }

/* ---- site-shell: Header ---- */
/* ================================================================
   Header — pill that expands into a sunday-style panel
   ================================================================ */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 9010;   /* the menu opens over everything on the page */
  display: flex;
  justify-content: center;
  padding-top: 20px;
  padding-inline: var(--bleed);   /* pill sizes to this padded box, not 100vw */
  pointer-events: none;
}
.pill {
  pointer-events: auto;
  width: min(400px, 100%);
  background: var(--white);
  border-radius: 16px;
  box-shadow: 0 1px 2px var(--shadow-1), 0 10px 34px var(--shadow-2);
  overflow: hidden;
  transition: width 0.6s var(--ease);
}
.pill.open { width: min(1000px, 100%); }
.pill-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px;
  height: 56px;
}
.pill-logo {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  background: var(--ink);
  border-radius: 12px;
  transition: background 0.3s var(--ease), color 0.3s var(--ease);
}
.pill-logo svg { width: 20px; height: 20px; color: var(--white); }
.pill-logo:hover { background: var(--grey); }
.pill-logo:hover svg { color: var(--ink); }
.pill-name {
  flex: 1;
  text-align: center;
  font-size: 17px;
  font-weight: 500;
  letter-spacing: -0.01em;
}
.pill-toggle {
  flex: 0 0 auto;
  width: 42px;
  height: 42px;
  border: 0;
  background: transparent;
  border-radius: 12px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  transition: background 0.3s var(--ease);
}
.pill-toggle:hover { background: var(--ink); }
.pill-toggle:hover span { background: var(--white); }
.pill-toggle span {
  display: block;
  width: 17px;
  height: 1.8px;
  background: var(--ink);
  border-radius: 2px;
  transition: transform 0.45s var(--ease), background 0.3s var(--ease);
}
.pill-toggle span + span { margin-top: 5px; }
.pill.open .pill-toggle span:first-child { transform: translateY(3.4px) rotate(45deg); }
.pill.open .pill-toggle span:last-child { transform: translateY(-3.4px) rotate(-45deg); }

.pill-drawer {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.6s var(--ease);
}
.pill.open .pill-drawer { grid-template-rows: 1fr; }
/* .pill-panel must stay AUTO-height: it's the drawer's grid item, and a
   definite height on it sizes the 0fr track to that height instead of
   collapsing it — the closed pill then hangs open. The 394px lives on the
   content below, so max-content (and with it the 1fr reveal) still lands on
   the fixed 1000 × 450 box (450 = 394 + the 56px bar). */
.pill-panel {
  min-height: 0;
  overflow: hidden;
}
.panel-main {
  display: grid;
  grid-template-columns: 1fr 1.1fr;
  gap: 40px;
  padding: 30px 34px 34px;
  height: 394px;
}
/* two columns: nav + slogan on the left, video + its meta row on the right.
   min-width:0 matters — a grid item sized by its content would otherwise be
   free to grow past the pill (the video's intrinsic width is 1920px). */
.panel-col { display: flex; flex-direction: column; min-height: 0; min-width: 0; }
/* social row — sits on the column floor, diagonally opposite the meta row.
   40px buttons, 16px marks, 8px apart. */
.panel-social {
  display: flex;
  gap: 8px;
  margin-top: auto;
}
.soc {
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: var(--grey);
  display: grid;
  place-items: center;
  cursor: pointer;
  transition: background 0.25s var(--ease);
}
/* the mark is a mask, so one colour drives it and the hover can invert */
.soc i {
  width: 16px;
  height: 16px;
  background: var(--ink);
  -webkit-mask: var(--m) center / contain no-repeat;
  mask: var(--m) center / contain no-repeat;
  transition: background 0.25s var(--ease);
}
.soc:hover { background: var(--ink); }
.soc:hover i { background: var(--white); }
.panel-nav { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.panel-nav a {
  font-size: clamp(22px, 2vw, 30px);
  font-weight: 500;
  letter-spacing: -0.02em;
  padding: 6px 12px;
  margin-left: -12px;
  border-radius: 10px;
  opacity: 0;
  transform: translateY(-8px);
  /* order: opacity, transform, background, color — the stagger delay below
     targets only the first two so the hover fill (bg/color) is never delayed */
  transition: opacity 0.4s var(--ease), transform 0.4s var(--ease),
              background 0s, color 0s;
  transition-delay: 0s, 0s, 0s, 0s;
}
/* entrance cascade — --base/--step drive the whole thing, so the touch block
   below can compress it to fit a shorter reveal by changing two numbers */
.panel-nav a:nth-child(2) { --i: 1; }
.panel-nav a:nth-child(3) { --i: 2; }
.panel-nav a:nth-child(4) { --i: 3; }
.panel-nav a:nth-child(5) { --i: 4; }
.panel-nav a:nth-child(6) { --i: 5; }
.pill.open .panel-nav a {
  opacity: 1;
  transform: none;
  --d: calc(var(--base, 0.12s) + var(--i, 0) * var(--step, 0.05s));
  transition-delay: var(--d), var(--d), 0s, 0s;
}
.panel-nav a:hover { background: var(--ink); color: var(--white); }
/* "Soon" tag rides next to a label whose destination doesn't exist yet */
.panel-nav a { display: inline-flex; align-items: center; gap: 10px; }
.nav-soon {
  font-style: normal;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--wash);
  color: var(--text-3);
  transition: background 0.25s var(--ease), color 0.25s var(--ease);
}
.panel-nav a:hover .nav-soon { background: rgba(var(--white-rgb), 0.18); color: var(--white); }
/* full column width at a locked 16:9 — declared rather than derived from the
   panel box, so the ratio holds at every pill width. 6px corners match the
   framed videos in the carousel (.car-frame). */
.panel-media {
  position: relative;
  flex: 0 0 auto;
  max-width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 6px;
  overflow: hidden;
  background: var(--grey);
}
.panel-media video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* under the video, ends flush with the column's corners. margin-top:auto
   drops it to the column floor (level with the slogan opposite) now that the
   16:9 video no longer eats the full height; it's a no-op once the column is
   content-sized, where the padding alone sets the gap. */
.panel-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: auto;
  padding-top: 16px;
  font-size: 14px;
  font-weight: 500;
  color: var(--text-3);
}
.panel-meta a { display: inline-flex; align-items: center; gap: 7px; color: var(--ink); }
/* the early-access dot blinks out to transparent and back. Opacity only —
   no scale, so it never nudges the label beside it, and no brightness
   filter: that pushed the blue toward white (so the low point read as pale
   cyan rather than as fading away) and it put a 6px circle on its own
   compositing layer, where the antialiased edge rounds off and the dot
   looks clipped. Long cycle on purpose: the drawer is only open for a
   moment. */
.panel-meta .dot {
  width: 6px;
  height: 6px;
  /* it is a flex item in the Early access row, and without this it gets
     squeezed while the drawer is still opening */
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--sky);
  animation: dotBreathe 2.6s ease-in-out infinite;
}
@keyframes dotBreathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.15; }
}
@media (prefers-reduced-motion: reduce) {
  .panel-meta .dot { animation: none; }
}

/* contract-address chip — sits where "Launching 2026" used to. Same muted
   14px/500 as the rest of .panel-meta; the whole chip is one button (address
   + copy glyph), and the "Copied" pop rides above it on a .copied class that
   nav.js holds for a beat after a successful copy. */
.ca-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  color: inherit;                 /* .panel-meta's 55% ink */
  font-variant-numeric: tabular-nums;
  transition: color 0.25s var(--ease);
}
.ca-chip:hover { color: var(--ink); }
.ca-chip svg { width: 14px; height: 14px; flex: 0 0 auto; }
/* same skin as .tip — the site's one tooltip look — just anchored to the chip */
.ca-pop {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  translate: -50% 4px;
  padding: 8px 13px;
  border-radius: 999px;
  background: var(--ink);
  color: var(--white);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s var(--ease), translate 0.25s var(--ease);
}
.ca-chip.copied .ca-pop { opacity: 1; translate: -50% 0; }

/* touch: the reveal used to hitch. The 34px blurred shadow had to be
   re-rasterised on every frame while the box grew, the cascade outlasted the
   reveal itself, and the panel video started loading the instant it played
   (that last one is deferred in main.js). */
@media (max-width: 760px), (pointer: coarse) {
  .pill {
    box-shadow: 0 1px 2px var(--shadow-1), 0 4px 12px var(--shadow-2);
    transition-duration: 0.42s;
  }
  .pill-drawer { transition-duration: 0.42s; }
  .panel-nav a { --base: 0.08s; --step: 0.028s; }
}

/* ---- site-shell: .btn-pill-icon / .pill-icon ---- */
.btn-pill.btn-pill-icon { display: inline-flex; align-items: center; gap: 8px; padding-right: 7px; }
.pill-icon {
  display: inline-flex;
  width: 28px;
  height: 28px;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--line);
  transition: background 0.2s var(--ease);
}
.pill-icon svg { width: 15px; height: 15px; }
.btn-pill-icon:hover .pill-icon { background: rgba(var(--white-rgb), 0.2); }

/* ---- site-shell: Footer ---- */
/* ================================================================
   Footer (inside .page, white)
   ================================================================ */
.footer {
  padding: 20px var(--bleed) var(--bleed);
  /* --sq grows on scroll (set in main.js) as the card clips inward at the
     footer; padding the content in by the same amount keeps the side padding
     constant, so the footer looks squeezed rather than cropped. */
  padding-inline: calc(var(--bleed) + var(--sq, 0px));
}
.news-strip {
  background: var(--card);
  border-radius: var(--r-md);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  /* twice the old 68px: the 40px pill plus 48px of air on each side. The
     760px block below sets its own padding for the stacked layout. */
  padding: 48px 20px;
  margin-bottom: 56px;
}
.news-strip p { font-size: 14px; }
.news-strip p strong { font-weight: 700; margin-right: 10px; }
.news-strip p span { color: var(--text-3); }
.news-strip .btn-pill { padding: 0 16px; font-size: 14px; }
.news-strip .btn-pill-icon { padding-right: 7px; }   /* circled icon hugs the edge */
/* inverted by default — dark pill, green glyph. Hover flips it to white with
   a hairline edge and puts the green in the disc instead of the glyph. */
.news-strip .news-cta {
  background: var(--ink);
  color: var(--white);
  border-color: transparent;
}
.news-strip .news-cta .pill-icon {
  background: rgba(var(--white-rgb), 0.2);
  color: #7aca96;
}
.news-strip .news-cta:hover {
  background: var(--white);
  color: var(--ink);
  border-color: var(--wash-2);
}
.news-strip .news-cta:hover .pill-icon { background: #7aca96; color: var(--ink); }
/* and the whole strip inverts under it */
.news-strip,
.news-strip p strong,
.news-strip p span { transition: background 0.3s var(--ease), color 0.3s var(--ease); }
.news-strip:has(.news-cta:hover) { background: var(--ink); }
.news-strip:has(.news-cta:hover) p strong { color: var(--white); }
.news-strip:has(.news-cta:hover) p span { color: rgba(var(--white-rgb), 0.55); }

.footer .grid { padding-inline: calc(var(--margin) - var(--bleed)); }
.foot-grid { row-gap: 40px; }
.foot-tag { grid-column: 1 / 4; font-size: 14px; font-weight: 500; }
.foot-col { grid-column: span 1; display: flex; flex-direction: column; gap: 8px; }
.foot-col h5 { font-size: 14px; font-weight: 700; margin-bottom: 4px; }
.foot-col a { font-size: 14px; color: var(--text-2); transition: color 0.25s; }
.foot-col a:hover { color: var(--ink); }
.foot-legal {
  margin-top: 220px;
  padding-block: 24px 0;
  font-size: 14px;
  color: var(--text-3);
}
.foot-legal span { grid-column: 1 / 4; }
.foot-legal a { grid-column: span 1; }
.foot-legal a:hover { color: var(--ink); }
/* the Legal column only exists on phones (desktop keeps the legal strip) */
.foot-col-legal { display: none; }

/* ---- site-shell: Easter-egg band ---- */
/* ================================================================
   Easter-egg band — sits on the blue body behind .page
   ================================================================ */
.egg {
  /* fixed fullscreen underlay — behind the page; the white page squeezes and
     scrolls up over it, exposing the edges (sunday.ai pattern). Hidden until
     the scroll nears the footer (.near, from js) so top-overscroll bounces
     can't flash the footer image behind the hero. */
  visibility: hidden;
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--ink) url("../assets/images/footer.jpg") center / cover no-repeat;
  padding: 0 var(--margin);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;   /* content lives in the reveal zone at the bottom */
  gap: 20px;
  overflow: hidden;
}
/* in-flow spacer after .page — its height IS the final reveal: scrolling stops
   with this much of the underlay showing. Derived from the wordmark's actual
   rendered height (width-driven: (100vw − 2·margin) · 297/1271) plus a fixed
   240px for the line + chips + headroom, so the text is never swallowed on
   wide screens. Capped so at least 160px of the white page stays visible. */
.egg.near { visibility: visible; }
.egg-spacer {
  height: var(--reveal-h);
}
:root {
  /* the final footer reveal height — shared by the spacer and (on phones)
     the egg itself */
  --reveal-h: min(calc((100vw - 2 * var(--margin)) * 0.2337 + 240px), calc(100vh - 160px));
}
@media (max-width: 760px) {
  /* phones: bottom-anchored 70vh underlay — taller than the reveal band so
     overscroll bounces show image (not black body), but far from full-height
     so the picture isn't absurdly zoomed inside the band */
  .egg {
    top: auto;
    height: 70vh;
  }
}
.egg-line { font-size: 14px; font-weight: 500; color: rgba(var(--white-rgb), 0.72); }
.egg-chips { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.chip-lite {
  display: inline-flex;
  align-items: center;
  background: rgba(var(--white-rgb), 0.12);
  color: rgba(var(--white-rgb), 0.85);
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  padding: 9px 14px;
}
/* enter as the curtain uncovers them (.egg gets .in from main.js) */
.egg-line, .chip-lite {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.egg.in .egg-line, .egg.in .chip-lite { opacity: 1; transform: none; }
.egg.in .egg-line { transition-delay: 0s; }
.egg.in .chip-lite:nth-child(1) { transition-delay: 0.1s; }
.egg.in .chip-lite:nth-child(2) { transition-delay: 0.18s; }
.egg.in .chip-lite:nth-child(3) { transition-delay: 0.26s; }
/* full-width wordmark standing on the base, letters filled by a video */
.egg-wordmark {
  width: 100%;
  height: auto;
  display: block;
  /* text block sits just above the wordmark, low in the reveal zone */
  margin-top: clamp(36px, 6vh, 72px);
}

/* ---- site-shell: Tooltip ---- */
/* ================================================================
   Tooltip — dark pill that shows text info on hover of [data-tip]
   ================================================================ */
.tip {
  position: fixed;
  z-index: 9600;   /* tooltips read above the open menu */
  background: var(--ink);
  color: var(--white);
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.01em;
  padding: 8px 13px;
  border-radius: 999px;
  pointer-events: none;
  white-space: nowrap;
  opacity: 0;
  translate: 0 4px;
  transition: opacity 0.25s var(--ease), translate 0.25s var(--ease);
}
.tip.show { opacity: 1; translate: 0 0; }
@media (hover: none) { .tip { display: none; } }

/* ---- site-shell: Responsive ---- */
/* ================================================================
   Responsive
   ================================================================ */
@media (max-width: 1000px) {
  :root { --margin: 40px; }
  .capability-grid { grid-template-columns: repeat(2, 1fr); }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  .sub-row.cols-3 { grid-template-columns: 1fr 1fr; }
  .sub-row.cols-3 .text-card { grid-column: 1 / 3; min-height: 240px; }
  .faq-label { grid-column: 1 / 7; margin-bottom: 24px; }
  .faq-list { grid-column: 1 / 7; }
  /* narrow: the second column drops under the first, the panel goes back to
     content height, and the slogan is dropped (no floor left to sit on) */
  .panel-main { grid-template-columns: 1fr; gap: 24px; height: auto; padding-bottom: 30px; }
  /* stacked: the social row keeps its own gap under the nav instead of being
     pushed to a floor that no longer exists */
  .panel-social { margin-top: 22px; }
  /* .panel-media keeps its 16:9 here — one ratio in both layouts */
}

@media (max-width: 760px) {
  /* one consistent 16px edge/gutter on mobile — margin, grid gutter, and the
     full-width bleed (was 40px, so cards no longer sat further in than text) */
  :root { --margin: 16px; --gutter: 16px; --bleed: 16px; }

  /* other 20px spacings → 16px on mobile (font-sizes left as-is) */
  .site-header { padding-top: 16px; }
  /* the 34px desktop inset is a big share of a 375px pill — tighten it so the
     nav sits closer to the logo's own 7px edge */
  .panel-main { padding: 24px 20px 20px; }
  .footer { padding-top: 16px; }
  .egg { gap: 16px; }
  .feature-title { padding-inline: 16px; }

  .statement-text { grid-column: 1 / 7; }
  .stat-grid { grid-template-columns: 1fr; }
  .stat-card { min-height: 260px; }

  .sub-row.cols-2, .sub-row.cols-3 { grid-template-columns: 1fr; }
  .sub-row.cols-3 .text-card { grid-column: auto; }
  .sub-row .media-card, .sub-row.cols-2 .media-card { aspect-ratio: 16 / 11; }
  .text-card { min-height: 220px; gap: 40px; }

  .feature-card { height: 72svh; min-height: 0; }
  .cap-card { aspect-ratio: 1 / 1; }

  /* same card height as the open How-it-works card on touch; the media
     row stretches to soak up the extra height */
  .car-card { height: var(--touch-card-h); }
  /* the chat graphics keep their desktop layout and are simply scaled to
     fit the narrower frame (--cs is frameWidth / 430, set in main.js) */
  .car-chat {
    width: 430px;
    height: calc(100% / var(--cs, 1));
    transform: scale(var(--cs, 1));
    transform-origin: top left;
  }
}

/* the split card only STACKS when the screen is genuinely narrow — a phone
   in landscape is wide and short, where stacking is exactly wrong (it needs
   height the viewport doesn't have). Width-based, never pointer-based. */
@media (max-width: 760px) {
  .car-text {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
    width: min(420px, 88vw);
  }
  .car-text-media { min-height: 280px; }   /* room for the chat bubbles */

  .inter-inner { grid-template-columns: 1fr; text-align: center; gap: 28px; justify-items: center; }
  .inter-left, .inter-right { text-align: center; }

  /* tall, airy signup box — content centred with generous breathing room */
  .news-strip { flex-direction: column; gap: 10px; text-align: center; padding-block: 100px; }
  /* headline and sub-line stack as two clean rows */
  .news-strip p strong { display: block; margin-right: 0; margin-bottom: 2px; }
  /* 2×2 column grid: Explore·About / Social·Legal */
  .foot-tag { grid-column: 1 / 7; }
  .foot-col { grid-column: span 3; }
  .foot-col-legal { display: flex; }
  /* the legal strip shrinks to just the © line (links live in the Legal col) */
  .foot-legal a { display: none; }
  .foot-legal span { grid-column: 1 / 7; }
  .foot-legal { row-gap: 8px; margin-top: 48px; }

  .rail { display: none; }
}

/* ---- site-shell: Custom circle cursor + media ---- */
/* ================================================================
   Custom circle cursor — white dot blended with "difference", so it
   reads black on white surfaces and white on dark ones (fine pointers
   only; touch devices keep no cursor, coarse pointers are untouched)
   ================================================================ */
@media (pointer: fine) {
  /* transparent 1×1 png first — some browsers (Safari) intermittently
     ignore a bare `cursor: none`, but always honor an image cursor */
  html.has-cursor-dot,
  html.has-cursor-dot * {
    cursor: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=") 0 0, none !important;
  }
  .cursor-dot {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 12000;
    pointer-events: none;
    /* blend on the moving wrapper itself — a transformed parent would
       isolate the stacking context and kill the inversion */
    mix-blend-mode: difference;
  }
  /* APP COPY: an arrow instead of the site's dot. White under the difference
     blend, so it inverts over whatever it covers — black on the light page,
     white on the dark one — with no shadow, outline or theme logic. */
  .cursor-dot i.arrow {
    position: absolute;
    left: 0;
    top: 0;
    display: block;
    width: 24px;
    height: 24px;
    translate: -4px -2px;                 /* the tip sits on the hotspot */
    background: #fff;
    clip-path: polygon(16.7% 8.3%, 83.3% 54.2%, 52% 58.3%, 35.4% 87.5%);
    opacity: 0;
    transition: opacity 0.2s var(--ease);
  }
  .cursor-dot.on i.arrow { opacity: 1; }
  /* No hover disc (2026-09-02): the pointer is just the arrow, in every state. */
  .cursor-dot i.ring { display: none; }

  /* The arrow + label ride in their OWN fixed layer, not inside .cursor-dot,
     because that one blends with difference: white content inside it would be
     composited into the same white disc and come out identical to it, i.e.
     invisible. Kept as a sibling of .cursor-dot (never a shared parent — a
     transformed wrapper would isolate the stacking context and kill the
     disc's inversion) and handed the exact same transform each frame. */
  .cursor-tag {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 12001;
    pointer-events: none;
  }
  .cursor-tag > span {
    position: absolute;
    left: 0;
    top: 0;
    translate: -50% -50%;
    display: grid;
    justify-items: center;
    row-gap: 7px;
    color: var(--white);
    font-size: 15px;
    font-weight: 500;
    letter-spacing: -0.01em;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.2s var(--ease);
  }
  .cursor-tag > span svg { width: 20px; height: 20px; display: block; }
  .cursor-tag.gate > span { opacity: 1; }
}

/* ================================================================
   Media is decorative — never draggable or long-press-saveable
   ================================================================ */
img,
video,
svg {
  -webkit-user-drag: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
}

/* ---- site-shell: Cross-page transitions ---- */
/* ================================================================
   Cross-page transitions
   Five separate documents means a click otherwise leaves the old page
   frozen on screen for the whole of the next document's round trip.

     .pg-leave  outgoing — content fades out, pill goes skeleton
     .pg-hold   incoming — held blank for a beat before the fade starts
     .pg-in     the fade itself; only exists while it runs

   Opacity only: a transform on .page would make it the containing
   block for its position:fixed children, and below 900px the docs
   explorer + scrim live inside it.
   ================================================================ */
.page {
  opacity: 1;
  /* eased both ways and unhurried — the previous 0.34s snapped shut before
     the eye could follow it, especially when the next page was cached */
  transition: opacity 0.42s var(--ease);
}
html.pg-hold .page,
html.pg-leave .page { opacity: 0; }

/* body used to be dark, so .page fading up over it blended THROUGH near-black
   and these rules forced the backdrop white for the length of the fade. body
   is white now, so they're redundant — and on /404 they were actively wrong,
   overriding body.err (0,1,1) with (0,1,2) and flashing the black page white
   on every load. The footer underlay still has to be pulled, though: it's a
   fixed dark sheet that .page would otherwise dissolve into. */
html.pg-hold .egg,
html.pg-in .egg,
html.pg-leave .egg { opacity: 0; }

/* ---- the pill: a placeholder of itself, easing back in WITH the page ----
   these durations match .page's 0.42s so the header and the content resolve
   together; :hover keeps the original 0.3s so pointer feedback stays quick */
.pill-logo { transition: background 0.42s var(--ease), color 0.42s var(--ease); }
.pill-logo:hover { transition-duration: 0.3s; }
.pill-name { transition: color 0.42s var(--ease); }
.pill-toggle { transition: background 0.42s var(--ease); }
.pill-toggle:hover { transition-duration: 0.3s; }
.pill-toggle span { transition: transform 0.45s var(--ease), background 0.42s var(--ease); }
.pill-toggle:hover span { transition-duration: 0.45s, 0.3s; }

html.pg-hold .pill-logo,
html.pg-leave .pill-logo { background: var(--grey); }
/* the mark stays white on the grey tile — greying it out too just left an
   empty square. Also has to out-rank .pill-logo:hover, hence the html prefix */
html.pg-hold .pill-logo svg,
html.pg-leave .pill-logo svg { color: var(--white); }
html.pg-hold .pill-name,
html.pg-leave .pill-name { color: var(--grey); }
html.pg-hold .pill-toggle span,
html.pg-leave .pill-toggle span { background: var(--grey); }

@media (prefers-reduced-motion: reduce) {
  .page { transition: none; }
  html.pg-hold .page,
  html.pg-leave .page { opacity: 1; }
}

/* ---- site-shell: Coming-soon modal ---- */
/* ================================================================
   Coming-soon modal — opened by .js-soon controls
   ================================================================ */
.soon {
  position: fixed;
  inset: 0;
  z-index: 300;             /* above the pill header (which sits at 100) */
  display: grid;
  place-items: center;
  padding: 20px;
  visibility: hidden;
  /* opening moves focus here (see soon.js) so Tab cannot walk the page behind
     the dialog. It is not an actionable control, so it takes no focus ring —
     the close button still rings normally when you Tab to it. */
  outline: none;
}
.soon.open { visibility: visible; }
.soon-scrim {
  position: absolute;
  inset: 0;
  /* no backdrop-filter here: blur on this fixed layer made Chrome drop the
     whole modal from compositing — a slightly deeper plain scrim instead */
  background: rgba(11, 11, 11, 0.55);
  opacity: 0;
  transition: opacity 0.35s var(--ease);
}
.soon.open .soon-scrim { opacity: 1; }
.soon-card {
  position: relative;
  background: var(--white);
  border-radius: 20px;
  /* same lift as the pill header */
  box-shadow: 0 1px 2px var(--shadow-1), 0 10px 34px var(--shadow-2);
  width: 740px;
  height: 430px;
  max-width: 100%;
  padding: 16px;
  display: grid;
  grid-template-columns: 340px 1fr;   /* fixed media column, copy takes the rest */
  opacity: 0;
  transform: translateY(18px) scale(0.97);
  transition: opacity 0.35s var(--ease), transform 0.5s var(--ease);
}
.soon.open .soon-card { opacity: 1; transform: none; }
.soon-media { border-radius: 6px; overflow: hidden; }
/* direct child only — the mock post nests its own <img> for the avatar,
   and the unscoped selector was stretching that to fill the box too */
.soon-media > img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* same slow push-in as the Enabled by Waypoint cards — matched curve and
   duration, not just a scale. The post sits over it and lifts on its own,
   so hovering anywhere in the frame moves both together. */
.soon-media > img { transition: transform 0.55s cubic-bezier(0.45, 0.05, 0.15, 1); }
.soon-media:hover > img { transform: scale(1.04); }

/* ---- mock X post, centred over the popup image ------------------------
   Everything inside is sized in em off .tw's own font-size, and that font
   size is a share of the MEDIA box's width (cqw), so the whole card scales
   as one object instead of the text and the chrome drifting apart. The
   clamp caps it at both ends: it can't get illegibly small in the narrow
   desktop column, and it can't blow up on a wide tablet. The 12px plain
   font-size above it is the fallback where container queries are missing. */
.soon-media { position: relative; container-type: inline-size; }
.tw {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  width: min(88%, 340px);
  display: block;
  background: #000;
  color: #e7e9ea;
  border-radius: 1.3em;
  padding: 1.15em;
  font-size: 10px;
  font-size: clamp(7px, 3.05cqw, 12px);
  line-height: 1.35;
  box-shadow: 0 0.6em 2em rgba(0, 0, 0, 0.35);
}
.tw-top { display: flex; align-items: center; gap: 0.7em; }
.tw-av { width: 2.7em; height: 2.7em; border-radius: 50%; display: block; flex: 0 0 auto; }
.tw-id { display: flex; flex-direction: column; min-width: 0; margin-right: auto; }
.tw-name {
  display: flex; align-items: center; gap: 0.25em;
  font-weight: 700; font-size: 1.05em; letter-spacing: -0.01em; white-space: nowrap;
}
.tw-check { width: 1.05em; height: 1.05em; flex: 0 0 auto; fill: #1d9bf0; }
.tw-handle { color: #71767b; font-size: 1em; white-space: nowrap; }
.tw-sub {
  flex: 0 0 auto;
  background: #eff3f4; color: #0f1419;
  font-weight: 700; font-size: 1em;
  padding: 0.5em 1.05em; border-radius: 999px;
}
.tw-more { width: 1.5em; height: 1.5em; flex: 0 0 auto; fill: #71767b; }
.tw-body { margin-top: 0.95em; font-size: 1.08em; line-height: 1.35; }
.tw-link { color: #1d9bf0; }
.tw-acts {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 1.1em;
  color: #71767b; font-size: 0.95em;
}
.tw-acts span { display: inline-flex; align-items: center; gap: 0.45em; }
.tw-acts svg { width: 1.3em; height: 1.3em; }
.tw-share { width: 1.3em; height: 1.3em; }

/* ---- hovers. The card is one link, so these are all :hover-within it and
   none of them are separate targets — clicking anywhere opens the profile.
   The tinted disc behind each glyph is padding + a negative margin of the
   same size, so it grows out of the icon without moving the count beside it. */
/* the card itself doesn't move or change its shadow — only its controls
   respond, so the post stays put under the cursor */
.tw:hover .tw-link { text-decoration: underline; }
.tw-sub, .tw-more, .tw-acts span, .tw-share {
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}
.tw-acts svg, .tw-share {
  border-radius: 50%;
  padding: 0.35em;
  margin: -0.35em;
  box-sizing: content-box;
  transition: background 0.18s var(--ease);
}
.tw:hover .tw-sub { background: #d7dbdc; }
.tw:hover .tw-more { fill: #e7e9ea; }
.tw-act:hover, .tw-share:hover { color: #1d9bf0; }
.tw-reply:hover svg { background: rgba(29, 155, 240, 0.15); }
.tw-rt:hover    { color: #00ba7c; }
.tw-rt:hover svg { background: rgba(0, 186, 124, 0.15); }
.tw-like:hover  { color: #f91880; }
.tw-like:hover svg { background: rgba(249, 24, 128, 0.15); }
.tw-save:hover svg { background: rgba(29, 155, 240, 0.15); }
.tw-share:hover { background: rgba(29, 155, 240, 0.15); }
@media (prefers-reduced-motion: reduce) {
  .tw-sub, .tw-more, .tw-acts span, .tw-acts svg, .tw-share { transition: none; }
}
/* the card pads 16px, so 4px here lands the text at 20px from the card edge;
   the left side owns the 20px gap to the image instead */
.soon-copy { padding: 4px 4px 4px 20px; display: flex; flex-direction: column; }
/* matches the blog card title (.post-title) rather than a section heading */
.soon-copy h3 { font-size: 20px; font-weight: 500; letter-spacing: -0.015em; line-height: 1.25; }
.soon-copy p { margin-top: 8px; font-size: 18px; line-height: 1.45; color: var(--ink); }
.soon-socials { display: flex; gap: 10px; margin-top: auto; }
/* bare glyph, no chip — the 40px box is kept purely as a tap target */
.soon-x {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 1;
  width: 40px;
  height: 40px;
  border: 0;
  /* bare glyph at rest — the 40px box exists only as a tap target and as the
     disc that fills in on hover */
  background: none;
  border-radius: 50%;
  color: var(--ink);
  display: grid;
  place-items: center;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.25s var(--ease), background 0.25s var(--ease);
}
.soon-x svg { width: 16px; height: 16px; }
.soon-x:hover { opacity: 1; background: var(--card); }
/* below the fixed frame width the card goes to a single column sheet */
@media (max-width: 780px) {
  .soon { padding: 16px; }
  .soon-card {
    width: 100%;
    height: auto;
    grid-template-columns: 1fr;
  }
  .soon-media { aspect-ratio: 16 / 10; }
  .soon-copy { padding: 20px 4px 4px; }
  /* h3 holds 20px here too — .post-title doesn't scale down either */
  .soon-copy p { font-size: 16px; }
  .soon-socials { margin-top: 24px; }
  /* the glyph sits over the image here, so it becomes a proper frosted disc
     instead of a bare mark — a drop-shadow alone left it hard to read on a
     light frame. 34px of inset keeps it clear of the media's rounded corner,
     and the glyph keeps 12px of padding inside the 40px disc. */
  .soon-x {
    top: 34px;
    right: 34px;
    color: var(--white);
    opacity: 1;
    background: rgba(20, 20, 20, 0.34);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    box-shadow: 0 1px 6px rgba(0, 0, 0, 0.18);
  }
  .soon-x:hover { background: rgba(20, 20, 20, 0.5); }
}
/* Phones take a squarer frame than tablets. The mock post is sized off the
   box's WIDTH, so on a narrow screen a 16/10 box is short enough that the card
   nearly fills it — no air around it, and its top corner collides with the
   close button that moves over the image at this size. Squaring the frame
   gives the post room on all four sides and drops it clear of the button,
   without changing the post's own size. */
@media (max-width: 560px) {
  .soon-media { aspect-ratio: 1 / 1; }
}
@media (prefers-reduced-motion: reduce) {
  .soon-scrim, .soon-card { transition: none; }
  .soon-card { transform: none; }
}

/* ---- site-shell: "Our story" lightbox ---- */
/* ================================================================
   "Our story" lightbox + custom player (built by js/story.js)
   ================================================================ */
.story {
  position: fixed;
  inset: 0;
  z-index: 320;              /* above the pill (100) and the soon dialog (300) */
  display: grid;
  place-items: center;
  padding: 20px;
  visibility: hidden;
  outline: none;             /* opening focuses the dialog, same as .soon */
}
.story.open { visibility: visible; }
.story-scrim {
  position: absolute;
  inset: 0;
  /* near-opaque + blur: the page behind keeps its videos running, and at .72
     they read through the scrim and compete with the film */
  background: rgba(8, 8, 8, 0.94);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
}
.story.open .story-scrim { opacity: 1; }
.story-card {
  position: relative;
  margin: 0;
  width: min(1200px, 100%);
  aspect-ratio: 16 / 9;
  max-height: calc(100svh - 40px);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: #0b0b0b;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.45);
  opacity: 0;
  transform: translateY(14px) scale(0.985);
  transition: opacity 0.35s var(--ease), transform 0.35s var(--ease);
}
.story.open .story-card { opacity: 1; transform: none; }
.story-card video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  cursor: pointer;
}
.story-x {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 3;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: var(--white);
  background: rgba(20, 20, 20, 0.34);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  cursor: pointer;
  transition: background 0.25s var(--ease), opacity 0.3s var(--ease);
}
.story-x:hover { background: rgba(20, 20, 20, 0.55); }
.story-x svg { width: 16px; height: 16px; }
/* control bar — floats over the bottom edge on a readability gradient */
.story-ui {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 26px 16px 12px;
  color: var(--white);
  background: linear-gradient(180deg, rgba(8, 8, 8, 0) 0%, rgba(8, 8, 8, 0.55) 100%);
  transition: opacity 0.3s var(--ease);
}
.story-ui button {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  padding: 0;
  border: 0;
  display: grid;
  place-items: center;
  background: none;
  color: inherit;
  cursor: pointer;
  opacity: 0.85;
  transition: opacity 0.2s var(--ease);
}
.story-ui button:hover { opacity: 1; }
.story-ui svg { width: 18px; height: 18px; }
.st-bar { position: relative; flex: 1; height: 24px; cursor: pointer; touch-action: none; }
.st-bar::before {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 50%;
  height: 3px;
  translate: 0 -50%;
  border-radius: 999px;
  background: rgba(var(--white-rgb), 0.28);
}
.st-fill {
  position: absolute;
  left: 0; top: 50%;
  width: 0;
  height: 3px;
  translate: 0 -50%;
  border-radius: 999px;
  background: var(--white);
  pointer-events: none;
}
.st-time {
  flex: 0 0 auto;
  font-size: 13px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  opacity: 0.85;
}
/* chrome sleeps while the film plays; any pointer wakes it (story.js) */
.story-card.ui-hidden .story-ui,
.story-card.ui-hidden .story-x { opacity: 0; pointer-events: none; }
.story-card.ui-hidden video { cursor: none; }
/* maximised fallback — used when the fullscreen API is denied (webviews):
   the card simply takes the whole viewport, letterboxed by object-fit */
.story.fs-max { padding: 0; }
.story.fs-max .story-card {
  width: 100%;
  height: 100%;
  max-height: none;
  aspect-ratio: auto;
  border-radius: 0;
}
@media (max-width: 620px) {
  .story { padding: 12px; }
  .story-ui { gap: 8px; padding: 22px 10px 8px; }
}
