/* ============================================================
   GUCCHI — animations.css
   Keyframes, motion primitives, and the styling for the pieces
   that gsap-scroll.js / interactions.js / contact.html bring in.

   Loaded AFTER sections.css and BEFORE responsive.css.
   Everything here respects prefers-reduced-motion at the foot
   of the file — add new motion above that block, not below it.
   ============================================================ */

/* ============================================================
   1. KEYFRAMES
   ============================================================ */

@keyframes rise {
  from { opacity: 0; transform: translateY(34px); }
  to   { opacity: 1; transform: none; }
}

@keyframes fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes maskUp {
  from { transform: translateY(105%) rotate(2deg); }
  to   { transform: none; }
}

/* The scroll cue: a slow, patient fall. */
@keyframes cueFall {
  0%   { transform: translateY(-40%); opacity: 0; }
  35%  { opacity: 1; }
  100% { transform: translateY(120%); opacity: 0; }
}

/* Gold hairline drawing itself across a rule. */
@keyframes drawRule {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* Breathing glow behind the crest / hero mark. */
@keyframes emberPulse {
  0%, 100% { opacity: .35; transform: scale(1); }
  50%      { opacity: .62; transform: scale(1.06); }
}

/* Map pins: a slow radar halo. */
@keyframes halo {
  0%   { transform: scale(1);   opacity: .55; }
  70%  { transform: scale(3.1); opacity: 0; }
  100% { transform: scale(3.1); opacity: 0; }
}

/* Spinner for the submit button while the mail is going out. */
@keyframes spin { to { transform: rotate(360deg); } }

/* Shimmer for loading placeholders. */
@keyframes shimmer {
  from { background-position: -160% 0; }
  to   { background-position:  260% 0; }
}

/* The preloader bar. */
@keyframes barPulse {
  0%, 100% { opacity: .7; }
  50%      { opacity: 1; }
}

/* Grain drift on the noise overlay. */
@keyframes grainDrift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-2%, 1%); }
  50%  { transform: translate(1%, -2%); }
  75%  { transform: translate(2%, 2%); }
  100% { transform: translate(0, 0); }
}

/* Slow float, for the 3D fallback stills. */
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-10px); }
}


/* ============================================================
   2. MOTION PRIMITIVES  (used when GSAP is absent or as a base)
   ============================================================ */

/* If JS never runs, nothing should be stuck invisible.
   main.js sets opacity:0 itself — but only after confirming it's alive. */
.no-js [data-rise],
.no-js [data-fade],
.no-js [data-stagger] > * { opacity: 1 !important; transform: none !important; }

.anim-rise  { animation: rise .9s var(--ease, cubic-bezier(.22,1,.36,1)) both; }
.anim-fade  { animation: fade .8s ease both; }
.anim-bob   { animation: bob 7s ease-in-out infinite; }

.d-1 { animation-delay: .08s; }
.d-2 { animation-delay: .16s; }
.d-3 { animation-delay: .24s; }
.d-4 { animation-delay: .32s; }
.d-5 { animation-delay: .40s; }

/* Grain overlay drifts, very slowly, so it never looks like a static PNG. */
.noise { animation: grainDrift 22s steps(6, end) infinite; }

/* Scroll cue */
.cue__dot { animation: cueFall 2.4s cubic-bezier(.6,.04,.3,1) infinite; }


/* ============================================================
   3. READING-PROGRESS RAIL  (injected by gsap-scroll.js)
   ============================================================ */
.srail {
  position: fixed;
  inset: 0 0 auto 0;
  height: 2px;
  z-index: 90;
  pointer-events: none;
  background: rgba(242, 181, 96, .10);
}
.srail > i {
  display: block;
  height: 100%;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--gold-600), var(--gold-400), var(--gold-300));
  box-shadow: 0 0 12px rgba(242, 181, 96, .55);
}


/* ============================================================
   4. HEADER STATES  (gsap-scroll.js toggles these)
   ============================================================ */
.hdr {
  transition: transform .5s var(--ease, cubic-bezier(.22,1,.36,1)),
              background .5s ease,
              backdrop-filter .5s ease,
              box-shadow .5s ease,
              padding .5s var(--ease, cubic-bezier(.22,1,.36,1));
  will-change: transform;
}
.hdr.is-stuck {
  background: rgba(26, 7, 8, .82);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  box-shadow: 0 1px 0 rgba(242, 181, 96, .16), 0 18px 40px -28px rgba(0, 0, 0, .9);
}
.hdr.is-stuck .hdr__in { padding-block: .7rem; }
.hdr.is-away { transform: translateY(-102%); }


/* ============================================================
   5. BUTTONS
   ============================================================ */
.btn { transition: transform .35s var(--ease, cubic-bezier(.22,1,.36,1)), background .35s ease, color .35s ease, border-color .35s ease; }
.btn .arw { transition: transform .4s var(--ease, cubic-bezier(.22,1,.36,1)); display: inline-block; }
.btn:hover .arw { transform: translateX(5px); }

.btn.is-busy { pointer-events: none; opacity: .72; }
.btn.is-busy .arw { animation: spin .85s linear infinite; }
.btn[disabled] { cursor: not-allowed; }


/* ============================================================
   6. CONTACT FORM — the bits sections.css doesn't cover
   ============================================================ */

/* Consent checkbox row */
.field--check {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: .5rem .75rem;
  margin-top: .25rem;
}
.field--check input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 3px;
  accent-color: var(--gold-400);
  cursor: pointer;
  flex: none;
}
.field--check label {
  text-transform: none;
  letter-spacing: .01em;
  font-size: var(--t-sm);
  color: rgba(251, 246, 234, .74);
  line-height: 1.65;
  cursor: pointer;
}
.field--check .err { grid-column: 1 / -1; }
.field--check.has-error input[type="checkbox"] { outline: 1px solid #E0645A; outline-offset: 3px; }

/* Live character count */
.cnt {
  font-family: var(--mono);
  font-size: var(--t-xs);
  color: rgba(251, 246, 234, .34);
  text-align: right;
  letter-spacing: .04em;
  margin: 0;
  transition: color .3s ease;
}
.cnt.is-near { color: var(--gold-400); }

/* Submit row */
.form__foot {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  flex-wrap: wrap;
  margin-top: .4rem;
}
.form__legal {
  font-size: var(--t-xs);
  color: rgba(251, 246, 234, .42);
  margin: 0;
  line-height: 1.6;
  max-width: 34ch;
}

/* Error messages arrive rather than appearing. */
.err:not(:empty) { animation: fade .28s ease both; }

/* Once it has gone out, dim the form so the confirmation reads first. */
.form.is-sent .field,
.form.is-sent .form__foot { opacity: .55; transition: opacity .6s ease; }

.note { animation: rise .5s var(--ease, cubic-bezier(.22,1,.36,1)) both; }

/* Small inline icons in the info column */
.info__blk h3 .ico {
  width: 18px;
  height: 18px;
  vertical-align: -3px;
  margin-right: .45rem;
  opacity: .85;
  filter: brightness(0) saturate(100%) invert(78%) sepia(38%) saturate(700%) hue-rotate(340deg) brightness(102%) contrast(94%);
}
.info__hint { color: rgba(251, 246, 234, .42) !important; font-size: var(--t-xs) !important; }
.info__blk--quiet p {
  font-family: var(--display);
  font-size: 1.1rem;
  font-style: italic;
  color: rgba(251, 246, 234, .55) !important;
  line-height: 1.7;
}
.lnk { border-bottom: 1px solid rgba(242, 181, 96, .32); transition: border-color .3s ease, color .3s ease; }
.lnk:hover { border-color: var(--gold-400); }

.spec--tight .spec__row { padding-block: .55rem; }

/* Click-to-copy affordance */
.is-copyable { cursor: copy; position: relative; transition: color .3s ease; }
.is-copyable:hover { color: var(--gold-400); }
.is-copied::after {
  content: "Copied";
  position: absolute;
  left: 50%;
  bottom: calc(100% + 6px);
  transform: translateX(-50%);
  background: var(--gold-400);
  color: var(--maroon-950);
  font-family: var(--body);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  padding: .25rem .5rem;
  border-radius: 2px;
  white-space: nowrap;
  animation: fade .2s ease both;
}


/* ============================================================
   7. CONTACT MAP  (.cmap)
   ============================================================ */
.cmap {
  position: relative;
  max-width: 880px;
  margin-inline: auto;
}
.cmap__svg {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
}

.cmap__land { filter: drop-shadow(0 30px 50px rgba(0, 0, 0, .5)); }

/* The outline draws itself in when the section arrives. */
.cmap__edge {
  stroke-dasharray: 1600;
  stroke-dashoffset: 1600;
  animation: drawEdge 2.6s cubic-bezier(.22, 1, .36, 1) forwards;
  animation-play-state: paused;
}
.cmap.is-in .cmap__edge,
.cmap[data-rise] .cmap__edge { animation-play-state: running; }
@keyframes drawEdge { to { stroke-dashoffset: 0; } }

.cmap__ridges path { opacity: .8; }

.cmap__river path {
  stroke-dasharray: 3 5;
  animation: flow 9s linear infinite;
}
@keyframes flow { to { stroke-dashoffset: -80; } }

/* District dots */
.cmap__dot circle:first-child {
  fill: var(--gold-400);
  filter: drop-shadow(0 0 8px rgba(242, 181, 96, .8));
}
.cmap__halo {
  fill: none;
  stroke: var(--gold-400);
  stroke-width: 1.2;
  transform-origin: center;
  transform-box: fill-box;
  animation: halo 3.4s ease-out infinite;
}
.cmap__dot:nth-child(2) .cmap__halo { animation-delay: 1.1s; }
.cmap__dot:nth-child(3) .cmap__halo { animation-delay: 2.2s; }

.cmap__dot text {
  fill: var(--cream-100);
  font-family: var(--display);
  font-size: 17px;
  text-anchor: middle;
  letter-spacing: .04em;
}
.cmap__alt {
  fill: rgba(242, 181, 96, .72) !important;
  font-family: var(--mono) !important;
  font-size: 10px !important;
  letter-spacing: .1em !important;
}

/* Delhi */
.cmap__hq rect { fill: var(--cream-200); }
.cmap__hq text {
  fill: rgba(251, 246, 234, .82);
  font-family: var(--display);
  font-size: 15px;
  letter-spacing: .04em;
}
.cmap__hq .cmap__alt { fill: rgba(251, 246, 234, .42) !important; }

/* The dashed road down to Delhi crawls. */
.cmap__route { animation: flow 6s linear infinite reverse; }

.cmap__key {
  display: flex;
  gap: 2rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 1.6rem;
  list-style: none;
  padding: 0;
  font-size: var(--t-xs);
  letter-spacing: .08em;
  color: rgba(251, 246, 234, .55);
}
.cmap__key li { display: flex; align-items: center; gap: .55rem; }
.key__dot, .key__sq {
  width: 9px;
  height: 9px;
  flex: none;
  background: var(--gold-400);
  box-shadow: 0 0 8px rgba(242, 181, 96, .7);
}
.key__dot { border-radius: 50%; }
.key__sq { background: var(--cream-200); transform: rotate(45deg); box-shadow: none; }


/* ============================================================
   8. 360° PRODUCT VIEWER STATES  (interactions.js)
   ============================================================ */

/* .pdp__media is a centring grid. The model and the fallback still are two
   separate children of it, so without this they would stack in two rows.
   Put them both in cell 1/1 and they overlay, which is what we want. */
.pdp__media > .pdp__model,
.pdp__media > .pdp__still {
  grid-area: 1 / 1;
}

.pdp__model {
  position: relative;
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: pan-y;
}
.pdp__model.is-grabbing { cursor: grabbing; }
.pdp__model:focus-visible { outline: 1px solid var(--gold-400); outline-offset: 8px; }

/* Nothing mounted (no WebGL / no [data-model]) — don't reserve the space. */
.pdp__model.is-static { height: 0; cursor: default; }

.pdp__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity .9s ease;
}
.pdp__model.is-loaded .pdp__canvas { opacity: 1; }

/* While the GLB is in flight, the still shows and shimmers gently.
   NOTE: .pdp__still is a SIBLING of .pdp__model (both live inside .pdp__media),
   so these are sibling combinators, not descendant ones. */
.pdp__model.is-live ~ .pdp__still { transition: opacity .9s ease; }
.pdp__model.is-live.is-loaded ~ .pdp__still { opacity: 0; pointer-events: none; }

/* WebGL unavailable: the still is the product shot, and it breathes. */
.pdp__model.is-static ~ .pdp__still { animation: bob 7s ease-in-out infinite; }
.pdp__model.is-static ~ .pdp__hint { display: none; }

.pdp__hint {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .6s ease .3s, transform .6s var(--ease, cubic-bezier(.22,1,.36,1)) .3s;
}
.pdp__hint.is-on { opacity: 1; transform: none; }


/* ============================================================
   9. LOADING SHIMMER  (generic)
   ============================================================ */
.is-loading {
  background-image: linear-gradient(100deg,
    rgba(251, 246, 234, .03) 30%,
    rgba(242, 181, 96, .10) 50%,
    rgba(251, 246, 234, .03) 70%);
  background-size: 220% 100%;
  animation: shimmer 1.5s linear infinite;
}
.pre__fill { animation: barPulse 1.6s ease-in-out infinite; }


/* ============================================================
   10. CTA BAND BLEED  (gsap-scroll.js scrubs --bleed)
   ============================================================ */
.band { --bleed: 0%; }
.band::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 2px;
  background: linear-gradient(90deg, var(--gold-600), var(--gold-300));
  transform: scaleX(calc(var(--bleed) / 100%));
  transform-origin: 0 50%;
  opacity: .85;
}


/* ============================================================
   11. CARD / PILLAR HOVER SHEEN  (interactions.js sets --gx/--gy)
   ============================================================ */

/* --- TRANSFORM OWNERSHIP -------------------------------------------------
   gsap-scroll.js parallaxes `.card__media img` and `.pack-strip figure img`
   by writing an INLINE transform. An inline style beats a stylesheet rule,
   so the old `:hover img { transform: scale() }` in sections.css could never
   fire — the two were fighting over one property on one element.

   Fix: GSAP keeps the transform on the <img>. The hover zoom moves up to the
   wrapper, which GSAP never touches. Both effects now work, and they compose.
   These rules load after sections.css, so they win.
   ------------------------------------------------------------------------ */
.card__media,
.pack-strip figure {
  transition: transform 1.1s var(--ease, cubic-bezier(.22,1,.36,1));
  will-change: transform;
}
.card:hover .card__media { transform: scale(1.07); }
.pack-strip figure:hover { transform: scale(1.06); }

/* Cancel the old rules that targeted the image itself. */
.card:hover .card__media img,
.pack-strip figure:hover img { transform: none; }

/* Filter is a different property — GSAP does not touch it, so keep it here. */
.pack-strip figure:hover img { filter: saturate(1.1) brightness(1); }

.card, .value, .pillar {
  --gx: 50%;
  --gy: 50%;
  transition: transform .5s var(--ease, cubic-bezier(.22,1,.36,1)), border-color .4s ease, background .4s ease;
  will-change: transform;
}
.card::after, .value::after, .pillar::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity .45s ease;
  background: radial-gradient(340px circle at var(--gx) var(--gy),
              rgba(242, 181, 96, .10), transparent 62%);
}
.card:hover::after, .value:hover::after, .pillar:hover::after { opacity: 1; }


/* ============================================================
   12. LOTTIE HOSTS
   ============================================================ */
.lot { display: block; line-height: 0; }
.lot svg { display: block; width: 100%; height: 100%; }
.lot--crest { width: clamp(64px, 8vw, 108px); margin-inline: auto; }
.lot--leaf  { width: 56px; }
.lot--cue   { width: 30px; }

.ember {
  position: absolute;
  inset: -18%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(242, 181, 96, .35), transparent 62%);
  filter: blur(18px);
  animation: emberPulse 5.5s ease-in-out infinite;
  pointer-events: none;
}


/* ============================================================
   13. FAQ CHEVRON
   ============================================================ */
.faq__ico {
  position: relative;
  width: 14px;
  height: 14px;
  flex: none;
}
.faq__ico::before, .faq__ico::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 1px;
  background: var(--gold-400);
  transition: transform .45s var(--ease, cubic-bezier(.22,1,.36,1)), opacity .3s ease;
}
.faq__ico::after { transform: rotate(90deg); }
.faq__q[aria-expanded="true"] .faq__ico::after { transform: rotate(0deg); opacity: 0; }


/* ============================================================
   14. REDUCED MOTION — the last word on everything above
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }

  .noise,
  .cue__dot,
  .cmap__halo,
  .cmap__river path,
  .cmap__route,
  .ember,
  .pre__fill,
  .is-loading,
  .pdp__model.is-static ~ .pdp__still { animation: none !important; }

  .cmap__edge { stroke-dashoffset: 0 !important; }
  .srail { display: none; }
  .hdr.is-away { transform: none !important; }

  .card:hover, .value:hover, .pillar:hover,
  .card, .value, .pillar { transform: none !important; }
}
