/* ============================ MOTION & POLISH ==============================
   The index has one idea and the motion serves it: you are opening things.
   A deck opens and its types spill out of it; a topic opens and the panel
   grows out of the row you pressed. Nothing slides in from off-screen for
   decoration, and nothing loops forever in the corner of your eye.

   Everything here is transform/opacity (compositor-only, no layout thrash),
   every keyframe is namespaced `wpm*` so this file never collides with — or
   silently redefines — a name owned by 34, and there is not one JS hook in it:
   the animation layer cannot be broken by an edit to the module that renders
   the markup. Blanket off-switch for prefers-reduced-motion at the bottom.
   ========================================================================== */

/* ---- the field behind the page -----------------------------------------
   Three orbs, three periods, none of them a multiple of another — 37, 43 and 53
   seconds — so the field drifts for nine minutes before it repeats itself and
   never reads as a loop. Transform only: the softness is in the gradients, so
   there is no filter to re-rasterise on a page that scrolls through 150 cards.
   `alternate` rather than a wrap-around, because a keyframe run that snaps back
   to its start is visible however slow it is. */
@keyframes wpaOrbA{ to{ transform:translate3d(7%, 8%,0) scale(1.14) } }
@keyframes wpaOrbB{ to{ transform:translate3d(-9%, 7%,0) scale(.88)  } }
@keyframes wpaOrbC{ to{ transform:translate3d(6%,-9%,0) scale(1.10) } }

.wq-aurora b{ animation:wpaOrbA 37s cubic-bezier(.45,0,.55,1) infinite alternate; }
.wq-aurora b:nth-child(2){ animation-name:wpaOrbB; animation-duration:43s; }
.wq-aurora b:nth-child(3){ animation-name:wpaOrbC; animation-duration:53s; }
/* The whole field arrives rather than being there — a page that fades up from
   its own background reads as one object instead of a header and a list. */
.wq-aurora{ animation:wpmFieldIn 1.2s ease both; }
@keyframes wpmFieldIn{ from{ opacity:0 } }

@keyframes wpmIn   { from{ opacity:0; transform:translateY(16px) } to{ opacity:1; transform:none } }
@keyframes wpmRise { from{ opacity:0; transform:translateY(12px) } to{ opacity:1; transform:none } }
@keyframes wpmSheen{ from{ background-position:100% 50% }        to{ background-position:0% 50% } }
@keyframes wpmSweep{ to  { transform:translateX(110%) } }
@keyframes wpmSpill{
  from{ opacity:0; transform:translateY(-10px) scale(.9) }
  to  { opacity:1; transform:none }
}

/* ---- arrival ------------------------------------------------------------
   renderHub() replaces the whole subtree and only runs on arrival or after a
   finished session, so this cannot re-fire while you are working. */
.wp-top, .wp-resume, .wq-bar, .wq-list-wrap{
  animation:wpmIn .55s cubic-bezier(.22,.85,.3,1) both;
}
.wp-top       { animation-delay:.02s; }
.wp-resume    { animation-delay:.09s; }
.wq-bar{ animation-delay:.15s; }
.wq-list-wrap { animation-delay:.21s; }

/* The headline gradient drifts once and then holds. A nine-second loop is
   movement in your peripheral vision for as long as the page is open. */
.wp-grad{ background-size:220% 100%; animation:wpmSheen 1.6s cubic-bezier(.4,0,.2,1) .2s both; }

/* ---- the toolbar --------------------------------------------------------- */
.wq-task{ transition:color .2s; }
.wq-tasks::before{ transition:transform .4s cubic-bezier(.34,1.3,.44,1); }

/* The types spill out of the deck that was pressed — staggered, and re-keyed
   on every deck change by the [data-deck] attribute so the run replays. */
.wq-types[data-deck] .wq-type{
  animation:wpmSpill .42s cubic-bezier(.28,1.2,.4,1) both;
  animation-delay:calc(var(--i) * 26ms);
}
.wq-type{ transition:background .18s, border-color .18s, color .18s, transform .2s cubic-bezier(.3,1.4,.5,1); }
.wq-type:hover{ color:var(--text); background:var(--chip-bg-hover); transform:translateY(-1px); }
.wq-type:active{ transform:scale(.96); }

/* ---- the index ----------------------------------------------------------
   Cards arrive with weight: they come up, settle, and the accent rule draws
   itself across the top. The stagger is capped so twelve never feels like a
   queue. */
@keyframes wpmCard{
  from{ opacity:0; transform:translateY(18px) scale(.97) }
  to  { opacity:1; transform:none }
}
@keyframes wpmRule{ from{ transform:scaleX(0) } }

.wq-list[data-anim="1"] .wq-card{
  animation:wpmCard .5s cubic-bezier(.22,.9,.28,1) both;
  animation-delay:calc(var(--i) * 30ms);
}
.wq-list[data-anim="1"] .wq-card::before{
  animation:wpmRule .7s cubic-bezier(.2,.9,.25,1) both;
  animation-delay:calc(.14s + var(--i) * 30ms);
}
.wq-list::before{ transition:opacity .35s ease; }

.wq-card{
  transition:transform .34s cubic-bezier(.2,.9,.3,1), box-shadow .34s,
             border-color .24s, opacity .24s;
}
.wq-card:hover{
  transform:translateY(-5px);
  border-color:color-mix(in srgb,var(--acc) 42%,transparent);
  /* the resting bloom, opened up — the card rises and its own colour spreads */
  box-shadow:0 1px 0 rgba(255,255,255,.07) inset,
             0 26px 48px -26px rgba(0,0,0,.95),
             0 0 0 1px color-mix(in srgb,var(--acc) 16%,transparent),
             0 32px 68px -32px color-mix(in srgb,var(--acc) 92%,transparent);
}
.wq-card:active{ transform:translateY(-2px) scale(.995); }
/* The rule runs the full width when the card is live — the clearest possible
   "this one" without moving anything else. */
.wq-card::before{ transition:transform .45s cubic-bezier(.2,.9,.25,1), opacity .3s; }
.wq-card:hover::before,
.wq-card.is-open::before,
.wq-card-main:focus-visible ~ .wq-setup::before{ transform:scaleX(1); opacity:1; }

.wq-media{ transition:box-shadow .34s; }
.wq-media img{ transition:transform .6s cubic-bezier(.2,.8,.2,1); }
.wq-card:hover .wq-media img{ transform:scale(1.055); }
.wq-media-n{ transition:color .28s, transform .28s cubic-bezier(.3,1.4,.5,1); }
.wq-card:hover .wq-media-n{ transform:translateY(-1px); }

/* A Task 2 cover has no photograph to push into, so the light moves instead:
   the specular edge crosses the block and the motif drifts a few pixels under
   it. Both are transforms on a layer that is already composited. */
.wq-card.is-task2 .wq-media::before{
  transition:transform .7s cubic-bezier(.2,.8,.2,1), opacity .4s;
}
.wq-card.is-task2 .wq-media::after{
  transition:transform .85s cubic-bezier(.3,.7,.3,1), opacity .5s;
}
.wq-card.is-task2:hover .wq-media::before,
.wq-card.is-task2:focus-within .wq-media::before{ transform:translate(-9px,5px) scale(1.09); opacity:.75; }
.wq-card.is-task2:hover .wq-media::after,
.wq-card.is-task2:focus-within .wq-media::after{ transform:translateX(48%); opacity:1; }

.wq-card-title{ transition:color .22s; }
.wq-cta{ transition:color .24s, background .28s, border-color .24s; }
.wq-cta u{ display:inline-block; transition:transform .3s cubic-bezier(.3,1.4,.5,1); }
/* The footer fills with the card's own hue rather than a generic lime, so the
   colour you learned in the grid is the colour that confirms the click. */
.wq-card:hover .wq-cta,
.wq-card-main:focus-visible .wq-cta{
  color:var(--text);
  background:linear-gradient(180deg, transparent, color-mix(in srgb,var(--acc) 14%,transparent));
  border-top-color:color-mix(in srgb,var(--acc) 34%,transparent);
}
.wq-card:hover .wq-cta u{ transform:translateX(5px); }

/* ---- the panel that grows out of the row --------------------------------- */
.wq-setup{ transition:grid-template-rows .46s cubic-bezier(.3,1,.35,1); }
.wq-setup-grid{
  opacity:0; transform:translateY(-8px);
  transition:opacity .3s ease .1s, transform .38s cubic-bezier(.25,.9,.3,1) .08s;
}
.wq-card.is-open .wq-setup-grid{ opacity:1; transform:none; }
.wq-card.is-open .wq-cta{
  color:var(--text);
  background:linear-gradient(180deg, transparent, color-mix(in srgb,var(--acc) 18%,transparent));
  border-top-color:color-mix(in srgb,var(--acc) 40%,transparent);
}
.wq-card.is-open .wq-cta u{ transform:rotate(90deg); }
.wq-card.is-open{ transform:none; }

.wq-seg::before{ transition:transform .4s cubic-bezier(.34,1.3,.44,1); }
.wq-seg-btn{ transition:color .18s; }
.wq-seg-btn:active{ transform:scale(.97); }
.wq-step{ transition:border-color .2s, box-shadow .2s; }
.wq-step:focus-within{
  border-color:color-mix(in srgb,var(--lime) 45%,transparent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--lime) 12%,transparent);
}
.wq-step-btn{ transition:background .16s, color .16s, transform .18s cubic-bezier(.3,1.5,.5,1); }
.wq-step-btn:hover{ background:var(--chip-bg-hover); color:var(--text); }
.wq-step-btn:active{ transform:scale(.86); }

.wq-start{ transition:transform .22s cubic-bezier(.2,.9,.3,1), box-shadow .26s, filter .2s; }
.wq-start:hover{
  transform:translateY(-2px); filter:saturate(1.08);
  box-shadow:0 18px 38px -20px color-mix(in srgb,var(--lime) 90%,transparent);
}
.wq-start:active{ transform:translateY(0) scale(.99); }
.wq-start:hover .wq-start-sweep{ animation:wpmSweep .85s ease; }
.wq-start u{ transition:transform .26s cubic-bezier(.3,1.4,.5,1); }
.wq-start:hover u{ transform:translateX(5px); }
.wq-zoom{ transition:transform .2s cubic-bezier(.3,1.5,.5,1), background .18s; }
.wq-zoom:hover{ transform:scale(1.1); background:#fff; }

/* ---- the odds and ends --------------------------------------------------- */
.wq-full{ transition:border-color .22s, background .22s, transform .24s cubic-bezier(.2,.9,.3,1); }
.wq-full:hover{
  transform:translateY(-2px); background:var(--card-bg-soft);
  border-color:color-mix(in srgb,var(--lime) 45%,transparent);
}
.wq-full-go{ display:inline-block; transition:transform .26s cubic-bezier(.3,1.4,.5,1); }
.wq-full:hover .wq-full-go{ transform:translateX(5px); }
.wp-resume{ transition:border-color .2s, background .2s, transform .22s cubic-bezier(.2,.9,.3,1); }
.wp-resume:hover{ transform:translateY(-1px); }
.wp-resume-go{ display:inline-block; transition:transform .26s cubic-bezier(.3,1.4,.5,1); }
.wp-resume:hover .wp-resume-go{ transform:translateX(4px); }
.wq-search{ transition:border-color .2s, box-shadow .2s, background .2s; }
.wq-search:focus-within{
  border-color:color-mix(in srgb,var(--lime) 50%,transparent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--lime) 12%,transparent);
}
.wq-search-x{ transition:opacity .18s, background .18s, transform .2s cubic-bezier(.3,1.5,.5,1); }
.wq-search-x:hover{ color:var(--text); background:var(--chip-bg-hover); transform:scale(1.1); }
.wq-mini{ transition:background .18s, border-color .18s, transform .22s cubic-bezier(.2,.9,.3,1); }
.wq-mini:hover{ background:var(--chip-bg-hover); border-color:var(--card-border-hi); transform:translateY(-1px); }

/* ---- the review ---------------------------------------------------------- */
/* 163.4 is 2πr for the r=26 ring, a constant — so the sweep needs no inline
   custom property and therefore no JS. */
.wp-rv-ring-f{ animation:wpmDrawRing 1.1s cubic-bezier(.25,.9,.3,1) .15s both; }
@keyframes wpmDrawRing{ from{ stroke-dashoffset:163.4 } }
.wp-rv-find-grid > .wp-find{ animation:wpmRise .42s cubic-bezier(.2,.85,.3,1) both; }
.wp-rv-find-grid > .wp-find:nth-child(1){ animation-delay:.16s; }
.wp-rv-find-grid > .wp-find:nth-child(2){ animation-delay:.22s; }
.wp-rv-find-grid > .wp-find:nth-child(3){ animation-delay:.28s; }
.wp-rv-find-grid > .wp-find:nth-child(4){ animation-delay:.34s; }
.wp-rv-find-grid > .wp-find:nth-child(5){ animation-delay:.40s; }
.wp-rv-find-grid > .wp-find:nth-child(n+6){ animation-delay:.46s; }
.wp-find{ transition:transform .2s cubic-bezier(.2,.9,.3,1), box-shadow .2s; }
.wp-find:hover{ transform:translateY(-1px); box-shadow:var(--shadow-lg); }
.wp-metric{ transition:background .18s; }
.wp-metric:hover{ background:var(--chip-bg-hover); }
.wp-checks label{ transition:background .16s, color .16s; }
.wp-checks input{ transition:transform .2s cubic-bezier(.3,1.5,.5,1); }
.wp-checks input:checked{ transform:scale(1.12); }
.wp-done-stat{ transition:transform .22s cubic-bezier(.2,.9,.3,1), border-color .2s; }
.wp-done-stat:hover{ transform:translateY(-2px); border-color:var(--card-border-hi); }
.wp-essay{ transition:transform .24s cubic-bezier(.2,.9,.3,1), border-color .2s, box-shadow .24s; }
.wp-essay:hover{ transform:translateY(-2px); box-shadow:var(--shadow-lg); }

/* ---- the next page, arriving --------------------------------------------
   Both lists load themselves as you scroll now, so a batch lands under cards
   that are already settled. `.is-new` is put on ONLY the twelve that just
   arrived (see growList / growMine in 27) — re-keying the container instead
   would replay the animation on everything above them and the whole index
   would bounce every twelve rows. */
.wq-list .wq-card.is-new{
  animation:wpmCard .52s cubic-bezier(.22,.9,.28,1) both;
  animation-delay:calc(var(--i) * 34ms);
}
.wq-list .wq-card.is-new::before{
  animation:wpmRule .7s cubic-bezier(.2,.9,.25,1) both;
  animation-delay:calc(.14s + var(--i) * 34ms);
}
.wp-essays .wp-essay.is-new{
  animation:wpmCard .48s cubic-bezier(.22,.9,.28,1) both;
  animation-delay:calc(var(--i) * 30ms);
}

/* The dots at the tail only breathe while a page is actually on its way; they
   are removed the moment the list runs out, so this is not a loop parked in
   the corner of the eye — it is the list saying it has not finished. */
@keyframes wpmTail{
  0%, 100%{ transform:translateY(0) scale(.8);  opacity:.22 }
  40%     { transform:translateY(-5px) scale(1); opacity:.85 }
}
.wq-tail i{ animation:wpmTail 1.15s ease-in-out infinite; }
.wq-tail i:nth-child(2){ animation-delay:.13s; }
.wq-tail i:nth-child(3){ animation-delay:.26s; }

/* A deleted essay leaves rather than blinking out — the undo bar arriving in
   the same beat is what makes the delete feel taken back rather than final. */
@keyframes wpmGone{
  from{ opacity:1; transform:none }
  to  { opacity:0; transform:scale(.95) translateY(-6px) }
}
.wp-essay.is-going{
  animation:wpmGone .26s cubic-bezier(.4,0,.65,1) both;
  pointer-events:none;
}
/* The bar it hands off to is the shared .bd-undo in core/12-otp.css — one
   snackbar for every delete a student can make, not one per feature. */

/* ---- focus, for anyone driving this from the keyboard -------------------- */
.wq-task:focus-visible,
.wq-type:focus-visible,
.wq-card-main:focus-visible,
.wq-seg-btn:focus-visible,
.wq-step-btn:focus-visible,
.wq-start:focus-visible,
.wq-mini:focus-visible,
.wq-full:focus-visible,
.wp-resume:focus-visible{
  outline:2px solid color-mix(in srgb,var(--lime) 70%,transparent); outline-offset:2px;
}

/* ---- reduced motion ------------------------------------------------------
   All of the above is decoration on a page that works without it, so this is a
   blanket off-switch rather than a per-rule negotiation. Two things survive:
   the segmented pill still moves (a segmented control with no indicator is
   broken, not merely still) and the topic panel still opens — it just arrives
   rather than grows. */
@media (prefers-reduced-motion: reduce){
  .wp-top, .wp-resume, .wq-bar, .wq-list-wrap, .wp-grad,
  .wq-types[data-deck] .wq-type,
  .wq-list[data-anim="1"] .wq-card, .wq-list[data-anim="1"] .wq-card::before,
  .wq-list .wq-card.is-new, .wq-list .wq-card.is-new::before,
  .wp-essays .wp-essay.is-new, .wp-essay.is-going, .wq-tail i,
  .wq-start:hover .wq-start-sweep, .wp-rv-ring-f, .wp-rv-find-grid > .wp-find,
  /* The field holds still. It keeps its colour — the orbs ARE the background
     here, so switching them off would leave the page on flat --bg. */
  .wq-aurora, .wq-aurora b{
    animation:none !important;
  }
  .wq-card.is-task2 .wq-media::before,
  .wq-card.is-task2 .wq-media::after{ transition:none !important; }
  .wq-card, .wq-card::before, .wq-media, .wq-media img, .wq-media-n, .wq-card-title,
  .wq-cta, .wq-cta u, .wq-task, .wq-type, .wq-setup, .wq-setup-grid, .wq-seg::before,
  .wq-start, .wq-start u, .wq-full, .wq-full-go, .wp-resume, .wp-resume-go,
  .wq-search, .wq-mini, .wq-zoom, .wq-step-btn, .wp-find, .wp-essay, .wp-done-stat,
  .wp-checks input{ transition:none !important; }
  .wq-card:hover, .wq-type:hover, .wq-start:hover, .wq-full:hover, .wp-resume:hover,
  .wq-mini:hover, .wp-find:hover, .wp-essay:hover, .wp-done-stat:hover{ transform:none !important; }
  /* The accent rule still shows its state, it just does not draw itself. */
  .wq-card::before{ transform:scaleX(1); opacity:.7; }
  .wq-setup-grid{ opacity:1; transform:none; }
}
