/* ============================================================
   Void Notes — motion
   Companion to motion.js. Every hidden state below is keyed to
   an attribute that only the script writes, so this stylesheet
   can never hide anything on its own.
   ============================================================ */

/* ------------------------------------------------------------
   ARRIVAL
   Two gestures, one shape: content lifts a little and settles.
   The hero adds a touch of scale because it is the first thing
   seen and can afford it; the rest below the fold does not.
   ------------------------------------------------------------ */
:root {
  --rise-step: 70ms;
  --rise-ease: cubic-bezier(0.16, 0.84, 0.24, 1);
}

[data-enter],
[data-rise] {
  opacity: 0;
  will-change: opacity, transform;
}
[data-enter] {
  transform: translate3d(0, 22px, 0) scale(0.985);
  transition:
    opacity 760ms var(--rise-ease) calc(var(--rise-i, 0) * 90ms + 60ms),
    transform 900ms var(--rise-ease) calc(var(--rise-i, 0) * 90ms + 60ms);
}
[data-rise] {
  transform: translate3d(0, 26px, 0);
  transition:
    opacity 620ms var(--rise-ease) calc(var(--rise-i, 0) * var(--rise-step)),
    transform 720ms var(--rise-ease) calc(var(--rise-i, 0) * var(--rise-step));
}
[data-enter='in'],
[data-rise='in'] {
  opacity: 1;
  transform: translate3d(0, var(--drift, 0px), 0);
  will-change: auto;
}

/* Parallax keeps moving after arrival, so it needs a transform
   transition slack enough to be overwritten every frame. */
.kh-hero-phone[data-enter='in'],
.kodehead-identity[data-rise='in'] {
  transition:
    opacity 760ms var(--rise-ease) 140ms,
    transform 420ms cubic-bezier(0.33, 1, 0.68, 1);
}

/* Cards lean in from the side they sit on: the ledger reads as
   a row assembling, not four boxes doing the same thing. */
.feature-ledger > article[data-rise] { transform: translate3d(0, 30px, 0) rotate(-0.35deg); }
.feature-ledger > article[data-rise]:nth-child(even) { transform: translate3d(0, 30px, 0) rotate(0.35deg); }
.product-facts > div[data-rise] { transform: translate3d(0, 18px, 0); }

/* ------------------------------------------------------------
   PROGRESS RAIL
   One lime hairline across the very top. It is the only thing
   on the page that knows the whole document at once.
   ------------------------------------------------------------ */
.motion-rail {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 40;
  width: 100%;
  height: 2px;
  pointer-events: none;
  background: linear-gradient(90deg, rgba(191, 206, 127, 0.16), rgba(191, 206, 127, 0.05));
  opacity: 0;
  transition: opacity 320ms ease;
}
html[data-scrolled] .motion-rail { opacity: 1; }
.motion-rail i {
  display: block;
  height: 100%;
  transform: scaleX(var(--motion-progress, 0));
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--lime-deep), var(--lime));
  box-shadow: 0 0 14px rgba(191, 206, 127, 0.55);
}

/* ------------------------------------------------------------
   HEADER
   The index bar is transparent over the hero and gains a
   full-bleed smoked panel once the page has moved. The detail
   pages already carry their own opaque bar, so they get the
   condense and a lit underline instead of a second panel.
   ------------------------------------------------------------ */
.site-header:not(.detail-header)::before {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 100vw;
  height: 100%;
  z-index: -1;
  transform: translateX(-50%) scaleY(0.4);
  transform-origin: 50% 0;
  background: linear-gradient(180deg, rgba(19, 20, 13, 0.92), rgba(19, 20, 13, 0.66));
  backdrop-filter: blur(14px) saturate(120%);
  border-bottom: 1px solid var(--line);
  opacity: 0;
  transition: opacity 340ms ease, transform 340ms var(--rise-ease);
}
html[data-scrolled] .site-header:not(.detail-header)::before {
  opacity: 1;
  transform: translateX(-50%) scaleY(1);
}
.site-header { transition: height 320ms var(--rise-ease); }
.detail-header {
  transition:
    height 320ms var(--rise-ease),
    border-color 340ms ease,
    box-shadow 340ms ease,
    background-color 340ms ease;
}
html[data-scrolled] .detail-header {
  background: rgba(9, 10, 10, 0.96);
  border-bottom-color: rgba(191, 206, 127, 0.34);
  box-shadow: 0 14px 34px rgba(0, 0, 0, 0.45);
}
html[data-scrolled] .site-header { height: 56px; }
@media (max-width: 900px) {
  html[data-scrolled] .site-header { height: 52px; }
}

/* ------------------------------------------------------------
   HOVER LIFE
   A single sheen that crosses a button once on hover, and a
   lime edge that grows out of the link it belongs to.
   ------------------------------------------------------------ */
.button {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.button::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 -60%;
  width: 45%;
  z-index: -1;
  transform: skewX(-18deg);
  background: linear-gradient(90deg, transparent, rgba(228, 255, 133, 0.28), transparent);
  transition: transform 620ms cubic-bezier(0.33, 1, 0.68, 1);
}
.button-primary::after { background: linear-gradient(90deg, transparent, rgba(19, 20, 13, 0.22), transparent); }
.button:hover::after { transform: translateX(360%) skewX(-18deg); }

.text-link { position: relative; }
.text-link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 100%;
  height: 1px;
  background: var(--lime);
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform 380ms var(--rise-ease);
}
.text-link:hover::after,
.text-link:focus-visible::after { transform: scaleX(1); }

/* Reduced motion: the script writes no hidden state, so there
   is nothing to reveal. Kill what is left — the sheen, the
   header's slide, the rail's own animation. */
@media (prefers-reduced-motion: reduce) {
  .button::after,
  .text-link::after { transition: none; }
  .site-header,
  .site-header::before,
  .detail-header,
  .motion-rail,
  .motion-rail i { transition: none; }
  .site-header:not(.detail-header)::before { transform: translateX(-50%); }
}
