/* ─── Manny's Vantage — Global Stylesheet ───────────────────────────────── */

/* Dashboard backgrounds (Studio OS — clean white) */
.dashboard-pattern {
  background-color: #FFFFFF;
  background-image: none;
}

.dashboard-pattern-teal {
  background-color: #FFFFFF;
  background-image: none;
}

/* Shop (public-facing) — subtle teal dot-grid for a polished, welcoming look */
.shop-pattern {
  background-color: #FAFBFC;
  background-image:
    radial-gradient(circle at 1px 1px, rgba(11, 74, 92, 0.055) 1px, transparent 0);
  background-size: 22px 22px;
  background-position: 0 0;
}
.shop-pattern::before {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 0;
  background:
    radial-gradient(1200px 600px at 50% -10%, rgba(11, 74, 92, 0.06), transparent 60%),
    radial-gradient(800px 400px at 100% 110%, rgba(11, 74, 92, 0.04), transparent 60%);
}

/* ── Stat / data number style ── */
.stat-value {
  font-family: 'Space Grotesk', sans-serif;
  font-weight: 700;
  letter-spacing: -0.03em;
  font-feature-settings: "tnum";
}

/* ── Premium card shadow (used on modals & elevated panels) ── */
.shadow-premium {
  box-shadow:
    0 1px 4px rgba(0, 0, 0, 0.04),
    0 6px 20px rgba(0, 0, 0, 0.06),
    0 24px 48px rgba(0, 0, 0, 0.04);
}
.shadow-premium-hover {
  box-shadow:
    0 4px 10px rgba(0, 0, 0, 0.06),
    0 16px 40px rgba(0, 0, 0, 0.10),
    0 40px 60px rgba(0, 0, 0, 0.04);
}

/* ── Animated gradient shimmer (skeleton loaders) ── */
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.shimmer {
  background: linear-gradient(
    90deg,
    rgba(241, 245, 249, 0) 0%,
    rgba(226, 232, 240, 0.6) 50%,
    rgba(241, 245, 249, 0) 100%
  );
  background-size: 200% 100%;
  animation: shimmer 1.8s ease-in-out infinite;
}

/* ── Pill badge refinements ── */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.4;
}

/* ── Table row hover ── */
.table-row-hover:hover {
  background-color: rgba(241, 245, 249, 0.7);
}

/* ── Smooth checkbox ── */
input[type="checkbox"] {
  border-radius: 5px;
  transition: box-shadow 0.15s ease;
}
input[type="checkbox"]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.25);
}

/* ── Mobile swipe hint ── */
.swipe-hint {
  position: relative;
  overflow: hidden;
}
.swipe-hint::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  width: 48px;
  height: 100%;
  background: linear-gradient(to right, transparent, white);
  pointer-events: none;
}

/* v4.429: dashboard-widget hover lift — replaces a framer-motion
   whileHover effect to keep `vendor-motion` (133 KB) out of the
   initial bundle. Same visual: -3px y, 1.003 scale, 200ms ease-out. */
.mv-widget-hover-lift {
  transition: transform 200ms ease-out;
  will-change: transform;
}
@media (hover: hover) {
  .mv-widget-hover-lift:hover {
    transform: translateY(-3px) scale(1.003);
  }
}

/* v5.9.556: customer-portal "Desk" layout — two columns on a laptop, stacked on a
   phone. Lives here rather than inline because a media query cannot be expressed in a
   React style object, and the portal is themed with inline styles everywhere else.
   NOTE: this file (public/index.css) is the one served at /index.css — the identically
   named file at the repo root is NOT copied into the build. */
.mv-portal-desk { display: block; }
.mv-portal-desk > aside { margin-bottom: 12px; }
@media (min-width: 860px) {
  .mv-portal-desk { display: grid; grid-template-columns: 300px 1fr; gap: 18px; align-items: start; }
  .mv-portal-desk > aside { position: sticky; top: 26px; margin-bottom: 0; }
}

/* v5.9.558: "Counter" — the same two-column idea as Desk but with more air. A wider
   rail, a bigger gap and a roomier container; Counter previously inherited the
   single-column 640px width, which made a two-column grid inside it read as cramped
   no matter how its internals were spaced. */
.mv-portal-counter { display: block; }
.mv-portal-counter > aside { margin-bottom: 16px; }
@media (min-width: 900px) {
  .mv-portal-counter { display: grid; grid-template-columns: 340px 1fr; gap: 28px; align-items: start; }
  .mv-portal-counter > aside { position: sticky; top: 26px; margin-bottom: 0; }
}

/* ─── Shop motion layer (v5.9.581) ──────────────────────────────────────
   Transform/opacity only — nothing here triggers layout. Every rule is
   switched off under prefers-reduced-motion at the bottom. */
:root { --mv-ease: cubic-bezier(.32,.72,0,1); --mv-spring: cubic-bezier(.34,1.4,.64,1); }
@keyframes mv-rise { from { opacity: 0; transform: translateY(18px) scale(.985); filter: blur(6px); } to { opacity: 1; transform: none; filter: blur(0); } }
@keyframes mv-line { from { clip-path: inset(0 0 100% 0); transform: translateY(.45em); opacity: .001; } to { clip-path: inset(-.1em 0 -.2em 0); transform: none; opacity: 1; } }
@keyframes mv-gradient { to { background-position: 200% center; } }
@keyframes mv-pop  { 0% { transform: scale(.6); opacity: 0; } 60% { transform: scale(1.18); opacity: 1; } 100% { transform: scale(1); } }
@keyframes mv-bump { 0% { transform: scale(1); } 40% { transform: scale(1.28); } 100% { transform: scale(1); } }
@keyframes mv-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes mv-shine { from { transform: translateX(-130%) skewX(-18deg); } to { transform: translateX(230%) skewX(-18deg); } }

.mv-reveal { opacity: 0; transform: translateY(14px); }
.mv-reveal.in { animation: mv-rise .7s var(--mv-ease) both; animation-delay: var(--mv-delay, 0ms); }
.mv-pop  { animation: mv-pop .38s cubic-bezier(.2,.9,.2,1.1) both; }
.mv-bump { animation: mv-bump .42s cubic-bezier(.2,.9,.2,1.1) both; }
.mv-fade { animation: mv-fade .25s ease both; }

/* v5.9.630: the product pop-up. Scrim and panel animate separately so the panel
   can play a real EXIT before it unmounts (the old modal hard-cut on close).
   Compositor-only throughout: opacity + transform. The scrim carries a stronger
   blur than the admin app's 4px on purpose — it sits over a dense photo grid, and
   4px there read as no blur at all. NO will-change anywhere here: it would make
   the scrim its own backdrop root and silently disable the blur (v5.9.615). */
@keyframes mv-pop-scrim-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes mv-pop-scrim-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes mv-pop-panel-in  {
  from { opacity: 0; transform: translateY(28px) scale(.965); }
  to   { opacity: 1; transform: none; }
}
@keyframes mv-pop-panel-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(16px) scale(.985); }
}
.mv-pop-scrim { background: rgba(8, 14, 18, .58); backdrop-filter: blur(10px) saturate(1.15); -webkit-backdrop-filter: blur(10px) saturate(1.15); animation: mv-pop-scrim-in .26s ease-out backwards; }
.mv-pop-scrim.closing { animation: mv-pop-scrim-out .2s ease-in forwards; }
.mv-pop-panel { animation: mv-pop-panel-in .38s cubic-bezier(0.05, 0.7, 0.1, 1) backwards; transform-origin: 50% 60%; }
.mv-pop-panel.closing { animation: mv-pop-panel-out .19s cubic-bezier(0.3, 0, 0.8, 0.15) forwards; }
/* the photo settles in a beat behind the panel */
.mv-pop-photo { animation: mv-pop-photo .7s cubic-bezier(0.05, 0.7, 0.1, 1) backwards; }
@keyframes mv-pop-photo { from { opacity: 0; transform: scale(1.05); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  .mv-pop-scrim, .mv-pop-scrim.closing, .mv-pop-panel, .mv-pop-panel.closing, .mv-pop-photo { animation: none; }
}

/* product photo: fade + settle once it has actually loaded (the box rule in
   ItemImage is untouched — this only changes opacity) */
.mv-img { opacity: 0; transition: opacity .45s ease, transform .6s cubic-bezier(.2,.8,.2,1); }
.mv-img.loaded { opacity: 1; }

/* cards: a real hover lift with a soft, brand-tinted shadow + a light sweep */
.mv-card { transition: transform .35s cubic-bezier(.2,.8,.2,1), box-shadow .35s ease; will-change: transform; }
.mv-card:hover { transform: translateY(-4px); }
.mv-card .mv-sheen { position: absolute; inset: 0; pointer-events: none; overflow: hidden; border-radius: inherit; }
.mv-card .mv-sheen::after { content: ''; position: absolute; top: -20%; bottom: -20%; width: 34%; left: 0; background: linear-gradient(90deg, transparent, rgba(255,255,255,.55), transparent); transform: translateX(-130%) skewX(-18deg); opacity: 0; }
.mv-card:hover .mv-sheen::after { opacity: 1; animation: mv-shine .9s ease-out 1; }

/* filter chips: a springier press + a settled active state */
main .m3-chip { transition: background-color .25s ease, color .25s ease, transform .25s cubic-bezier(.2,.9,.2,1.1), box-shadow .25s ease; }
main .m3-chip:active { transform: scale(.95); }
main .m3-chip.active { box-shadow: 0 1px 0 rgba(11,74,92,.06), 0 6px 14px -8px rgba(11,74,92,.35); }

/* the search + filter block turns to glass once it sticks */
@keyframes mv-slide-in { from { transform: translateX(28px); opacity: 0; } to { transform: none; opacity: 1; } }
.mv-slide-in { animation: mv-slide-in .5s var(--mv-spring) both; }
@media (min-width: 640px) { .mv-sticky { position: sticky; top: var(--mv-top, 0px); z-index: 29; } }
.mv-sticky { transition: box-shadow .3s ease, background-color .3s ease; }
.mv-sticky[data-stuck="1"] { background: rgba(250,251,252,.86); backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); box-shadow: 0 1px 0 rgba(11,74,92,.06), 0 12px 30px -18px rgba(11,74,92,.25); }

/* hero: masked line reveals, a slow gradient sweep, a cascade of trust marks,
   and scroll-linked depth (--hero-p is 0 at the top, 1 once the hero has left) */
.mv-hero { --hero-p: 0; opacity: calc(1 - var(--hero-p) * .55); transform: translateY(calc(var(--hero-p) * -22px)) scale(calc(1 - var(--hero-p) * .025)); transform-origin: 50% 0; will-change: transform, opacity; }
.mv-line { animation: mv-line .9s var(--mv-ease) both; animation-delay: var(--mv-delay, 0ms); }
.mv-gradient { background-size: 200% auto; animation: mv-gradient 7s linear infinite; }
.mv-cascade > * { animation: mv-rise .7s var(--mv-ease) both; }
.mv-cascade > *:nth-child(1) { animation-delay: 520ms; } .mv-cascade > *:nth-child(2) { animation-delay: 580ms; } .mv-cascade > *:nth-child(3) { animation-delay: 640ms; } .mv-cascade > *:nth-child(4) { animation-delay: 700ms; } .mv-cascade > *:nth-child(5) { animation-delay: 760ms; }

/* magnetic controls lean toward the pointer; the spring brings them home */
[data-magnetic] { transition: transform .55s var(--mv-spring), background-color .2s ease, color .2s ease; will-change: transform; }

/* photos drift against the pointer inside their box */
.mv-ph img { transition: transform .6s var(--mv-ease), opacity .45s ease; }

/* layout changes morph (View Transitions) with the same long ease */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: .35s; animation-timing-function: var(--mv-ease); }
::view-transition-group(*) { animation-duration: .5s; animation-timing-function: var(--mv-ease); }

/* primary buttons: a quiet press, no bounce */
.mv-press { transition: transform .15s ease, box-shadow .2s ease, background-color .2s ease; }
.mv-press:active { transform: scale(.96); }

@media (prefers-reduced-motion: reduce) {
  .mv-reveal, .mv-reveal.in, .mv-pop, .mv-bump, .mv-fade, .mv-slide-in, .mv-line, .mv-gradient, .mv-cascade > *, .mv-card .mv-sheen::after { animation: none !important; opacity: 1; transform: none; clip-path: none; }
  .mv-hero { opacity: 1; transform: none; }
  [data-magnetic], .mv-ph img { transition: none; }
  .mv-img { opacity: 1; transition: none; }
  .mv-card, main .m3-chip, .mv-press { transition: none; }
  .mv-card:hover { transform: none; }
}


/* ─── Shop hero marquee (v5.9.592) ──────────────────────────────────────
   The fleet, moving. Track is the item list rendered twice, so translating
   exactly -50% loops with no seam. Masked at both edges so tiles arrive and
   leave rather than popping. */
@keyframes mv-marquee { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }
.mv-marquee {
  position: relative; overflow: hidden; width: 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 9%, #000 91%, transparent);
}
.mv-marquee-track { display: flex; gap: 12px; width: max-content; animation: mv-marquee 54s linear infinite; will-change: transform; }
.mv-marquee:hover .mv-marquee-track { animation-play-state: paused; }
.mv-marquee-card {
  position: relative; flex: 0 0 auto; width: 132px; height: 92px; padding: 10px 12px 16px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 14px; cursor: pointer; overflow: hidden;
  background: linear-gradient(180deg, #ffffff, #f7f9fa);
  border: 1px solid rgba(10,10,10,.06);
  box-shadow: 0 1px 2px rgba(10,10,10,.03), 0 10px 24px -18px rgba(11,74,92,.45);
  transition: transform .45s cubic-bezier(.32,.72,0,1), box-shadow .45s ease, border-color .3s ease;
}
.mv-marquee-card:hover { transform: translateY(-5px); border-color: rgba(11,74,92,.28); box-shadow: 0 2px 4px rgba(10,10,10,.04), 0 18px 34px -18px rgba(11,74,92,.55); }
.mv-marquee-price {
  position: absolute; left: 0; right: 0; bottom: 5px; text-align: center;
  font-size: 10px; font-weight: 700; color: #0B4A5C; letter-spacing: .01em;
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  .mv-marquee-track { animation: none; }
  .mv-marquee-card { transition: none; }
  .mv-marquee-card:hover { transform: none; }
}


/* ─── Command Center hub nav (v5.9.605) ─────────────────────────────────
   Polish on the pill itself. Transform/colour only — nothing here can
   trigger layout, so it cannot slow a hub switch down. */
/* v5.9.619: the travelling selected-pill. left/width are set imperatively as
   plain layout and are NEVER transitioned; only the accent colour cross-fades
   here, and the slide itself is a WAAPI transform (see CommandCenter). Starts
   invisible so it cannot flash at 0,0 before the first measurement. */
.mv-hub-indicator {
  position: absolute;
  top: 2px;
  bottom: 2px;
  left: 0;
  width: 0;
  opacity: 0;
  border-radius: 999px;
  pointer-events: none;
  z-index: 0;
  /* v5.9.622: colour only. Transitioning box-shadow repainted the pill on every
     frame of a slide that is otherwise pure compositor work. */
  transition: background-color 260ms var(--mv-ease-emphasized, cubic-bezier(0.2, 0, 0, 1));
}
/* The tab's own colour flip stays in step with the pill sliding under it. */
/* v5.9.622: the label's colour transition lives on `.mv-hubnav button` below —
   that selector is more specific and always won, so a rule here was inert. */
@media (prefers-reduced-motion: reduce) {
  .mv-hub-indicator { transition: none; }
}

/* v5.9.622: this rule (specificity 0,1,1) was overriding .mv-hub-tab above, so
   the selected label was still flipping on the SHOP curve over .28s while the
   pill slid under it on 260ms emphasized — the two halves of one gesture were
   out of step. Now both use the emphasized token at nav duration.
   `will-change: transform` is gone: it held a permanent compositor layer on all
   five tabs to serve a 180ms :active scale, and the compositor promotes for the
   duration of a running transition anyway. `box-shadow` is gone too — dead since
   v5.9.619 moved every bit of elevation onto the travelling pill. */
.mv-hubnav button {
  transition: background-color var(--mv-dur-nav, 200ms) var(--mv-ease-emphasized, cubic-bezier(0.2, 0, 0, 1)),
              color var(--mv-dur-nav, 200ms) var(--mv-ease-emphasized, cubic-bezier(0.2, 0, 0, 1)),
              transform .18s cubic-bezier(.34,1.4,.64,1);
}
.mv-hubnav button:active { transform: scale(.94); }
.mv-hubnav button[aria-selected="true"] svg { animation: mv-hubicon .34s cubic-bezier(.34,1.4,.64,1) both; }
@keyframes mv-hubicon { 0% { transform: scale(.7); } 60% { transform: scale(1.14); } 100% { transform: scale(1); } }

@media (prefers-reduced-motion: reduce) {
  .mv-hubnav button { transition: none; }
  .mv-hubnav button:active { transform: none; }
  .mv-hubnav button[aria-selected="true"] svg { animation: none; }
}
