/* =====================================================================
   SIGNAL COMBAT · THE RIFT (the approved world plate)
   js/world-map.js holds every place; js/world.js draws the plate and the effects in one world space; js/map.js reads the
   game onto it and moves the camera. This file: how those look. The illustration is the world: nothing here repaints it,
   and everything over it stays faint enough to leave it alone.
   ===================================================================== */

/* one illustration, moving only with the camera: no parallax between its layers, no idle drift (either would slide the
   places off the art they belong to). The camera's transform is measured from the world's top left (map.js frameRift) */
.ws.rift { background: #0B0D1A; }
.ws.rift .wl { transform: none !important; transition: none !important; }
.ws.rift .ws-drift { animation: none; }
.ws.rift .ws-cam { transform-origin: 0 0; }

/* the plate: the 800-wide placeholder at once, the right size over it as soon as it's decoded */
.wa-plate { position: absolute; pointer-events: none; }
.wa-plate img { position: absolute; inset: 0; width: 100%; height: 100%; display: block; max-width: none; -webkit-user-select: none; user-select: none; -webkit-user-drag: none; }
.wa-plate .hi { opacity: 0; transition: opacity .45s ease; }
.wa-plate.ok .hi { opacity: 1; }
.wa-plate.ok .lq { visibility: hidden; transition: visibility 0s linear .5s; }
.wa-plate.no .lq { visibility: visible; }

/* ---------- the Road's hum: a faint light standing along the fissure, pulses running up it toward the mountain ---------- */
.w-rift { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; will-change: transform; }
.w-rift polyline { fill: none; stroke-linecap: round; stroke-linejoin: round; }
.w-rift .rh-base { stroke: #CFEFFF; opacity: .1; mix-blend-mode: screen; }
.w-rift .rh-glow { stroke: #9ADBFF; opacity: 0; stroke-dasharray: 30 970; stroke-dashoffset: 1000; mix-blend-mode: screen; }
.w-rift .rh-hum { stroke: #F4FCFF; opacity: 0; stroke-dasharray: 8 992; stroke-dashoffset: 1000; mix-blend-mode: screen; }
@media (prefers-reduced-motion: no-preference) {
  .w-rift .rh-base { animation: rh-breathe 7s ease-in-out infinite; animation-delay: var(--t0); }
  .w-rift .rh-glow { opacity: .2; animation: rh-run 9s linear infinite; animation-delay: calc(var(--t0) - 1.1s); }
  .w-rift .rh-hum { opacity: .8; animation: rh-run 9s linear infinite; animation-delay: var(--t0); }
  .w-rift .rh-hum.b { opacity: .5; animation-duration: 13s; animation-delay: calc(var(--t0) - 6.5s); }
}
@keyframes rh-run { from { stroke-dashoffset: 1000; } to { stroke-dashoffset: 0; } }
@keyframes rh-breathe { 0%, 100% { opacity: .08; } 50% { opacity: .16; } }

/* ---------- the eruption: the plate's own fire, a slow glow behind its core, embers rising out of the crater ---------- */
.fx-glow.rift { background: radial-gradient(closest-side, rgba(255, 160, 80, .2), rgba(214, 86, 44, .07) 58%, transparent); }
.fx-ember.rift { width: 2.5px; height: 2.5px; animation-name: fx-ember-r; }
@keyframes fx-ember-r { 0% { opacity: 0; transform: translate(0, 0); } 12% { opacity: .85; } 100% { opacity: 0; transform: translate(var(--dx), -7cqh); } }

/* ---------- SORA's lifts: the art's own cars on the plate's own cables (world.js riftLifts) ----------
   Each car and its light move by their transform alone (one keyframed animation each, made from the manifest). They run
   while their part of the world is on screen (lf-held stops them where they are); with reduced motion they don't move at all:
   every car stays home, where the art drew it (the rule for every animation in .ws: combat.css) */
.lf { position: absolute; display: block; max-width: none; pointer-events: none; -webkit-user-select: none; user-select: none; -webkit-user-drag: none;
  animation-timing-function: linear; animation-iteration-count: infinite; animation-fill-mode: both; will-change: transform; }
/* the light a lit car carries: a gain on what's behind it (colour-dodge: the plate's black lines stay black) */
.lf-light { mix-blend-mode: color-dodge; }
.lf-zone { position: absolute; pointer-events: none; }
.ws.rift.lf-held .lf { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .ws.rift .lf { animation: none !important; will-change: auto; } }

/* ---------- 3.8.2 · the searchlights: the art's own beams (world.js riftBeams), a gain on the plate (colour-dodge, as the lifts' light),
   swinging about their lamps between −sweep and +sweep, their painted place in the middle. Reduced motion: at home */
.bm { position: absolute; display: block; max-width: none; pointer-events: none; -webkit-user-select: none; user-select: none;
  animation-name: bm-sweep; animation-timing-function: ease-in-out; animation-iteration-count: infinite; animation-direction: alternate; will-change: transform; }
.bm.dodge, .bm-band { mix-blend-mode: color-dodge; }
.bm-band { position: absolute; inset: 0; display: block; pointer-events: none; isolation: isolate; }
.bm-half { position: absolute; inset: 0; display: block; pointer-events: none; }
.bm-cover { position: absolute; display: block; max-width: none; pointer-events: none; -webkit-user-select: none; user-select: none; }
@keyframes bm-sweep { from { transform: rotate(calc(var(--bs) * -1)); } to { transform: rotate(var(--bs)); } }
@media (prefers-reduced-motion: reduce) { .ws.rift .bm { animation: none !important; will-change: auto; } }

/* ---------- a land's lights: its own colour over the plate's lamps and windows, breathing (out while the land is locked) ---------- */
.lt.rift { opacity: .5; }
.lt.rift.lt-beacon { opacity: .7; }
.lt.rift.lt-glow { opacity: .3; animation: lt-glow-r 6s ease-in-out infinite; animation-delay: calc(var(--t0) - var(--o)); }
@keyframes lt-glow-r { 0%, 100% { opacity: .2; } 50% { opacity: .38; } }
.lt.rift.lt-neon { opacity: .42; animation: lt-neon-r 5.2s steps(1) infinite; animation-delay: calc(var(--t0) - var(--o)); }
@keyframes lt-neon-r { 0%, 88%, 100% { opacity: .42; } 89%, 91% { opacity: .12; } 93% { opacity: .5; } }
.lt.rift.lt-bay { border-radius: 2px; transform: translate(-50%, -50%) scaleX(1.6); opacity: .34; }
.lt.rift.lt-lantern { opacity: .55; }
.scm .lt.rift.m-dim { opacity: 0 !important; animation: none !important; }

/* ---------- a land the Road hasn't reached: its art a step darker, feathered at the edge; the veil lifts once when it opens ---------- */
.m-veils { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; mix-blend-mode: multiply; }
.m-landveil { fill: #666C8C; opacity: 1; }          /* multiplied: darker and cooler, the linework still there (asleep, not erased) */
@media (prefers-reduced-motion: no-preference) {
  .scm .m-landveil.m-t-out { animation: m-out 2.2s ease both; animation-delay: calc(var(--mt, 0s) + .3s); }
}
@media (prefers-reduced-motion: reduce) { .scm .m-landveil.m-t-out { opacity: 0; } }

/* ---------- the lands you can open: nothing until you point at one; then a faint haze of its colour over it ---------- */
.w-hits.rift .w-land { fill: transparent; stroke: none; }
.w-hits.rift .m-hl { fill: var(--c); opacity: 0; pointer-events: none; transition: opacity .3s ease; }
.w-hits.rift .w-land:hover + .m-hl { opacity: .07; }
.w-hits.rift .w-land.on + .m-hl { opacity: .09; }
.w-hits.rift .w-land:focus-visible { stroke: var(--orange, #FC4C01); stroke-width: 2; stroke-dasharray: 6 6; vector-effect: non-scaling-stroke; }
.w-hits.rift .w-land:focus-visible + .m-hl { opacity: .09; }

/* the challenge board is small on the plate (it stands among the tents): its tap reaches past its art, a finger's width */
.ws.rift .wo-pvp::before { content: ''; position: absolute; inset: -32% -26%; }

/* the den (the cave) and the Source's water: places you can tap, quiet until you point at them */
.m-places { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
.m-places .m-den, .m-places .m-src { fill: transparent; pointer-events: auto; cursor: pointer; outline: none; transition: fill .25s ease; }
.m-places .m-den:hover { fill: rgba(159, 177, 196, .07); }
.m-places .m-src:hover { fill: rgba(252, 140, 60, .06); }
.m-places .m-den:focus-visible, .m-places .m-src:focus-visible { stroke: var(--orange, #FC4C01); stroke-width: 2; stroke-dasharray: 6 6; vector-effect: non-scaling-stroke; }
.m-places .rd-eyes circle { fill: #FF2A2A; opacity: 0; }

/* the edge of the frame: a soft vignette, lighter than the layered world's (the plate has its own darkness) */
.ws-vig.rift { background: radial-gradient(ellipse 86% 82% at 50% 50%, transparent 64%, rgba(3, 4, 9, .34) 100%); }

/* the map's own marks hold still while a story uses the world (its camera, its dimming) */
.ws.rift.m-hide .w-hits, .ws.rift.m-hide .m-places { display: none; }

/* a phone (and a tablet held upright): the world lifts over the card so what the card is about stays in view. map.js sets
   how far (--v-lift: never past the card's top edge, so nothing but the world shows above it) */
@media (min-width: 761px) and (max-width: 860px) {
  html.v-rift.v-onworld .v-world { transform: translateY(calc(-1 * var(--v-lift, 0px))); }
  html.v-rift .v-world { transition: transform .45s var(--v-ease, cubic-bezier(.2, .7, .2, 1)); }
}
@media (max-width: 760px) {
  html.v-rift.v-onworld .v-world { transform: translateY(calc(-1 * var(--v-lift, 0px))); }
}
@media (prefers-reduced-motion: reduce) { html.v-rift .v-world { transition: none !important; } }
