/* ═══════════════════════════════════════════════════════════════════════════
   Sky, horizon, street and the things that move along it (§7).
   ═════════════════════════════════════════════════════════════════════════ */

.sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(180deg, #cfe6f2 0%, #fdf6ea 100%);
  transition: background 0.9s ease;
  overflow: hidden;
}

.sky-orb { position: absolute; top: 26px; right: 26px; width: 52px; height: 52px; }
.sky-orb svg { display: block; }

.sky-stars { position: absolute; inset: 0; opacity: 0; transition: opacity 1s ease; }
.sky.is-night .sky-stars { opacity: 1; }
.sky-stars i {
  position: absolute;
  background: #fff6e2;
  border-radius: 50%;
  animation: twinkle 3s ease-in-out infinite;
}
@keyframes twinkle { 0%, 100% { opacity: 0.45; } 50% { opacity: 1; } }

/* The distant city. Sits above the street, below everything the player
   interacts with, and never animates. */
.sky-horizon {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--street-h) - 18px);
  height: 118px;
  line-height: 0;
  pointer-events: none;
}
.sky-horizon svg { display: block; width: 100%; height: 100%; }

/* ── The street ─────────────────────────────────────────────────────────────

   This used to read as a highway, and it was three things doing it: a dashed
   white centre line down the middle, a cool grey-green asphalt tone, and a
   road that took up more of the band than the pavement did.

   All three are gone. There is no centre line at all now — a lane marking is
   the single most road-coded graphic there is, and a cozy street does not have
   one. `--kerb` is how much of the band is roadway, and it is deliberately the
   smaller share: the pavement is the part of the street the player lives on,
   so it gets the room.
   ────────────────────────────────────────────────────────────────────────── */
.street {
  --kerb: 26px;             /* height of the roadway; the rest is pavement */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--street-h);
  z-index: 4;
  pointer-events: none;     /* never swallow a swipe meant for the content */
  overflow: hidden;
}

/* On the game screen the street is a real row in the column rather than an
   absolute band, so the tower can stand on it. Absolutely positioned, it ended
   up underneath the on-screen keyboard and the building floated. */
.play-stage {
  position: relative;
  z-index: 3;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.play-stage .sky-horizon {
  bottom: 44px;          /* just above the pavement */
  height: 104px;
  z-index: 1;
}
.street-game {
  --kerb: 18px;          /* shallower band, same pavement-first proportion */
  position: relative;
  flex: 0 0 56px;
  height: 56px;
  z-index: 4;
}
.street-game .street-ground { height: 44px; }

.street-ground {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 56px;
  /* Overridden per city by sky.js, which knows the local paving stone. */
  background: #d8c6a6;
  transition: background 0.9s ease;
}

/*
  The roadway.

  Drawn as a translucent overlay rather than its own colour so it darkens
  whatever paving tone sky.js has set inline for the city — six cities, one
  rule. The two gradients are stone joints: barely-there at this size, but
  enough that the surface has grain instead of being a flat slab.
*/
.street-ground::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--kerb);
  background:
    repeating-linear-gradient(90deg, rgba(74, 56, 38, 0.07) 0 1px, transparent 1px 14px),
    repeating-linear-gradient(0deg, rgba(74, 56, 38, 0.05) 0 1px, transparent 1px 8px),
    rgba(66, 48, 32, 0.13);
}

/*
  The kerb.

  Was a 2px hairline of flat black at 12%. Now it is a warm shadow with a lit
  top edge, which is what a kerbstone actually looks like and what stops the
  pavement and the road reading as two colours of the same flat plane.
*/
.street-ground::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--kerb);
  height: 3px;
  background: linear-gradient(180deg,
    rgba(255, 244, 224, 0.45) 0 1px,
    rgba(74, 56, 38, 0.22) 1px 3px);
}

/*
  Soft ends.

  An edge-to-edge band with nothing happening at its margins reads as an
  infinite thoroughfare. Darkening the last few percent gives the street a
  sense of continuing around a corner rather than running forever.
*/
.street::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 6;
  background: linear-gradient(90deg,
    rgba(74, 56, 38, 0.16) 0%, transparent 7%,
    transparent 93%, rgba(74, 56, 38, 0.16) 100%);
}

.lane-statics, .lane-walk, .lane-road {
  position: absolute;
  left: 0;
  right: 0;
}
/* All three hang off the kerb, so re-proportioning the street moves the cast
   with it instead of leaving pedestrians standing in the road. */
.lane-statics { bottom: calc(var(--kerb) + 8px); height: 0; }
.lane-walk    { bottom: calc(var(--kerb) + 4px); height: 0; }
.lane-road    { bottom: 2px;  height: 0; }

/*
  Lane order, made explicit.

  `.lane-statics` is a sibling of `.street-ground` and comes *before* it in the
  markup, so with everything at `z-index: auto` the opaque pavement painted
  straight over the bottom 26px of every tree, lamppost and kiosk — they were
  standing behind the ground rather than on it. It went unnoticed while the
  cast was tall and top-heavy (a tree canopy still reads as a tree with no
  trunk) and became obvious the moment anything had detail near its feet.

  Depth order on a pavement, back to front: the ground, then what stands on
  it, then what walks past it, then the traffic nearest the viewer.
*/
.lane-statics { z-index: 1; }
.lane-walk, .lane-road { z-index: 2; }

/* ── Props ──────────────────────────────────────────────────────────────── */
.prop {
  position: absolute;
  bottom: 0;
  line-height: 0;
}
.prop svg { display: block; }

/*
  Contact shadow.

  Nothing on the street had one, so every prop looked pasted onto the pavement
  rather than standing on it. A soft warm ellipse under the feet is what
  attaches an actor to the ground.
*/
.prop::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -1px;
  width: 82%;
  height: 6px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(74, 56, 38, 0.26), rgba(74, 56, 38, 0) 72%);
  pointer-events: none;
}
.lane-walk .prop::before { width: 70%; height: 5px; }
/* Bunting hangs in the air; it has nothing to cast a shadow onto. */
.prop-static.is-raised::before { display: none; }

.prop-static {
  transform: translateX(-50%);
  bottom: 0;
  opacity: 0.95;
}
/* Static props sit on the pavement, so they hang from their own baseline. */
.lane-statics .prop-static { bottom: -4px; }
/* …except the ones strung overhead, which set their own offset. */
.lane-statics .prop-static.is-raised { bottom: auto; opacity: 0.85; }

.prop-mover {
  left: 0;
  animation: prop-travel var(--dur, 8000ms) linear forwards;
  will-change: transform;
}
.prop-mover.flipped svg { transform: scaleX(-1); }

/* Everything on the street is lit by the same sky. One filter rather than a
   night colour for all eighteen props in all six city rosters. */
.is-night .prop { filter: brightness(0.72) saturate(0.85) sepia(0.12); }
/* …except a lit lamp, which is the one thing that should be brighter. */
.is-night .prop:has(circle[fill='#ffe9a8']) { filter: none; }

@keyframes prop-travel {
  from { transform: translateX(var(--from, -140px)); }
  to   { transform: translateX(var(--to, 100vw)); }
}

/* Pedestrians read as further back than traffic. */
.lane-walk .prop-mover { transform-origin: bottom center; opacity: 0.92; }
.lane-walk .prop-mover svg { transform: scale(0.86); }
.lane-walk .prop-mover.flipped svg { transform: scale(0.86) scaleX(-1); }

/* ── The walk cycle ─────────────────────────────────────────────────────────

   The other half of the conveyor-belt problem. Travel alone — a rigid figure
   translating at a constant rate — is exactly what a bag on a baggage belt
   does. What separates walking from being carried is that the body works: legs
   swing from the hip, the torso rises and falls over each step, wheels turn.

   props.js marks the parts (.bob, .swing-a, .swing-b, .wheel, .pedal) and
   carries the pivot inline in viewBox units; everything below is timing.
   `--step` is one stride, set per spawn by ambient.js so that two people on
   screen together are never in lockstep.

   The global prefers-reduced-motion rule in base.css flattens all of this.
   ────────────────────────────────────────────────────────────────────────── */
.prop svg .bob,
.prop svg .swing-a,
.prop svg .swing-b,
.prop svg .wheel,
.prop svg .pedal {
  transform-box: view-box;
}

.prop-mover svg .swing-a,
.prop-mover svg .swing-b {
  animation: limb-swing var(--step, 620ms) ease-in-out infinite alternate;
}
.prop-mover svg .swing-b { animation-direction: alternate-reverse; }

/* Twice per stride: the body rises over each leg in turn, not once per pair. */
.prop-mover svg .bob {
  animation: body-bob calc(var(--step, 620ms) / 2) ease-in-out infinite alternate;
}

.prop-mover svg .wheel { animation: wheel-roll var(--roll, 900ms) linear infinite; }
.prop-mover svg .pedal { animation-timing-function: linear; }

@keyframes limb-swing {
  from { transform: rotate(-16deg); }
  to   { transform: rotate(16deg); }
}
@keyframes body-bob {
  from { transform: translateY(0.7px); }
  to   { transform: translateY(-0.8px); }
}
@keyframes wheel-roll {
  to { transform: rotate(360deg); }
}

/*
  Stopping to look at something.

  Pausing travel *and* the limbs together is what makes it read as a person
  who has stopped, rather than as a person marching on the spot.
*/
.prop-mover.is-paused,
.prop-mover.is-paused * { animation-play-state: paused; }

@media (max-width: 420px) {
  :root { --street-h: 78px; }
  .street { --kerb: 22px; }
  .street-ground { height: 48px; }
  .sky-horizon { height: 96px; }
}
