/* Motion (ADR-014): the original Pocketly site's movement on the server-rendered app.
   Everything that moves is inside prefers-reduced-motion: no-preference, so reduced motion gets the
   final state with no transitions at all (J6). Hidden-until-seen states exist only under .motion,
   which motion.js sets before the first paint.
   Cycle 29 (design review): nothing loops without a Pause control; nothing pulses for attention;
   press feedback in 160 ms; hover effects only where a pointer can hover; pages people come back to
   (everything but the landing) settle in 0.3 s instead of 0.7 s. */
:root { --dur-fast: .16s; --dur-base: .28s; --dur-slow: .55s; --ease-soft: cubic-bezier(.33, 0, .2, 1); --rise: 8px; --enter: .32s; }
/* The landing is seen once and explains Pocketly: its sections take longer to arrive. */
.is-landing { --rise: 18px; --enter: .7s; }

/* ---------- the header: a material layer, not an opaque bar (cycle 30) ----------
   At the top of the page it is part of the page (no divider). Once content passes under it, a blurred
   material and a hairline edge appear, so text never reads through it and no hard line is needed. */
.top { position: sticky; top: 0; z-index: 25; }
.top::before { content: ""; position: absolute; inset: 0 calc(50% - 50vw); z-index: -1; background: var(--material); backdrop-filter: blur(24px) saturate(1.6); -webkit-backdrop-filter: blur(24px) saturate(1.6);
  box-shadow: 0 1px 0 var(--hairline); pointer-events: none; }
.top.at-top::before { opacity: 0; } /* enhance.js marks the top of the page; without script the material simply stays */

/* A soft light behind the hero (still: decoration never loops). */
.lp-hero { position: relative; }
.lp-hero::before { content: ""; position: absolute; z-index: -1; inset: -10% 0 -10% 30%; background: radial-gradient(closest-side, color-mix(in srgb, var(--color-plan) 16%, transparent), transparent 70%); filter: blur(10px); pointer-events: none; }

@media (prefers-reduced-motion: no-preference) {
  /* ---------- rise into view (once) ---------- */
  .motion :is(.lp-hero-copy > *, .board, .lp-sec > *, .lp-split > *, .ol-row, main > .wrap > *):not(.is-in) { opacity: 0; transform: translateY(var(--rise)); }
  .motion :is(.lp-hero-copy > *, .board, .lp-sec > *, .lp-split > *, .ol-row, main > .wrap > *) {
    transition: opacity var(--enter) var(--ease-settle), transform var(--enter) var(--ease-settle), border-color var(--dur-base) var(--ease-soft),
      box-shadow var(--dur-base) var(--ease-soft), background-color var(--dur-base) var(--ease-soft), color var(--dur-base) var(--ease-soft);
  }
  .motion .board:not(.is-in) { transform: translateY(28px) scale(.985); }
  .motion .lp-hero-copy > .lp-display:not(.is-in) { transform: translateY(26px); }
  .motion .lp-display span { animation: rise .9s var(--ease-settle) .35s both; }

  /* ---------- pointer feedback: colour eases, the press itself is quick ---------- */
  button, .btn, .nav-cta, .card, input, select, textarea, .top nav a, details > summary::before {
    transition: background-color var(--dur-base) var(--ease-soft), border-color var(--dur-base) var(--ease-soft), color var(--dur-base) var(--ease-soft),
      box-shadow var(--dur-base) var(--ease-soft), transform var(--dur-fast) var(--ease-settle), opacity var(--dur-base) var(--ease-soft);
  }
  button:active:not(:disabled), .btn:active, .nav-cta:active { transform: scale(.97); transition-duration: .1s; }
  .nav-cta .arrow, .btn .arrow { transition: transform var(--dur-fast) var(--ease-settle); }
  /* The hero's main button points once, after the page has settled (the landing only). */
  .is-landing .lp-hero .btn.primary .arrow { animation: nudge 2.4s var(--ease-settle) 1.2s 2; }
  /* Nav links: an underline that grows instead of appearing. */
  .top nav a { text-decoration: none !important; background: linear-gradient(currentColor, currentColor) 0 calc(100% - 6px) / 0 2px no-repeat; transition: background-size var(--dur-base) var(--ease-settle), color var(--dur-base) var(--ease-soft); }
  .top nav a[aria-current="page"] { background-size: 100% 2px; }
  /* Fields glow when they take focus. */
  :is(input, select, textarea):focus { border-color: var(--color-plan); box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-plan) 18%, transparent); transition-duration: var(--dur-fast); }

  /* Hover only where a pointer hovers: on touch screens a tapped button would stay lifted. */
  @media (hover: hover) and (pointer: fine) {
    button.primary:hover, .btn.primary:hover { transform: translateY(-2px); box-shadow: 0 16px 36px -14px color-mix(in srgb, var(--color-accent) 85%, transparent); }
    button.primary:active:not(:disabled), .btn.primary:active { transform: scale(.97); }
    .btn:not(.primary):hover, button:not(.primary):not(.link):hover { border-color: var(--color-plan); }
    .nav-cta:hover { background: color-mix(in srgb, var(--color-plan) 16%, transparent); }
    .nav-cta:hover .arrow { transform: translateX(3px); }
    .top nav a:hover { background-size: 100% 2px; }
    .board:hover { border-color: color-mix(in srgb, var(--color-plan) 42%, transparent); box-shadow: 0 50px 110px -50px rgba(0, 0, 0, .95), 0 0 0 1px color-mix(in srgb, var(--color-plan) 10%, transparent), inset 0 1px 0 color-mix(in srgb, var(--color-plan) 20%, transparent); }
    .board-card > summary:hover .board-q { color: var(--color-plan); }
  }

  /* The header's material fades in as content starts to pass under it. */
  .top::before { transition: opacity var(--dur-base) var(--ease-soft); }
  /* The phone menu grows out of its button (top right): origin-aware, no overshoot. */
  .menu[open] > .menu-panel { animation: grow-in .18s var(--ease-settle) both; }

  /* ---------- open sections unfold (quickly: they are opened often) ---------- */
  details[open] > :not(summary) { animation: unfold .2s var(--ease-settle) both; }
  details > summary::before { display: inline-block; }
  details[open] > summary::before { transform: rotate(180deg); }

  /* ---------- notices arrive (feedback after saving) ---------- */
  .motion .notice { animation: slide-in var(--dur-base) var(--ease-settle) both; }

  /* ---------- landing: the real-life board ---------- */
  .board.is-paged .board-card.is-current { animation: card-in .55s var(--ease-settle) both; }
  .board-card { transition: border-color var(--dur-slow) var(--ease-soft), background-color var(--dur-slow) var(--ease-soft); }
  .board-card[open] .board-a { animation: none; }
  .board-card[open] .board-a > * { animation: rise .6s var(--ease-settle) both; }
  .board-card[open] .board-a > :nth-child(2) { animation-delay: .08s; }
  .board-card[open] .board-a > :nth-child(3) { animation-delay: .16s; }
  .board-card[open] .board-a > :nth-child(4) { animation-delay: .24s; }
  .board-card[open] .board-tap { color: var(--color-engine); }
  .board-q { transition: color var(--dur-base) var(--ease-soft); }
  /* Progress pips: the current one fills while the board plays. */
  .board-pip.is-current.is-playing::after { animation: fill 7s linear both; }
  .board.is-held .board-pip.is-current.is-playing::after { animation-play-state: paused; }

  /* ---------- cycle 42: the plan's cards arrive in order; the figure's payments land after it ---------- */
  .motion :is(.pcs > *, .hero-pays > li):not(.is-in) { opacity: 0; transform: translateY(14px); }
  .motion :is(.pcs > *, .hero-pays > li) { transition: opacity .55s var(--ease-settle), transform .6s var(--ease-settle); }
  .motion .hero-pays > li:nth-child(1) { transition-delay: .55s; } .motion .hero-pays > li:nth-child(2) { transition-delay: .8s; } .motion .hero-pays > li:nth-child(3) { transition-delay: 1.05s; }
  .pc.is-in .pc-steps li { animation: rise .45s var(--ease-settle) both; }
  .pc.is-in .pc-steps li:nth-child(2) { animation-delay: .08s; } .pc.is-in .pc-steps li:nth-child(3) { animation-delay: .16s; } .pc.is-in .pc-steps li:nth-child(4) { animation-delay: .24s; }
  .pc.is-in .pc-steps li:nth-child(5) { animation-delay: .32s; } .pc.is-in .pc-steps li:nth-child(6) { animation-delay: .4s; } .pc.is-in .pc-steps li:nth-child(7) { animation-delay: .48s; }
  /* Cycle 49: the decision's route stops arrive in order (motion.js staggers them by 40 ms), along the route's direction. */
  .motion .rt-track > li:not(.is-in) { opacity: 0; transform: translateX(-10px); }
  .motion .rt-track > li { transition: opacity .4s var(--ease-settle), transform .45s var(--ease-settle); }
  @media (max-width: 700px) { .motion .rt-track > li:not(.is-in) { transform: translateY(-8px); } }
  /* Quick check: the ring fills as answers are given. */
  .qc-ring { transition: stroke-dashoffset .35s var(--ease-settle), stroke var(--dur-base) var(--ease-soft); }
  /* Choosing an answer: the pill takes the colour quickly. */
  .choice, .toggle { transition: background-color var(--dur-fast) var(--ease-soft), border-color var(--dur-fast) var(--ease-soft); }

  /* ---------- theme switch: a short crossfade instead of a jump ---------- */
  html.theme-anim, html.theme-anim * { transition: background-color .35s var(--ease-soft), color .35s var(--ease-soft), border-color .35s var(--ease-soft), fill .35s var(--ease-soft), stroke .35s var(--ease-soft) !important; }
}

/* Reduced transparency: the header and the save bar become solid. */
@media (prefers-reduced-transparency: reduce) {
  .top::before, .form-actions { background: var(--color-bg) !important; backdrop-filter: none !important; -webkit-backdrop-filter: none !important; }
}

@keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes unfold { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
@keyframes slide-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@keyframes card-in { from { opacity: 0; transform: translateY(14px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes chip-in { from { opacity: 0; transform: translateY(6px) scale(.92); } to { opacity: 1; transform: none; } }
/* No overshoot: nothing was thrown, so nothing bounces (critically damped). */
@keyframes settle { from { opacity: 0; transform: scale(.85); } to { opacity: 1; transform: none; } }
@keyframes grow-in { from { opacity: 0; transform: scale(.96); } to { opacity: 1; transform: none; } }
@keyframes nudge { 0%, 100% { transform: none; } 40% { transform: translateX(4px); } 60% { transform: translateX(1px); } }
@keyframes fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Printing (evidence pack, save as PDF) always shows everything in its final state. */
@media print {
  .motion *, .motion *::before, .motion *::after { opacity: 1 !important; transform: none !important; animation: none !important; transition: none !important; }
  .top { position: static !important; }
}
/* Cycle 121 (V8.1, tried and withdrawn): cross-document view transitions made Chromium log "Transition was aborted
   because of invalid state" whenever a navigation interrupted another (J7), and animated disclosures left freshly opened
   content briefly unstable (J1, J5, J9). Pages and disclosures change without motion; the motion budget goes to the
   moments where money or time moves (ADR-014). */

/* Cycle 174 (W5.7): when a payment lands, its stop on the journey fills once (about half a second, never looping);
   reduced motion shows it already filled. */
@media (prefers-reduced-motion: no-preference) {
  .jr li.just-paid > .bm { animation: paid-land .5s cubic-bezier(.2, .8, .2, 1) both; }
}
@keyframes paid-land { 0% { transform: scale(.7); box-shadow: 0 0 0 0 color-mix(in srgb, var(--color-done) 60%, transparent); } 70% { transform: scale(1.06); } 100% { transform: none; box-shadow: 0 0 0 6px transparent; } }


/* Cycle 192 (W11.1): Pocketly's moments, each 400 ms or less and once: Arrive (above, the first page of a visit), Commit
   and File (a confirmation's tick draws itself: the disc, then the stroke), Progress (the step just told fills its dot on
   Now), Paid (above). Everything else stays still. Reduced motion shows each one finished. */
@media (prefers-reduced-motion: no-preference) {
  .flash-wrap.is-toast .notice::before { animation: tick-disc .22s var(--ease-settle) .08s both; }
  .flash-wrap.is-toast .notice::after { animation: tick-draw .2s ease-out .2s both; }
  .jr-dots i.just-done { animation: dot-fill .36s var(--ease-settle) both; }
}
@keyframes tick-disc { from { transform: scale(.4); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes tick-draw { 0% { clip-path: inset(100% 100% 0 0); } 45% { clip-path: inset(calc(100% - 2px) 0 0 0); } 100% { clip-path: inset(0); } }
@keyframes dot-fill { 0% { transform: scale(.5); background: var(--fill-strong); } 60% { transform: scale(1.25); } 100% { transform: none; background: var(--color-done); } }

/* Cycle 193 (W11.3): a chart builds once per visit (motion.js): bars grow from where they start, points and labels
   settle, in order of time, 300 ms in all. Grid lines, today's line and the baseline are there from the start. */
@media (prefers-reduced-motion: no-preference) {
  .charts .vz-pending .vz-draw .vz-lane:not(.vz-axis) .vz-svg > :not(.vz-tick, .vz-today, .vz-base) { opacity: 0; }
  .charts .vz-building .vz-draw .vz-lane:not(.vz-axis) .vz-svg > :not(.vz-tick, .vz-today, .vz-base) { transform-box: fill-box; transform-origin: center; animation: vz-in .18s var(--ease-settle) both; }
  .charts .vz-building .vz-draw .vz-lane:not(.vz-axis) .vz-svg > rect { transform-origin: left center; animation-name: vz-grow; }
}
@keyframes vz-in { from { opacity: 0; transform: scale(.5); } to { opacity: 1; transform: none; } }
@keyframes vz-grow { from { opacity: 0; transform: scaleX(0); } to { opacity: 1; transform: none; } }

/* Cycle 195 (W12.3): plain view never moves, whatever the system setting (motion.js adds no motion class either). */
html.is-plain *, html.is-plain *::before, html.is-plain *::after { animation: none !important; transition: none !important; }
