/* ==========================================================================
   EXPANSION STORE — MICRO-INTERACTION LAYER              (Lane 23 · Loop 53)
   Loads AFTER expansion.html's inline <style>, so equal-specificity rules
   here win on document order. Purely additive: no token is redefined, no
   selector, id, or data-testid is changed, nothing Lane 4's Cypress suite
   asserts on is touched.

   Token namespace note: the partner portal is built on atlas-portal.css
   (--gold, --gold-halo, --glass-hi, --sh-*, --dur, --ease). The --mg-*
   namespace belongs to _REVIEW/shop-peptide-preview and does NOT resolve
   here — an undefined custom property is invalid at computed-value time and
   would drop the colour entirely. Portal tokens only, below.

   01  Motion drivers & shared primitives
   02  Package cards — lift, gold rim, pointer-tracked specular
   03  Select button — sweep + pressed state
   04  Order summary — armed state
   05  Checkout button — arrival pulse, then resting halo
   06  Scroll reveal
   07  Reduced motion / contrast / coarse pointer / print
   ========================================================================== */

/* ==========================================================================
   01  MOTION DRIVERS
   --dur-3 is the only new duration: 320ms, for entrances only. Hover and
   state transitions stay inside the portal's <=200ms contract.
   ========================================================================== */
.ex-card,.ex-select,.ex-sum,.ex-back{
  --dur-3:320ms;
  --ease-out:cubic-bezier(.16,.84,.44,1);
}

/* ==========================================================================
   02  PACKAGE CARDS
   Three layers of feedback, each doing one job:
     · translateY  — the card is liftable, so it is pickable
     · gold rim    — this is the brand's "live" colour, only on live cards
     · specular    — a pointer-tracked highlight, so the glass reads as glass
   Owned cards are excluded from all three: they are not purchasable, and
   promising interaction that does nothing is the cheap kind of polish.
   ========================================================================== */
.ex-card{
  --mx:50%; --my:0%;                   /* pointer position, written by JS */
  isolation:isolate;
  transition:border-color var(--dur-2) var(--ease),
             box-shadow var(--dur-2) var(--ease),
             background-color var(--dur-2) var(--ease),
             transform var(--dur-2) var(--ease);
}
/* Content rides above the specular sheen. */
.ex-card > *{position:relative;z-index:1}

/* Pointer-tracked specular. border-radius:inherit keeps it inside the glass
   corner, so no overflow:hidden is needed and the focus outline stays whole. */
.ex-card::after{
  content:"";position:absolute;inset:0;z-index:0;
  border-radius:inherit;pointer-events:none;
  background:radial-gradient(360px circle at var(--mx) var(--my),
             var(--gold-soft) 0%,transparent 62%);
  opacity:0;transition:opacity var(--dur-2) var(--ease);
}
.ex-card:not(.is-owned):hover::after,
.ex-card:not(.is-owned):focus-within::after{opacity:1}

/* Hover — the glassmorphic glowing rim. Layered shadows read as a light
   source rather than a drop shadow: 1px gold hairline, tight halo, wide halo,
   then the portal's own elevation and inner specular underneath. */
.ex-card:not(.is-owned):hover{
  transform:translateY(-3px);
  border-color:var(--gold-brd-2);
  box-shadow:0 0 0 1px var(--gold-brd),
             0 0 22px -4px var(--gold-halo),
             0 0 52px -14px var(--gold-halo),
             var(--sh-2),
             inset 0 1px 0 var(--glass-hi);
}

/* Selected — permanently lit, and lifted a touch further on hover so the
   card still answers the pointer once it is the chosen one. */
.ex-card.is-selected{
  border-color:var(--gold);
  box-shadow:0 0 0 1px var(--gold),
             0 0 26px -6px var(--gold-halo),
             var(--sh-2),
             inset 0 1px 0 var(--glass-hi);
}
.ex-card.is-selected:not(.is-owned):hover{
  transform:translateY(-4px);
  box-shadow:0 0 0 1px var(--gold),
             0 0 26px -2px var(--gold-halo),
             0 0 64px -16px var(--gold-halo),
             var(--sh-2),
             inset 0 1px 0 var(--glass-hi);
}
/* Keyboard parity: a card reached by Tab gets the same lit rim as hover. */
.ex-card:not(.is-owned):focus-within{
  border-color:var(--gold-brd-2);
  box-shadow:0 0 0 1px var(--gold-brd),
             0 0 22px -4px var(--gold-halo),
             var(--sh-2),
             inset 0 1px 0 var(--glass-hi);
}
/* Owned stays inert — no lift, no rim, no sheen. */
.ex-card.is-owned{transform:none}
.ex-card.is-owned:hover{box-shadow:var(--sh-2),inset 0 1px 0 var(--glass-hi)}

/* The gold "included" pill gets a soft ring so it reads as earned, not muted. */
.ex-card.is-owned .ex-owned{box-shadow:0 0 0 1px var(--ok-brd)}

/* ==========================================================================
   03  SELECT BUTTON
   A single light sweep on hover. It runs once per pointer-enter, 620ms,
   left to right — long enough to register as material, short enough that it
   never delays the click.
   ========================================================================== */
.ex-select{position:relative;overflow:hidden;isolation:isolate}
.ex-select > *{position:relative;z-index:1}
.ex-select::before{
  content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(105deg,transparent 32%,var(--glass-hi) 50%,transparent 68%);
  transform:translateX(-120%);opacity:0;
}
.ex-select:not(:disabled):hover::before{animation:ex-sweep 620ms var(--ease) 1}
@keyframes ex-sweep{
  0%{transform:translateX(-120%);opacity:0}
  18%{opacity:.9}
  100%{transform:translateX(120%);opacity:0}
}
.ex-select:not(:disabled):hover{
  border-color:var(--gold);
  box-shadow:0 0 0 1px var(--gold-brd),0 0 18px -8px var(--gold-halo)}
.ex-select:not(:disabled):active{transform:translateY(1px)}
/* Pressed — the gold fill already exists in the page layer; add the halo so
   the selected control and the selected card share one visual language. */
.ex-select[aria-pressed="true"]{box-shadow:var(--sh-1),0 0 20px -8px var(--gold-halo)}
.ex-select[aria-pressed="true"]:hover{box-shadow:var(--sh-2),0 0 24px -6px var(--gold-halo)}

/* ==========================================================================
   04  ORDER SUMMARY — ARMED
   :has() lets the summary answer a selection two subtrees away with no JS.
   Decorative only: browsers without :has() lose a glow, nothing else.
   ========================================================================== */
.ex-grid:has(.ex-card.is-selected) .ex-sum{
  border-color:var(--gold-brd);
  box-shadow:0 0 0 1px var(--gold-brd),
             0 0 40px -18px var(--gold-halo),
             var(--sh-2),
             inset 0 1px 0 var(--glass-hi);
}
.ex-sum{transition:border-color var(--dur-2) var(--ease),box-shadow var(--dur-2) var(--ease)}

/* Price flash — fires when expansion.js rewrites the figure, so the number
   the partner is about to be charged never changes silently. */
.ex-sum dd.is-flash{animation:ex-flash 620ms var(--ease) 1}
@keyframes ex-flash{
  0%{color:var(--gold-ink);transform:translateY(-2px)}
  100%{color:var(--ink);transform:none}
}

/* ==========================================================================
   05  CHECKOUT BUTTON
   Arms when a package is selected: three attention pulses, then it settles
   into a permanent resting halo. Infinite throb reads as a broken page, not
   a premium one — the pulse is an arrival announcement, not a state.
   The animation is bound to :not(:disabled), so expansion.js toggling the
   disabled property restarts or stops it with no extra wiring.
   ========================================================================== */
#btn-expansion-checkout:not(:disabled):not([aria-busy="true"]){
  box-shadow:var(--sh-1),0 0 22px -7px var(--gold-halo);   /* resting state */
  animation:ex-armed 2100ms var(--ease) 3;
}
@keyframes ex-armed{
  0%  {box-shadow:var(--sh-1),0 0 0 0 var(--gold-halo)}
  55% {box-shadow:var(--sh-2),0 0 0 13px transparent}
  100%{box-shadow:var(--sh-1),0 0 22px -7px var(--gold-halo)}
}
/* The moment the user engages, stop asking for attention. */
#btn-expansion-checkout:hover,
#btn-expansion-checkout:focus-visible,
#btn-expansion-checkout:active{animation:none}
#btn-expansion-checkout:not(:disabled):hover{
  box-shadow:var(--sh-2),0 0 30px -8px var(--gold-halo)}
#btn-expansion-checkout[aria-busy="true"]{animation:none;box-shadow:var(--sh-1)}

/* Back link and secure note — small courtesies. */
.ex-back{transition:border-color var(--dur) var(--ease),color var(--dur) var(--ease),
                    background-color var(--dur) var(--ease),transform var(--dur) var(--ease)}
.ex-back:hover{transform:translateX(-2px)}
.ex-secure svg{transition:color var(--dur) var(--ease)}
.ex-sum:hover .ex-secure svg{color:var(--gold-ink)}

/* ==========================================================================
   06  SCROLL REVEAL
   The hidden state is applied by JS (.ex-r), never by this file alone, so a
   JS failure can never leave the store invisible. expansion-polish.js also
   force-reveals everything after 700ms as a second backstop.
   ========================================================================== */
.ex-r{opacity:0;transform:translateY(14px)}
.ex-r.ex-in{opacity:1;transform:none;
  transition:opacity var(--dur-3) var(--ease-out),transform var(--dur-3) var(--ease-out)}
/* Stagger, capped at two steps — the grid is two cards wide. */
.ex-r[data-r="1"].ex-in{transition-delay:70ms}
.ex-r[data-r="2"].ex-in{transition-delay:140ms}

/* ==========================================================================
   07  ENVIRONMENT GUARDS
   ========================================================================== */

/* Reduced motion — atlas-portal.css already neutralises duration globally.
   This block restores the *information* those animations carried, statically:
   the armed checkout keeps its halo, reveals start visible, sweeps are off. */
@media (prefers-reduced-motion:reduce){
  .ex-card:not(.is-owned):hover,
  .ex-card.is-selected:not(.is-owned):hover{transform:none}
  .ex-select::before{display:none}
  .ex-r{opacity:1;transform:none}
  #btn-expansion-checkout:not(:disabled):not([aria-busy="true"]){
    animation:none;
    box-shadow:var(--sh-1),0 0 0 2px var(--gold-brd),0 0 22px -7px var(--gold-halo);
  }
  .ex-sum dd.is-flash{animation:none}
}

/* Coarse pointers get no specular (there is no hover to track) but keep the
   selected/armed states, which carry real state. */
@media (hover:none){
  .ex-card::after{display:none}
  .ex-card:not(.is-owned):hover{transform:none}
  .ex-select::before{display:none}
}

/* High contrast — halos are decorative; borders must stay crisp. */
@media (prefers-contrast:more){
  .ex-card:not(.is-owned):hover,
  .ex-card:not(.is-owned):focus-within{box-shadow:0 0 0 2px var(--gold),var(--sh-2)}
  .ex-card.is-selected{box-shadow:0 0 0 2px var(--gold),var(--sh-2)}
  .ex-card::after{display:none}
}

@media print{
  .ex-card,.ex-sum,.ex-r{opacity:1!important;transform:none!important;box-shadow:none!important}
  .ex-card::after,.ex-select::before{display:none!important}
}
