/* =============================================================================
   USMLE PULSE — home-motion.css
   Loaded ONLY by index.html.

   WHY A SEPARATE FILE
   landing.css is shared by ten pages. Motion written for the homepage hero
   would have leaked into the library, FAQ, mentorship and terms pages, which
   were not asked for and not tested. This file cannot reach them.

   WHAT THIS IS NOT
   It is not a redesign. Nothing here changes a colour, a size, a spacing value,
   a font or a word. Every rule adds transition, transform or opacity to
   elements that already exist, and every one of them is switched off under
   prefers-reduced-motion.

   WHY THESE ELEMENTS
   Measured on the running page before writing any of it, the hero cards were
   completely inert: .hl-card, .hl-stat, the task item and the "Open plan" link
   all reported `transition: all 0s` with no :hover rule anywhere in any
   stylesheet. The buttons already had hover states and the sections below the
   fold already scroll-reveal, so those are left alone rather than doubled up.

   MOTION BUDGET
   Reuses the tokens main.css already defines — --ease-out is exactly the
   cubic-bezier(.16,1,.3,1) a premium entrance wants, so nothing new is
   invented here.
============================================================================= */

/* -----------------------------------------------------------------------------
   0. THE NO-JAVASCRIPT GUARANTEE

   Entrance animations hide things and then reveal them, which means a broken
   script leaves a blank hero. Everything below is gated on .hm-js, set by a
   one-line inline script in the head. If that never runs, none of these rules
   apply and the page renders exactly as it does today — visible.
----------------------------------------------------------------------------- */

/* The page was not visible when the entrance was released, so there was no
   animation to see. Everything lands in its finished state at once — see the
   note in home-motion.js. */
.hm-js.hm-instant .hero *,
.hm-js.hm-instant .hero-live .hl-card,
.hm-js.hm-instant [data-hero-mockup] { transition: none !important; }

/* NOTE ON will-change: it is deliberately absent. It was here, and it is the
   wrong tool for a one-shot entrance — it pins every element it touches onto
   its own compositing layer for the WHOLE life of the page to optimise an
   animation that finishes in under a second. The browser already promotes an
   element while a transform/opacity transition is actually running, which is
   the only moment it helps. Leaving it on is pure memory cost afterwards. */

/* -----------------------------------------------------------------------------
   1. HERO ENTRANCE — left column

   A staggered read, in the order a person actually reads it: label, headline,
   the promise, the action, the proof. The delays are transition-delays on a
   single class flip rather than keyframes, so the whole thing costs one style
   recalculation and animates on the compositor.
----------------------------------------------------------------------------- */
.hm-js .hero .eyebrow,
.hm-js .hero h1,
.hm-js .hero p.lead,
.hm-js .hero .hero-cta,
.hm-js .hero .hero-trial,
.hm-js .hero .hero-cta-sub,
.hm-js .hero .hero-trust {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity .62s var(--ease-out), transform .62s var(--ease-out);
}
.hm-js .hero h1 .accent { opacity: 0; transition: opacity .62s var(--ease-out); }
.hm-js.hm-ready .hero h1 .accent { opacity: 1; }

/* The headline is one <h1> with a span inside it, not two elements, so the
   second line gets its own delay to land a beat after the first.

   It fades ONLY — no transform, and no display change. The first version made
   the span an inline-block so it could be translated. That works, and it also
   quietly alters the headline's line metrics: an atomic inline sits on the
   baseline differently and can change the height of the line box. Changing the
   size of the largest text on the page is exactly the kind of layout edit this
   file promised not to make, for a refinement nobody would notice.

   Opacity applies to inline elements as-is. Same two-stage read, nothing moved. */

.hm-js.hm-ready .hero .eyebrow,
.hm-js.hm-ready .hero h1,
.hm-js.hm-ready .hero h1 .accent,
.hm-js.hm-ready .hero p.lead,
.hm-js.hm-ready .hero .hero-cta,
.hm-js.hm-ready .hero .hero-trial,
.hm-js.hm-ready .hero .hero-cta-sub,
.hm-js.hm-ready .hero .hero-trust {
  opacity: 1;
  transform: none;
}

.hm-js.hm-ready .hero .eyebrow      { transition-delay: .06s; }
.hm-js.hm-ready .hero h1            { transition-delay: .14s; }
.hm-js.hm-ready .hero h1 .accent    { transition-delay: .24s; }
.hm-js.hm-ready .hero p.lead        { transition-delay: .34s; }
.hm-js.hm-ready .hero .hero-cta     { transition-delay: .44s; }
.hm-js.hm-ready .hero .hero-trial   { transition-delay: .50s; }
.hm-js.hm-ready .hero .hero-cta-sub { transition-delay: .50s; }
.hm-js.hm-ready .hero .hero-trust   { transition-delay: .56s; }

/* The three proof stats resolve one after another rather than as a block. */
.hm-js .hero-trust .stat { opacity: 0; transform: translateY(10px);
  transition: opacity .5s var(--ease-out), transform .5s var(--ease-out); }
.hm-js.hm-ready .hero-trust .stat { opacity: 1; transform: none; }
.hm-js.hm-ready .hero-trust .stat:nth-of-type(1) { transition-delay: .60s; }
.hm-js.hm-ready .hero-trust .stat:nth-of-type(2) { transition-delay: .68s; }
.hm-js.hm-ready .hero-trust .stat:nth-of-type(3) { transition-delay: .76s; }

/* -----------------------------------------------------------------------------
   2. HERO ENTRANCE — the cards, and the guest mockup

   The cards come in from the right, which is where they live, so the movement
   reads as "arriving into place" rather than as decoration. translateX is kept
   small and the hero is already `overflow: clip`, so it cannot widen the page.
----------------------------------------------------------------------------- */
.hm-js .hero-live .hl-card,
.hm-js [data-hero-mockup] {
  opacity: 0;
  transform: translateX(26px) scale(.985);
  transition: opacity .68s var(--ease-out), transform .68s var(--ease-out);
}
.hm-js.hm-ready .hero-live .hl-card,
.hm-js.hm-ready [data-hero-mockup] { opacity: 1; transform: none; }

.hm-js.hm-ready .hero-live .hl-card:nth-child(1) { transition-delay: .20s; }
.hm-js.hm-ready .hero-live .hl-card:nth-child(2) { transition-delay: .30s; }
.hm-js.hm-ready .hero-live .hl-card:nth-child(3) { transition-delay: .40s; }
.hm-js.hm-ready [data-hero-mockup]               { transition-delay: .22s; }

/* -----------------------------------------------------------------------------
   3. THE CARDS, ONCE THEY ARE THERE

   These were the deadest things on the page — no transition and no hover rule
   in any stylesheet. A card that holds your progress should acknowledge that
   you have pointed at it.

   The lift is 3px. Enough to register, not enough to notice as movement.
----------------------------------------------------------------------------- */
.hl-card {
  transition: transform .3s var(--ease-out),
              border-color .3s var(--ease-out),
              box-shadow .3s var(--ease-out),
              background .3s var(--ease-out);
}
@media (hover: hover) {
  .hl-card:hover {
    transform: translateY(-3px);
    border-color: rgba(255,255,255,.22);
    background: rgba(255,255,255,.08);
    box-shadow: 0 18px 40px rgba(0,0,0,.28);
  }
}

/* A highlight that follows the cursor across the card. The position comes from
   two custom properties the script writes; when it has never written them the
   fallback puts the glow off-card, so nothing shows until the pointer is
   actually inside. Painting it on a pseudo-element keeps it off the card's own
   compositing layer. */
.hl-card { position: relative; overflow: hidden; }
.hl-card::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(220px circle at var(--hm-x, -999px) var(--hm-y, -999px),
              rgba(94,234,212,.10), transparent 70%);
  opacity: 0; transition: opacity .35s var(--ease-out);
}
@media (hover: hover) { .hl-card:hover::before { opacity: 1; } }
/* The card's own content has to sit above the glow. */
.hl-card > * { position: relative; z-index: 1; }

/* The three number tiles. Individually interactive, so individually
   responsive — but a step quieter than the card that holds them. */
.hl-stat { transition: transform .25s var(--ease-out), background .25s var(--ease-out); }
@media (hover: hover) {
  .hl-stat:hover { transform: translateY(-2px); background: rgba(0,0,0,.26); }
}

/* A value that has just changed gets one restrained acknowledgement. */
@keyframes hm-bump { 0% { transform: none; } 40% { transform: scale(1.06); } 100% { transform: none; } }
.hl-val.hm-changed, .hl-bar-row span.hm-changed { animation: hm-bump .42s var(--ease-out); }

/* -----------------------------------------------------------------------------
   4. THE LIVE BADGE

   It says LIVE and it did not move. A 3-second breath at 0.82 opacity is under
   the threshold most people consciously notice, which is the point — it should
   register as "this is current", not as blinking.
----------------------------------------------------------------------------- */
@keyframes hm-live { 0%, 100% { opacity: 1; } 50% { opacity: .82; } }
.hl-live { animation: hm-live 3s ease-in-out infinite; }

/* -----------------------------------------------------------------------------
   5. TODAY'S TASK

   Content untouched. The row acknowledges the pointer and the scrollbar
   surfaces when there is more to read than fits — that box already scrolls at
   max-height 230px and gave no sign of it.
----------------------------------------------------------------------------- */
.hl-today-one { transition: background .25s var(--ease-out); border-radius: var(--r-md); }
@media (hover: hover) {
  .hl-today-one:hover { background: rgba(255,255,255,.05); }
}
.hl-today-one { scrollbar-width: thin; scrollbar-color: transparent transparent; transition: scrollbar-color .3s ease, background .25s var(--ease-out); }
.hl-today-one:hover { scrollbar-color: rgba(255,255,255,.28) transparent; }
.hl-today-one::-webkit-scrollbar { width: 6px; }
.hl-today-one::-webkit-scrollbar-thumb { background: transparent; border-radius: 3px; transition: background .3s ease; }
.hl-today-one:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,.28); }

/* "Open plan →" — the arrow is part of the text, so the whole label carries the
   nudge. It is a link out of the page; the movement says which way you go. */
.hl-open { transition: color .2s var(--ease-out), transform .25s var(--ease-out); display: inline-block; }
@media (hover: hover) {
  .hl-open:hover { transform: translateX(3px); color: #ffd77a; }
}

/* The exam-date controls inside the progress card. */
.hl-exam-btn { transition: transform .2s var(--ease-out), background .2s var(--ease-out), filter .2s var(--ease-out); }
@media (hover: hover) { .hl-exam-btn:hover { transform: translateY(-1px); filter: brightness(1.08); } }
.hl-exam-btn:active { transform: scale(.97); }

/* -----------------------------------------------------------------------------
   6. THE HERO BUTTONS

   main.css already gives every button a hover and an active state, so this only
   sharpens the two in the hero to the tactility a primary action deserves. The
   global button system is not touched.
----------------------------------------------------------------------------- */
.hero-cta .btn { transition: transform .18s var(--ease-out), box-shadow .28s var(--ease-out),
                             background .28s var(--ease-out), border-color .28s var(--ease-out), filter .2s var(--ease-out); }
@media (hover: hover) {
  .hero-cta .btn-primary:hover { transform: translateY(-2px); filter: brightness(1.04); }
  .hero-cta .btn-ghost:hover   { transform: translateY(-1px); border-color: rgba(255,255,255,.55); background: rgba(255,255,255,.07); }
}
.hero-cta .btn:active { transform: scale(.97); }

/* A light sweep across the primary CTA, once every 9 seconds. Long enough
   apart that it reads as sheen rather than as an animation running. */
.hero-cta .btn-primary { position: relative; overflow: hidden; }
.hero-cta .btn-primary::after {
  content: ""; position: absolute; top: 0; bottom: 0; width: 38%;
  left: -50%; pointer-events: none;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.22), transparent);
  animation: hm-sweep 9s ease-in-out infinite;
}
@keyframes hm-sweep {
  0%, 82% { left: -50%; }
  94%, 100% { left: 130%; }
}

/* -----------------------------------------------------------------------------
   7. AMBIENT BACKGROUND

   The grid and the two colour washes are part of the identity and are left
   exactly as they are. This drifts the existing grid by one cell over 72
   seconds and breathes the wash behind it. Both animate properties that do not
   trigger layout, and at that speed neither is perceptible as movement — the
   panel simply stops feeling like a screenshot.
----------------------------------------------------------------------------- */
/* THE GRID IS LEFT ALONE, ON PURPOSE.

   Drifting it was written first and then removed. It animated
   background-position on a full-viewport masked layer, which repaints that
   whole layer on every frame for the entire life of the page — a continuous
   cost for a movement of 0.6px per second that nobody can perceive. The one
   ambient layer that stays animates a transform instead, which the compositor
   handles without repainting anything.

   One cheap ambient layer is also simply enough. Two competing drifts is how
   a background stops being ambient and starts being busy. */

.hm-js .hero > .hm-amb {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(680px 380px at 18% 12%, rgba(20,184,166,.10), transparent 62%);
  animation: hm-amb 46s ease-in-out infinite alternate;
}
@keyframes hm-amb {
  from { transform: translate3d(-3%, -2%, 0); opacity: .55; }
  to   { transform: translate3d(4%, 3%, 0);  opacity: .95; }
}

/* The ECG trace. This is the one piece of decoration on the page, so it is held
   to a whisper: a hairline at 5% opacity along the bottom of the hero, drawn
   once on load and then still. It is the brand felt in the motion rather than
   another graphic competing with the headline. */
.hm-ecg { position: absolute; left: 0; right: 0; bottom: 6%; height: 90px; width: 100%;
          pointer-events: none; z-index: 0; opacity: .5; }
.hm-ecg path { fill: none; stroke: var(--brand-bright, #5eead4); stroke-width: 1.4;
               opacity: .12; stroke-dasharray: 1600; stroke-dashoffset: 1600; }
.hm-js.hm-ready .hm-ecg path { animation: hm-ecg-draw 3.4s var(--ease-out) .5s forwards; }
@keyframes hm-ecg-draw { to { stroke-dashoffset: 0; } }

/* -----------------------------------------------------------------------------
   8. THE CHAT BUTTON
----------------------------------------------------------------------------- */
.hm-js .chat-fab { transition: transform .28s var(--ease-out), box-shadow .28s var(--ease-out); }
.hm-js .chat-fab.hm-pop { animation: hm-pop .45s var(--ease-out) both; }
@keyframes hm-pop { from { opacity: 0; transform: scale(.8); } to { opacity: 1; transform: scale(1); } }
@media (hover: hover) { .chat-fab:hover { transform: scale(1.06); } }
.chat-fab:active { transform: scale(.94); }

/* -----------------------------------------------------------------------------
   9. FOCUS STATES

   Keyboard users get the same quality of feedback as the mouse. The outline is
   not removed anywhere — it is made deliberate, and the same lift the pointer
   gets is applied so both routes feel like the same product.
----------------------------------------------------------------------------- */
.hero-cta .btn:focus-visible,
.hl-open:focus-visible,
.hl-exam-btn:focus-visible,
.hl-card a:focus-visible,
.hl-card button:focus-visible {
  outline: 2px solid var(--brand-bright, #5eead4);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}
.hero-cta .btn:focus-visible { transform: translateY(-2px); }

/* -----------------------------------------------------------------------------
   10. SMALL SCREENS

   No cursor, less room, and a horizontal entrance is the one thing that could
   push the page sideways. Distances shrink and the X movement is dropped
   entirely; the ambient layers come off because they are the least valuable
   thing on a phone and the most expensive.
----------------------------------------------------------------------------- */
@media (max-width: 767px) {
  .hm-js .hero-live .hl-card,
  .hm-js [data-hero-mockup] { transform: translateY(16px) scale(.99); }
  .hm-js .hero .eyebrow,
  .hm-js .hero h1,
  .hm-js .hero p.lead,
  .hm-js .hero .hero-cta,
  .hm-js .hero .hero-trust { transform: translateY(12px); }
  .hm-js .hero > .hm-amb,
  .hm-ecg { display: none; }
  .hero-cta .btn-primary::after { animation: none; }
  .hl-card::before { display: none; }
}

/* -----------------------------------------------------------------------------
   11. REDUCED MOTION

   Everything off. Not "faster" — off. The page must arrive complete and stay
   still, and nothing above is load-bearing for reading it.
----------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .hm-js .hero .eyebrow,
  .hm-js .hero h1,
  .hm-js .hero h1 .accent,
  .hm-js .hero p.lead,
  .hm-js .hero .hero-cta,
  .hm-js .hero .hero-trial,
  .hm-js .hero .hero-cta-sub,
  .hm-js .hero .hero-trust,
  .hm-js .hero-trust .stat,
  .hm-js .hero-live .hl-card,
  .hm-js [data-hero-mockup] {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .hl-live,
  .hm-js .hero > .hm-amb,
  .hero-cta .btn-primary::after,
  .hm-js.hm-ready .hm-ecg path,
  .hm-js .chat-fab.hm-pop { animation: none !important; }
  .hm-ecg path { stroke-dashoffset: 0; }
  .hl-card, .hl-stat, .hl-today-one, .hl-open, .hero-cta .btn, .chat-fab { transition: none !important; }
  .hl-card:hover, .hl-stat:hover, .hero-cta .btn:hover, .chat-fab:hover { transform: none !important; }
  .hl-card::before { display: none; }
  .hl-val.hm-changed, .hl-bar-row span.hm-changed { animation: none !important; }
}
