/* Directory motion: shortcuts, hub rows, chips, result rows and page-to-page
   transitions (docs/design/motion.md, "Directory interactions"). Same rules
   as the homepage field: transform and opacity only, motion answers the
   visitor, and reduced motion keeps light but drops movement. */

:root {
    /* Timing: feedback ~100 ms, small moves 180-260 ms, nothing over 500 ms */
    --m-press: 90ms;
    --m-hover: 180ms;
    --m-move: 260ms;
    --m-page: 320ms;
    --m-ripple: 460ms;
    /* Slow out: quick start, soft landing. Spring: slight overshoot on release. */
    --m-ease-out: cubic-bezier(.2, .8, .2, 1);
    --m-spring: cubic-bezier(.3, 1.4, .5, 1);
    --m-light: rgba(67, 229, 233, .14);
    --m-ripple-1: rgba(70, 224, 214, .42);   /* teal */
    --m-ripple-2: rgba(169, 139, 255, .42);  /* violet */
    --m-ripple-3: rgba(244, 188, 92, .36);   /* saffron */
    --m-ripple-4: rgba(170, 235, 255, .40);  /* pale aqua */
}

html[data-theme="light"] {
    --m-light: rgba(9, 113, 108, .10);
    --m-ripple-1: rgba(9, 113, 108, .30);    /* deep teal */
    --m-ripple-2: rgba(107, 70, 201, .26);   /* violet */
    --m-ripple-3: rgba(201, 122, 18, .28);   /* saffron */
    --m-ripple-4: rgba(196, 84, 128, .22);   /* lotus */
}

/* ---- Interactive surfaces (marked data-motion by js/motion.js) ---------- */

.home-hints a,
.hub-category,
.hub-subcategories a,
.hub-chips a,
.search-result, .shop-link {
    position: relative;
    isolation: isolate;
    transition:
        transform var(--m-hover) var(--m-ease-out),
        border-color var(--m-hover) var(--m-ease-out),
        color var(--m-hover) var(--m-ease-out);
}

/* Pointer light: a soft glow that follows the pointer across the surface
   (arcs, secondary action). js/motion.js sets --mx and --my. */
.home-hints a::before,
.hub-category::before,
.hub-subcategories a::before,
.hub-chips a::before,
.search-result::before, .shop-link::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background: radial-gradient(160px circle at var(--mx, 50%) var(--my, 50%), var(--m-light), transparent 70%);
    opacity: 0;
    transition: opacity var(--m-hover) var(--m-ease-out);
    pointer-events: none;
}

.hub-category { border-radius: 12px; padding-inline: .6rem; margin-inline: -.6rem; }
.search-result { border-radius: 12px; padding-inline: .6rem; margin-inline: -.6rem; }
.home-hints a { border-radius: 999px; padding-inline: .7rem; }

@media (hover: hover) and (pointer: fine) {
    .home-hints a:hover::before,
    .hub-category:hover::before,
    .hub-subcategories a:hover::before,
    .hub-chips a:hover::before,
    .search-result:hover::before, .shop-link:hover::before { opacity: 1; }

    /* Lift on hover; the count follows a beat later (follow-through). */
    .home-hints a:hover,
    .hub-subcategories a:hover,
    .hub-chips a:hover { transform: translateY(-2px); }
    .hub-category:hover strong { transform: translateX(4px); }
    .hub-category:hover span { transform: translateX(4px); transition-delay: 40ms; }
    .search-result:hover { transform: translateX(3px); }
}

.hub-category strong,
.hub-category span { display: inline-block; transition: transform var(--m-move) var(--m-ease-out); }

/* Anticipation: a quick squash on press, released with a small spring. */
.home-hints a:active,
.hub-subcategories a:active,
.hub-chips a:active { transform: scale(.95); transition-duration: var(--m-press); }
.hub-category:active,
.search-result:active, .shop-link:active { transform: scale(.985); transition-duration: var(--m-press); }
.home-hints a:not(:active),
.hub-subcategories a:not(:active),
.hub-chips a:not(:active) { transition-timing-function: var(--m-spring); }

/* Tap ripple (js/motion.js): grows fast, settles slowly, fades last. */
.motion-ripple {
    position: absolute;
    z-index: -1;
    width: 12px;
    height: 12px;
    margin: -6px 0 0 -6px;
    border-radius: 50%;
    pointer-events: none;
}

/* Overflow is clipped only where a ripple can appear. */
.home-hints a, .hub-category, .hub-subcategories a, .hub-chips a, .search-result, .shop-link { overflow: hidden; }

/* A product card lifts as a whole; its button carries the press and ripple. */
.shop-card { transition: transform var(--m-hover) var(--m-ease-out), border-color var(--m-hover) var(--m-ease-out); }
@media (hover: hover) and (pointer: fine) {
    .shop-card:hover { transform: translateY(-3px); border-color: color-mix(in srgb, var(--cyan) 40%, var(--line)); }
}
@media (prefers-reduced-motion: reduce) { .shop-card:hover { transform: none; } }

/* ---- Staging: hub content arrives in order, once, on page load ---------- */

@keyframes motion-arrive {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: none; }
}

.hub-page h1,
.hub-lede,
.hub-categories > li,
.hub-chips a,
.hub-page .search-results > li, .shop-grid > .shop-card {
    animation: motion-arrive var(--m-move) var(--m-ease-out) backwards; /* backwards: hover can move it afterwards */
}
.hub-lede { animation-delay: 40ms; }
.hub-chips a { animation-delay: 80ms; }
.hub-categories > li:nth-child(1), .hub-page .search-results > li:nth-child(1), .shop-grid > .shop-card:nth-child(1) { animation-delay: 80ms; }
.hub-categories > li:nth-child(2), .hub-page .search-results > li:nth-child(2), .shop-grid > .shop-card:nth-child(2) { animation-delay: 110ms; }
.hub-categories > li:nth-child(3), .hub-page .search-results > li:nth-child(3), .shop-grid > .shop-card:nth-child(3) { animation-delay: 140ms; }
.hub-categories > li:nth-child(4), .hub-page .search-results > li:nth-child(4), .shop-grid > .shop-card:nth-child(4) { animation-delay: 170ms; }
.hub-categories > li:nth-child(5), .hub-page .search-results > li:nth-child(5), .shop-grid > .shop-card:nth-child(5) { animation-delay: 200ms; }
.hub-categories > li:nth-child(6), .hub-page .search-results > li:nth-child(6), .shop-grid > .shop-card:nth-child(6) { animation-delay: 230ms; }
.hub-categories > li:nth-child(n+7), .hub-page .search-results > li:nth-child(n+7), .shop-grid > .shop-card:nth-child(n+7) { animation-delay: 260ms; }

/* ---- Page to page: native view transitions (no JavaScript) -------------- */
/* Browsers without support simply load the next page. The clicked chip or
   row and the next page's heading share a view-transition-name, so the
   heading grows out of what was tapped (staging, slow in and slow out). */

@view-transition { navigation: auto; }

.site-header { view-transition-name: site-header; }

::view-transition-group(*) {
    animation-duration: var(--m-page);
    animation-timing-function: var(--m-ease-out);
}

::view-transition-old(root) { animation: motion-page-out 160ms ease-in both; }
::view-transition-new(root) { animation: motion-page-in var(--m-page) var(--m-ease-out) 60ms both; }

@keyframes motion-page-out { to { opacity: 0; transform: scale(.99); } }
@keyframes motion-page-in { from { opacity: 0; transform: translateY(10px); } }

/* ---- Living illustrations: our own art, gently alive -------------------- */
/* Shop gems (GemArt.razor) and the category artwork. Slow, offset per card so
   nothing moves in lockstep; transform and opacity only. */

.gem-art * { transform-box: view-box; }

/* Pendant: a pendulum around the bail. Slow in and out at each end (arcs). */
.gem-sway { transform-origin: 100px 44px; animation: gem-sway 3.6s ease-in-out infinite alternate; }
@keyframes gem-sway { from { transform: rotate(-4deg); } to { transform: rotate(4deg); } }

/* A glint crosses the facets about every six seconds, then rests. */
.gem-glint { animation: gem-glint 6s cubic-bezier(.4, 0, .2, 1) infinite; transform: translateX(-60px); }
@keyframes gem-glint {
    0% { transform: translateX(-60px); }
    22%, 100% { transform: translateX(110px); }
}

/* Highlights breathe, each a little after the last (overlapping action). */
.gem-twinkle { animation: gem-twinkle 2.4s ease-in-out infinite alternate; animation-delay: calc(var(--i, 0) * -0.3s); }
@keyframes gem-twinkle { from { opacity: .35; } to { opacity: 1; } }

/* Bead strand: a soft wave travels along the beads (follow-through). */
.gem-bead { animation: gem-bead 3.2s ease-in-out infinite alternate; animation-delay: calc(var(--i, 0) * -0.18s); }
@keyframes gem-bead { from { transform: translateY(0); } to { transform: translateY(-2.5px); } }

/* Bracelet: turns once a minute. */
.gem-turn { transform-origin: 100px 80px; animation: gem-turn 60s linear infinite; }
@keyframes gem-turn { to { transform: rotate(360deg); } }

/* Orgonite disc: the green pattern in the dark centre glows softly. */
.gem-yantra { animation: gem-yantra 3.4s ease-in-out infinite alternate; }
@keyframes gem-yantra { from { opacity: .65; } to { opacity: 1; filter: drop-shadow(0 0 2px #45f07c); } }
@media (prefers-reduced-motion: reduce) { .gem-yantra { animation: none; } }

/* Mala tassel trails the strand a beat behind (follow-through). */
.gem-tassel { transform-origin: 100px 131px; animation: gem-tassel 3.2s ease-in-out infinite alternate; animation-delay: -.8s; }
@keyframes gem-tassel { from { transform: rotate(-5deg); } to { transform: rotate(5deg); } }

/* ---- Hover: the chosen piece comes alive, the rest recede ---------------- */
/* Staging: one product takes the stage. Its illustration springs up, moves
   more boldly, glows in its own stone colour, and a ribbon of light flows
   around its card; the other cards soften. */
@property --flow { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

.shop-card { position: relative; transition: opacity 280ms ease-out, filter 280ms ease-out, transform var(--m-hover) var(--m-ease-out), border-color var(--m-hover) var(--m-ease-out); }
.gem-art { position: relative; z-index: 1; transition: transform 460ms cubic-bezier(.3, 1.4, .5, 1); }
.shop-visual::before {
    content: ""; position: absolute; inset: -6% -6% -14%; z-index: 0; pointer-events: none;
    background: radial-gradient(circle at 50% 46%, color-mix(in srgb, var(--aura, var(--cyan)) 48%, transparent), transparent 62%);
    opacity: 0; transition: opacity 360ms ease-out;
}
.shop-card::after {
    content: ""; position: absolute; inset: -1px; border-radius: inherit; padding: 1.5px; pointer-events: none;
    background: conic-gradient(from var(--flow), transparent 0 62%, var(--cyan) 76%, var(--violet) 86%, transparent 94%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude;
    opacity: 0; transition: opacity 300ms ease-out;
}
@keyframes card-flow { to { --flow: 360deg; } }
@keyframes gem-sway-strong { from { transform: rotate(-9deg); } to { transform: rotate(9deg); } }
@keyframes gem-bead-strong { from { transform: translateY(0); } to { transform: translateY(-5px); } }

@media (hover: hover) and (pointer: fine) {
    .shop-grid:has(.shop-card:hover) .shop-card:not(:hover) { opacity: .55; filter: saturate(.7); transform: scale(.985); }
    .shop-card:hover .shop-visual::before { opacity: 1; }
    .shop-card:hover::after { opacity: 1; animation: card-flow 2.6s linear infinite; }
    .shop-card:hover .gem-art { transform: scale(1.07); }
    .shop-card:hover .gem-sway { animation-name: gem-sway-strong; animation-duration: 1.7s; }
    .shop-card:hover .gem-glint { animation-duration: 1.9s; animation-delay: 0s; }
    .shop-card:hover .gem-bead { animation-name: gem-bead-strong; animation-duration: 1.1s; }
    .shop-card:hover .gem-turn { animation-duration: 9s; }
    .shop-card:hover .gem-tassel { animation-duration: 1.2s; }
    .shop-card:hover .gem-twinkle { animation-duration: .9s; }
}

/* Offset neighbouring cards so the shop never moves in unison. */
.shop-card:nth-child(3n+2) .gem-sway, .shop-card:nth-child(3n+2) .gem-glint { animation-delay: -1.4s; }
.shop-card:nth-child(3n) .gem-sway, .shop-card:nth-child(3n) .gem-glint { animation-delay: -2.7s; }
.shop-card:nth-child(2n) .gem-turn { animation-direction: reverse; }

/* Category artwork (CategoryArt.razor): a very slow drift, like a camera
   breathing over the illustration. Thumbnails stay still. */
.card-visual .category-art, .detail-visual .category-art { animation: art-drift 24s ease-in-out infinite alternate; }
@keyframes art-drift {
    from { transform: scale(1.04) translate(-1%, -.6%); }
    to { transform: scale(1.08) translate(1%, .6%); }
}

@media (prefers-reduced-motion: reduce) {
    .gem-sway, .gem-glint, .gem-twinkle, .gem-bead, .gem-turn, .gem-tassel,
    .card-visual .category-art, .detail-visual .category-art { animation: none !important; }
    .gem-glint { opacity: 0; }
    /* Hover keeps its light (aura, softened neighbours) but nothing moves or grows. */
    .shop-card:hover .gem-art, .shop-grid:has(.shop-card:hover) .shop-card:not(:hover) { transform: none; }
    .shop-card:hover::after { animation: none; }
}

/* ---- Shop icon (ShopBagIcon.razor) --------------------------------------- */
/* Idle: every 7 s the gem catches light and three sparkles twinkle out of the
   bag (secondary action, restraint). Hover/focus: anticipation squash, a hop
   with stretch, a soft landing; the handle swings a beat later
   (follow-through); the sparkles burst outward. */
.bag-icon { width: 24px; height: 24px; overflow: visible; }
.bag-icon * { transform-box: fill-box; }
.bag-body { transform-origin: 50% 100%; }
.bag-handle { transform-origin: 50% 100%; }
.bag-spark { fill: var(--cyan, #43e5e9); opacity: 0; transform-origin: center; animation: bag-twinkle 7s ease-in-out infinite; }
.bag-spark-2 { fill: var(--violet, #9c72ff); animation-delay: .25s; }
.bag-spark-3 { animation-delay: .5s; }
.bag-gem { animation: bag-gem 7s ease-in-out infinite; }

@keyframes bag-twinkle {
    0%, 78% { opacity: 0; transform: scale(.3); }
    84% { opacity: 1; transform: scale(1.15); }
    92%, 100% { opacity: 0; transform: scale(.6); }
}
@keyframes bag-gem {
    0%, 76%, 100% { filter: none; }
    82% { filter: brightness(1.8) drop-shadow(0 0 2px var(--cyan, #43e5e9)); }
}
@keyframes bag-hop {
    0% { transform: none; }
    18% { transform: translateY(1px) scale(1.08, .88); }      /* anticipation: squash */
    48% { transform: translateY(-3.5px) scale(.94, 1.08); }   /* stretch on the rise */
    74% { transform: translateY(0) scale(1.05, .95); }        /* land */
    100% { transform: none; }
}
@keyframes bag-handle-swing {
    0%, 20% { transform: rotate(0); }
    45% { transform: rotate(-14deg); }
    70% { transform: rotate(8deg); }
    100% { transform: rotate(0); }
}
@keyframes bag-burst {
    0% { opacity: 0; transform: translate(0, 0) scale(.3); }
    35% { opacity: 1; transform: translate(var(--bx), var(--by)) scale(1.2); }
    100% { opacity: 0; transform: translate(calc(var(--bx) * 1.8), calc(var(--by) * 1.8)) scale(.4); }
}
.bag-spark-1 { --bx: 2px; --by: -2px; }
.bag-spark-2 { --bx: 3px; --by: 1px; }
.bag-spark-3 { --bx: -1px; --by: -3px; }

.shop-icon-link:hover .bag-body, .shop-icon-link:focus-visible .bag-body { animation: bag-hop 560ms cubic-bezier(.3, 1.2, .5, 1); }
.shop-icon-link:hover .bag-handle, .shop-icon-link:focus-visible .bag-handle { animation: bag-handle-swing 700ms ease-out 60ms; }
.shop-icon-link:hover .bag-spark, .shop-icon-link:focus-visible .bag-spark { animation: bag-burst 700ms ease-out both; }
.shop-icon-link:hover .bag-spark-2, .shop-icon-link:focus-visible .bag-spark-2 { animation-delay: 70ms; }
.shop-icon-link:hover .bag-spark-3, .shop-icon-link:focus-visible .bag-spark-3 { animation-delay: 140ms; }
.shop-icon-link:active .bag-icon { transform: scale(.88); transition: transform var(--m-press) ease-out; }

@media (prefers-reduced-motion: reduce) {
    .bag-spark, .bag-gem, .bag-body, .bag-handle { animation: none !important; }
    .bag-spark { opacity: 0; }
    .shop-icon-link:active .bag-icon { transform: none; }
}

/* ---- Shop tagline and scene (ShopScene.razor) ---------------------------- */
/* "Have you Zunvi'd it?": the verb shimmers slowly through the palette.
   Scene, on a 6 s loop: three items arc into the bag at 0.0, 1.2 and 2.4 s
   (arcs: steady sideways + accelerating fall), the bag squashes on each catch
   (squash and stretch) and a sparkle flashes (secondary action), then rests. */
.shop-tagline-verb {
    background: linear-gradient(90deg, var(--cyan, #43e5e9), var(--violet, #9c72ff), #f4bc5c, var(--cyan, #43e5e9));
    background-size: 300% 100%;
    -webkit-background-clip: text; background-clip: text; color: transparent;
    animation: tagline-shimmer 9s linear infinite;
}
@keyframes tagline-shimmer { to { background-position: 300% 0; } }

.shop-scene { width: 120px; height: 100px; overflow: visible; color: var(--ink); }
.shop-scene * { transform-box: fill-box; }
.scene-bag { transform-origin: 50% 100%; animation: scene-catch 6s ease-out infinite; }
.scene-bag-gem { opacity: 0; animation: scene-filled 6s ease-out infinite; }
.scene-fly { animation: scene-fly-x 6s linear infinite; opacity: 0; }
.scene-fall { animation: scene-fall-y 6s cubic-bezier(.45, 0, .9, .6) infinite; }
.scene-fly-1 { --from-x: -46px; }
.scene-fly-2 { --from-x: 46px; }
.scene-fly-3 { --from-x: -30px; }
.scene-fly-2, .scene-fly-2 .scene-fall { animation-delay: 1.2s; }
.scene-fly-3, .scene-fly-3 .scene-fall { animation-delay: 2.4s; }
.scene-spark { fill: var(--cyan, #43e5e9); opacity: 0; transform-origin: center; animation: scene-spark 6s ease-out infinite; }
.scene-spark:nth-child(2) { fill: var(--violet, #9c72ff); animation-delay: 1.2s; }
.scene-spark:nth-child(3) { fill: #f4bc5c; animation-delay: 2.4s; }

/* One flight takes the first ~16% of the loop (about 1 s). */
@keyframes scene-fly-x {
    0% { opacity: 0; transform: translateX(var(--from-x)); }
    2% { opacity: 1; }
    15% { opacity: 1; transform: translateX(0); }
    17%, 100% { opacity: 0; transform: translateX(0) scale(.5); }
}
@keyframes scene-fall-y {
    0% { transform: translateY(-34px); }
    15%, 100% { transform: translateY(6px); }
}
@keyframes scene-catch {
    0%, 14% { transform: none; }
    17% { transform: scale(1.06, .9); }   /* first catch */
    21% { transform: scale(.98, 1.03); }
    24%, 34% { transform: none; }
    37% { transform: scale(1.06, .9); }   /* second, 1.2 s later */
    41% { transform: scale(.98, 1.03); }
    44%, 54% { transform: none; }
    57% { transform: scale(1.08, .88); }  /* third */
    62% { transform: scale(.98, 1.03); }
    66%, 100% { transform: none; }
}
@keyframes scene-filled { 0%, 60% { opacity: 0; } 64%, 92% { opacity: 1; } 100% { opacity: 0; } }
@keyframes scene-spark {
    0%, 15% { opacity: 0; transform: scale(.3); }
    18% { opacity: 1; transform: scale(1.2); }
    26%, 100% { opacity: 0; transform: scale(.5); }
}

@media (prefers-reduced-motion: reduce) {
    .shop-tagline-verb { animation: none; }
    .scene-bag, .scene-fly, .scene-fall, .scene-spark { animation: none !important; }
    .scene-fly, .scene-spark { opacity: 0; }
    .scene-bag-gem { animation: none !important; opacity: 1; }
}

/* ---- Reduced motion: light still answers, nothing moves ----------------- */

@media (prefers-reduced-motion: reduce) {
    @view-transition { navigation: none; }

    .home-hints a, .hub-category, .hub-subcategories a, .hub-chips a, .search-result, .shop-link,
    .hub-category strong, .hub-category span { transform: none !important; transition-property: border-color, color; }

    .hub-page h1, .hub-lede, .hub-categories > li, .hub-chips a, .hub-page .search-results > li, .shop-grid > .shop-card {
        animation: motion-fade 200ms linear backwards;
    }
}

@keyframes motion-fade { from { opacity: 0; } }
