/* MomentSearch - type, motion and texture: the parts Tailwind can't express.
 *
 * The design tokens (paper / ink / coral / line, Newsreader + Geologica) are declared
 * in landing.html's <head>. NOTHING here introduces a colour or a typeface that
 * isn't already in that palette - this file only adds movement.
 *
 * Motion budget: entrances are one 60ms cascade, there are no looping
 * animations, and everything collapses under prefers-reduced-motion (bottom).
 */

/* ── Base ──────────────────────────────────────────────────────────────────── */
html,body{background:#FBF7F0;color:#1E1B16;font-family:Geologica,system-ui,sans-serif}
.display{font-family:Newsreader,Georgia,serif}

/* A display line that ends on a single short word reads as a mistake at any
   size, and this headline did it at every breakpoint ("...in any / video.").
   balance evens the line lengths; pretty only guards the last line, which is
   what body copy needs. Both degrade to normal wrapping where unsupported. */
.balance{text-wrap:balance}
.pretty{text-wrap:pretty}
::selection{background:rgba(255,90,60,.22)}
/* Solid ember rather than a translucent coral haze: the translucent version
   composited to ~1.5:1 against the paper background, well under the 3:1 WCAG
   2.2 minimum for a focus indicator. Ember is already a page token (the step
   numerals use it), so this borrows rather than adds a color. */
a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible{
  outline:3px solid #B82C14;outline-offset:3px}

/* A field that failed its own check, in the same ember as the focus ring and
   the error copy - one color for "something here needs attention". */
input.field-invalid,textarea.field-invalid{border-color:#B82C14}

/* Product first, with the maker directly beneath and one clear action right. */
.brand-lockup{display:flex;flex-direction:column;align-items:flex-start;line-height:1}
.brand-main{display:flex;align-items:center;color:#1E1B16;text-decoration:none}
.brand-name{display:inline-flex;align-items:center;font-size:1.1rem;font-weight:700;letter-spacing:-.035em}
.brand-o{width:.78em;height:.78em;margin:0 .015em;overflow:visible;color:#1E1B16;flex:none}
.brand-o-footer{font-size:1em}
.brand-search{color:#FF5A3C}
.brand-powered{display:flex;align-items:center;gap:.3rem;margin-top:.35rem;color:#6B6357;
  font-size:.625rem;font-weight:500;text-decoration:none}
.brand-powered img{width:auto;height:1rem;opacity:.82;transition:opacity .2s ease}
.brand-powered:hover img{opacity:1}

/* ── Paper grain ───────────────────────────────────────────────────────────
 * A single fixed multiply layer so the whole page reads as printed stock
 * rather than a flat #FBF7F0 fill. Purely decorative, never interactive.     */
.grain{position:fixed;inset:0;z-index:60;pointer-events:none;opacity:.04;mix-blend-mode:multiply;
  background-repeat:repeat;background-size:180px 180px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/></filter><rect width='180' height='180' filter='url(%23n)'/></svg>")}

/* ── Entrance cascade ──────────────────────────────────────────────────────
 * Every above-the-fold element gets .rise and a --d slot; 60ms apart.        */
.rise{opacity:0;animation:rise .62s cubic-bezier(.22,.68,.36,1) forwards;
  animation-delay:calc(var(--d,0)*60ms)}
@keyframes rise{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}

/* The Newsreader headline arrives word by word, with a slight settle. */
.word{display:inline-block;opacity:0;animation:word .7s cubic-bezier(.22,.68,.36,1) forwards;
  animation-delay:calc(200ms + var(--i,0)*70ms)}
@keyframes word{from{opacity:0;transform:translateY(20px) rotate(1.4deg)}to{opacity:1;transform:none}}

/* …then the coral word writes itself. A nib sweeps left to right and the
 * letters appear behind it, so the word reads as being written rather than
 * faded in. The clip and the nib share one easing curve and one duration, which
 * is what keeps the pen tip exactly on the edge of the ink for the whole sweep
 * - they are two properties of the same motion, so they must not drift.
 *
 * The curve is symmetric ease-in-out on purpose: a hand accelerates, holds a
 * steady pace across the word, then settles. A front-loaded ease-out curve
 * makes the pen sprint through the first letters and crawl through the last,
 * which reads as a wipe rather than as writing.
 *
 * The word's .word wrapper still runs the cascade, but its content starts fully
 * clipped, so nothing shows through early: the slot arrives empty and then
 * fills. WRITE_DELAY (.75s) lands just after the cascade has settled.          */
.mark{position:relative;white-space:nowrap}
.mark .ink{display:inline-block;clip-path:inset(0 100% 0 0);
  animation:write 1.05s .75s cubic-bezier(.42,0,.58,1) forwards}
@keyframes write{to{clip-path:inset(0 -.06em 0 0)}}

/* The nib: a short coral stroke at the leading edge that lifts off the page
 * once the word is finished. Its own width is excluded from the travel so it
 * stops flush with the final letter instead of overshooting.                  */
.mark .nib{position:absolute;top:.1em;bottom:.2em;left:0;width:2px;border-radius:2px;
  background:#FF5A3C;opacity:0;transform:translateX(-1px);
  animation:nib 1.05s .75s cubic-bezier(.42,0,.58,1) forwards}
@keyframes nib{
  0%{left:0;opacity:1}
  88%{opacity:1}
  100%{left:100%;opacity:0}
}

/* …and it signs off with the underline, once the writing is done (.75 + 1.05).
 * non-scaling-stroke keeps the line an even weight despite the non-uniform
 * scale that stretching the viewBox to the word's width implies.            */
.mark svg{position:absolute;left:0;width:100%;bottom:-.16em;height:.36em;overflow:visible}
.mark path{fill:none;stroke:#FF5A3C;stroke-width:3.4;stroke-linecap:round;
  vector-effect:non-scaling-stroke;stroke-dasharray:340;stroke-dashoffset:340;
  animation:draw .75s 1.8s cubic-bezier(.4,0,.2,1) forwards}
@keyframes draw{to{stroke-dashoffset:0}}

/* ── Step spine ────────────────────────────────────────────────────────────
 * The hairline through the numerals says "sequence", which only works if it
 * actually touches the first and last numeral. So its ends are derived from the
 * grid rather than eyeballed: each numeral's centre sits 1.5rem into its column
 * (0.5rem of column padding + half of the 2rem dot), and the three columns
 * divide the row minus its two 2rem gaps. The half-pixel pulls the 1px rule onto
 * the dots' optical centre instead of a pixel below it.                       */
.spine{top:calc(1rem - .5px);left:1.5rem;right:calc((100% - 4rem) / 3 - 1.5rem)}

/* Solid coral badge rather than an outline on paper: at 32px an outline reads
   as a bullet, not a step in a sequence - the filled dot is what the spine
   (above) and the mobile rail (below) both need to visually anchor into. */
.step-num{top:.125rem}

/* Below sm the grid is one column and .spine (a horizontal rule) has nothing
   to span, so the sequence cue would vanish entirely without this: a vertical
   rail down the badge column. Row heights vary with text wrap, so this can't
   be pinned to the last badge's exact centre the way .spine pins to a fixed
   row height - anchoring 1rem short of both ends (roughly the first and last
   badge's centre) reads as "runs through the steps" without needing that. */
.spine-v{top:1rem;left:1rem;bottom:1rem}

/* ── "Try it live" chooser ─────────────────────────────────────────────────
 * Opens in the page rather than over it: nothing to dismiss, and on a slide the
 * choice stays visible next to the thing that prompted it. Grid rows let the
 * content open smoothly without guessing its height; visibility removes the
 * collapsed cards from the tab order.                                       */
.reveal{display:grid;grid-template-rows:0fr;margin-top:0;opacity:0;visibility:hidden;
  transition:grid-template-rows .45s cubic-bezier(.22,.68,.36,1),opacity .3s ease,visibility .45s}
.reveal>div{min-height:0;overflow:hidden}
.reveal.open{grid-template-rows:1fr;margin-top:1.5rem;opacity:1;visibility:visible}
.chev{display:inline-block;transition:transform .3s cubic-bezier(.22,.68,.36,1)}
.chev.up{transform:rotate(180deg)}

/* The two cards land in sequence rather than both at once. Scoped to .open so
 * the cascade plays when the panel is opened - not silently at page load while
 * the panel is still collapsed (and again on every reopen, since toggling .open
 * restarts the animation).                                                    */
.optcard{opacity:0}
.reveal.open .optcard{animation:pop .5s cubic-bezier(.22,.68,.36,1) forwards;
  animation-delay:calc(var(--i,0)*70ms)}
.optcard:hover{transform:translateY(-1px)}
@keyframes pop{from{opacity:0;transform:translateY(12px) scale(.985)}to{opacity:1;transform:none}}

/* ── Reduced motion ────────────────────────────────────────────────────────
 * Everything above is decoration; none of it carries meaning that survives
 * only in the movement. So: land on the final frame immediately.            */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:1ms!important;animation-iteration-count:1!important;
    transition-duration:1ms!important;scroll-behavior:auto!important}
  .word,.rise{opacity:1}
  .reveal.open .optcard{opacity:1;box-shadow:none}
  .mark path{stroke-dashoffset:0}
  /* the written word must end up readable, and the pen has no business
     appearing at all when motion is unwelcome */
  .mark .ink{clip-path:none}
  .mark .nib{display:none}
}
