/* ==========================================================================
   THE MOTION SYSTEM

   One vocabulary for the whole site, so a card on Gear behaves like a card on
   the hub. Three rules hold it together:

     1. Nothing important depends on motion. Every reveal is a progressive
        enhancement over content that is already in the document, and a failsafe
        shows everything after 2.5s whatever happened.
     2. Motion answers an action wherever it can. The only ambient movement is
        the hero's parallax and a slow light drift, both of which stop when the
        section leaves the screen.
     3. A touch screen gets none of the hover work, and a reader who asked for
        reduced motion gets none of it at all.
   ========================================================================== */

:root {
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 180ms;
  --dur: 320ms;
  --dur-slow: 620ms;
}

/* ------------------------------ section reveal ------------------------------ *
   Hidden only where scripting can bring it back, so JS off means content on. */
/* The hiding is gated on .ww-motion, which motion.js sets on <html> as its very
   first action. `@media (scripting: enabled)` was not enough: it is true whenever
   the browser CAN run script, including when the script was never on the page.
   That is exactly what happened — index.html shipped motion.css without
   motion.js, and the whole Watch section sat at opacity 0 with its thumbnails
   loaded and nothing to reveal them. Now a missing or broken script means
   nothing is ever hidden. */
.ww-motion [data-rise] { opacity: 0; transform: translateY(18px); }
.ww-motion [data-rise].is-in { opacity: 1; transform: none; }
[data-rise] { transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out); }
/* A row of cards arrives in sequence rather than all at once. */
[data-rise][style*="--i"] { transition-delay: calc(var(--i) * 70ms); }

/* ------------------------------ card depth ------------------------------ */
.pcard, .guidecard, .nextcard, .deep, .ncard__link, .comm, .kitcard, .pers, .fcard, .topic, .edmini {
  transition:
    transform var(--dur) var(--ease-out),
    border-color var(--dur) var(--ease-out),
    box-shadow var(--dur) var(--ease-out),
    background-color var(--dur) var(--ease-out);
  will-change: transform;
}
@media (hover: hover) and (pointer: fine) {
  .pcard:hover, .guidecard:hover, .nextcard:hover, .kitcard:hover, .comm:hover, .edmini:hover {
    transform: translateY(-4px);
    box-shadow: 0 22px 48px -28px rgba(0, 0, 0, 0.95), 0 0 0 1px rgba(214, 255, 63, 0.14);
  }
  .deep:hover, .ncard__link:hover, .pers:hover, .fcard:hover {
    transform: translateY(-5px);
    box-shadow: 0 26px 56px -30px rgba(0, 0, 0, 0.95), 0 0 0 1px rgba(214, 255, 63, 0.2);
  }
}

/* --------------------------- restrained image move --------------------------- */
.deep__media img, .ncard__media img, .fcard__media img, .pers__media img, .gcard__media img {
  transition: transform var(--dur-slow) var(--ease-out), filter var(--dur-slow) var(--ease-out);
  transform-origin: center 55%;
}
@media (hover: hover) and (pointer: fine) {
  .deep:hover .deep__media img,
  .ncard__link:hover .ncard__media img,
  .fcard:hover .fcard__media img,
  .pers:hover .pers__media img,
  .gcard:hover .gcard__media img { transform: scale(1.055); filter: saturate(1.08); }
}

/* -------------------------- navigation indicator -------------------------- *
   One underline that grows from the centre, and a persistent one on the page
   you are actually on. */
.nav a { position: relative; }
.nav a::after {
  content: ""; position: absolute; left: 50%; right: 50%; bottom: 6px;
  height: 2px; background: var(--lime); border-radius: 2px;
  transition: left var(--dur) var(--ease-out), right var(--dur) var(--ease-out);
}
.nav a:hover::after, .nav a:focus-visible::after { left: 0; right: 0; }
.nav a[aria-current="page"]::after { left: 0; right: 0; }

/* ----------------------------- button feedback ----------------------------- */
.btn, .filter, .trow, .topic__reveal, .updatebar__btn, .opt {
  transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out),
    background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .btn:hover { transform: translateY(-2px); }
  .btn--lime:hover { box-shadow: 0 12px 34px -14px rgba(214, 255, 63, 0.8); }
}
.btn:active, .filter:active, .trow:active, .topic__reveal:active { transform: translateY(0) scale(0.985); }

/* ------------------------------- disclosures ------------------------------- *
   <details> cannot transition its own height, so the content slides instead. */
details[open] > :not(summary) { animation: ww-open var(--dur) var(--ease-out); }
@keyframes ww-open {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}
summary { transition: color var(--dur-fast) var(--ease-out); }

/* ------------------------------ the dossier ------------------------------ */
.pdlg__scrim { animation: ww-fade var(--dur) var(--ease-out); }
.pdlg__panel { animation: ww-panel var(--dur-slow) var(--ease-out); }
@keyframes ww-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes ww-panel {
  from { opacity: 0; transform: translateY(20px) scale(0.985); }
  to { opacity: 1; transform: none; }
}

/* --------------------------- ambient hero light --------------------------- *
   A slow drift across the top of a hero, paused whenever the hero is off
   screen. It never moves anything a reader is reading. */
.chero::after, .topper::after, .phero::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(58% 80% at var(--glow-x, 22%) 0%, rgba(214, 255, 63, 0.09), transparent 62%);
  animation: ww-glow 18s var(--ease-in-out) infinite alternate;
  animation-play-state: paused;
}
.is-live.chero::after, .is-live.topper::after, .is-live.phero::after { animation-play-state: running; }
@keyframes ww-glow {
  from { --glow-x: 18%; opacity: 0.75; }
  to { --glow-x: 46%; opacity: 1; }
}
.topper, .phero { position: relative; }
.topper__in, .phero__body { position: relative; z-index: 1; }

/* ------------------------------ state feedback ------------------------------ *
   Something changed because you did something: a short pulse on the count. */
.is-bumped { animation: ww-bump 460ms var(--ease-out); }
@keyframes ww-bump {
  0% { transform: scale(1); }
  38% { transform: scale(1.12); }
  100% { transform: scale(1); }
}
.ckitem { transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out); }
.ckitem__n { transition: background-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out); }

/* --------------------------------- filters --------------------------------- */
.topic { transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out); }
.topic.is-filtering { opacity: 0; transform: translateY(8px); }

/* ================================ opt-outs ================================ */
@media (hover: none), (pointer: coarse) {
  .pcard:hover, .guidecard:hover, .nextcard:hover, .kitcard:hover, .comm:hover,
  .deep:hover, .ncard__link:hover, .pers:hover, .fcard:hover, .btn:hover, .edmini:hover {
    transform: none; box-shadow: none;
  }
  .deep__media img, .ncard__media img, .fcard__media img, .pers__media img, .gcard__media img {
    transition: none;
  }
  .deep:hover .deep__media img, .ncard__link:hover .ncard__media img,
  .fcard:hover .fcard__media img, .pers:hover .pers__media img,
  .gcard:hover .gcard__media img { transform: none; filter: none; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  [data-rise] { opacity: 1 !important; transform: none !important; }
  .chero::after, .topper::after, .phero::after { animation: none; }
  .deep__media img, .ncard__media img, .fcard__media img,
  .pers__media img, .gcard__media img { transform: none !important; filter: none !important; }
}

/* ==========================================================================
   MOTION, PART TWO — the additions for the colour upgrade.
   Same three rules as above: nothing important depends on motion, motion
   answers an action wherever it can, and a reader who asked for less gets
   none of it.
   ========================================================================== */

/* -------------------------- entry, without a gate -------------------------- *
   The elements are visible in CSS. GSAP animates FROM a hidden copy of that
   state and clears its own properties afterwards, so a failed script leaves a
   finished-looking page rather than an empty one. The class path below is the
   no-GSAP fallback and does the same thing with a keyframe. */
.ww-entered { animation: ww-arrive 450ms var(--ease-out) backwards; animation-delay: calc(var(--i, 0) * 70ms); }
@keyframes ww-arrive { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

/* ------------------------------- atmosphere ------------------------------- *
   A very slow drift across a feature band. It is paused unless the band is on
   screen, stopped entirely by the pause control, and it lives behind a
   gradient rather than behind text. */
[data-atmos]::before { animation: ww-drift 26s var(--ease-in-out) infinite alternate; animation-play-state: paused; }
[data-atmos].is-live::before { animation-play-state: running; }
/* The pools move, the element does not. A transform here escaped the section
   and pushed horizontal scroll onto the document; background-position is
   confined to the element by definition. */
@keyframes ww-drift {
  from { background-position: 0% 0%, 100% 100%, 0 0; }
  to   { background-position: 100% 30%, 0% 70%, 0 0; }
}
/* One switch stops everything that moves on its own. */
.ww-paused [data-atmos]::before,
.ww-paused .chero::after, .ww-paused .topper::after, .ww-paused .phero::after,
.ww-paused [data-kenburns] { animation-play-state: paused !important; }

/* A slow image pan, for the few places a still photograph is the whole point. */
[data-kenburns] { animation: ww-kb 30s var(--ease-in-out) infinite alternate; will-change: transform; }
@keyframes ww-kb { from { transform: scale(1.02) translate3d(0,0,0); } to { transform: scale(1.09) translate3d(-1.2%, -1.4%, 0); } }

/* --------------------------- accent-aware hover --------------------------- *
   The card lift already existed; it now brightens in the section's own colour
   instead of always lime, which is what makes a page read as sections rather
   than as one long list. */
@media (hover: hover) and (pointer: fine) {
  .ww-atmos .pcard:hover, .ww-atmos .guidecard:hover, .ww-atmos .nextcard:hover,
  .ww-atmos .kitcard:hover, .ww-atmos .comm:hover, .ww-atmos .edmini:hover,
  .ww-atmos .deep:hover, .ww-atmos .ncard__link:hover, .ww-atmos .pers:hover,
  .ww-atmos .fcard:hover, .ww-atmos .gcard:hover, .ww-atmos .topic:hover {
    box-shadow: 0 24px 52px -28px rgba(0, 0, 0, 0.95), 0 0 0 1px rgb(var(--a-rgb) / 0.42);
    border-color: rgb(var(--a-rgb) / 0.5);
  }
}

/* ------------------------------ panel motion ------------------------------ *
   Detail panels, the comparison tray and the filter transitions all share one
   feel. 150–250ms for a control, 350–650ms for something that changes the
   layout. */
.ww-panel-in { animation: ww-panel-in 420ms var(--ease-out); }
@keyframes ww-panel-in {
  from { opacity: 0; transform: translateY(14px) scale(0.99); }
  to   { opacity: 1; transform: none; }
}
.ww-tray { transition: transform 380ms var(--ease-out), opacity 240ms var(--ease-out); }
.ww-tray[hidden] { display: none !important; }
.ww-tray.is-out { transform: translateY(110%); opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  [data-atmos]::before, [data-kenburns] { animation: none !important; transform: none !important; }
  .ww-entered { animation: none !important; }
  .ww-panel-in { animation: none !important; }
  .ww-tray { transition: none !important; }
}

/* ------------------------------ the pause control ------------------------------ *
   It lived in gear.css, which only the gear page loads — so on the hub, the city
   and the news page the same button rendered unstyled at 21px tall, well under a
   tap target. Anything that appears beside a continuous animation belongs with
   the animation system, not with one page. */
/* The pause control for anything that moves on its own. */
.motionbtn {
  /* Bottom-LEFT. The image credit already owns the bottom-right corner of
     every hero, and the two were printing on top of each other. */
  position: absolute; left: var(--gutter); bottom: 1rem; z-index: 3;
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.42rem 0.9rem; border-radius: 999px; cursor: pointer; min-height: 44px;
  background: rgba(8,10,11,0.78); color: var(--cream);
  border: 1px solid var(--line); font-size: 0.8125rem; font-weight: 700;
  backdrop-filter: blur(3px);
}
.motionbtn:hover { border-color: var(--a); color: var(--a); }
.motionbtn__i { width: 9px; height: 11px; flex: none; border-left: 3px solid currentColor; border-right: 3px solid currentColor; }
.motionbtn[aria-pressed="true"] .motionbtn__i {
  border: none; width: 0; height: 0;
  border-left: 10px solid currentColor; border-top: 6px solid transparent; border-bottom: 6px solid transparent;
}

