/* ============================================================
   MonoVault — marketing site
   Strictly monochrome: black, white, and greys between them.
   No hue anywhere, matching the app's "Mono" default theme.
   ============================================================ */

:root {
  /* Tells the browser to render scrollbars and native controls dark,
     so nothing pale is left over at the page edge. */
  color-scheme: dark;

  --bg: #000000;
  --bg-2: #070707;
  --surface: #0b0b0b;
  --surface-2: #141414;
  --line: #232323;
  --line-2: #3a3a3a;
  --fg: #ffffff;
  --fg-2: #a1a1a1;
  --fg-3: #636363;

  --wrap: 1180px;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* Effects that cannot be expressed as a flat colour get their own
     tokens, because their *direction* flips between modes: on black we
     add light, on white we add shadow. */
  --wash: rgba(255, 255, 255, 0.055);
  --nav-bg: rgba(0, 0, 0, 0.72);
  --veil: rgba(0, 0, 0, 0.72);
  --shadow-phone: 0 60px 140px rgba(0, 0, 0, 0.85), 0 20px 60px rgba(0, 0, 0, 0.6);
  --shadow-card: 0 40px 90px rgba(0, 0, 0, 0.85);
  --shadow-fab: 0 6px 18px rgba(0, 0, 0, 0.7);
  --screen-edge: rgba(255, 255, 255, 0.08);
}

/* Light mode uses the app's real lightTokens palette, so the site and
   the app's light theme are the same white and the same greys. */
:root.light {
  color-scheme: light;

  --bg: #ffffff;
  --bg-2: #fafbfc;
  --surface: #ffffff;
  --surface-2: #f4f5f7;
  --line: #eaecf0;
  --line-2: #d0d5dd;
  --fg: #0c0d10;
  --fg-2: #61646c;
  --fg-3: #98a0ae;

  /* On white, a white wash is invisible and heavy black shadows read as
     dirt. Both invert: the wash darkens, the shadows soften. */
  --wash: rgba(12, 13, 16, 0.045);
  --nav-bg: rgba(255, 255, 255, 0.78);
  --veil: rgba(12, 13, 16, 0.4);
  --shadow-phone: 0 40px 90px rgba(12, 13, 16, 0.18),
                  0 14px 40px rgba(12, 13, 16, 0.1);
  --shadow-card: 0 30px 70px rgba(12, 13, 16, 0.22);
  --shadow-fab: 0 6px 16px rgba(12, 13, 16, 0.22);
  --screen-edge: rgba(12, 13, 16, 0.06);
}

/* Colours cross-fade instead of snapping when the theme flips. */
:root.theming,
:root.theming * {
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease),
              color 0.4s var(--ease) !important;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: 'Inter', -apple-system, system-ui, sans-serif;
  overflow-x: hidden;
}

::selection { background: var(--fg); color: var(--bg); }

img, svg { display: block; max-width: 100%; }
ul { list-style: none; }
a { color: inherit; text-decoration: none; }

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 32px; }
.wrap-narrow { max-width: 760px; margin: 0 auto; padding: 0 32px; }

/* ---------- type ---------- */

.display {
  font-size: clamp(2.9rem, 6.6vw, 5.4rem);
  font-weight: 900;
  line-height: 0.96;
  letter-spacing: -0.045em;
  margin-bottom: 28px;
}

/* Each line sits in an overflow-hidden box so its inner span can be
   masked upward — the classic "type rises out of nothing" reveal. */
.ln { display: block; overflow: hidden; }
.ln > span { display: block; }

.h-lg {
  font-size: clamp(2.2rem, 4.4vw, 3.6rem);
  font-weight: 900;
  line-height: 1.02;
  letter-spacing: -0.04em;
  margin-bottom: 22px;
  max-width: 14ch;
}
.h-xl {
  font-size: clamp(2.6rem, 6vw, 4.6rem);
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.045em;
  margin-bottom: 16px;
}

.kicker {
  display: inline-block;
  margin-bottom: 20px;
  padding: 6px 13px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--fg-2);
}

/* ---------- loader ----------
   A plain splash: the mark on the page background, nothing else. No
   spinner, no progress bar — the page is usually ready inside a
   second, and an indicator for a wait that short only draws attention
   to a wait that was not there.

   Dismissal is a handoff rather than a fade-out: the backdrop clears
   while the mark travels to the centre of the hero phone screenshot,
   so the splash appears to *become* the screenshot. script.js measures
   the phone and writes a transform that shrinks and repositions the
   mark in one motion; the CSS transition below owns the easing. */
.loader {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: grid;
  place-items: center;

  /* ===== CSS failsafe =====
     If script.js never loads or throws before it reaches the loader,
     this fades the whole overlay on its own. A stalled page is worse
     than an early reveal, so it gives up rather than waiting forever. */
  animation: loader-failsafe 6s linear forwards;
}
@keyframes loader-failsafe {
  0%, 92% { opacity: 1; }
  100%    { opacity: 0; visibility: hidden; }
}

/* The backdrop is a pseudo-element instead of a background on .loader
   itself, so it can clear independently while the mark stays opaque
   and completes its flight over the revealed page. Fading .loader as a
   whole would pull the mark with it and there would be no handoff. */
.loader::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--bg);
  transition: opacity 0.5s var(--ease);
}
.loader.done::before { opacity: 0; }

/* Settles in rather than appearing: a short rise and a fade, once, with
   a beat of delay so it is not competing with the page's own first paint.

   The transition is declared here, not under `.done`, because the
   transform that drives the flight is written inline by JS — the
   property must already be transitioning when that value lands.
   Opacity is delayed most of the way through the trip so the mark
   stays solid while it travels and only dissolves as it arrives. */
.loader-mark {
  position: relative;
  z-index: 1;
  width: clamp(84px, 18vw, 120px);
  height: auto;
  animation: loader-mark-in 0.8s var(--ease) both;
  transition: transform 0.9s var(--ease), opacity 0.4s var(--ease) 0.55s;
  will-change: transform;
}
@keyframes loader-mark-in {
  from { opacity: 0; transform: translateY(10px) scale(0.96); }
  to   { opacity: 1; transform: none; }
}

.loader.done { pointer-events: none; }
.loader.done .loader-mark { opacity: 0; }

/* Reduced motion: the global reduce block already collapses every
   duration, so the flight resolves instantly. Neutralising the
   transform as well keeps the mark from jumping across the screen
   for one frame on its way out. */
@media (prefers-reduced-motion: reduce) {
  .loader-mark { animation: none; }
  .loader.done .loader-mark { transform: none !important; }
}

/* ---------- cursor ---------- */

.cursor {
  position: fixed;
  top: 0; left: 0;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--fg);
  pointer-events: none;
  z-index: 9999;
  /* `difference` inverts against whatever is behind it, so a white dot
     reads correctly on black *and* on white. It is the one effect that
     needs no light-mode variant. */
  mix-blend-mode: difference;
  transform: translate(-50%, -50%);
  transition: width 0.28s var(--ease), height 0.28s var(--ease),
              opacity 0.28s var(--ease);
}
.cursor.grow { width: 42px; height: 42px; opacity: 0.85; }
/* A pointer-based cursor is meaningless on touch, and the blend mode
   costs paint time — so it only exists on real pointers. */
@media (hover: none), (pointer: coarse) { .cursor { display: none; } }

/* ---------- nav ---------- */

/* Two states, one element. At the top of the page the bar spans the full
   width and is completely transparent — it reads as part of the hero. Once
   the page scrolls it contracts into a floating capsule, which is what
   earns it a background and a shadow: it now overlaps content it does not
   own and has to separate itself from it.

   The morph animates max-width / height / padding / radius rather than
   swapping between two layouts, so there is nothing to snap between. */
.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  /* Padding, not `top`, lifts the capsule off the screen edge: it animates
     without the element ever leaving the top of the viewport. */
  padding-top: 0;
  transition: padding-top 0.55s var(--ease);
}
.nav.stuck { padding-top: 14px; }

.nav-inner {
  width: 100%;
  max-width: var(--wrap);
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 70px;
  padding: 0 32px;
  /* Transparent rather than absent. A border that already occupies layout
     can animate its colour; one that appears from `none` would pop in and
     shift the row by a pixel as it does. */
  border: 1px solid transparent;
  border-radius: 0;
  background: transparent;
  transition: max-width 0.55s var(--ease), height 0.55s var(--ease),
              padding 0.55s var(--ease), border-radius 0.55s var(--ease),
              border-color 0.45s var(--ease), background 0.45s var(--ease),
              box-shadow 0.55s var(--ease);
}

.nav.stuck .nav-inner {
  max-width: 760px;
  height: 58px;
  padding: 0 10px 0 22px;
  border-radius: 999px;
  border-color: var(--line);
  background: var(--nav-bg);
  /* Only blurred once stuck. At the top there is nothing behind it to
     blur, and an always-on backdrop-filter costs a compositing pass on
     every frame of the hero animation for no visible gain. */
  backdrop-filter: blur(20px) saturate(180%);
  box-shadow: 0 10px 34px rgba(0, 0, 0, 0.34);
}
:root.light .nav.stuck .nav-inner { box-shadow: 0 10px 34px rgba(12, 13, 16, 0.1); }

.logo {
  display: flex;
  align-items: center;
  gap: 11px;
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: -0.02em;
  /* Keeps the wordmark intact when the capsule reaches its narrowest. */
  flex: none;
}
.logo img {
  width: 30px; height: 30px;
  transition: width 0.55s var(--ease), height 0.55s var(--ease);
}
/* Scaled down with the bar so the mark stays proportional to its height. */
.nav.stuck .logo img { width: 26px; height: 26px; }

/* `margin-left: auto` stays on the links in both states. Moving the auto
   margin to a different child when stuck would hand the free space to
   another element and jump the whole row sideways mid-transition. */
.nav-links {
  display: flex;
  gap: 30px;
  margin-left: auto;
  transition: gap 0.55s var(--ease);
}
.nav.stuck .nav-links { gap: 4px; }

.nav-links a {
  position: relative;
  padding: 8px 0;
  border-radius: 999px;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--fg-2);
  white-space: nowrap;
  transition: color 0.3s var(--ease), background 0.3s var(--ease),
              padding 0.55s var(--ease);
}
.nav.stuck .nav-links a { padding: 8px 14px; }
.nav-links a:hover { color: var(--fg); }

/* An underline while the bar is full width, a filled pill once it is a
   capsule: a straight rule under a link fights the rounded container it
   would then be sitting inside. */
.nav-links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: 2px;
  width: 100%; height: 1px;
  background: var(--fg);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform 0.4s var(--ease), opacity 0.35s var(--ease);
}
.nav-links a:hover::after { transform: scaleX(1); transform-origin: left; }
.nav.stuck .nav-links a::after { opacity: 0; }
.nav.stuck .nav-links a:hover { background: var(--wash); }

/* ---------- theme toggle ----------
   A single key, borrowed from the app's own iconography, that turns like
   a key in a lock when the theme flips. One shape in both states: nothing
   to cross-fade, and the rotation alone carries the state change. */
.theme-btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 38px; height: 38px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--fg-3);
  cursor: pointer;
  transition: color 0.3s var(--ease), background 0.3s var(--ease),
              transform 0.2s var(--ease);
}
/* No border at rest so the bar stays quiet; the hover wash is enough to
   confirm it is a control. */
.theme-btn:hover { color: var(--fg); background: var(--wash); }
/* A short press-in on the button, not the key: scaling the icon here
   would overwrite the rotate transform that carries the theme state. */
.theme-btn:active { transform: scale(0.9); }

.theme-btn .key {
  width: 18px; height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* The key turns through 135° between modes, like turning in a lock.
     Centre origin so it pivots rather than swings. */
  transform: rotate(-45deg);
  transition: transform 0.6s var(--ease);
}
:root.light .theme-btn .key { transform: rotate(90deg); }

/* The `.theming` rule above sets `transition` — a shorthand, with
   !important — on every element while the theme cross-fades. That resets
   this key's transform transition to `none` for exactly the 450ms the
   rotation was supposed to happen in, so the key used to snap round
   instantly. Re-stating it with higher specificity keeps the turn. */
:root.theming .theme-btn .key {
  transition: transform 0.6s var(--ease) !important;
}

/* Keyboard focus must stay visible in both themes. `currentColor` would
   vanish on the filled store button, whose text is the page background —
   var(--fg) is always the opposite of the page, so the ring holds
   everywhere the selector reaches. */
.theme-btn:focus-visible,
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: 3px;
}

/* The store buttons and the footer's oversized brand link are large,
   rounded targets. The default 3px offset traces their bounding box
   rather than their shape, so the ring reads as a stray rectangle —
   these give it the element's own radius and pull it in tight. */
.store:focus-visible {
  outline-offset: 2px;
  border-radius: 15px;
}
.f-logo:focus-visible {
  outline-offset: 4px;
  border-radius: 26px;
}
@media (max-width: 860px) {
  .f-logo:focus-visible { border-radius: 21px; }
}

/* Footer links are underlined on hover by a background-image, which a
   keyboard user never triggers. Showing that same underline on focus
   keeps the two states consistent instead of relying on the ring alone. */
.f-col a:focus-visible {
  color: var(--fg);
  background-size: 100% 1px;
}

/* ---------- mobile menu ----------
   Below 980px the inline links go. They are replaced rather than shrunk:
   three links, a toggle and a button in one 70px bar leaves every target
   too small and too close to its neighbour to hit reliably on a phone.
   The sheet gives each destination a full-width row instead.

   Both the button and the sheet are display:none here and switched on in
   the breakpoint, so a desktop visitor never has them in the layout. */
.menu-btn {
  display: none;
  place-items: center;
  width: 38px; height: 38px;
  flex: none;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--fg-2);
  cursor: pointer;
  transition: color 0.3s var(--ease), background 0.3s var(--ease);
}
.menu-btn svg {
  width: 21px; height: 21px;
  stroke-width: 1.8;
  stroke-linecap: round;
}
/* The burger is one path, so it cannot fold into a cross. The open state
   is carried by the wash and by aria-expanded, which is what a screen
   reader reads anyway. */
.menu-btn[aria-expanded="true"] { background: var(--wash); color: var(--fg); }

.menu {
  display: none;
  border-top: 1px solid var(--line);
  background: var(--nav-bg);
  backdrop-filter: blur(20px) saturate(180%);
  animation: menu-in 0.34s var(--ease);
}
/* Closed is the attribute, not a class: the links stay out of the tab
   order and out of the accessibility tree while the sheet is shut. */
.menu[hidden] { display: none; }
@keyframes menu-in { from { opacity: 0; transform: translateY(-10px); } }

.menu nav { display: flex; flex-direction: column; padding: 6px 32px 16px; }
.menu a {
  display: flex;
  align-items: center;
  padding: 16px 2px;
  border-bottom: 1px solid var(--line);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--fg);
}
.menu a:last-child { border-bottom: 0; }
/* A chevron drawn from two borders, same trick as the tick marks. */
.menu a::after {
  content: '';
  width: 7px; height: 7px;
  margin-left: auto;
  border-right: 1.6px solid var(--fg-3);
  border-bottom: 1.6px solid var(--fg-3);
  transform: rotate(-45deg);
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 14px 28px;
  border: 1px solid var(--fg);
  border-radius: 999px;
  background: var(--fg);
  color: var(--bg);
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  transition: transform 0.35s var(--ease), background 0.35s var(--ease),
              color 0.35s var(--ease);
}
.btn:hover { transform: translateY(-3px); }
.btn:active { transform: translateY(-1px) scale(0.985); }
.btn-sm { padding: 9px 19px; font-size: 0.86rem; }

.btn-ghost { background: transparent; color: var(--fg); border-color: var(--line-2); }
.btn-ghost:hover { background: var(--fg); color: var(--bg); border-color: var(--fg); }

/* ---------- hero ---------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: 128px 0 84px;
}
/* A faint vertical wash keeps the top of the page from reading as a
   flat black rectangle without introducing any colour. */
.hero::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(80% 55% at 50% 0%, var(--wash), transparent 70%);
  pointer-events: none;
}

.hero-inner {
  position: relative;
  display: grid;
  grid-template-columns: 1.08fr 0.92fr;
  gap: 64px;
  align-items: center;
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 26px;
  padding: 8px 16px 8px 14px;
  border: 1px solid var(--line-2);
  border-radius: 999px;
  background: var(--surface);
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--fg);
  letter-spacing: -0.01em;
}
/* The dot reads as a live status light: a solid core with one soft
   ring pulsing outward. Slow and low-contrast so it stays calm. */
.tag-dot {
  position: relative;
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--fg);
  flex: none;
}
.tag-dot::after {
  content: '';
  position: absolute;
  inset: -4px;
  border-radius: 50%;
  border: 1px solid var(--fg);
  animation: halo 2.6s var(--ease) infinite;
}
@keyframes halo {
  0%        { transform: scale(0.5); opacity: 0.85; }
  70%, 100% { transform: scale(1.35); opacity: 0; }
}

.lede {
  max-width: 30rem;
  margin-bottom: 34px;
  color: var(--fg-2);
  font-size: 1.1rem;
  line-height: 1.62;
}

.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 46px; }

.specs { display: flex; gap: 40px; }
.specs li { display: flex; flex-direction: column; gap: 3px; }
.specs b { font-size: 1.22rem; font-weight: 800; letter-spacing: -0.03em; }
.specs span {
  font-size: 0.72rem;
  color: var(--fg-3);
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

.scroll-hint {
  position: absolute;
  left: 50%;
  bottom: 30px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  color: var(--fg-3);
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.scroll-hint i {
  width: 1px;
  height: 40px;
  background: linear-gradient(var(--fg-3), transparent);
  animation: drop 2s var(--ease) infinite;
}
@keyframes drop {
  0%   { transform: scaleY(0); transform-origin: top; }
  45%  { transform: scaleY(1); transform-origin: top; }
  55%  { transform: scaleY(1); transform-origin: bottom; }
  100% { transform: scaleY(0); transform-origin: bottom; }
}

/* ---------- screenshots ----------
   These are the real renders, keyed off their studio backdrop, so the
   handset and its shadow are already in the image. There is no CSS frame
   left to build: the job here is sizing and the tilt. */

.hero-device, .feat-device { display: flex; justify-content: center; }

.shot {
  width: 340px;
  max-width: 100%;
  /* The tilt in script.js writes a 3D transform to this element, so it
     needs its own perspective or the rotation reads as a flat skew. */
  transform-style: preserve-3d;
  transition: transform 0.4s var(--ease);
  will-change: transform;
}
/* Intrinsic width/height are on the tag so the row reserves its space
   before the image lands and the section does not jump. */
.shot img { width: 100%; height: auto; }

/* ---------- seed phrase ---------- */

/* ============ SEED PHRASE ============
   A mock of the field rather than a screenshot, so the twelve words stay
   crisp at any zoom and are real text for screen readers and search. It
   borrows .feat-device from the sections above, so it lands in the same
   column as the phones and needs no grid rules of its own. */

/* Visually hidden but still announced. Used for the caption that explains
   the dot row, which is meaningless read aloud as "bullet bullet bullet". */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.seed-note {
  margin-top: 26px;
  padding-left: 15px;
  border-left: 2px solid var(--line-2);
  color: var(--fg-2);
  font-size: 0.93rem;
  line-height: 1.65;
}

.seed-card {
  width: 340px;
  max-width: 100%;
  padding: 26px 24px 28px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 20px;
  box-shadow: var(--shadow-card);
}

/* The masked state, shown above the revealed one so the order matches
   what actually happens: hidden first, visible only on request. */
.seed-field {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 15px 16px;
  background: var(--wash);
  border: 1px solid var(--line);
  border-radius: 13px;
}

.seed-label {
  color: var(--fg-2);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.seed-dots {
  color: var(--fg);
  font-size: 1.02rem;
  letter-spacing: 0.16em;
  /* Never wraps to a second line of dots, which would read as content. */
  overflow: hidden;
  white-space: nowrap;
}

.seed-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 22px 0 18px;
  color: var(--fg-2);
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
.seed-divider::before,
.seed-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

/* Numbered because word order is the whole point of a recovery phrase.
   Two columns of six reads faster than one column of twelve, and matches
   how every hardware wallet prints them. */
.seed-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px 18px;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: seed;
}
.seed-grid li {
  display: flex;
  align-items: baseline;
  gap: 9px;
  color: var(--fg);
  font-family: ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, Consolas, monospace;
  font-size: 0.9rem;
  counter-increment: seed;
}
.seed-grid li::before {
  content: counter(seed);
  min-width: 15px;
  color: var(--fg-2);
  font-size: 0.72rem;
  /* Right-aligned so 1 and 12 share a common edge and the words below
     line up in a single column rather than stepping in by a digit. */
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* ---------- showcase ---------- */

.showcase {
  padding: 118px 0 104px;
  border-bottom: 1px solid var(--line);
  text-align: center;
}
.showcase .h-lg { max-width: none; margin-inline: auto; }
.showcase-lede {
  max-width: 42rem;
  margin: 0 auto 54px;
  color: var(--fg-2);
  font-size: 1.04rem;
  line-height: 1.68;
}
/* Wider than the text column on purpose — the three handsets need the
   room to stay legible, and the overrun signals "look at this". */
.showcase-shot {
  width: min(100%, 1080px);
  height: auto;
  margin: 0 auto;
}

/* ---------- phone (unused) ----------
   The hand-built mockup styles below still describe .phone / .screen and
   everything inside them. Nothing on the page uses them now that the
   real shots are in, but they are the only record of how those screens
   were composed, so they stay until the shots are settled. */


.phone {
  position: relative;
  width: 340px;
  padding: 8px;
  border-radius: 52px;
  /* The handset itself stays dark in both themes — real phones do not
     turn white — but its shadow has to lighten or it looks like ink
     spilled on the page. */
  background: linear-gradient(145deg, #17181c, #000000);
  border: 0.5px solid rgba(255, 255, 255, 0.08);
  box-shadow: var(--shadow-phone),
              0 0 0 1px rgba(255, 255, 255, 0.03) inset,
              0 2px 8px rgba(255, 255, 255, 0.02) inset;
  transition: box-shadow 0.5s var(--ease), transform 0.4s var(--ease);
  will-change: transform;
}
/* Dynamic Island notch - iPhone 17 style */
.phone::before {
  content: '';
  position: absolute;
  top: 28px;
  left: 50%;
  transform: translateX(-50%);
  width: 110px;
  height: 32px;
  border-radius: 20px;
  background: #000000;
  border: 0.5px solid rgba(255, 255, 255, 0.05);
  z-index: 4;
}

.screen {
  position: relative;
  border-radius: 44px;
  background: var(--bg);
  /* Top padding clears the Dynamic Island (28px top + 32px tall). */
  padding: 72px 16px 18px;
  /* Every phone is the same height regardless of how much content it
     holds — otherwise the shorter screens (detail, lock) render as
     visibly smaller handsets than the vault ones. */
  min-height: 640px;
  overflow: hidden;
  box-shadow: 0 0 1px var(--screen-edge) inset;
}

.ico {
  width: 17px; height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  flex: none;
}
.ico.xs { width: 12px; height: 12px; }
/* The overflow dots and star are solid, not outlined. */
.ico.dots, .ico.star { fill: currentColor; stroke: none; }

/* app bar — logo, wordmark, then search and overflow actions */
.s-top {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
  padding: 0 2px;
  color: var(--fg-2);
}
/* No radius: the mark is a cut-out now, so there is no square backdrop
   left to round off. */
.s-logo { width: 24px; height: 24px; }
.s-name { font-size: 0.95rem; font-weight: 800; letter-spacing: -0.025em; color: var(--fg); }
.s-acts { margin-left: auto; display: flex; align-items: center; gap: 14px; }


/* Section header — 26px w800 title with the count beside it, matching
   the real screen rather than a small uppercase label. */
.s-head {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin-bottom: 12px;
  padding: 0 2px;
}
.s-head b {
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: -0.042em;
}
.s-head i {
  font-style: normal;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--fg-3);
}

/* Filter pills — icon + optional label, rounded square to match
   MonoPill(roundedSquare). Unselected pills collapse to icon only,
   exactly as collapseLabel does in the app. */
.s-pills { display: flex; gap: 8px; margin-bottom: 20px; padding: 0 1px; }
.pill {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 9px 15px;
  /* Fully rounded, matching the app. The 10px rounded-square was a guess
     from before there were screenshots to work from. */
  border-radius: 999px;
  border: 1px solid transparent;
  background: var(--surface-2);
  color: var(--fg-2);
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
  flex-shrink: 0;
}
.pill.on { background: var(--fg); border-color: var(--fg); color: var(--bg); }

/* groups */
.s-groups { display: flex; gap: 13px; margin-bottom: 22px; overflow-x: auto; padding: 0 1px; }
.grp { display: flex; flex-direction: column; align-items: center; gap: 7px; flex-shrink: 0; }
.grp small { font-size: 0.6rem; color: var(--fg-2); font-weight: 500; }

/* A flat filled tile with no outline, and initials in the muted grey
   rather than full contrast — the tile is a target, not a headline. */
.av {
  display: grid;
  place-items: center;
  width: 54px; height: 54px;
  border-radius: 16px;
  background: var(--surface-2);
  border: 1px solid transparent;
  font-size: 0.8rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  color: var(--fg-2);
}
/* "New" is the same tile with a lighter glyph — not a dashed outline,
   which read as a disabled state. */
.av.new { color: var(--fg-3); font-size: 1.2rem; font-weight: 400; }
.av.sm { width: 38px; height: 38px; border-radius: 12px; font-size: 0.68rem; }
.av.lg { width: 52px; height: 52px; border-radius: 15px; font-size: 0.82rem; }


/* entries — bottom padding keeps the last row clear of the floating FAB */
.s-list { display: flex; flex-direction: column; gap: 7px; padding-bottom: 56px; }
.ent {
  display: flex; align-items: center; gap: 11px;
  padding: 10px 11px;
  border-radius: 13px;
  background: var(--surface);
  border: 1px solid var(--line);
}
.ent-t { display: flex; flex-direction: column; line-height: 1.35; min-width: 0; }
.ent-t b { font-size: 0.76rem; font-weight: 700; letter-spacing: -0.01em; }
.ent-t i {
  font-style: normal;
  font-size: 0.64rem;
  color: var(--fg-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: 2px;
}
/* Copy glyph drawn in CSS: two offset squares. */
.cp {
  position: relative;
  margin-left: auto;
  width: 13px; height: 13px;
  border: 1.5px solid var(--fg-3);
  border-radius: 3px;
  flex: none;
}
.cp::after {
  content: '';
  position: absolute;
  top: -4px; left: -4px;
  width: 100%; height: 100%;
  border: 1.5px solid var(--fg-3);
  border-radius: 3px;
  opacity: 0.5;
}

/* The FAB floats over the list rather than sitting after it — the list
   scrolls beneath with 110px of bottom padding to clear it. */
/* A rounded square, not a circle or a pill — this is the shape the app
   actually uses, and it echoes the avatar tiles above it. */
.s-fab {
  position: absolute;
  right: 18px;
  bottom: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 52px; height: 52px;
  border-radius: 17px;
  box-shadow: var(--shadow-fab);
  background: var(--fg);
  color: var(--bg);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
/* The labelled variant grows sideways instead of becoming a pill. */
.s-fab.wide { width: auto; padding: 0 20px; }
.s-fab .ico { width: 16px; height: 16px; stroke-width: 2.4; }

/* lock screen */
.screen.lock {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* Centred content, but the top pad still clears the island. */
  padding: 84px 26px 44px;
  text-align: center;
}
/* The mark stands on its own — the tile behind it was invented for the
   mockup and the real screen does not have one. */
.lock-mark { width: 74px; height: 74px; margin-bottom: 14px; }
/* Letter-spaced caps, set as a wordmark rather than a heading. */
.lock-word {
  font-size: 1.05rem;
  font-weight: 900;
  letter-spacing: 0.3em;
  /* The tracking is applied to the right of every glyph including the
     last, which visually shifts the word left of centre. Half of it back
     as padding re-centres the block. */
  padding-left: 0.3em;
  text-transform: uppercase;
  margin-bottom: 26px;
}
.lock-h { font-size: 0.92rem; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 7px; }
.lock-p {
  font-size: 0.62rem;
  color: var(--fg-3);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin-bottom: 26px;
}

/* An outlined field with the label notched into its top edge, matching
   the app's Material outline input. */
.lock-field {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 13px 14px;
  margin-bottom: 14px;
  border-radius: 13px;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  color: var(--fg-3);
}
/* Sits on the border with the screen's own background behind it, so the
   line appears to break for the text rather than run under it. */
.lock-legend {
  position: absolute;
  top: -6px; left: 12px;
  padding: 0 5px;
  background: var(--bg);
  font-size: 0.56rem;
  color: var(--fg-2);
}
.lock-field .dots { flex: 1; text-align: left; letter-spacing: 0.18em; font-size: 0.8rem; color: var(--fg); }
.lock-btn {
  width: 100%;
  padding: 14px;
  border-radius: 13px;
  background: var(--fg);
  color: var(--bg);
  font-size: 0.82rem;
  font-weight: 800;
}
/* The crypto line is a footnote, not a feature: smallest type on the
   screen and the faintest grey that still passes as readable. */
.lock-foot {
  display: flex; align-items: center; gap: 7px;
  margin-top: 20px;
  color: var(--fg-3);
  font-size: 0.6rem;
}
.lock-foot .ico { width: 12px; height: 12px; }

/* detail screen */
.d-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20px;
  padding: 0 2px;
}
.d-top > b { font-size: 0.95rem; font-weight: 800; letter-spacing: -0.03em; margin-right: auto; }
.d-top .ico { color: var(--fg); }
.d-acts { display: flex; align-items: center; gap: 16px; }

/* The identity block runs beside the avatar rather than under it — the
   name, address, and group read as one left-aligned column. */
.d-head { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 24px; }
.d-id { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; min-width: 0; }
.d-id b { font-size: 1.12rem; font-weight: 800; letter-spacing: -0.035em; }
.d-id i { font-style: normal; font-size: 0.68rem; color: var(--fg-2); }

/* The group reads as a chip because it is a link to a filtered list, not
   a static caption. */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 3px;
  padding: 6px 12px;
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--fg-2);
  font-size: 0.64rem;
  font-weight: 600;
}

/* Section labels for FIELDS / HISTORY. */
.s-label {
  margin-bottom: 9px;
  padding: 0 2px;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-3);
}

.d-fields { display: flex; flex-direction: column; gap: 8px; margin-bottom: 24px; }
.fld {
  position: relative;
  display: flex; flex-direction: column; gap: 2px;
  padding: 9px 11px;
  border-radius: 12px;
  background: var(--surface);
  border: 1px solid var(--line);
}
.fld small {
  font-size: 0.55rem;
  color: var(--fg-3);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.fld > span:not(.fld-acts) { font-size: 0.72rem; }
.fld .mask { letter-spacing: 0.2em; font-size: 0.8rem; }
/* Both field actions ride in one right-anchored group, so a field with an
   eye and a copy button keeps the copy icon in the same column as a field
   that only has copy. */
.fld-acts {
  position: absolute;
  right: 12px; top: 50%;
  transform: translateY(-50%);
  display: flex; align-items: center; gap: 12px;
  color: var(--fg-3);
}
.fld-acts .cp { margin: 0; }

/* History is a description list: label left, value right. */
.d-hist { display: flex; flex-direction: column; gap: 11px; padding: 0 2px; }
.d-hist div { display: flex; justify-content: space-between; gap: 14px; font-size: 0.68rem; }
.d-hist dt { color: var(--fg-2); }
.d-hist dd { color: var(--fg); font-weight: 500; }

/* ---------- marquee ---------- */

.marquee {
  overflow: hidden;
  padding: 22px 0;
  border-block: 1px solid var(--line);
  background: var(--bg-2);
  /* Fade both ends so words dissolve instead of being cut mid-letter. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.marquee-track {
  display: flex;
  width: max-content;
  animation: slide 30s linear infinite;
  /* Promote to its own layer: without this the track is repainted each
     frame and stutters on long pages. */
  will-change: transform;
}
/* Pausing on hover would break the "never stops" feel, so it doesn't. */
/* Each group is a self-contained copy of the word list. Because the
   gap lives *inside* the group, one group width is the exact loop
   distance. There are four groups and the track shifts by 25% — one
   group — so the run is seamless *and* wide enough that no screen
   size can outrun the content and expose a gap. */
.marquee-group {
  display: flex;
  align-items: center;
  gap: 30px;
  padding-right: 30px;
}
.marquee span {
  font-size: 1.05rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--fg-2);
  white-space: nowrap;
}
.marquee i { color: var(--fg-3); font-size: 0.5rem; font-style: normal; }

@keyframes slide {
  from { transform: translateX(0); }
  to   { transform: translateX(-25%); }
}

/* ---------- feature sections ---------- */

/* Every section sits on the same pure black. The grey panel made the
   phone's shadow read as a smudge, so it's gone — sections are now
   separated by their hairline rule alone. */
.feature { padding: 130px 0; border-bottom: 1px solid var(--line); }

.feat-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 90px;
  align-items: center;
}
/* Flip puts the device on the left without reordering the DOM, so
   the reading order stays copy-then-image for screen readers. */
.feat-grid.flip .feat-copy { order: 2; }

.feat-copy p {
  max-width: 30rem;
  margin-bottom: 30px;
  color: var(--fg-2);
  font-size: 1.04rem;
  line-height: 1.68;
}

.ticks { display: flex; flex-direction: column; gap: 14px; }
.ticks li {
  position: relative;
  padding-left: 34px;
  font-size: 0.96rem;
  color: var(--fg);
}
/* A solid disc rather than a thin ring. The outlined version put a 1px
   circle around a 1.6px tick, so two competing weights sat inside 17px
   and the mark read as fuzzy at normal viewing distance. Filling it makes
   the tick a cut-out, which stays crisp at any size. */
.ticks li::before {
  content: '';
  position: absolute;
  left: 0; top: 2px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--fg);
}
/* The tick itself, knocked out of the disc in the page colour. Drawn from
   two borders on a rotated box: the long arm is the right border and the
   short arm the bottom, which is why the box is taller than it is wide.
   Rotated 45° about its own centre and nudged up-left, because a tick
   centred geometrically always looks like it is sitting too low. */
.ticks li::after {
  content: '';
  position: absolute;
  left: 6.9px; top: 6.1px;
  width: 5px; height: 9px;
  border-right: 2px solid var(--bg);
  border-bottom: 2px solid var(--bg);
  border-bottom-right-radius: 1px;
  transform: rotate(45deg);
}

/* ---------- honest ---------- */

.honest {
  padding: 104px 0;
  border-bottom: 1px solid var(--line);
}
.honest h3 {
  font-size: clamp(1.6rem, 3.2vw, 2.3rem);
  font-weight: 900;
  letter-spacing: -0.035em;
  margin-bottom: 16px;
}
.honest p { color: var(--fg-2); font-size: 1.06rem; line-height: 1.72; }

/* ---------- download ---------- */

.download { padding: 132px 0 140px; text-align: center; }
.dl-sub { margin-bottom: 10px; color: var(--fg-2); font-size: 1.14rem; }
.dl-note {
  max-width: 34rem;
  margin: 0 auto 40px;
  color: var(--fg-3);
  font-size: 0.82rem;
  line-height: 1.6;
}

.stores { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }

.store {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 13px;
  padding: 14px 24px;
  border-radius: 15px;
  border: 1px solid var(--line-2);
  transition: transform 0.35s var(--ease), background 0.35s var(--ease),
              color 0.35s var(--ease), border-color 0.35s var(--ease);
}
.s-ico { width: 26px; height: 26px; flex: none; }
.s-txt { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.18; }
/* 0.72 opacity over the white button rendered as washed-out grey.
   A fixed weight and letter-spacing keeps it legible as a label
   without competing with the line beneath it. */
.s-txt small {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}
.s-txt strong { font-size: 1rem; font-weight: 700; letter-spacing: -0.02em; }

.store.live { background: var(--fg); color: var(--bg); border-color: var(--fg); }
.store.live:hover { transform: translateY(-4px); }
.store.live:active { transform: translateY(-1px) scale(0.99); }

/* iOS: inert by construction — no href, dashed edge, dimmed. */
.store.soon {
  color: var(--fg-3);
  border-style: dashed;
  cursor: default;
  padding-right: 74px;
}
.badge {
  position: absolute;
  right: 15px; top: 50%;
  transform: translateY(-50%);
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line-2);
  color: var(--fg-2);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---------- download confirmation ----------
   Downloading an APK is a real commitment — a large file the browser
   will warn about, followed by a sideload. The dialog states what is
   about to happen before anything is fetched. */

.modal {
  position: fixed;
  inset: 0;
  z-index: 500;
  display: grid;
  place-items: center;
  padding: 24px;
  /* `hidden` until opened; the attribute alone is overridden by the
     `display: grid` above, so visibility is driven by [hidden]. */
}
.modal[hidden] { display: none; }

.modal-veil {
  position: absolute;
  inset: 0;
  background: var(--veil);
  backdrop-filter: blur(8px);
  animation: veil-in 0.3s var(--ease);
}
@keyframes veil-in { from { opacity: 0; } }

.modal-card {
  position: relative;
  width: min(430px, 100%);
  padding: 30px 30px 24px;
  border-radius: 22px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  box-shadow: var(--shadow-card);
  text-align: left;
  animation: card-in 0.42s var(--ease);
}
@keyframes card-in {
  from { opacity: 0; transform: translateY(16px) scale(0.97); }
}

.modal-card h3 {
  font-size: 1.3rem;
  font-weight: 800;
  letter-spacing: -0.035em;
  margin-bottom: 10px;
}
.modal-card p {
  color: var(--fg-2);
  font-size: 0.92rem;
  line-height: 1.62;
  margin-bottom: 18px;
}

.modal-facts {
  display: flex;
  flex-direction: column;
  gap: 9px;
  margin-bottom: 24px;
  padding: 14px 16px;
  border-radius: 14px;
  background: var(--surface-2);
  border: 1px solid var(--line);
}
.modal-facts div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  font-size: 0.82rem;
}
.modal-facts dt { color: var(--fg-3); }
.modal-facts dd { color: var(--fg); font-weight: 600; }

.modal-actions { display: flex; gap: 10px; }
.modal-actions .btn { flex: 1; padding: 12px 20px; font-size: 0.9rem; }

/* ---------- footer ---------- */

/* Positioned so the oversized mark can anchor to the footer's own
   corners rather than to the padded .wrap column inside it — that is
   what lets it reach the true right edge of the screen. */
.footer {
  position: relative;
  padding: 88px 0 34px;
  border-top: 1px solid var(--line);
  background: var(--bg-2);
  /* Clips the mark where it bleeds past the section's edges. */
  overflow: hidden;
}

/* Brand block sits opposite the link columns. It gets a fixed-ish share
   via minmax so the columns can't squeeze the tagline into a ribbon.

   Lifted above the mark: the mark is absolutely positioned, so without
   a stacking context of its own this content would paint underneath it. */
.footer-top {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: minmax(260px, 1fr) auto;
  gap: 56px 80px;
  align-items: start;
}

.f-logo {
  display: inline-block;
  line-height: 0;
  /* The icon is the one piece of colour down here, so it gets to move. */
  transition: transform 0.5s var(--ease);
}
.f-logo img {
  width: 112px;
  height: 112px;
  /* Matches the launcher icon's own corner radius at this size. */
  border-radius: 26px;
  /* No border or background — the 512px source is a cut-out with alpha. */
}
.f-logo:hover { transform: translateY(-4px) rotate(-3deg); }

.f-tagline {
  margin: 22px 0 0;
  max-width: 34ch;
  color: var(--fg-2);
  font-size: 0.95rem;
  line-height: 1.65;
}

.f-cols {
  display: grid;
  grid-template-columns: repeat(3, minmax(140px, auto));
  gap: 40px;
}
.f-col { display: flex; flex-direction: column; gap: 13px; }
.f-col h3 {
  margin: 0 0 4px;
  color: var(--fg-3);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.f-col a,
.f-col span {
  color: var(--fg-2);
  font-size: 0.9rem;
  text-decoration: none;
  width: fit-content;
}
/* Underline grows from the left rather than appearing all at once. */
.f-col a {
  background-image: linear-gradient(currentColor, currentColor);
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 0% 1px;
  transition: color 0.25s var(--ease), background-size 0.35s var(--ease);
}
.f-col a:hover { color: var(--fg); background-size: 100% 1px; }
/* The third column is statements of fact, not links — mark them as such. */
.f-col span { color: var(--fg-3); cursor: default; }

/* The closing block. Only the word is in flow here — the mark is pinned
   to the footer's bottom-right corner instead, so this row just reserves
   the vertical space the word needs. */
.f-wordmark {
  position: relative;
  z-index: 1;
  margin: 64px 0 30px;
}

/* Outlined rather than filled — at this size a solid word would out-shout
   every real link above it. The stroke keeps it architectural.

   Large enough that the word and the mark together span the full row,
   with the vw term doing the work: the word then grows and shrinks in
   step with the mark beside it and the pair stays balanced at any
   width, instead of the gap between them opening up on wide screens. */
.f-wordmark-text {
  display: block;
  font-size: clamp(2.6rem, 10.4vw, 8.8rem);
  font-weight: 900;
  letter-spacing: -0.045em;
  line-height: 0.8;
  white-space: nowrap;
  color: transparent;
  -webkit-text-stroke: 1px var(--line-2);
  user-select: none;
}

/* Anchored to the footer's bottom-right corner. It is a direct child of
   .footer rather than of .wrap, so `right` is measured from the real
   edge of the screen, not the edge of the centred 1180px column.

   Lifted slightly off the very bottom edge and 10% smaller than before,
   so it sits on the baseline without getting cut. Only the right offset
   is negative, bleeding sideways and trimmed by the footer's overflow.

   Faint enough that the copyright line stays readable across it. */
.f-wordmark-mark {
  position: absolute;
  z-index: 0;
  right: -1%;
  bottom: 24px;
  width: clamp(243px, 37vw, 518px);
  height: auto;
  opacity: 0.16;
  user-select: none;
  pointer-events: none;
}

/* Lifted for the same reason as .footer-top — the mark now reaches up
   into this row from the corner below it. */
.footer-bottom {
  position: relative;
  z-index: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  justify-content: space-between;
  align-items: center;
  padding-top: 26px;
  border-top: 1px solid var(--line);
  color: var(--fg-3);
  font-size: 0.84rem;
}

@media (max-width: 860px) {
  .footer { padding-top: 64px; }
  .footer-top { grid-template-columns: 1fr; gap: 44px; }
  /* Two columns rather than three: at this width three wrap their own
     labels, which looks broken. */
  .f-cols { grid-template-columns: repeat(2, minmax(130px, 1fr)); gap: 32px; }
  .f-logo img { width: 88px; height: 88px; border-radius: 21px; }
  .f-wordmark { margin: 52px 0 28px; }
  /* Still cornered and still bled sideways, just not wide enough to
     swallow the whole row on a phone. */
  .f-wordmark-mark { width: clamp(180px, 42vw, 292px); bottom: 20px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; gap: 8px; }
}

/* Honour the same motion preference the rest of the page does. */
@media (prefers-reduced-motion: reduce) {
  .f-logo, .f-logo:hover { transition: none; transform: none; }
}

/* ---------- animation start states ----------
   GSAP overwrites these once it initialises. They are declared in CSS
   so nothing flashes in its final position on a slow connection. */

[data-anim] { will-change: transform, opacity; }
[data-anim="fade"], [data-anim="device"], [data-anim="stagger"] > li { opacity: 0; }
.ln > span { transform: translateY(105%); }

/* If JS fails or motion is reduced, everything must still be visible. */
.no-js [data-anim], .no-js .ln > span { opacity: 1 !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  [data-anim], [data-anim="stagger"] > li { opacity: 1 !important; }
  .ln > span { transform: none !important; }
}

/* ---------- responsive ---------- */

@media (max-width: 980px) {
  .hero { min-height: auto; padding: 118px 0 70px; }
  .hero-inner { grid-template-columns: 1fr; gap: 54px; text-align: center; }
  .hero-device { order: -1; }
  .lede { margin-inline: auto; }
  .hero-cta, .specs { justify-content: center; }
  .tag { margin-inline: auto; }
  .scroll-hint { display: none; }

  .feat-grid { grid-template-columns: 1fr; gap: 52px; }
  .feat-grid.flip .feat-copy { order: 0; }
  .h-lg { max-width: none; }

  /* With the links hidden the toggle takes over pushing the row apart —
     it is the first thing after the logo, so the auto margin stays on it
     and the menu button rides along beside it. */
  .nav-links { display: none; }
  .theme-btn { margin-left: auto; }
  .menu-btn { display: grid; }
  .menu { display: block; }
  .menu[hidden] { display: none; }
}

@media (max-width: 560px) {
  .wrap, .wrap-narrow { padding: 0 20px; }
  /* The sheet already carries Download; keeping the bar copy as well
     crowds the wordmark off the row on a narrow phone. */
  .nav-inner .btn-sm { display: none; }
  .menu nav { padding: 6px 20px 16px; }
  .feature { padding: 84px 0; }
  .download { padding: 90px 0 96px; }
  .specs { gap: 26px; }
  .store { flex: 1 1 100%; justify-content: center; }
  .shot { width: 100%; max-width: 300px; }
  /* Matches .shot: fill the column rather than sitting at a fixed width
     that would overflow a 320px screen once padding is counted. */
  .seed-card { width: 100%; max-width: 340px; padding: 22px 18px 24px; }
  .seed-grid { gap: 9px 14px; }
  .showcase { padding: 84px 0 76px; }
  .showcase-lede { margin-bottom: 38px; }
}
