/* ========================================================================
   stepkeep — motion.css
   Entrance-animation gate, responsive breakpoints, colour-scheme overrides.
   Split from css/components.css; load order in HTML preserves the cascade.
   ======================================================================== */

/* --- Entrance animation -------------------------------------------------
   Gated on an inline flag rather than the script itself. With scripting off the
   attribute is never set, the sections are plainly visible, and nothing on this
   page depends on JavaScript. With scripting on, main.js adds the attribute and
   an observer animates each section as it arrives.

   A target rests in the keyframe's `from` state, so the observer only ever runs
   the animation forwards. Leaving targets visible until the observer fires is
   what reads as a blink: a section paints at full opacity for the frames between
   entering the viewport and the callback, then snaps to opacity 0 and fades in.
   Resting transparent means the first frame a section is painted in is already
   the start of its fade.

   Resting transparent does leave off-screen sections hidden, which is the blank
   content a marketing page ships to print or to a capture tool. Four guards:
   no-JS and reduced-motion never set the flag; the head script drops the flag if
   main.js never installs the observer; print resets below; and tools/reveal.mjs
   scrolls the page before verify.mjs or preview.mjs audits, screenshots or crops
   it, while the audit fails if any target never reveals.

   `:not(.reveal)` and the explicit `to` keyframe both matter: together they mean
   a target the observer has already revealed stays visible even if the gate is
   dropped afterwards, instead of falling back to the resting rule. */

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

html[data-anim='on']
  :is(
    .section__head,
    .export,
    .step-card,
    .local__proof,
    .plan,
    .business,
    .table-scroll,
    .choose__col,
    .faq__item,
    .cta__inner
  ):not(.reveal) {
  opacity: 0;
  transform: translateY(14px);
}

html[data-anim='on'] .reveal {
  animation: rise 0.5s var(--ease) both;
}

html[data-anim='on'] .reveal[data-rise='1'] { animation-delay: 60ms; }
html[data-anim='on'] .reveal[data-rise='2'] { animation-delay: 120ms; }
html[data-anim='on'] .reveal[data-rise='3'] { animation-delay: 180ms; }

@media print {
  /* Nothing should animate onto paper, and nothing may stay transparent on it. */
  .hero__lead > *,
  .hero__title .marked,
  .desk__screen,
  .desk__page {
    animation: none !important;
    opacity: 1 !important;
    clip-path: none !important;
  }

  html[data-anim='on']
    :is(
      .section__head,
      .export,
      .step-card,
      .local__proof,
      .plan,
      .business,
      .table-scroll,
      .choose__col,
      .faq__item,
      .cta__inner
    ) {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}

/* --- Responsive ---------------------------------------------------------- */

@media (max-width: 62rem) {
  .local,
  .step-card {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Stacked, the claims band would otherwise sit between the buttons and the
     screenshot and put the proof a full screen further down. Three columns are
     kept here rather than two: at two, the third claim sits alone on a
     half-width second row and the three stop reading as one line. */
  .claims {
    gap: var(--sp-4);
  }

  /* Narrower page, the page keeps its share; the window gives up a little
     so the ring at its left stays clear of the sheet. */
  .desk__screen {
    width: 76%;
  }

  .desk__page {
    width: 42%;
    top: 9%;
  }

  .step-card__text {
    order: 2;
  }

  .plans {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .plans .plan--featured {
    grid-column: 1 / -1;
    order: -1;
  }

  .site-footer__inner {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 44rem) {
  .nav-toggle {
    display: inline-flex;
  }

  .site-nav {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: var(--sp-3) var(--gutter) var(--sp-5);
    background: var(--paper);
    border-bottom: var(--border);
    box-shadow: var(--shadow-card);
  }

  .site-nav.is-open {
    display: flex;
  }

  .site-nav > a:not(.btn) {
    padding-block: 0.75rem;
    border-bottom: var(--border);
    background-size: 0% 2px;
  }

  .site-nav .btn {
    margin-top: var(--sp-4);
  }

  .exports,
  .plans,
  .choose,
  .zoom {
    grid-template-columns: minmax(0, 1fr);
  }

  .zoom {
    gap: var(--sp-2);
  }

  .export__shot {
    height: 260px;
  }

  .claims {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4);
  }

  /* On a phone the window fills the column, so the page cannot lie beside
     it. It stacks below instead, in front, pulled up over the window's foot so
     the two still read as one desk and not as two figures. */
  .desk {
    padding-bottom: 0;
  }

  .desk__screen {
    width: 100%;
  }

  .desk__page {
    position: relative;
    right: auto;
    top: auto;
    width: 72%;
    margin: -2.25rem 0 1.25rem auto;
    transform: rotate(1.2deg);
  }

  .desk__sheet {
    aspect-ratio: 0.86;
  }

  .desk__cap {
    max-width: none;
  }

  /* The wipe is written for a page that stands beside the window; stacked, a
     plain fade is the calmer entrance, and it leaves the tilt alone. */
  html[data-anim='on'] .desk__page {
    animation-name: hero-fade;
  }

  /* Full-width buttons: at this width a 50%-wide target beside another one is
     the single most missed tap on a phone. */
  .hero__actions .btn {
    width: 100%;
  }

  /* Stacked, the number reads as a heading over the claim it belongs to, which
     is the next best thing to sitting beside it. */
  .speed {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-3);
  }

  .perm {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem;
  }

  .cta__actions .btn {
    width: 100%;
  }

  .site-footer__nav {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* --- Colour scheme overrides ---------------------------------------------
   The dark blocks in this file are written for the system preference. Once the
   visitor has used the switch in the header, data-theme is set, and these rules
   make that explicit choice win in both directions: an un-guarded
   prefers-color-scheme rule must not repaint a page the visitor asked to keep
   light, and a rule reachable only through the media query would never reach a
   visitor who asked for dark on a light machine. The colour values themselves
   stay in tokens.css, which owns both palettes. */
:root[data-theme='light'] .hero__title .marked {
  background-size: 100% var(--stroke-h);
  background-position: left 0 bottom 0.35em;
  color: inherit;
}
