﻿/* =========================================================================
   street.css -- the shop from the outside, and the door you knock on.

   Two kinds of layer live here and they must not be mixed up:

     IN THE PAINTING  (inside .street__stage) -- the lantern, the two
       window glows, the rain caught in the lamplight, the puddle. These
       are pinned to things that are actually drawn in the picture, so
       they have to pan, crop and zoom with it.

     ON THE GLASS  (a direct child of .street) -- the rain, the mist, the
       vignette, the sky flash. These belong to the window you are looking
       through, not to the street beyond it. They used to live in the
       painting, and on a phone the painting is three times wider than the
       screen -- so the vignette's dark edges were both off-screen and the
       rain was being drawn across 1218px to show 375px of it.
   ========================================================================= */

.street{
  position:fixed; inset:0;
  overflow:hidden;
  background:#080a10;
}

/* The picture is held at its true shape and made big enough to cover the
   window. Everything else is placed in per-cent of THIS box, so the door
   and the sign stay put no matter what size the screen is. */
.street__stage{
  position:absolute; top:0; left:50%;

  /*
   * THE HEIGHT OF THE WINDOW, honestly measured.
   *
   * This was 100svh and that left a bare dark strip along the bottom of
   * an iPhone. .street is position:fixed, and a fixed box is measured
   * against the LARGE viewport -- the screen with the address bar tucked
   * away. svh is the SMALL viewport, the screen with the bar showing.
   * So the picture was built ~80px shorter than the box holding it, and
   * the moment the bar retracted you could see under the painting.
   *
   * vh is the large one on every phone browser, which is the one that
   * matches. (100lvh is the same number, spelled newer.)
   */
  --vh: 100vh;

  /*
   * A TWELFTH WIDER THAN THE WINDOW, ALWAYS.
   *
   * The picture used to be exactly the width of a wide screen, which left
   * nothing spare -- so the door sat where the painting put it and could
   * not be moved without a black stripe appearing down one side.
   *
   * 1.12 is not a taste. To put the door (54% across the picture) in the
   * middle of the screen the picture has to hang 54% of its width to the
   * left of centre, which means the 46% left over has to still cover half
   * the screen: width x 0.46 >= vw / 2, so width >= 1.087 x vw. 1.12
   * gives that with 29px to spare on a 1920 screen.
   */
  width:  max(calc(100vw * 1.12), calc(var(--vh) * 1536 / 1024));
  height: max(var(--vh), calc(100vw * 1.12 * 1024 / 1536));

  /*
   * The picture is bigger than the window, so something gets cropped.
   * Centring it split the crop evenly -- and on a wide, short window that
   * sliced the shop's own name off the top of its sign.
   *
   * Everything worth seeing (the sign, the lantern, the windows, the door)
   * is in the upper part of the painting. The bottom is cobbles. So the
   * crop is shared 24% off the top, 76% off the bottom.
   *
   * (var(--vh) - 100%) is minus the amount of overflow, so this can never
   * expose an edge at any screen shape.
   */
  --zoom: 1;
  transform:translateX(var(--pan, -50%))
            translateY(calc((var(--vh) - 100%) * 0.24))
            scale(var(--zoom));
  transform-origin:54% 47%;
  transition:transform 1.15s cubic-bezier(.5,0,.2,1);
  will-change:transform;
  backface-visibility:hidden;
}

/*
 * THE DOOR IN THE MIDDLE, MEASURED OFF THE PICTURE.
 *
 * -50% centres the PAINTING. The door is not in the middle of the
 * painting -- reading the brightness across the shopfront, the lit
 * doorway sits at 54% across -- so centring the painting leaves the door
 * about 77px right of the middle of a 1920 screen, which is plainly
 * lopsided once you look for it.
 *
 * -54% centres the DOOR instead, which is the thing the whole page is
 * about. It applies at every screen shape now, not just tall ones, and
 * the extra width above is what makes that safe to do.
 */
.street__stage{ --pan:-54%; }

.street__shot{
  position:absolute; inset:0;
  background-size:100% 100%;
  background-position:center;
  /* Which painting: index.php hands over two, the full one and a phone-
     sized one at two fifths of the bytes. A phone picks the small one
     here, and nothing else about the street has to know. */
  background-image:var(--pic);
}
@media (max-width: 900px){
  .street__shot{ background-image:var(--pic-m, var(--pic)); }
}

/* ------------------------------------------------------------ arriving --

   The street does not snap on. It settles, the way a camera settles: the
   painting eases down out of a slight push-in while the picture itself
   fades up out of the dark. Under a second and a half, and it is the
   difference between a web page loading and a place you have walked to.
*/
.street__shot{ opacity:0; transition:opacity .7s ease; }   /* was 1.15s: a slow fade reads as a slow page */
.street.is-ready .street__shot{ opacity:1; }

.street__stage{ --zoom:1.055; }
.street.is-ready .street__stage{
  --zoom:1;
  transition:transform 2.4s cubic-bezier(.16,.7,.24,1);
}

/* The open-door painting is a second picture stacked on the first. It is
   only ever wanted at the moment somebody walks in, so it stays at nothing
   until then -- these two rules come AFTER the is-ready rule above on
   purpose, because they carry the same weight and the last one wins. */
.street.is-ready .street__shot--open{ opacity:0; }
.street.is-entering .street__shot--open{ opacity:1; transition:opacity .5s ease; }

/* --------------------------------------------- alive: in the painting -- */

/* the lantern over the door.
   3.1s is the shop's heartbeat. The puddle and the vignette are given the
   same 3.1s so that everything warm on the screen flickers on the same
   flame instead of three things wobbling out of time with each other. */
.lantern{
  position:absolute; left:47.6%; top:14.5%;
  width:14%; height:16%;
  background:radial-gradient(circle at 50% 50%,
      rgba(255,214,150,.55), rgba(255,168,74,.22) 38%, transparent 72%);
  mix-blend-mode:screen;
  animation:lantern-flicker 3.1s ease-in-out infinite;
  pointer-events:none;
}
@keyframes lantern-flicker{
  0%,100%{ opacity:.82; transform:scale(1) }
  17%    { opacity:1;   transform:scale(1.05) }
  34%    { opacity:.70; transform:scale(.97) }
  52%    { opacity:.95 }
  71%    { opacity:.76; transform:scale(1.02) }
  86%    { opacity:1 }
}

/* the two windows, breathing */
.glow{
  position:absolute;
  mix-blend-mode:screen;
  pointer-events:none;
  background:radial-gradient(ellipse at 50% 50%,
      rgba(255,196,110,.30), rgba(255,158,60,.10) 50%, transparent 76%);
}
.glow--l{ left:18%; top:22%; width:26%; height:48%;
          animation:glow-breathe 8.3s ease-in-out infinite; }
.glow--r{ left:59%; top:20%; width:28%; height:50%;
          animation:glow-breathe 6.9s ease-in-out infinite reverse; }
@keyframes glow-breathe{
  0%,100%{ opacity:.72 } 40%{ opacity:1 } 70%{ opacity:.64 }
}

/*
 * RAIN CAUGHT IN THE LAMPLIGHT.
 *
 * The one detail that makes the whole street real. Drizzle over a dark
 * wall is grey and you barely see it; the same drizzle crossing the cone
 * under a lamp turns gold and you cannot stop watching it. So this is a
 * fourth rain layer, warm instead of blue, faded out at its own edges so
 * it only exists where the lamp reaches.
 *
 * It lives in the painting because it is pinned to that lamp, and it is
 * cut to just the cone rather than the whole picture, so it costs a small
 * layer instead of a full-screen one.
 */

/*
 * THE PUDDLE.
 *
 * The cobbles in front of the step are wet and the lamp is above them, so
 * there is a pool of the shop's own light lying on the ground. It wobbles
 * on the same 3.1s as the flame, because it IS the flame -- just the copy
 * of it lying in the water.
 */
.puddle{
  /* centred on the door (51.8%), not on the picture. 22% of the painting
     is wider than the door's own 16.4% on purpose -- light lying in water
     spreads past the thing that cast it. */
  position:absolute; left:40.8%; top:78%; width:22%; height:20%;
  pointer-events:none;
  mix-blend-mode:screen;
  background:radial-gradient(ellipse 48% 50% at 50% 38%,
      rgba(255,192,112,.30), rgba(255,164,72,.11) 46%, transparent 74%);
  animation:puddle-wobble 3.1s ease-in-out infinite;
}
@keyframes puddle-wobble{
  0%,100%{ opacity:.72; transform:scale(1,1) }
  17%    { opacity:1;   transform:scale(1.05,.98) }
  34%    { opacity:.60; transform:scale(.97,1.02) }
  52%    { opacity:.90 }
  71%    { opacity:.70; transform:scale(1.03,.99) }
  86%    { opacity:1 }
}

/* ------------------------------------------------ alive: on the glass -- */

/*
 * THE DRIZZLE.
 *
 * Rewritten, for two reasons.
 *
 * It jumped. The old one slid its background sideways 95px and down 190px
 * over 1.05s and then snapped home. 190 is exactly one tile so the drop
 * downwards was seamless -- but 95 is HALF a tile, so once every 1.05
 * seconds the entire rain shunted sideways. Once you have seen it you
 * cannot stop seeing it.
 *
 * And it cost a full repaint of the screen, sixty times a second, twice
 * over. Animating background-position is not something a graphics card
 * can do; it hands the whole frame back to the processor and asks for it
 * to be painted again. A phone spends that in battery.
 *
 * Now: each layer holds still and the SHEET the drops are printed on
 * slides down by exactly one tile -- 150px of a 150px tile -- which is a
 * plain transform, which a graphics card does for free, and which lands
 * on an image identical to the one it started from, so the loop cannot be
 * seen. The lean comes from skewing the sheet, which tips the fall and
 * the drops together, so rain falls the way it leans.
 */

/*
 * WHERE THE DROPS SIT IN A TILE, AND WHY NOT EVENLY.
 *
 * Placed by jittered grid: the tile is cut into cells and one drop goes
 * somewhere random inside each cell. Scattering them at pure random leaves
 * clumps and bald patches; spacing them evenly builds a grid; jitter is
 * the middle, and it is what renderers use for exactly this problem.
 *
 * Then re-rolled until no two drops in a tile sit within 4.5% of each
 * other across it. Two drops sharing a column read as a vertical line, and
 * a vertical line in rain is the thing the eye grabs onto first.
 *
 * Every length, width and brightness is different. Nothing here is copied.
 */

/* far off: smaller, slower, fainter, leaning less. Barely drops at all at
   this distance -- more the idea of rain than rain. */

/* close to the glass: fewer, longer, faster, leaning more. These are the
   ones you can nearly count. */

/* the lamplit layer borrows the same machinery, in the painting */

/* mist across the cobbles.
   Warmed from grey-blue towards the colour of the shop, because the light
   spilling out of the windows is what is lighting it. */
.mist{
  position:absolute; left:-25%; right:-25%; bottom:0; height:34%;
  pointer-events:none;
  background:linear-gradient(0deg,
      rgba(206,186,158,.15), rgba(186,180,180,.06) 42%, transparent 80%);
  animation:mist-drift 26s ease-in-out infinite alternate;
}
@keyframes mist-drift{ from{ transform:translateX(-3%) } to{ transform:translateX(3%) } }

/*
 * A STORM, A LONG WAY OFF.
 *
 * Once every 23 seconds, and it is a soft blue wash over the top of the
 * sky, not a strobe -- two beats, a fifth of a second apart, nowhere near
 * full brightness. You are not meant to catch it every time. You are
 * meant to look up the third time it happens and realise the weather has
 * been going on the whole while.
 */
/*
 * IT LIGHTS THE STREET, NOT JUST THE SKY.
 *
 * The old one faded to nothing 60% of the way down, so a bolt lit the top
 * of the picture and left the shop, the door and the cobbles exactly as
 * dark as they were -- which is not what lightning does. Lightning is the
 * brightest thing that will ever happen on that street; for a fifth of a
 * second you see everything.
 *
 * So the wash now reaches all the way to the bottom of the screen. It is
 * still strongest overhead, because that is where it came from.
 */
.flash{
  position:absolute; inset:0;
  pointer-events:none;
  opacity:0;
  mix-blend-mode:screen;
  background:
    radial-gradient(130% 86% at 50% -12%,
        rgba(202,224,255,.66), rgba(174,200,244,.30) 38%,
        rgba(150,176,222,.13) 68%, rgba(130,152,196,.05) 100%);
  /* Lit by street.js: each strike is its own animation, rolled fresh --
     how bright, how many flickers, how long, and when the next one comes.
     A looped keyframe here could only ever do the same blink on the same
     beat, and that is what made it look like a screen going wrong. */
}

/* The rain, drawn one drop at a time by rain.js. One sheet over the whole
   street, lightening what is under it, in place of four tiled layers. */
.rainfall{
  position:absolute; inset:0;
  pointer-events:none;
  mix-blend-mode:screen;
}
/*
 * TWICE A CYCLE, AND FOUR BEATS EACH.
 *
 * Once a cycle put every flash in the same place in the rhythm, and a
 * storm you can set your watch by is not a storm. Two strikes at 41% and
 * 89% means the gaps are uneven -- long, then short, then long -- which is
 * how weather actually behaves, and it halves the wait without making the
 * sky twitchy.
 *
 * Each strike is stab, dark, BIGGER, fade. Real lightning almost never
 * goes off once: the first stroke lights the channel and the return stroke
 * a heartbeat later is the bright one. Skipping the dark beat in between
 * is what makes a fake flash look like somebody flicking a light switch.
 */

/*
 * The dark at the edges, so the middle is where you look.
 *
 * This is a frame around the WINDOW, so it lives on the glass. It used to
 * be inside the painting, which is 1218px wide on a 375px phone -- both
 * of its dark sides were off the screen and the only part of it anybody
 * on a phone ever saw was the top and bottom. Warmed towards brown, so
 * the edge of the screen reads as a dark street and not as a dead pixel.
 */
.street__edges{
  position:absolute; inset:0; pointer-events:none;
  background:radial-gradient(118% 96% at 52% 48%,
      transparent 26%, rgba(12,8,6,.46) 70%, rgba(6,4,3,.92) 100%);
  transition:opacity .5s ease;
  animation:edge-breathe 3.1s ease-in-out infinite;
}
@keyframes edge-breathe{ 0%,100%{ opacity:1 } 17%{ opacity:.93 } 71%{ opacity:.97 } }

/* --------------------------------------------------------- the sign ---- */



/* -------------------------------------------------------- the name -----

   There is none on the picture at all -- not over it, and not on the
   board. See index.php: once the door was centred the board's lettering
   ran off the side of the screen, and the board was always painted blank
   anyway. Nothing here has to line up with anything.
   ------------------------------------------------------------------------- */

/* --------------------------------------------------------- the door ---- */

.door{
  position:absolute;
  /* centred on 54%, where the painted door actually is, rather than the
     51.8% this was guessed at */
  left:45.8%; top:21.5%; width:16.4%; height:58%;
  border:0; padding:0; margin:0;
  background:transparent;
  cursor:pointer;
  border-radius:44% 44% 3% 3% / 12% 12% 3% 3%;
  -webkit-tap-highlight-color:transparent;
  transition:filter .4s var(--ease), transform .4s var(--ease);
}
.door::before{                     /* warmth leaking round the frame */
  content:""; position:absolute; inset:-9%;
  border-radius:inherit;
  background:radial-gradient(ellipse at 50% 52%,
      rgba(255,196,110,.34), rgba(255,160,60,.14) 46%, transparent 74%);
  mix-blend-mode:screen;
  opacity:0;
  transition:opacity .55s var(--ease);
  pointer-events:none;
}
.door:hover::before,.door:focus-visible::before{ opacity:1; }
.door:hover,.door:focus-visible{ filter:brightness(1.1); }

/*
 * A PHONE HAS NO HOVER.
 *
 * Every bit of feedback this door had was written for a mouse pointer,
 * and then the tap highlight was turned off on top -- so on a phone,
 * pressing the front door of the shop did visibly nothing at all until
 * the card slid up. Held down, it now warms and gives under the thumb.
 */
.door:active{ filter:brightness(1.18); transform:scale(.994); }
.door:active::before{ opacity:1; transition-duration:.12s; }

/*
 * AND YOU CAN HEAR IT, IN THE PICTURE.
 *
 * One ring, going out from where the knuckles landed. It is the answer to
 * "did that register?" in the fifty milliseconds before anything else on
 * the screen has moved.
 */
.door::after{
  content:"";
  position:absolute; left:50%; top:50%;
  width:42%; aspect-ratio:1;
  margin:-21% 0 0 -21%;
  border-radius:50%;
  border:1px solid rgba(255,214,150,.6);
  opacity:0;
  pointer-events:none;
}
.door.is-knocked::after{ animation:knock-ring .95s var(--ease) forwards; }
@keyframes knock-ring{
  from{ opacity:.8; transform:scale(.3) }
  to  { opacity:0;  transform:scale(2.7) }
}

/* the whisper under the steps */
.knock{
  /* Pinned to the window, not to the painting, so it can never be cropped
     away no matter what shape somebody's screen is -- and lifted clear of
     the home bar on a phone, which viewport-fit=cover puts it behind. */
  position:fixed; left:50%;
  bottom:calc(clamp(16px, 4.5vh, 50px) + env(safe-area-inset-bottom, 0px));
  transform:translateX(-50%);
  z-index:3;
  font-family:var(--sans);
  font-size:clamp(9px,.86vw,13px);
  letter-spacing:.34em;
  text-transform:uppercase;
  color:rgba(255,214,150,.82);
  text-shadow:0 2px 10px rgba(0,0,0,.9);
  pointer-events:none;
  opacity:0;
  transition:opacity .5s var(--ease), transform .5s var(--ease);
  white-space:nowrap;
}
.street.is-ready .knock{ opacity:.62; animation:knock-breathe 4.4s ease-in-out infinite; }
.street.is-ready:has(.door:hover) .knock,
.street.is-ready:has(.door:focus-visible) .knock{
  opacity:1; transform:translateX(-50%) translateY(-4px);
}
@keyframes knock-breathe{ 0%,100%{opacity:.44} 50%{opacity:.85} }

/* ------------------------------------------------------ the switch ---- */

/*
 * THE SOUND SWITCH.
 *
 * Sat next to the hint at the bottom, in the same whisper, because it is
 * the same kind of thing: something offered, never something insisted on.
 * Off is the honest default and off is what it says.
 *
 * The word next to it is "rain", not "sound" -- because rain is what you
 * get, and a person deciding whether to let a website make a noise at
 * them deserves to be told which noise.
 */
.ear{
  position:fixed; z-index:4;
  left:calc(env(safe-area-inset-left, 0px) + 12px);
  bottom:calc(env(safe-area-inset-bottom, 0px) + 10px);
  display:inline-flex; align-items:center; justify-content:center;
  min-width:44px; min-height:44px; padding:0 11px;
  border:0; background:transparent;
  color:rgba(240,228,205,.42);
  font-family:var(--sans);
  font-size:11px; letter-spacing:.26em; text-transform:uppercase;
  text-shadow:0 2px 10px rgba(0,0,0,.95);
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  opacity:0;
  transition:opacity .6s .9s var(--ease), color .3s var(--ease);
}
.street.is-ready .ear{ opacity:1; }
.ear svg{ width:17px; height:17px; filter:drop-shadow(0 2px 6px rgba(0,0,0,.9)); }
.ear:hover,.ear:focus-visible,.ear:active{ color:rgba(255,214,150,.9); }

/* off: the speaker is crossed out and the waves are not there */
.ear__wave{ opacity:0; transition:opacity .3s var(--ease); }
.ear__off { opacity:1; transition:opacity .3s var(--ease); }
.ear.is-on{ color:rgba(255,206,140,.82); }
.ear.is-on .ear__wave{ opacity:1; }
.ear.is-on .ear__off { opacity:0; }

.street.is-entering .ear{ opacity:0; transition:opacity .4s ease; }

/* --------------------------------------------------- walking through --- */

/*
 * Two beats, not one.
 *
 * It used to push in and open the door at the same moment, and the open
 * door -- a whole second painting, the thing the picture is FOR -- went
 * by underneath the movement without ever being looked at. Now the door
 * opens first and the street holds still for a sixth of a second, and
 * then you go in. It is 160 milliseconds. It changes the whole thing.
 */
.street.is-entering .street__stage{
  --zoom:3.4;
  transition:transform 1.55s .16s cubic-bezier(.5,0,.32,1);
}
/* the weather is on the near side of the door, so it goes as you cross */
.street.is-entering .rainfall,
.street.is-entering .mist,
.street.is-entering .flash,
.street.is-entering .street__edges{ opacity:0; transition:opacity .75s .25s ease; }
/* the breathing animation outranks a plain opacity, so it has to be
   called off by name before the hint can fade */
.street.is-entering .knock{ animation:none; opacity:0; }

.flood{
  position:fixed; inset:0; z-index:20;
  background:radial-gradient(circle at 52% 50%,
      rgba(255,206,140,.95), rgba(255,170,80,.6) 32%, rgba(20,12,6,0) 68%);
  opacity:0; pointer-events:none;
}
.street.is-entering .flood{ animation:flood-in 1.62s .18s ease-in forwards; }
@keyframes flood-in{
  0%{ opacity:0 } 62%{ opacity:.55 } 100%{ opacity:1 }
}

/* ------------------------------------------------------- the doorstep -- */

.step{
  position:fixed; inset:0; z-index:30;
  display:flex; align-items:flex-end; justify-content:center;
  padding:clamp(16px,4vh,52px);
  /* the home bar on a phone sits over the bottom of a viewport-fit=cover
     page, and the bottom of this box is where the buttons are */
  padding-bottom:calc(clamp(16px,4vh,52px) + env(safe-area-inset-bottom, 0px));
  background:linear-gradient(0deg, rgba(4,3,2,.86), rgba(4,3,2,.36) 55%, transparent);
  opacity:0; pointer-events:none;
  transition:opacity .5s var(--ease);
}
.step.is-open{ opacity:1; pointer-events:auto; }

.step__card{
  width:min(430px,100%);
  padding:clamp(22px,3.4vw,32px);
  background:linear-gradient(180deg,#241a11,#130d08);
  border:1px solid rgba(216,178,106,.26);
  border-radius:6px;
  box-shadow:0 30px 80px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,196,124,.16);
  transform:translateY(26px);
  transition:transform .55s var(--spring);
}
.step.is-open .step__card{ transform:none; }

.step__card h2{ font-size:clamp(21px,2.6vw,26px); margin-bottom:6px; color:var(--cream); }
.step__card p{ margin:0 0 16px; }
.step__card .field{ margin-bottom:12px; }
.step__row{ display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.step__err{ color:#e8a898; font-size:14px; margin:10px 0 0; }
.step__quiet{ margin-top:14px; }

/* reading or writing ---------------------------------------------------- */

.fork{ display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:6px; }
.fork__pick{
  display:flex; flex-direction:column; align-items:center; gap:9px;
  padding:20px 12px;
  border:1px solid rgba(216,178,106,.28);
  border-radius:5px;
  background:linear-gradient(180deg, rgba(216,178,106,.07), rgba(0,0,0,.28));
  color:var(--cream-dim);
  font-family:var(--serif); font-size:16px;
  cursor:pointer;
  transition:all .28s var(--ease);
}
.fork__pick svg{ width:30px; height:30px; opacity:.8; }
.fork__pick:hover,.fork__pick:focus-visible,.fork__pick:active{
  border-color:rgba(255,184,92,.7);
  background:linear-gradient(180deg, rgba(255,184,92,.18), rgba(0,0,0,.2));
  color:var(--flame-hot);
  transform:translateY(-3px);
  box-shadow:0 12px 30px rgba(0,0,0,.5), 0 0 26px rgba(255,184,92,.18);
}
.fork__pick:hover svg{ opacity:1; }

@media (max-width:420px){
  .fork{ grid-template-columns:1fr; }

  /*
   * The hint and the sound switch are both along the bottom, and measured
   * on a 320px phone they overlap by 11px -- the switch runs to x 96 and
   * the centred hint starts at x 85. So on a narrow screen the hint takes
   * the line above and they stop arguing.
   */
  .knock{ bottom:calc(env(safe-area-inset-bottom, 0px) + 58px); }
}

/* --------------------------------------------------------- the hush ---- */

/*
 * Somebody who has asked their phone to stop moving things has asked for
 * ALL of it, including the weather. What is left is the painting, lit,
 * held still -- which is a perfectly good picture of a shop.
 */
@media (prefers-reduced-motion: reduce){
  .mist,.lantern,.glow,.knock,.puddle,.flash,
  .street__edges{ animation:none !important; }
  .rainfall{ display:none; }        /* rain.js draws nothing for reduced motion either */
  .flash{ display:none; }
  .street__stage{ transition:none !important; --zoom:1 !important; }
  .street__shot{ transition:none !important; }
  .door::after{ display:none; }
}

/* iPhone zooms the page in on any text box under 16px. Never let it. */
@media (max-width:860px){
  .step__card .field{ font-size:16px; }
  .fork__pick{ padding:24px 12px; }
}

/* -------------------------------------------------------------------------
   THE CARD LIFTS ITSELF OFF THE KEYBOARD

   This card sits at the bottom of the screen, which is right until a
   phone keyboard comes up and takes the bottom of the screen. --kb is
   how much of it is covered; street.js keeps that number honest.
   ------------------------------------------------------------------------- */

/*
 * The card sits at the bottom of the screen, so the keyboard lifts it by
 * exactly as much as the keyboard covers.
 *
 * Not by adding to .step's padding: that padding is a clamp(), and
 * calc(clamp(...) + 336px) does not resolve -- the whole declaration is
 * thrown out and the value silently goes back to what it was, which is
 * how this looked fixed while still being broken. A margin on the card
 * is plain arithmetic and cannot fail that way.
 */
.step__card{
  margin-bottom:var(--kb, 0px);
  max-height:calc(100svh - var(--kb, 0px) - 32px);
  overflow-y:auto;
  transition:transform .55s var(--spring), margin-bottom .18s ease;
}

/* =========================================================================
   THE WEATHER IS DIFFERENT EVERY TIME YOU COME

   The street was one night, painted once, and it was the same night for
   ever. A place you visit every day should not be. Nothing about the
   painting changes -- the shop, the lamp, the door, the cobbles are all
   exactly where they were -- only the weather over it does.

   Five nights. One is picked when you arrive, and never the same one
   twice running, so the change is something you notice rather than
   something you might. None of them is a costume: they are all the same
   street, and on four of the five it is still raining, because it is that
   sort of town.

   All of it rides on the rain layers that already existed. Each layer is
   already driven by four numbers -- how far apart the drops are, how far
   they fall, how fast, and how much they lean -- so a night is five lines
   of numbers and not a second set of anything.
   ========================================================================= */

/* --- an ordinary wet night. This is the one the street was built as --- */
/*     (no rules needed: it is what the layers already say)               */

/*
 * A NOTE ON THESE NUMBERS.
 *
 * The tiles got a great deal bigger when the rain was rewritten -- the
 * middle layer went from a 28x150 tile to 158x330 -- so every number below
 * was rescaled with it. The gaps grew by the same factor as the tile and
 * the speeds grew with the fall distance, which means every night still
 * falls at exactly the pixels-per-second it did before: heavy rain reads
 * as heavy because it is faster and closer together, not because the
 * numbers happen to be small.
 *
 * Only --gap, --drop, --speed and --wind ever get set here. The second
 * tiling works itself out from them, so a night is still one line.
 */

/* --- it is coming down properly ------------------------------------- */
.street[data-sky="downpour"] .mist       { opacity:.5; }
.street[data-sky="downpour"] .street__edges{ filter:brightness(.92); }
/* Heavy enough now to be worth lighting up. Two strikes to a 13s cycle is
   one about every six and a half seconds, at three quarters brightness --
   a storm that is close, but not overhead. */

/* --- thunder a long way off, and the rain hard under it -------------- */
/* Two strikes to a 7s cycle: the sky answers about every three and a half
   seconds, at full brightness. This is the one that is overhead. */
.street[data-sky="storm"] .street__edges{ filter:brightness(.86); }

/* --- the rain has stopped and the street is still wet ---------------- */
/*     The puddle stays. That is the whole idea: you can tell it has been
       raining, which is a truer picture of a clear night in that town
       than simply turning the weather off. */
.street[data-sky="clearing"] .mist      { opacity:.22; }
.street[data-sky="clearing"] .puddle    { opacity:1; filter:brightness(1.15); }
.street[data-sky="clearing"] .flash     { display:none; }
.street[data-sky="clearing"] .street__edges{ filter:brightness(1.06); }

/* --- fog off the river, and the lamp with a halo round it ------------ */
.street[data-sky="fog"] .mist{
  opacity:1;
  height:78%;                       /* up past the windows, not just the cobbles */
  background:linear-gradient(0deg,
      rgba(206,192,168,.4), rgba(198,190,180,.2) 46%, transparent 88%);
}
/*
 * The lamp does not get brighter in fog. It gets BIGGER, because you are
 * seeing the light in the air rather than the lamp itself.
 *
 * By its width and height, NOT by a transform. The lantern already has a
 * flicker animation and that animation sets transform in its keyframes --
 * an animation beats a plain declaration, so scale() here would have been
 * thrown away every frame and the fog would have had an ordinary lamp in
 * it. Growing the box instead does not fight the flame at all.
 */
.street[data-sky="fog"] .lantern{ width:21%; height:24%; left:44.1%; top:10.5%; }
.street[data-sky="fog"] .glow{ filter:blur(6px); }
.street[data-sky="fog"] .flash{ display:none; }

/* Somebody who has asked for stillness gets one night, always the same
   one, and it is the calm one. A page that is different every visit is a
   page that moves, even if nothing on it is animated. */
@media (prefers-reduced-motion: reduce){
  .street[data-sky]{ }
  .street[data-sky="storm"] .flash,
  .street[data-sky="downpour"] .flash{ display:none; }
}

/* =========================================================================
   THE STREET IS ALIVE, WHATEVER THE WEATHER IS DOING

   Everything below runs on every night, including the clear one. The
   weather changes; these do not. A street where the only moving thing is
   the weather is a photograph with a filter over it -- on a clear night it
   stopped dead, and stopping dead is what gives a painting away.

   Three things, all cheap, none of them asking to be looked at:
     the dust turning over in the lamplight
     the light itself breathing across the front of the shop
     and somebody inside, once in a while
   ========================================================================= */

/* ---- dust in the lamplight -------------------------------------------- *
 *
 * Not snow, not sparkle. The specks you only see because a lamp is behind
 * them -- the ones that are in every room you have ever been in and that
 * you never notice until a shaft of light catches them.
 *
 * Six of them in a tile, drifting UP and sideways rather than down,
 * because dust in still air rises on the warmth of the lamp. That is the
 * detail that separates dust from rain: it does not obey gravity, it obeys
 * the heat.
 *
 * Masked to the lamp's cone, because dust away from the light is invisible
 * and drawing it there would just be noise on the screen.
 */
.dust{
  position:absolute; left:34%; top:4%; width:42%; height:56%;
  pointer-events:none;
  overflow:hidden;
  opacity:.5;
  mix-blend-mode:screen;
  -webkit-mask-image:radial-gradient(ellipse 54% 58% at 50% 24%,
      #000 6%, rgba(0,0,0,.5) 46%, transparent 78%);
          mask-image:radial-gradient(ellipse 54% 58% at 50% 24%,
      #000 6%, rgba(0,0,0,.5) 46%, transparent 78%);
}
.dust::before{
  content:"";
  position:absolute; left:-20%; right:-20%; bottom:-260px; top:0;
  background-image:
    radial-gradient(1.4px 1.4px at 14% 12%, rgba(255,232,190,.75), transparent),
    radial-gradient(1px 1px    at 63% 27%, rgba(255,226,180,.55), transparent),
    radial-gradient(1.7px 1.7px at 38% 48%, rgba(255,236,200,.62), transparent),
    radial-gradient(1.1px 1.1px at 86% 61%, rgba(255,228,184,.5),  transparent),
    radial-gradient(1.3px 1.3px at 24% 77%, rgba(255,234,196,.6),  transparent),
    radial-gradient(0.9px 0.9px at 71% 91%, rgba(255,222,176,.45), transparent);
  background-size:190px 260px;
  animation:dust-turn 26s linear infinite;
}
/* exactly one tile up and a third of one across, so the loop cannot be
   seen -- the same trick the rain uses, at a twentieth of the speed */
@keyframes dust-turn{
  from{ transform:translate3d(0, 0, 0) }
  to  { transform:translate3d(63px, -260px, 0) }
}

/* ---- the light breathing across the shopfront -------------------------- *
 *
 * A very slow warm wash that moves across the front of the shop and back.
 * It is not a glow and it is not a flicker -- the lantern already does
 * both. This is the whole street being lit by something that is not quite
 * steady, which is what an oil lamp and a wet road do to each other.
 *
 * 41 seconds, so it never lines up with the lantern's 3.1 or the windows'
 * 8.3 and 6.9. Three rhythms that never meet read as a place. Three that
 * meet every few seconds read as a loop.
 */
.street__glare{
  position:absolute; inset:-10%;
  pointer-events:none;
  mix-blend-mode:screen;
  background:radial-gradient(46% 38% at 50% 30%,
      rgba(255,206,140,.09), rgba(255,180,90,.03) 52%, transparent 76%);
  animation:glare-drift 41s ease-in-out infinite alternate;
}
@keyframes glare-drift{
  from{ transform:translate3d(-4%, 1%, 0) scale(1) }
  to  { transform:translate3d(5%, -2%, 0) scale(1.13) }
}

/* ---- the shopfront is wet ---------------------------------------------- *
 *
 * The whole front of the shop is glass, and glass in rain has water
 * creeping down it. Very faint, very slow -- 14 seconds to cross -- and
 * blurred sideways so it reads as water on a pane rather than as more
 * rain. You are not supposed to see this one working. You are supposed to
 * notice that the windows look wet.
 */
.wet{
  position:absolute; left:8%; top:12%; width:84%; height:62%;
  pointer-events:none;
  overflow:hidden;
  opacity:.5;
  mix-blend-mode:screen;
}
.wet::before{
  content:"";
  position:absolute; left:0; right:0; top:-340px; height:calc(100% + 340px);
  /* BOTH tiles are 340px tall, and the slide below is 340px. They have to
     be: sliding 340 past a 227px tile lands on a different picture every
     lap and the whole thing jumps once every fourteen seconds. Only the
     WIDTHS differ (37 and 61, which share no factor), so the two streams
     of water never line up across the glass. */
  background-image:
    linear-gradient(180deg, transparent 0%,  rgba(214,232,255,.10) 34%, transparent 52%),
    linear-gradient(180deg, transparent 22%, rgba(206,226,252,.07) 58%, transparent 74%);
  background-size:37px 340px, 61px 340px;
  background-position:0 0, 19px 0;
  animation:wet-run 14s linear infinite;
}
@keyframes wet-run{
  from{ transform:translate3d(0,0,0) }
  to  { transform:translate3d(0,340px,0) }   /* exactly one tile of the first */
}

/* =========================================================================
   SNOW

   A sixth night, and the only one where nothing falls straight.

   Snow is not slow rain. Rain is pulled down; snow is carried, and it
   wanders on the way. So it is two movements at once: the flakes fall
   inside their own layer, and the layer itself sways slowly sideways.
   Neither on its own is snow -- falling alone is ash, swaying alone is a
   curtain -- and the two together are what makes it read.

   The lamp gets its cone back: snow in lamplight is the warmest thing in
   this whole shop, and it is the reason to have snow at all.
   ========================================================================= */

/*
 * The layer itself is grown past the edges of the screen before it is
 * allowed to sway. Swaying a layer that stops exactly at the edge of the
 * screen drags a flakeless band in from the side -- a clean vertical line
 * of no-snow, which is the most obvious tell there is. inset:-6% -9% puts
 * 9% of spare either side; the sway is 2.5% of a 118%-wide box, which is
 * 2.95% of the screen. Three times the room it needs.
 *
 * Each layer sways on its own clock (13s, 19s, 9s) and two of them run
 * backwards, so the three never line up. Snow that all leans the same way
 * at the same moment is a windscreen wiper.
 */

/* Flakes, not drops: round, so the size varies where a drop's length would. */

/* the lamp's cone gets big soft flakes and turns them gold */

/* Snow lies. The cobbles go paler and the mist thins, because a snowy
   night is a BRIGHT night -- that is the thing everybody remembers about
   one and the thing a dark snow scene always gets wrong. */
.street[data-sky="snow"] .mist{
  opacity:.42;
  background:linear-gradient(0deg,
      rgba(226,232,240,.3), rgba(214,222,234,.12) 44%, transparent 82%);
}
.street[data-sky="snow"] .puddle{ opacity:.5; filter:brightness(1.2) saturate(.7); }
.street[data-sky="snow"] .street__edges{ filter:brightness(1.1); }
.street[data-sky="snow"] .flash{ display:none; }
/* dust does not turn over in freezing air the way it does in a warm room */
.street[data-sky="snow"] .dust{ opacity:.24; }
.street[data-sky="snow"]     .wet   { opacity:.22; }
.street[data-sky="downpour"] .wet{ opacity:.85; }
.street[data-sky="storm"]    .wet{ opacity:1; }

@media (prefers-reduced-motion: reduce){
  .dust::before,
  .street__glare,
  .wet::before,
  .flash{ animation:none !important; }
  /* a flash frozen part-way through is just a haze over the whole
     picture, which is worse than no lightning at all */
  .flash{ display:none; }
}

