/* ============================================================
   world-portal.css — the two houses as worlds, not cards
   ------------------------------------------------------------
   Layout is untouched. Only the interaction changes.

   Rule from the brief, enforced structurally: the logo and the typography
   NEVER move. Every animation here targets `.ds-wp__atmos*` layers, which sit
   between the photograph and the content. Nothing selects an <img> logo or a
   text node, so the type physically cannot drift.

   The two worlds are built from opposite materials:
     Dangerous Street — smoke, flicker, wet reflections, atmosphere DARKENS
     NEUN             — daylight, dust, breathing, atmosphere BRIGHTENS
   ============================================================ */

.ds-wp {
  --wp-ease: cubic-bezier(.4, 0, .2, 1);   /* ease-in-out, per the brief */
  --wp-enter: 600ms;
  position: relative;
  isolation: isolate;                      /* atmosphere can never bleed onto the page */
}

/* Every atmosphere layer: never interactive, never above the content. */
.ds-wp__atmos,
.ds-wp__atmos-2 {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;                              /* photo = 0, content = 2 */
  opacity: 0;
  transition: opacity 700ms var(--wp-ease);
}

/* ============ WORLD 01 — DANGEROUS STREET ============ */
/* Underground. Raw. Industrial. After dark. */

/* smoke: two offset drifts so it never reads as one looping texture */
.ds-wp[data-world="dangerous"] .ds-wp__atmos {
  background:
    radial-gradient(60% 45% at 25% 70%, rgba(190,185,175,.16) 0%, rgba(190,185,175,0) 60%),
    radial-gradient(45% 35% at 70% 55%, rgba(170,165,158,.13) 0%, rgba(170,165,158,0) 65%);
  filter: blur(14px);
  will-change: transform, opacity;
}
/* wet floor: a low sheen that only lives in the bottom third */
.ds-wp[data-world="dangerous"] .ds-wp__atmos-2 {
  background: linear-gradient(0deg, rgba(201,168,124,.10) 0%, rgba(201,168,124,0) 22%);
  mix-blend-mode: screen;
  will-change: opacity;
}

@media (hover: hover) and (pointer: fine) {
  .ds-wp[data-world="dangerous"]:hover .ds-wp__atmos {
    opacity: 1;
    animation: wp-smoke 19s ease-in-out infinite;
  }
  .ds-wp[data-world="dangerous"]:hover .ds-wp__atmos-2 {
    opacity: 1;
    animation: wp-wet 7s ease-in-out infinite;
  }
  /* camera pushes forward 2% — on the photograph only */
  .ds-wp[data-world="dangerous"]:hover img[data-hov="scale"] {
    transform: scale(1.02) !important;
  }
  /* atmosphere becomes slightly darker + the border glows warm grey */
  .ds-wp[data-world="dangerous"]:hover {
    box-shadow: inset 0 0 0 1px rgba(196,188,174,.34), inset 0 0 90px rgba(0,0,0,.34);
    animation: wp-flicker 5.5s ease-in-out infinite;
  }
}

@keyframes wp-smoke {
  0%   { transform: translate3d(-2.5%, 1.5%, 0) scale(1.06); }
  50%  { transform: translate3d(3%, -2%, 0) scale(1.12); }
  100% { transform: translate3d(-2.5%, 1.5%, 0) scale(1.06); }
}
@keyframes wp-wet {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}
/* soft light flicker: a hair of brightness, never a strobe */
@keyframes wp-flicker {
  0%, 100%  { filter: brightness(1); }
  42%       { filter: brightness(1); }
  44%       { filter: brightness(.955); }
  46%       { filter: brightness(1.01); }
  48%       { filter: brightness(.97); }
  52%       { filter: brightness(1); }
}

/* ============ WORLD 02 — NEUN ============ */
/* Quiet luxury. Architecture. Gallery. No smoke. No flicker. No darkness. */

/* daylight: a slow warm sweep, as if the sun moved across a gallery wall */
.ds-wp[data-world="neun"] .ds-wp__atmos {
  background: linear-gradient(102deg,
    rgba(255,247,232,0) 0%,
    rgba(255,246,230,.30) 42%,
    rgba(255,248,236,.16) 58%,
    rgba(255,247,232,0) 100%);
  background-size: 220% 100%;
  will-change: background-position, opacity;
}
/* dust: fine specks, lit by that daylight */
.ds-wp[data-world="neun"] .ds-wp__atmos-2 {
  background-image:
    radial-gradient(1.4px 1.4px at 18% 30%, rgba(255,252,244,.85) 50%, transparent 51%),
    radial-gradient(1.1px 1.1px at 62% 22%, rgba(255,252,244,.7) 50%, transparent 51%),
    radial-gradient(1.6px 1.6px at 78% 61%, rgba(255,252,244,.6) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 34% 72%, rgba(255,252,244,.75) 50%, transparent 51%),
    radial-gradient(1px 1px at 47% 48%, rgba(255,252,244,.65) 50%, transparent 51%),
    radial-gradient(1.3px 1.3px at 88% 36%, rgba(255,252,244,.55) 50%, transparent 51%);
  will-change: transform, opacity;
}

@media (hover: hover) and (pointer: fine) {
  .ds-wp[data-world="neun"]:hover .ds-wp__atmos {
    opacity: 1;
    animation: wp-daylight 16s ease-in-out infinite;
  }
  .ds-wp[data-world="neun"]:hover .ds-wp__atmos-2 {
    opacity: .9;
    animation: wp-dust 24s linear infinite;
  }
  /* gentle camera breathing — slower and softer than the DS push */
  .ds-wp[data-world="neun"]:hover img[data-hov="scale"] {
    animation: wp-breathe 9s ease-in-out infinite;
  }
  /* warm natural light. Brightens; never darkens. */
  .ds-wp[data-world="neun"]:hover {
    box-shadow: inset 0 0 0 1px rgba(160,142,112,.30), inset 0 0 110px rgba(255,246,230,.30);
  }
}

@keyframes wp-daylight {
  0%, 100% { background-position: 120% 0; }
  50%      { background-position: -20% 0; }
}
@keyframes wp-dust {
  0%   { transform: translate3d(0, 0, 0); }
  100% { transform: translate3d(-3%, -7%, 0); }
}
@keyframes wp-breathe {
  0%, 100% { transform: scale(1.008); }
  50%      { transform: scale(1.022); }
}

@media (prefers-reduced-motion: reduce) {
  .ds-wp__atmos,
  .ds-wp__atmos-2 { display: none !important; }
  .ds-wp[data-world]:hover img[data-hov="scale"] { animation: none !important; transform: none !important; }
  .ds-wp[data-world]:hover { animation: none !important; }
}

/* ============================================================
   Selector polish (17 Jul 2026). Buttons, focus, motion guards.
   Everything here is additive: no layout, no sizing, no routing.
   ============================================================ */

/* Keyboard focus. tileBase already spends `outline` on the ACTIVE state, so a
   focus ring built on outline would be invisible on the selected card exactly
   when a keyboard user needs it most. A box-shadow ring is independent of it,
   and the dark halo keeps the gold readable over the pale NEUN stone. */
.ds-wp:focus-visible {
  box-shadow: 0 0 0 3px #C9A87C, 0 0 0 6px rgba(0, 0, 0, .72) !important;
  outline: none !important;
}

/* Selected state must not rely on colour alone (WCAG 1.4.1): the active pill
   also carries a dot. */
.ds-wp[data-world][aria-current="true"] [data-wstatus]::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}

/* Local contrast behind the pill only — not a wash over the whole image.
   Sits under the content, above the photograph. */
.ds-wp[data-world="dangerous"] .ds-wp__atmos-2::after,
.ds-wp[data-world="neun"] .ds-wp__atmos-2::after {
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 46%;
  height: 34%;
  pointer-events: none;
}
.ds-wp[data-world="dangerous"] .ds-wp__atmos-2::after {
  background: radial-gradient(120% 100% at 100% 0%, rgba(5, 5, 5, .55) 0%, rgba(5, 5, 5, 0) 72%);
}
.ds-wp[data-world="neun"] .ds-wp__atmos-2::after {
  background: radial-gradient(120% 100% at 100% 0%, rgba(252, 251, 249, .62) 0%, rgba(252, 251, 249, 0) 72%);
}

/* Hover edge definition. Restrained: a border presence, not a neon glow. */
@media (hover: hover) and (pointer: fine) {
  .ds-wp[data-world="dangerous"]:hover { box-shadow: 0 0 0 1px rgba(201, 168, 124, .55), 0 26px 60px -34px rgba(0, 0, 0, .8); }
  .ds-wp[data-world="neun"]:hover     { box-shadow: 0 0 0 1px rgba(120, 108, 92, .5), 0 26px 60px -34px rgba(120, 110, 95, .45); }
}

/* MOTION GUARDS.
   The push-in is applied by Shop.dc.html's JS as an INLINE transform, so only
   !important can neutralise it. Two cases must be neutralised:

   1. reduced-motion — a stated preference, honoured absolutely.
   2. coarse pointers — on touch, `mouseenter` fires on tap and then never
      leaves, so the image would silently sit zoomed for the rest of the
      session and a scroll-tap could zoom it by accident. The brief asks for no
      accidental zoom while scrolling; the honest answer on touch is no push at
      all, since there is no hover to express. */
@media (prefers-reduced-motion: reduce) {
  .ds-wp img[data-hov="scale"] { transform: none !important; transition: none !important; }
  .ds-wp__atmos, .ds-wp__atmos-2 { transition: none !important; }
}
@media (hover: none), (pointer: coarse) {
  .ds-wp img[data-hov="scale"] { transform: none !important; }
}
