/* ===== Amino House — experience layer ==================================
   The confirming state, the working state, route transitions and skeletons.

   ── NO NEW VISUAL LANGUAGE ────────────────────────────────────────────
   Everything here is built from tokens that already exist:
     colour   --ink --muted --accent --accent-soft --border --border-strong
              --off --green --green-soft, and #fff surfaces
     radius   the graded scale (card 14 → inner tile 10 → chip 8), never a
              child rounder than its parent
     shadow   --shadow-sm / --shadow-md / --shadow-lg, unchanged
     motion   --dur-quick --dur-base --dur-calm, --ease-out --ease-inout,
              --stagger — all from motion.css
     grammar  .oc-card for the confirming panel, .btn for every control

   ONE derived token, and it is named as a derivation rather than a new
   idea: --dur-nav, the route-transition length. It is deliberately SHORTER
   than --dur-quick (150ms), because a navigation fade is the only motion in
   the system that sits between the customer and the thing they asked for.
   Everything else here uses the ladder as published.

   ── AND ONE DELIBERATE DIVERGENCE, STATED ─────────────────────────────
   TOKENS.md: "NO scroll-triggered fade-ins anywhere in the store — motion
   only communicates state or directs attention." A route transition is not
   a scroll-triggered fade-in: it communicates that a navigation is under
   way and that the destination is the same site. It is admitted on those
   terms, held to 120ms, made interruptible, and never allowed to delay the
   navigation it describes. The ban on decorative entrance animation stands.
   ====================================================================== */

:root {
  /* Derived from --dur-quick (150ms), shortened: this one is in the way. */
  --dur-nav: 120ms;
}

/* ── 1 · THE CONFIRMING STATE ─────────────────────────────────────────── */

.ah-confirm { text-align: center; }
.ah-confirm-state { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.ah-confirm-state[hidden] { display: none; }
.ah-confirm h2 { font-size: 22px; font-weight: 600; letter-spacing: -0.015em; margin: 0; }
.ah-confirm p { color: var(--muted); font-size: 14.5px; line-height: 1.55; margin: 0; max-width: 44ch; text-wrap: pretty; }
.ah-confirm-safe {
  margin-top: 6px; padding-top: 12px; border-top: 1px solid var(--border);
  width: 100%; font-size: 13px;
}

/* The indicator: three dots breathing in sequence, at the system's own rhythm.
   Not a spinner — a wheel that goes nowhere reads as waiting on a machine. */
.ah-confirm-dots { display: inline-flex; gap: 6px; margin-bottom: 4px; }
.ah-confirm-dots i {
  width: 7px; height: 7px; border-radius: 100px; background: var(--border-strong);
  animation: ahBreathe 1.5s var(--ease-inout) infinite;
}
.ah-confirm-dots i:nth-child(2) { animation-delay: var(--stagger); }
.ah-confirm-dots i:nth-child(3) { animation-delay: calc(var(--stagger) * 2); }
@keyframes ahBreathe {
  0%, 100% { opacity: .35; transform: scale(.86); }
  45%      { opacity: 1;   transform: scale(1); }
}

/* The two settled states reuse the success vocabulary: icon in a soft tile,
   copy behind it. Same tile radius as every other inner tile in the system. */
.ah-confirm-mark {
  width: 44px; height: 44px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent);
  animation: ahPop var(--dur-base) var(--ease-out);
}
.ah-confirm-mark--ok { background: var(--green-soft, #f0fdf4); color: var(--green, #15803d); }
.ah-confirm-mark svg { stroke: currentColor; }
.ah-confirm[data-ah-confirm-now="paid"] .ah-confirm-mark--ok svg path {
  stroke-dasharray: 26; stroke-dashoffset: 26;
  animation: ahDraw var(--dur-draw) var(--ease-out) 60ms forwards;
}
.ah-confirm-state > * { animation: ahRise var(--dur-calm) var(--ease-out) backwards; }
.ah-confirm-state > *:nth-child(2) { animation-delay: var(--stagger); }
.ah-confirm-state > *:nth-child(3) { animation-delay: calc(var(--stagger) * 2); }
.ah-confirm-state > *:nth-child(n + 4) { animation-delay: calc(var(--stagger) * 3); }

@media (max-width: 720px) {
  .ah-confirm h2 { font-size: 19px; }
  .ah-confirm p { font-size: 14px; }
}

/* ── 2 · THE WORKING STATE ────────────────────────────────────────────
   A button that is doing something looks like itself, dimmed, with a
   small sweep passing through it. It does NOT change its label: a button
   whose text changes to "Working…" loses its width and shifts the layout
   around it at the exact moment somebody is watching for a result.      */

.btn.is-working,
.is-working {
  position: relative;
  pointer-events: none;
  opacity: .78;
  overflow: hidden;
}
.btn.is-working::after,
.is-working::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .28), transparent);
  animation: ahSheen 1s var(--ease-inout) infinite;
  pointer-events: none;
}
/* On a light-surfaced button the white sweep is invisible; use the ink at low alpha instead. */
.btn-light.is-working::after,
.btn-ghost.is-working::after,
.pb-copy.is-working::after {
  background: linear-gradient(90deg, transparent, rgba(14, 22, 38, .10), transparent);
}

/* The pressed state, extended to the controls that were not .btn.
   `.btn:active` already exists in polish.css and is untouched. */
.pb-bank:active,
.pb-copy:active,
.co-deliv:active,
.co-reserve-opt:active,
.acct-tab:active { transform: translateY(1px); transition-duration: .1s; }

/* ── 3 · ROUTE TRANSITIONS ────────────────────────────────────────────
   Both halves are opacity only. No transform: a page that slides has a
   direction, and a direction is a claim about hierarchy that a shop's
   navigation does not have.                                             */

html.ah-nav-in body { animation: ahFade var(--dur-base) var(--ease-out); }
html.ah-nav-out body { opacity: .55; transition: opacity var(--dur-nav) var(--ease-out); }

/* ── 4 · SKELETONS ────────────────────────────────────────────────────
   A skeleton is the final layout with its content replaced by bars, so
   there is nothing to re-flow when the real thing lands. The bars carry
   the same radius the real element does, one step down the graded scale.

   The sweep is the SAME ahSheen the free-shipping bar uses. Nothing new. */

.ah-sk {
  position: relative;
  overflow: hidden;
  background: var(--off);
  border-radius: 8px;
  color: transparent;
}
.ah-sk::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .7), transparent);
  animation: ahSheen 1.2s var(--ease-inout) infinite;
}
.ah-sk--text { height: 12px; margin: 4px 0; }
.ah-sk--line { height: 12px; width: 100%; }
.ah-sk--half { width: 52%; }
.ah-sk--third { width: 34%; }
.ah-sk--thumb { width: 100%; aspect-ratio: 1; border-radius: 10px; }
.ah-sk--pill { height: 32px; border-radius: 100px; }

/* A skeletonised container hides its real children until it stands down, so
   the two can never be on screen at once. */
[data-ah-skeleton].is-skeleton > :not(.ah-sk-shell) { display: none; }
[data-ah-skeleton]:not(.is-skeleton) > .ah-sk-shell { display: none; }

/* ── 5 · CHECKOUT STEP TRANSITIONS ────────────────────────────────────
   PURE CSS, and that is the point: preview.js's render() already toggles
   `.active` and `.done` on each step, so keying the animation off those
   classes means the step machinery, its validation and the money path are
   not touched by a single line. The class change IS the trigger.        */

.co-step.active .co-step-body { animation: ahRise var(--dur-base) var(--ease-out); }
/* The number becoming a checkmark is the progress indicator advancing. */
.co-step.done .co-step-n { animation: ahPop var(--dur-base) var(--ease-out); }
.co-step.done .co-step-n svg { animation: ahFade var(--dur-quick) var(--ease-out); }
/* The summary line arriving is the step reporting what it captured. */
.co-step.done [data-co-summary] { animation: ahFade var(--dur-base) var(--ease-out); }

/* ── 6 · EMPTY STATES ─────────────────────────────────────────────────
   One shell for every "there is nothing here yet", matching .co-empty,
   which is the empty state this system already had.                     */

.ah-empty {
  display: flex; flex-direction: column; align-items: center; text-align: center;
  gap: 10px; padding: 56px 24px; max-width: 460px; margin: 0 auto;
}
.ah-empty-icon {
  width: 56px; height: 56px; border-radius: 10px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--off); color: var(--muted); margin-bottom: 4px;
}
.ah-empty-icon svg { stroke: currentColor; }
.ah-empty h2, .ah-empty h3 { font-size: 20px; font-weight: 600; letter-spacing: -0.015em; margin: 0; }
.ah-empty p { color: var(--muted); font-size: 14.5px; line-height: 1.55; margin: 0; text-wrap: pretty; }
.ah-empty .btn { margin-top: 8px; }
@media (max-width: 720px) {
  .ah-empty { padding: 40px 18px; }
  .ah-empty h2, .ah-empty h3 { font-size: 18px; }
}

/* ── 7 · REDUCED MOTION ───────────────────────────────────────────────
   The house rule: the state still HAPPENS and is still visible, it just
   does not move. Every animation introduced above is listed.

   The skeleton and the confirming dots keep their SHAPE and lose their
   sweep — a static grey bar still says "content is coming", which is the
   whole message. The dots stay visible at full opacity so the waiting
   state is still legible without motion carrying it.                    */
@media (prefers-reduced-motion: reduce) {
  .ah-confirm-dots i { animation: none !important; opacity: .8; transform: none; }
  .ah-confirm-mark,
  .ah-confirm-state > * { animation: none !important; }
  .ah-confirm[data-ah-confirm-now="paid"] .ah-confirm-mark--ok svg path { stroke-dashoffset: 0; animation: none !important; }

  .btn.is-working::after, .is-working::after { animation: none !important; display: none; }
  .btn.is-working, .is-working { opacity: .7; }

  html.ah-nav-in body { animation: none !important; }
  html.ah-nav-out body { opacity: 1; transition: none !important; }

  .ah-sk::after { animation: none !important; display: none; }

  .co-step.active .co-step-body,
  .co-step.done .co-step-n,
  .co-step.done .co-step-n svg,
  .co-step.done [data-co-summary] { animation: none !important; }

  .pb-bank:active, .pb-copy:active, .co-deliv:active,
  .co-reserve-opt:active, .acct-tab:active { transform: none; }
}

/* ── 8 · THE BUSY CART ROW ────────────────────────────────────────────
   The row a request is open on stops responding and says so quietly. It
   dims rather than spins: the numbers on it are the thing being changed,
   and covering them would hide the very value the customer is watching. */
.cart-item[data-cart-busy] { opacity: .6; pointer-events: none; transition: opacity var(--dur-quick) var(--ease-out); }
.cart-note--return { margin: 0 0 12px; }
@media (prefers-reduced-motion: reduce) {
  .cart-item[data-cart-busy] { transition: none !important; }
}

/* ── 9 · THE SAVED-ARTICLES SKELETON ──────────────────────────────────
   Three rows in the real row's shape. The thumb keeps the row's own
   dimensions so the swap moves nothing; the bars are the shared .ah-sk. */
.ah-sk-row { pointer-events: none; }
.ah-sk-row .r-row-thumb { background: var(--off); }
.ah-sk-row .r-row-body { display: flex; flex-direction: column; justify-content: center; gap: 6px; width: 100%; }

/* ── 10 · [hidden] ON A BUTTON ────────────────────────────────────────
   `[hidden]` is only a browser default of `display: none`, and ANY class
   that sets `display` outranks it — `.btn` sets `inline-flex`, so a button
   marked hidden in markup or JS stays on screen. This system has already
   met that three times and patched it per component (`.pb-sheet-actions
   .btn[hidden]`, `.co-pay-fields[hidden]`, `.ah-ac-list[hidden]`), each
   time after the symptom rather than before.

   Caught here by a screenshot: "I've made the transfer" set `hidden` on
   itself once pressed and went on sitting under the panel it had opened.
   One rule for every button, so the next one is not a fourth bug. */
.btn[hidden] { display: none; }
