/* ==========================================================================
   Bethvault, motion layer
   Two kinds of rule live here:

   1. The feature demos (network, upload, feed, drives, search, admin panel).
      Their base styles apply everywhere, because they are real UI that works
      with or without animation — a switch still switches under reduced motion.

   2. Animation, all of it behind html.motion. That class is added in <head>
      only when the visitor hasn't asked for reduced motion, so without it
      every element simply renders in its finished state.
   ========================================================================== */

:root {
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --beam-core: #eafff4;
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
}

/* ==========================================================================
   Shared small controls
   ========================================================================== */
.mini-btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  overflow: hidden;
  font: inherit;
  font-size: .75rem;
  font-weight: 500;
  color: var(--text);
  padding: 7px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 9px;
  background: var(--wash-03);
  cursor: pointer;
  transition: border-color .2s var(--ease), background .2s var(--ease), transform .2s var(--ease);
}
.mini-btn svg { width: 14px; height: 14px; flex: none; }
.mini-btn:hover:not(:disabled) { border-color: var(--text-3); background: var(--wash-06); }
.mini-btn:active:not(:disabled) { transform: scale(.97); }
.mini-btn:disabled { opacity: .45; cursor: default; }
.mini-btn > span { position: relative; z-index: 1; }

.mini-btn-accent { color: var(--accent-ink); border-color: var(--accent-line); background: var(--accent-dim); }
.mini-btn-accent:hover:not(:disabled) { border-color: var(--accent); background: rgba(62,207,142,.18); }
.mini-fill {
  position: absolute; inset: 0;
  background: rgba(62,207,142,.28);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.mini-sw {
  position: relative;
  width: 26px; height: 15px; flex: none;
  border-radius: 999px;
  background: var(--wash-12);
  border: 1px solid var(--line-strong);
  transition: background .25s var(--ease), border-color .25s var(--ease);
}
.mini-sw::after {
  content: "";
  position: absolute; top: 1.5px; left: 1.5px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--text-2);
  transition: transform .3s var(--ease-spring), background .25s var(--ease);
}
[aria-checked="true"] > .mini-sw { background: var(--accent); border-color: var(--accent); }
[aria-checked="true"] > .mini-sw::after { transform: translateX(11px); background: var(--on-accent); }

.caret {
  display: inline-block;
  width: 1.5px; height: 1.05em;
  margin-left: 1px;
  vertical-align: -.15em;
  background: var(--accent);
}
.motion .caret { animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }

/* ==========================================================================
   Feature 1 · No internet — a live network
   Three editors feed the host, the host feeds three destinations, and the
   internet sits above it all on a cable you can pull. The card is two rows
   tall, so the stage grows to fill it.
   ========================================================================== */
.b-card > .net { flex: 1; display: flex; flex-direction: column; }
/* Grid items default to min-width: auto, so the nowrap node labels would set
   a floor the card can't shrink past and it overflows a phone screen. The
   labels ellipsize instead. */
.bento > .b-card, .net-stage > *, .net-col, .b-split > * { min-width: 0; }

.net-stage {
  position: relative;
  flex: 1;
  min-height: 270px;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  column-gap: clamp(26px, 9%, 84px);
}
.net-wires {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  overflow: visible;
  pointer-events: none;
}
.net-wires path {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 1;
}

.net-col {
  position: relative;
  display: flex; flex-direction: column;
  justify-content: space-around;
  gap: 14px;
  height: 100%;
  padding-block: 8px;
}
.net-core {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  /* lift the stack so the hub, not the cloud, sits on the centre line
     where the wires converge */
  margin-top: -84px;
}

.net-node {
  position: relative;
  z-index: 1;
  display: flex; flex-direction: column;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-2);
  line-height: 1.3;
  min-width: 0;
  transition: border-color .4s var(--ease), box-shadow .4s var(--ease);
}
.net-node b { font-size: .75rem; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.net-node small { font-size: .64rem; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.net-col:last-child .net-node { text-align: right; }

.net-hub {
  align-items: center;
  text-align: center;
  padding: 14px 18px 12px;
  border-color: var(--accent-line);
  background: linear-gradient(180deg, rgba(62,207,142,.14), rgba(62,207,142,.05));
  box-shadow: 0 0 40px -12px rgba(62,207,142,.5);
}
.net-hub svg { width: 30px; height: 30px; margin-bottom: 8px; border-radius: 8px; }
.net-hub b { color: var(--accent-ink); }

.net-cloud {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .7rem;
  color: var(--text-2);
  padding: 6px 12px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--bg-2);
  transition: color .35s var(--ease), border-color .35s var(--ease), opacity .35s var(--ease);
}
.net-cloud svg { width: 15px; height: 15px; }
.net-cloud-x { opacity: 0; transition: opacity .3s var(--ease); }

/* The uplink is two halves, so going offline pulls them apart like a cable
   coming out rather than a line quietly fading. */
.net-up {
  width: 1px; height: 38px;
  display: flex; flex-direction: column; justify-content: space-between;
}
.net-up i {
  display: block; width: 1px; height: 50%;
  background-image: linear-gradient(var(--line-strong) 50%, transparent 0);
  background-size: 1px 5px;
  transition: transform .45s var(--ease-spring), opacity .35s var(--ease);
}

/* Packets ride the wires. Each is placed at the stage origin and moved along
   its wire's path, which motion.js writes into offset-path. */
.pkt {
  position: absolute; left: 0; top: 0;
  z-index: 2;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px 1px rgba(62,207,142,.85);
  offset-anchor: center;
  offset-rotate: 0deg;
  opacity: 0;
  pointer-events: none;
}
.motion .pkt { animation: flow var(--dur, 2.2s) cubic-bezier(.45, 0, .55, 1) infinite; animation-delay: var(--delay, 0s); }
.net:not(.is-shown) .pkt,
.net:not(.is-shown) .net-hub { animation-play-state: paused; }
@keyframes flow {
  0%   { offset-distance: 0%;   opacity: 0; }
  10%  { opacity: 1; }
  90%  { opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}
@supports not (offset-path: path("M0 0")) { .pkt { display: none; } }

.motion .net-hub { animation: hub 2.2s ease-in-out infinite; }
@keyframes hub {
  0%, 100% { box-shadow: 0 0 40px -12px rgba(62,207,142,.5); }
  50%      { box-shadow: 0 0 56px -8px rgba(62,207,142,.8); }
}
/* a node flashes as a packet reaches it */
.net-node.ping { border-color: var(--accent-line); box-shadow: 0 0 18px -6px rgba(62,207,142,.7); transition-duration: 0s; }

.net-foot {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 8px 14px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.net-switch {
  display: inline-flex; align-items: center; gap: 8px;
  font: inherit; font-size: .75rem; font-weight: 500;
  color: var(--text);
  padding: 5px 11px 5px 7px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  background: var(--wash-03);
  cursor: pointer;
  transition: border-color .2s var(--ease);
}
.net-switch:hover { border-color: var(--text-3); }
.net-status { font-size: .74rem; color: var(--text-3); transition: color .3s var(--ease); }
.net-count {
  margin-left: auto;
  font-size: .74rem; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}
.net-count b { color: var(--text); font-weight: 600; display: inline-block; }

/* offline */
.net.offline .net-cloud { color: var(--text-3); border-style: dashed; opacity: .6; }
.net.offline .net-cloud-x { opacity: 1; }
.net.offline .net-up i:first-child { transform: translateY(-6px); opacity: .35; }
.net.offline .net-up i:last-child  { transform: translateY(6px);  opacity: .35; }
.net.offline .net-status { color: var(--accent-ink); }

.motion .net-count b.tick { animation: tick .45s var(--ease-out); }
@keyframes tick {
  0%   { transform: translateY(40%); opacity: 0; color: var(--accent-ink); }
  100% { transform: none; opacity: 1; }
}

/* ==========================================================================
   Feature 2 · Resumable upload
   ========================================================================== */
.rs {
  padding: 14px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--bg-2);
}
.rs-top { display: flex; align-items: center; gap: 10px; }
.rs-file, .rs-bytes { font-family: var(--mono); font-size: .68rem; }
.rs-file { color: var(--text); }
.rs-bytes { margin-left: auto; color: var(--text-3); font-variant-numeric: tabular-nums; }
.rs-wifi { color: var(--accent-ink); display: grid; transition: color .25s var(--ease); }
.rs-wifi svg { width: 15px; height: 15px; }
.rs-slash { opacity: 0; transition: opacity .2s var(--ease); }

.rs-bar {
  position: relative;
  height: 4px; margin: 11px 0 9px;
  border-radius: 99px;
  background: var(--wash-08);
}
.rs-bar i {
  position: absolute; inset: 0 auto 0 0;
  border-radius: inherit;
  background: var(--accent);
  box-shadow: 0 0 12px rgba(62,207,142,.55);
  transition: background .25s var(--ease), box-shadow .25s var(--ease);
}
/* where the transfer stopped — stays visible after it resumes, so you can
   see it carried on from there rather than from zero */
.rs-mark {
  position: absolute; top: -4px; bottom: -4px;
  width: 2px; margin-left: -1px;
  border-radius: 2px;
  background: var(--warn);
  box-shadow: 0 0 8px var(--warn);
}
.rs-state {
  font-size: .74rem;
  color: var(--text-2);
  min-height: 1.5em;
  margin-bottom: 11px;
  transition: color .25s var(--ease);
}

.rs.dropped .rs-bar i { background: var(--warn); box-shadow: 0 0 12px rgba(240,180,41,.5); }
.rs.dropped .rs-wifi { color: var(--warn-ink); }
.rs.dropped .rs-slash { opacity: 1; }
.rs.dropped .rs-state { color: var(--warn-ink); }
.rs.resuming .rs-state { color: var(--accent-ink); }
.rs.done .rs-state { color: var(--accent-ink); }

.motion .rs.dropped .rs-bar i { animation: stall 1s ease-in-out infinite; }
@keyframes stall { 50% { opacity: .45; } }
.motion .rs-mark:not([hidden]) { animation: drop-in .35s var(--ease-spring); }
@keyframes drop-in { from { transform: scaleY(0); } }

/* ==========================================================================
   Feature 3 · Attribution feed
   ========================================================================== */
.feed {
  position: relative;
  height: 184px;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(180deg, #000 70%, transparent);
          mask-image: linear-gradient(180deg, #000 70%, transparent);
}
.feed-list { display: flex; flex-direction: column; gap: 8px; }
.feed-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--bg-2);
}
.av {
  width: 26px; height: 26px; flex: none;
  display: grid; place-items: center;
  border-radius: 50%;
  font-size: .6rem; font-weight: 700; letter-spacing: .02em;
  color: hsl(var(--h) 70% 82%);
  background: hsl(var(--h) 40% 24%);
  border: 1px solid hsl(var(--h) 45% 36%);
}
/* in light, the initials become tinted chips rather than dark discs */
:root[data-theme="light"] .av {
  color: hsl(var(--h) 45% 28%);
  background: hsl(var(--h) 60% 92%);
  border-color: hsl(var(--h) 45% 80%);
}
.feed-txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.feed-txt b {
  font-family: var(--mono); font-size: .68rem; font-weight: 400;
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.feed-txt small { font-size: .68rem; color: var(--text-3); }
.feed-row.is-new { border-color: var(--accent-line); }
.feed-row.is-new small { color: var(--accent-ink); }
.feed-row { transition: border-color 1.2s var(--ease); }
.feed-row small { transition: color 1.2s var(--ease); }

/* ==========================================================================
   Feature 4 · Pick a drive
   ========================================================================== */
.drv-list { position: relative; display: flex; flex-direction: column; gap: 4px; }
.drv-ind {
  position: absolute; left: 0; right: 0; top: 0;
  height: 38px;
  border: 1px solid var(--accent-line);
  border-radius: 10px;
  background: var(--accent-dim);
  pointer-events: none;
  transition: transform .5s var(--ease-out), height .5s var(--ease-out);
}
.drv-opt {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  font: inherit; font-size: .8rem;
  color: var(--text-2);
  text-align: left;
  padding: 9px 12px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: none;
  cursor: pointer;
  transition: color .25s var(--ease);
}
.drv-opt svg { width: 16px; height: 16px; flex: none; opacity: .7; }
.drv-opt small { margin-left: auto; font-size: .66rem; color: var(--text-3); }
.drv-opt:hover { color: var(--text); }
.drv-opt[aria-checked="true"] { color: var(--text); }
.drv-opt[aria-checked="true"] svg { color: var(--accent-ink); opacity: 1; }
.drv-path {
  margin-top: 12px;
  padding: 9px 11px;
  font-family: var(--mono); font-size: .66rem;
  color: var(--accent-ink);
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--bg-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ==========================================================================
   Feature 5 · Search
   ========================================================================== */
/* The wide card shares a row with the storage card, which is taller, so
   grid stretches it. The demo panel stretches with it and the results list
   takes up the extra height, showing as many rows as fit. */
.b-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: 32px;
  align-items: stretch;
}
.b-split > .sr { display: flex; flex-direction: column; }
.b-copy { display: flex; flex-direction: column; }
.b-split > .sr { margin-top: 0; }

.sr {
  padding: 12px;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--bg-1);
  box-shadow: 0 24px 50px -30px var(--shadow-deep);
}
.sr-field {
  display: flex; align-items: center; gap: 9px;
  padding: 0 11px;
  height: 38px;
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--bg-2);
  cursor: text;
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
}
.sr-field:focus-within { border-color: var(--accent-line); box-shadow: 0 0 0 3px var(--accent-dim); }
.sr-field svg { width: 15px; height: 15px; color: var(--text-3); flex: none; }
.sr-field input {
  flex: 1; min-width: 0;
  font: inherit; font-size: .82rem;
  color: var(--text);
  background: none; border: 0; outline: 0;
}
.sr-field input::placeholder { color: var(--text-3); }
.sr-field input::-webkit-search-cancel-button { display: none; }
.sr-scope {
  font-size: .6rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-3);
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: 999px;
  white-space: nowrap;
}

/* Fixed height: results change on every keystroke and the card must not
   jump while someone is typing. */
.sr-results {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1;
  /* an explicit minimum (not auto) so the rows inside never push the list
     taller — its height comes from the card, never from the results */
  min-height: 172px;
  margin: 8px 0;
  overflow: hidden;
}
.sr-row {
  display: flex; align-items: center; gap: 10px;
  height: 41px; flex: none;
  padding: 0 8px;
  border-radius: 8px;
  transition: background .25s var(--ease);
}
.sr-thumb {
  position: relative;
  width: 30px; height: 24px; flex: none;
  border-radius: 5px;
  background: linear-gradient(135deg, hsl(var(--h) 38% 24%), hsl(var(--h) 42% 44%));
}
.sr-thumb::after {
  content: "";
  position: absolute; inset: 0;
  display: grid; place-items: center;
  border-radius: inherit;
  background: var(--accent) no-repeat center / 10px
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2 6.2l2.6 2.5L10 3.4' fill='none' stroke='%2304120c' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  opacity: 0;
  transform: scale(.6);
  transition: opacity .2s var(--ease), transform .3s var(--ease-spring);
}
.sr-row.sel { background: var(--accent-dim); }
.sr-row.sel .sr-thumb::after { opacity: 1; transform: none; }
.sr-txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.sr-name {
  font-family: var(--mono); font-size: .7rem;
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sr-name mark { background: rgba(62,207,142,.22); color: var(--accent-hi); border-radius: 3px; padding: 0 1px; }
:root[data-theme="light"] .sr-name mark { color: var(--accent-ink); }
.sr-dir { font-size: .66rem; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sr-empty { font-size: .78rem; color: var(--text-3); padding: 12px 8px; }

.sr-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--line);
}
.sr-count { font-size: .72rem; color: var(--text-3); font-variant-numeric: tabular-nums; }

.motion .sr-row.enter { animation: row-in .4s var(--ease-out) both; animation-delay: calc(var(--i, 0) * 40ms); }
@keyframes row-in { from { opacity: 0; transform: translateY(6px); } }

/* ==========================================================================
   How it works · the rail
   ========================================================================== */
.steps-rail { display: none; }

@media (min-width: 1001px) {
  .steps-rail {
    position: relative;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 18px;
    height: 12px;
    margin-top: 52px;
  }
  .steps-rail + .steps { margin-top: 22px; }
  /* nodes line up with each card's number badge: 26px card padding + half
     the 30px badge */
  .rail-node {
    position: relative;
    left: 35px;
    width: 12px; height: 12px;
    border-radius: 50%;
    background: var(--bg);
    border: 1.5px solid var(--line-strong);
    transition: border-color .4s var(--ease), background .4s var(--ease), box-shadow .4s var(--ease);
  }
  .rail-node.lit {
    border-color: var(--accent);
    background: var(--accent);
    box-shadow: 0 0 0 5px var(--accent-dim), 0 0 18px rgba(62,207,142,.6);
  }
  .rail-track {
    position: absolute;
    top: 5.5px; left: 41px;
    width: calc((100% - 36px) * 2 / 3 + 36px);
    height: 1px;
    background: var(--line-strong);
  }
  .rail-fill {
    position: absolute; inset: 0;
    background: linear-gradient(90deg, var(--accent), var(--accent-hi));
    box-shadow: 0 0 10px rgba(62,207,142,.7);
    transform: scaleX(0);
    transform-origin: 0 50%;
  }
}

/* a step "powers on" when the rail reaches it (or, on narrow screens, when it
   scrolls into view) */
.motion .step { transition: border-color .5s var(--ease), box-shadow .5s var(--ease), opacity .6s var(--ease) var(--d, 0s), transform .6s var(--ease) var(--d, 0s); }
.motion .step .step-n {
  background: var(--bg-3);
  color: var(--text-3);
  transition: background .4s var(--ease), color .4s var(--ease), box-shadow .5s var(--ease);
}
.motion .step.lit { border-color: var(--accent-line); box-shadow: 0 0 60px -30px rgba(62,207,142,.5); }
.motion .step.lit .step-n {
  background: var(--accent);
  color: var(--on-accent);
  box-shadow: 0 0 0 5px var(--accent-dim), 0 0 22px rgba(62,207,142,.45);
}

.motion .step-chips span {
  opacity: 0;
  transform: translateY(6px) scale(.9);
  transition: opacity .35s var(--ease-out), transform .5s var(--ease-spring),
              color .3s var(--ease), border-color .3s var(--ease), background .3s var(--ease);
  transition-delay: calc(var(--i, 0) * 110ms + .2s);
}
.motion .step.lit .step-chips span { opacity: 1; transform: none; }
.step-chips span.joined { color: var(--accent-ink); border-color: var(--accent-line); background: var(--accent-dim); }

.motion [data-live-count].tick { display: inline-block; animation: tick .45s var(--ease-out); }

/* ==========================================================================
   Admin panel · working switches and a swappable event
   ========================================================================== */
button.sw {
  padding: 0;
  cursor: pointer;
  font: inherit;
}
button.sw:hover { border-color: var(--text-3); }
.motion .sw::after { transition: transform .35s var(--ease-spring), background .2s var(--ease); }
.panel-note { transition: color .6s var(--ease); }
.panel-note.flash { color: var(--accent-ink); transition-duration: 0s; }

.scope-events { display: flex; flex-direction: column; gap: 6px; }
.scope-ev {
  width: 100%;
  font: inherit; font-size: .85rem;
  color: var(--text);
  text-align: left;
  cursor: pointer;
  transition: border-color .35s var(--ease), background .35s var(--ease);
}
.scope-ev:not(.scope-active):hover { border-color: var(--line-strong); }
.scope-ev .tag-mute { margin-left: auto; }
.scope-ev .tag-accent { margin-left: 6px; }
.scope-ev.scope-active .tag-mute { display: none; }
.scope-ev.scope-active .tag-accent { margin-left: auto; }
.scope-ev:not(.scope-active) b { font-weight: 500; color: var(--text-2); }

/* checklist items tick in one by one */
.motion .check-list li {
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 80ms + .25s);
}
.motion .split-copy.in .check-list li { opacity: 1; transform: none; }

/* ==========================================================================
   Page-wide motion
   ========================================================================== */

/* scroll progress hairline */
.scroll-progress {
  position: fixed; inset: 0 0 auto;
  height: 2px;
  z-index: 200;
  pointer-events: none;
}
.scroll-progress i {
  display: block; height: 100%;
  background: linear-gradient(90deg, transparent, var(--accent) 30%, var(--accent-hi));
  box-shadow: 0 0 10px rgba(62,207,142,.7);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
html:not(.motion) .scroll-progress { display: none; }

/* headings: each word rises out of a blur */
.motion .split-h.reveal { opacity: 1; transform: none; }
.motion .split-h .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(.42em) rotate(2deg);
  filter: blur(10px);
  transition: opacity .8s var(--ease-out), transform 1s var(--ease-out), filter .8s var(--ease-out);
  transition-delay: calc(var(--d, 0s) + var(--i) * 55ms);
}
.motion .split-h.in .w { opacity: 1; transform: none; filter: blur(0); }
/* once finished, drop the filter so text renders crisply and cheaply */
.motion .split-h.done .w { filter: none; transition: none; }

/* cursor spotlight on cards */
.motion .spot { position: relative; isolation: isolate; }
.motion .spot::before,
.motion .spot::after {
  content: "";
  position: absolute;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s var(--ease);
}
.motion .spot::before {
  inset: 0;
  z-index: -1;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%), rgba(62,207,142,.085), transparent 45%);
}
/* a brighter ring where the cursor meets the border */
.motion .spot::after {
  inset: -1px;
  padding: 1px;
  background: radial-gradient(240px circle at var(--mx, 50%) var(--my, 50%), rgba(62,207,142,.7), transparent 65%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}
.motion .spot:hover::before,
.motion .spot:hover::after { opacity: 1; }

/* magnetic buttons with a light sweep */
.motion .btn-mag {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  transform: translate(var(--tx, 0px), var(--ty, 0px));
  transition: background .18s var(--ease), transform .45s var(--ease-out);
}
.motion .btn-mag::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 30%, rgba(255,255,255,.55) 50%, transparent 70%);
  transform: translateX(-130%);
  pointer-events: none;
}
.motion .btn-mag:hover::after { animation: sheen .9s var(--ease-out); }
.motion .hero .btn-mag::after { animation: sheen-idle 6s var(--ease-out) 2.2s infinite; }
@keyframes sheen { to { transform: translateX(130%); } }
@keyframes sheen-idle {
  0%   { transform: translateX(-130%); }
  18%, 100% { transform: translateX(130%); }
}

/* ---------- hero ---------- */
.motion .pill-dot { animation: pulse 2.4s var(--ease) infinite; }
.motion .hero-glow { animation: breathe 7s ease-in-out infinite; }
.motion .cta-glow {
  left: var(--gx, 50%);
  animation: breathe 7s ease-in-out infinite;
}
@keyframes breathe {
  50% { scale: 1.08; opacity: .8; }
}

/* the window flattens out of its tilt as you scroll to it, and leans a
   little towards the pointer; the values are written by motion.js */
@media (min-width: 721px) {
  .motion .app-window {
    transform:
      translateY(var(--hy, 0px))
      rotateX(var(--rx, 7deg))
      rotateY(var(--ry, 0deg))
      scale(var(--hs, .985));
    will-change: transform;
  }
}

/* photos land one at a time, as uploads would */
.motion .app-grid .tile {
  opacity: 0;
  transform: translateY(12px) scale(.92);
  filter: blur(6px);
  transition: opacity .6s var(--ease-out), transform .8s var(--ease-out), filter .6s var(--ease-out),
              box-shadow .3s var(--ease);
  transition-delay: calc(var(--i) * 110ms);
}
.motion .app-grid.landed .tile { opacity: 1; transform: none; filter: none; }
.motion .app-grid.settled .tile { transition-delay: 0s; }
.motion .app-grid .tile::before {
  content: "";
  position: absolute; inset: 0;
  z-index: 1;
  background: linear-gradient(100deg, transparent 25%, rgba(255,255,255,.22) 50%, transparent 75%);
  transform: translateX(-110%);
}
.motion .app-grid.landed .tile::before {
  animation: sheen .9s var(--ease-out) both;
  animation-delay: calc(var(--i) * 110ms + .25s);
}
.motion .tile-tag {
  opacity: 0;
  transform: translateY(5px) scale(.9);
  transition: opacity .4s var(--ease-out), transform .5s var(--ease-spring);
  transition-delay: calc(var(--i) * 110ms + .45s);
}
.motion .app-grid.landed .tile-tag { opacity: 1; transform: none; }

.tile.picked { box-shadow: 0 0 0 2px var(--accent), 0 0 26px -4px rgba(62,207,142,.7); }
.tile.fresh  { animation: fresh 1.6s var(--ease-out); }
@keyframes fresh {
  0%   { box-shadow: 0 0 0 0 rgba(62,207,142,.9); }
  100% { box-shadow: 0 0 0 14px rgba(62,207,142,0); }
}
.tb-accent.press { animation: press .35s var(--ease-out); }
@keyframes press {
  40% { transform: scale(.82); box-shadow: 0 0 0 6px rgba(62,207,142,.25); }
}

.live-cursor {
  position: absolute; left: 0; top: 0;
  z-index: 5;
  display: none;
  pointer-events: none;
  transition: transform 1.15s var(--ease-out), opacity .4s var(--ease);
}
.live-cursor svg {
  width: 16px; height: 16px;
  fill: #7aa7ff;
  stroke: #0a0a0a; stroke-width: 1;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,.6));
  transition: transform .15s var(--ease);
}
.live-cursor span {
  position: absolute; left: 13px; top: 14px;
  font-size: .62rem; font-weight: 600;
  white-space: nowrap;
  color: #fff;
  padding: 2px 7px;
  border-radius: 6px 999px 999px 999px;
  background: #3d6fe0;
}
.live-cursor.click svg { transform: scale(.8); }
@media (min-width: 721px) {
  .motion .live-cursor { display: block; opacity: 0; }
  .motion .live-cursor.on { opacity: 1; }
}

/* ---------- stats ---------- */
.stat b { font-variant-numeric: tabular-nums; }

/* ---------- pricing: a beam of light runs round the card ---------- */
@property --beam {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}
.motion .free-card { position: relative; isolation: isolate; }
.motion .free-card::before {
  content: "";
  position: absolute; inset: 0;
  z-index: 3;
  border-radius: inherit;
  padding: 1.5px;
  background: conic-gradient(from var(--beam),
    transparent 0 62%,
    rgba(62,207,142,.0) 66%,
    rgba(62,207,142,.95) 80%,
    var(--beam-core, #eafff4) 84%,
    rgba(62,207,142,.95) 88%,
    transparent 94%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  animation: beam 5.5s linear infinite;
}
@keyframes beam { to { --beam: 360deg; } }

.motion .price-free {
  background: linear-gradient(100deg, var(--text) 40%, var(--accent-hi) 50%, var(--text) 60%) 100% 0 / 250% 100%;
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}
.motion .free-card.in .price-free { animation: glint 1.6s var(--ease-out) .5s both; }
.price-free span { -webkit-text-fill-color: var(--text-3); color: var(--text-3); }
@keyframes glint { from { background-position: 100% 0; } to { background-position: 0 0; } }

.motion .free-list li {
  opacity: 0;
  transform: translateX(-8px);
  transition: opacity .5s var(--ease-out), transform .6s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 70ms + .35s);
}
.motion .free-card.in .free-list li { opacity: 1; transform: none; }

/* ---------- responsive ---------- */
@media (max-width: 1000px) {
  .b-split { grid-template-columns: minmax(0, 1fr); gap: 26px; }
}
@media (max-width: 720px) {
  .net-foot .net-count { margin-left: 0; width: 100%; }
  /* A phone has roughly 280px for three columns. The device lines are the
     first thing to go, so the names themselves read in full. */
  .net-stage { column-gap: 14px; min-height: 230px; }
  .net-node { padding: 8px 9px; }
  .net-node small { display: none; }
  .net-node b { font-size: .7rem; white-space: normal; overflow-wrap: anywhere; }
  .net-hub { padding: 10px 10px 9px; }
  .net-hub svg { width: 24px; height: 24px; margin-bottom: 6px; }
  .net-core { margin-top: -72px; }
}

/* ---------- reduced motion: final states, no loops ---------- */
@media (prefers-reduced-motion: reduce) {
  .pkt { display: none; }
}
html:not(.motion) .pkt { display: none; }

:root[data-theme="light"] { --beam-core: #0b7a50; }
/* the search panel floats on a softer shadow over white */
:root[data-theme="light"] .sr { box-shadow: 0 18px 40px -26px var(--shadow-deep); }
