/*
 * Copyright Stefan Press Consulting
 * Created using Claude Code.
 */
/*
 * hero.css -- the showroom band: backdrop, car and wordmark.
 *
 * Shared by index.html (full screen, and the way in) and app.html (compact,
 * for a page you have already reached). Extracted from index.html when the
 * dashboard started drawing the same car, so the two cannot drift.
 */
body{overflow-x:hidden}

  .hero{
    /* 100vh on iOS is the LARGE viewport — the height the page would have if
       Safari's toolbars were hidden. They are not hidden on first load, so the
       showroom is taller than the screen and the car sits partly below the
       fold, on the one page whose entire job is showing the car. dvh is the
       viewport as it actually is right now; the vh line stays underneath it as
       the fallback for anything that does not know dvh. */
    min-height:100vh;
    min-height:100dvh;
    display:flex;flex-direction:column;
    background:
      radial-gradient(1100px 620px at 50% 34%, #223247 0%, rgba(34,50,71,0) 70%),
      linear-gradient(180deg,#0b0f14 0%,#10161f 55%,#080b10 100%);
    position:relative;overflow:hidden;
  }
  .hero::after{ /* floor sheen */
    content:'';position:absolute;left:0;right:0;bottom:0;height:34%;
    background:linear-gradient(180deg,rgba(255,255,255,.028),transparent 65%);
    pointer-events:none;
  }
  .hero-head{text-align:center;padding:44px 20px 8px;position:relative;z-index:3}
  /* THE BIGGEST THING ON THE PAGE FOLLOWS THE COMPANY'S ACCENT.
     It was three hard-coded ambers — #f0c05a, #a97c1c and the glow behind
     them — so a firm that had chosen navy got a navy top bar, navy buttons,
     navy figures and then this, in amber, at three times the size of anything
     else on the screen. The tokens are the ones style.css already derives from
     brand_accent; unskinned, they are the same three colours as before. */
  .hero-head h1{
    font-size:clamp(1.9rem,5.2vw,3.1rem);margin:0 0 10px;letter-spacing:.5px;
    background:linear-gradient(180deg,#fff 10%,var(--brand2) 58%,var(--brand-edge) 100%);
    -webkit-background-clip:text;background-clip:text;color:transparent;
    text-shadow:0 2px 30px rgba(var(--brand-rgb),.14);font-weight:700;
  }
  .hero-head p{color:#93a3b8;margin:0;font-size:clamp(.9rem,2.2vw,1.05rem);letter-spacing:.3px}
  .hero-head .since{color:#5f6f84;font-size:.8rem;margin-top:9px;letter-spacing:2.4px;text-transform:uppercase}

  .stage{flex:1;display:flex;align-items:center;justify-content:center;padding:8px 16px 30px;position:relative;z-index:2}
  .car{width:min(1120px,96vw);height:auto;display:block;filter:drop-shadow(0 40px 46px rgba(0,0,0,.6))}

  /* The way in.
     Deliberately unannounced — no label, no pulse, no permanent glow. The lamp
     is dark until the pointer is actually on it, so finding it is the reward.
     Keyboard and screen-reader users still reach it: it is a real link with an
     aria-label and a visible focus ring. */
  #headlightHit{cursor:pointer}
  #headlightHit:focus{outline:none}
  .lampGlow{opacity:0;transition:opacity .45s ease;pointer-events:none}
  .lampCore{transition:fill .35s ease}
  #headlightHit:hover .lampGlow,
  #headlightHit:focus-visible .lampGlow{opacity:1}
  #headlightHit:focus-visible .lampRim{stroke:#fff;stroke-width:3}
  .beam{opacity:0;transition:opacity .7s ease;pointer-events:none}
  #headlightHit:hover .beam,.beam.on{opacity:.8}

  .tag-tail{color:var(--brand);font-weight:600;white-space:nowrap}
  .tag-tail em{font-style:italic;color:var(--brand2);letter-spacing:.5px}

/* Compact variant -- the dashboard.
   The same showroom, no longer the whole screen: it sits above the tiles
   instead of replacing them, so the page still opens on the work. */
.hero.hero-compact{min-height:0;border-radius:14px;overflow:hidden;margin-bottom:22px}
.hero.hero-compact .hero-head{padding:26px 20px 0}
.hero.hero-compact .hero-head h1{font-size:clamp(1.4rem,3.4vw,2.1rem)}
.hero.hero-compact .stage{padding:0 16px 6px}
.hero.hero-compact .car{width:min(760px,88%);filter:drop-shadow(0 22px 26px rgba(0,0,0,.5))}
/* The floor under the car. The contact line used to supply it — even empty, its
   padding was the band's bottom edge — and it has moved to the foot of the
   page, so the band keeps the same edge without it. */
.hero.hero-compact{padding-bottom:18px}

/* The inert lamp, on a page that asked for neither the sign-in link nor the
   dashboard's next-car button — the iPad's: no pointer, no hover glow, nothing
   to click. The dashboard's lamp is a button, and is #headlightHit like the
   link, so the rules above reach it. */
.lamp-inert{pointer-events:none}

/* ---------- Unread bulletins: flash the lights ----------
   While the board has something unread, the dashboard car is also the
   notification. AAS_CAR.alert(true) puts .alerting on the car and the headlight
   flashes in the rhythm below until alert(false) takes the class off again.
   The lamp only draws the eye; what can be read and acted on is the
   "N new posts" notice on the board itself.

   No lingering half-lit state on purpose. An earlier version left a low glow
   behind and it would not reliably go out again once the animation had run —
   the lamp stayed lit after the board was marked read, which says the opposite
   of the truth. One class, two states, nothing to get stuck in.

   The tail glow is dark at rest, like the headlight's, and is lit only by the
   tail lamp's own hover and focus and by the turn, further down. */
.tailGlow{opacity:0;pointer-events:none}

/*
 * FLASH-FLASH, THEN FOUR SECONDS OF DARK.
 *
 * Two short pulses inside the first second of a five-second cycle, and nothing
 * for the rest of it. A light that pulses continuously is one people learn to
 * ignore, which is the opposite of what a notification is for; a pair with a
 * long gap reads as a car signalling. The dark is 80% of the cycle so it never
 * competes with the work on the page.
 *
 * `infinite` HERE rather than a burst repeated by a timer. The old shape had
 * the burst length in car.js and the gap in board.js, two files that had to
 * agree about one rhythm and expressed it in different units. A cycle that
 * carries its own gap cannot drift from itself.
 */
@keyframes carLampFlash{
  0%   {opacity:0}
  3%   {opacity:1}
  7%   {opacity:0}
  11%  {opacity:1}
  15%  {opacity:0}
  100% {opacity:0}
}
/* THE HEADLIGHT ONLY. The tail lamp is a BUTTON — it glows on hover and on
   focus to say so — and a lamp that also blinks by itself is one light saying
   two things. On the dashboard the headlight has since become a button too,
   the one that drives the next car in, which puts it in the same position;
   the signal has been left on it, and nowhere else. */
.car.alerting .lampGlow{animation:carLampFlash 5s ease-in-out infinite}
/* The bulb brightens with the glow rather than staying lit through the dark,
   which would leave the car permanently half-on between flashes. */
@keyframes carLampCore{
  0%,100%{fill:#fff8e6}
  3%,7%  {fill:#fffdf4}
  11%,15%{fill:#fffdf4}
}
.car.alerting .lampCore{animation:carLampCore 5s ease-in-out infinite}

/* Flashing lights are exactly what this preference is for. The lamps still come
   up, and still say the same thing — they simply hold steady instead of
   blinking, and go out with the class like everything else. */
@media (prefers-reduced-motion:reduce){
  /* Flashing lights are exactly what this preference is for. The lamp still
     comes up and still says the same thing — it simply holds steady instead of
     blinking, and goes out with the class like everything else. */
  .car.alerting .lampGlow{animation:none;opacity:.55}
  .car.alerting .lampCore{animation:none;fill:#fffdf4}
}

/* ---------------------------------------------------------------------------
   TURNING ROUND. Press the tail lamp: the car goes AWOOOOOGA, swings a quarter
   turn to show you its back, reads its own name off the number plate, and then
   finishes the turn. Press it again and it comes back the same way.

   THE ROTATION IS A TRANSITION AND THE HOP IS AN ANIMATION, running at the
   same time on two DIFFERENT properties. Doing both in `transform` means the
   keyframes must restate the rotation at every step, and then the return trip
   needs a second set of keyframes with every angle reversed. `rotate` and
   `translate` are their own properties now, so each half is written once and
   the way back is the same declaration with a different angle.

   --turn and --rear are set by car.js, and the DURATIONS ARE WRITTEN TWICE —
   once here as transitions, once there as the timers that sequence them. They
   have to agree. Nothing reads one off the other, so a change made in one
   place and not the other shows up as a swap you can watch happening.
   --------------------------------------------------------------------------- */
/*
 * THE ROTATION IS ON THE TWO GROUPS, NOT ON THE SVG.
 *
 * It was on the SVG, which was fine while the only two stops were 0 and 180.
 * A stop at 90 needs the back of the car to appear exactly where the profile
 * vanishes — and a child cannot do that from inside a rotated parent, because
 * a 3D transform on an element with no `transform-style: preserve-3d` FLATTENS
 * its children into the parent's plane. A parent turned 90 degrees is a plane
 * with no width, and nothing nested inside it can rotate back out.
 *
 * So `.side` and `.rear` are siblings sharing one pivot, each with its own
 * angle. The profile runs 0 → 90 → 180 and is edge-on in the middle; the back
 * runs -90 → 0 → 90 and is edge-on at both ends. Their crossover is at exactly
 * the moment neither has any width, which is why the swap cannot be seen.
 *
 * `.car` keeps the hop, and only the hop. It is the one movement that belongs
 * to the whole car rather than to a face of it.
 */
.car{ transform-origin:50% 62%; }

/*
 * ONE PIVOT MEANS ONE NUMBER, NOT `50%` TWICE.
 *
 * `fill-box` makes 50% the centre of each group's OWN bounding box, so the
 * sentence above — two siblings, one pivot — was false the moment it was
 * written. Measured in the browser:
 *
 *   .side  bounding box  -60 .. 1158   50% = 549
 *   .rear  bounding box  414 ..  786   50% = 600
 *
 * and the -60 is the HEADLIGHT BEAM, a polygon that reaches out past the nose
 * and is transparent until the lamp is hovered. An invisible decoration was
 * setting where the car turned. Mirroring the profile about 549 instead of
 * about its own centre threw it 138 units to the left — 11% of the frame,
 * about 90px on an iPad — so the car facing right sat visibly off-centre while
 * the car facing left did not.
 *
 * `view-box` puts both groups in the same coordinate system: the viewBox, the
 * one thing on the page that cannot move. 608 is where the paintwork actually
 * balances, measured with the beam, the two glows and the smoke taken out —
 * the profile therefore lands in exactly the same place at 180 degrees as it
 * starts in at 0.
 *
 * The Y half does nothing for a `rotate:y` — a turn about a vertical axis does
 * not care how far up that axis the origin sits — and is left as a percentage
 * so it goes on agreeing with the hop above.
 */
.side{
  transform-box:view-box;
  transform-origin:608px 62%;
  rotate:y var(--turn, 0deg);
  transition:rotate 620ms cubic-bezier(.4,.05,.35,1);   /* must match SWING_MS */
}
.rear{
  transform-box:view-box;
  transform-origin:608px 62%;
  rotate:y var(--rear, 90deg);
  transition:rotate 260ms cubic-bezier(.3,.6,.4,1);     /* must match OPEN_MS  */
  opacity:0;
}
/* Hidden outright rather than left edge-on: a zero-width group still rounds up
   to a hairline down the middle of the car on some displays, and two hairlines
   crossing at the swap is exactly the artefact this is trying to avoid. */
.car.rear-on .rear{opacity:1}
.car.rear-on .side{opacity:0}

/* The hop: crouch, leap, land a little heavy. A car that pivots on the spot
   like a turntable is correct and dull; this one puts its weight into it. */
@keyframes carTurnHop{
  0%   {translate:0 0;      scale:1 1}
  14%  {translate:0 10px;   scale:1.03 .93}    /* crouch */
  42%  {translate:0 -34px;  scale:.98 1.04}    /* airborne */
  68%  {translate:0 -20px;  scale:1 1}
  86%  {translate:0 6px;    scale:1.04 .94}    /* landing squash */
  100% {translate:0 0;      scale:1 1}
}
.car.turning{animation:carTurnHop 1.1s cubic-bezier(.3,.7,.3,1)}

/* Both lamps burn through the manoeuvre. Not the four-pulse alert burst —
   that one MEANS something (unread posts) and borrowing it here would teach
   people to ignore it. This is faster, and it stops when the car stops. */
@keyframes carTurnFlash{
  0%,100%{opacity:.15}
  50%    {opacity:1}
}
.car.turning .lampGlow,
.car.turning .tailGlow{animation:carTurnFlash .26s ease-in-out 6}
.car.turning .lampCore{fill:#fffdf4}
.car.turning .tailCore{opacity:1}

/* The plate revolves: squashed to nothing and back, which is how a prism
   turning is drawn in two dimensions. car.js swaps the word at the pinch,
   where the plate has no height and the change cannot be seen happening. */
@keyframes plateFlip{
  0%   {scale:1 1}
  50%  {scale:1 .04}
  100% {scale:1 1}
}
/* .42s here, FLIP_MS there. car.js swaps the word at half of this, where the
   plate has no height and the change cannot be seen happening. */
.plate.flipping{animation:plateFlip .42s ease-in-out}
/*
 * THE PLATE NEEDS NO COUNTER-ROTATION ANY MORE, and that is the fix.
 *
 * It used to live on the profile, where the car's own rotateY(180deg) mirrored
 * everything inside it, text included — so after one press the plate read
 * LASIARPPA, a rendering artefact wearing the costume of a joke. It was
 * corrected by giving the plate the same rotation as the car, nesting one
 * inside the other so 180 and 180 made 360.
 *
 * That worked, and it was answering the wrong question: a number plate is not
 * visible from the side of a car. It is on the back now, in the group that is
 * only ever shown square-on, so there is no mirroring left to undo. The bug is
 * gone rather than cancelled — and the writing is where somebody stands to
 * read it, which is what the DB5's revolving plate was always about.
 */
.plate{
  transform-box:fill-box;
  transform-origin:50% 50%;
}

/* The tail lamp is a control, so it says so on hover and on focus — the same
   affordance the headlight has, and the same reason. */
#taillightHit{cursor:pointer}
/* The ring is for a keyboard, as on the headlight, the handle and the tyres.
   Left to the browser's own :focus outline, a mouse press drew it too — and
   a showroom hands focus to the same lamp on the car that replaces this one,
   so every car after that arrived wearing a box round its tail. */
#taillightHit:focus{outline:none}
#taillightHit:hover .tailGlow,
#taillightHit:focus-visible .tailGlow{opacity:.85}
#taillightHit:focus-visible{outline:2px solid var(--brand,#f0c05a);outline-offset:6px;border-radius:8px}

/* Reduced motion: it still turns, because facing the other way is the POINT
   and is not decoration. It simply arrives there without the leap, the squash
   or the strobe. */
@media (prefers-reduced-motion:reduce){
  /* It still turns, still stops at the back, and still reads the name out —
     all three are the POINT and none of them is decoration. What goes is the
     leap, the squash and the strobe, and the swings get short enough that the
     movement reads as a change of state rather than as motion. */
  .side{transition:rotate .12s linear}
  .rear{transition:rotate .12s linear}
  .car.turning{animation:none}
  /* The plate still changes — it is information, not decoration — it simply
     does not spin to do it. */
  .plate.flipping{animation:none}
  .car.turning .lampGlow,
  .car.turning .tailGlow{animation:none;opacity:.55}
}

/* The headlight is the only control on the sign-in page, and on a phone it is
   too small to hit reliably.
   The SVG is a shrinkable flex item in a .stage with 16px of side padding, so
   the scale is (viewport - 32) / 1200. The transparent hit circle is r="54",
   which is 108 user units across — 29.5 CSS px on a 360px Android, 32.2 on a
   390px iPhone. Apple's minimum is 44.
   r is a real SVG 2 geometry property in CSS (Safari 9+, Chrome 43+, Firefox
   69+), and where it is not supported the r="54" attribute simply stands. So
   the LAMP stays exactly the size it was drawn; only the target grows.
   Scoped to a media query on purpose: at the 1120px desktop car this would be
   a 168px invisible pointer region across the whole nose, which gives the
   puzzle away. Above 560px the untouched target already clears 47px.
   The dashboard's next-car button is made from the same markup and gets the
   same target. It reaches over a front tyre on some cars, grown or not, so
   car.js clips it away over the tyres and counts only the lamp as drawn
   there — see THE TYRES there. The pointer, the glow and the focus follow
   the clip without a rule here. */
@media (max-width:560px){
  #headlightHit > circle:first-of-type{ r:90px; }
}

/* No pointer means no approach, so nothing on a touch screen can hint at the
   lamp before it is pressed. The dark-until-found puzzle is deliberate and
   stays; this only confirms the press once it lands, so a near-miss is
   distinguishable from a hit. */
@media (hover:none){
  #headlightHit:active .lampGlow{opacity:1}
}

@media(max-width:600px){
  .hero-head{padding:28px 16px 4px}
  .hero.hero-compact .hero-head{padding:20px 14px 0}
}

/* ---------------------------------------------------------------------------
   THE DOOR HANDLE, which opens the dashboard.

   It looks like a handle and not like a button, deliberately: the joke only
   works if you find it. What it does have is the affordances a control must
   have whether or not it is announced — a pointer, a hover, and a focus ring
   for anyone arriving by keyboard, who gets the aria-label the SVG carries.
   --------------------------------------------------------------------------- */
#doorHandleHit{cursor:pointer}
.doorHandle{transition:filter .18s ease}
#doorHandleHit:hover .doorHandle{filter:brightness(1.5)}
#doorHandleHit:focus{outline:none}
#doorHandleHit:focus-visible .doorHandle{filter:brightness(1.8)}
#doorHandleHit:focus-visible{outline:2px solid var(--brand,#f0c05a);outline-offset:4px;border-radius:6px}

/* ---------------------------------------------------------------------------
   SMOKE, one turn in four — see puff() in car.js for why it is not every time.

   Three puffs per pipe at different sizes and delays, so it reads as billowing
   rather than as one circle growing. They start inside the tip and drift up and
   back: `back` is +x here because the exhausts point at the viewer in the rear
   view, and smoke from a pipe pointing at you spreads outward as much as it
   rises.

   opacity is animated rather than display, so the clearing is a fade over
   seconds — car.js takes the class off a while after the car has stopped, and
   an instant cut would look like the animation ending rather than like exhaust
   drifting off.
   --------------------------------------------------------------------------- */
.smoke .puff{
  fill:#c9d3dd;
  opacity:0;
  transform-box:fill-box;
  transform-origin:50% 50%;
  pointer-events:none;
}
@keyframes puffOut{
  0%   {opacity:0;   scale:.30; translate:0 0}
  15%  {opacity:.62}
  55%  {opacity:.34}
  100% {opacity:0;   scale:3.4;  translate:0 -62px}
}
/* Denser and slower than it was: bigger at the end, drifting further, and
   five overlapping lifetimes rather than three so there is always more than
   one in the air. The opacity is up a little as well — this is a tired old
   engine at the one moment it is pointed at the viewer. */
.car.smoking .puff{animation:puffOut 2.8s ease-out infinite}
.car.smoking .p2{animation-delay:.45s;animation-duration:3.2s}
.car.smoking .p3{animation-delay:.9s; animation-duration:2.5s}
.car.smoking .p4{animation-delay:1.35s;animation-duration:3.4s}
.car.smoking .p5{animation-delay:1.8s;animation-duration:2.7s}
/* Leaving the class removes the animation, and a puff mid-flight would vanish
   on the frame it went. The fade-out lives here: the group dims over a second
   and a half whichever frame the animation happened to be on. */
.smoke{transition:opacity 1.5s ease}
.car:not(.smoking) .smoke{opacity:0}

@media (prefers-reduced-motion:reduce){
  /* The smoke is a joke, and a joke is exactly what this preference is for.
     No drifting: it appears, holds, and goes with the class. */
  .car.smoking .puff{animation:none;opacity:.34;scale:1.6}
}

/* =========================================================== THE SHOWROOM ===
 *
 * One car drives off and the next drives on behind it, so for about a second
 * there are two in the frame. That overlap is the whole effect and it is why
 * these are two elements moving independently rather than one element being
 * re-rendered — a single node cannot be in two places at once.
 *
 * `translate` and not `transform`, for the reason stated further up this file:
 * the car already uses `transform` for the turn and the hop, and putting a
 * third thing in there means whichever writes last wins. They are separate
 * properties now precisely so two animations can run at once without arguing.
 *
 * The easing is deliberately NOT symmetrical. A car pulling away accelerates
 * from a standstill; one arriving slows into its place. Matching curves would
 * read as a slide.
 */
.car{ translate: var(--drive, 0%) 0; }

.car.driving-off{
  transition: translate 2600ms cubic-bezier(.42,0,.85,.35);   /* must match DRIVE_MS */
}
.car.driving-on{
  transition: translate 2600ms cubic-bezier(.15,.7,.35,1);    /* must match DRIVE_MS */
}

/* Two cars in one box, both on the floor, neither pushing the other along.
   The stage is the positioning context; each car sits in it. */
.hero .car, .stage .car{ position:relative; }
.car.driving-off{ position:absolute; left:0; right:0; }

/*
 * REDUCED MOTION TAKES THE DRIVE AWAY AND KEEPS THE CAST.
 *
 * Somebody who has asked for less movement has not asked to see one car for
 * ever. The swap still happens — it simply happens as a cut, which is no
 * movement at all rather than a slower version of the same sweep across the
 * screen.
 */
@media (prefers-reduced-motion: reduce){
  .car.driving-off, .car.driving-on{ transition:none; }
  .car{ translate:0 0; }
  /* EVERY CAR ON A CHASING STAGE IS EXEMPT, not only the one mid-crossing. No
     chase of either kind is ever STARTED under this preference, and car.js
     finishes a running one as soon as the preference is turned on. This rule
     covers the moment before car.js hears about it, and a browser that never
     reports the change. There car.js waits until the next pass across the
     stage is due and then cuts to the next car instead of starting the run.
     Until then the chase keeps its speeding car off the stage by a pixel
     --drive, before its first pass, between each of the back-and-forth's,
     and after its last until the next car takes the stage. Zeroed, that car
     would stand in the middle of the empty showroom. Kept, it stays out of
     sight, and the leaving and arriving cars simply cut to where they were
     going, because their transitions are off above. In such a browser a pass
     that had already started finishes its run, because nothing here takes
     its transition away. car.js marks the stage for exactly the
     length of a chase; .crossing is named as well so a car mid-run never
     depends on the mark alone. */
  [data-chase] .car, .car.crossing{ translate: var(--drive, 0%) 0; }
}

/* ======================================================= THE TOW AND TYRES ===
 *
 * The flatbed that sometimes takes the car away, and the tyres that go flat
 * when pressed. car.js does the moving — the tow runs in its frame loop, where
 * the car, the bed and the cable share one clock — so what is here is only
 * what a stylesheet is for: how things look, and how long a change of state
 * takes to happen.
 */

/* The truck is an <svg class="tow"> on the stage, placed and scaled from
   car.js. Every rule for it hangs off .tow, because the names inside it —
   .beacon, .reverse, .flash-a — are plain words a later rule could reach. */
.stage .tow{display:block}

/* The roof beacons, one lamp then the other, for as long as the truck is on
   the page. A steady pair of lights on a truck parked in a showroom reads as a
   truck with its lights on; alternating is what says "working vehicle". */
@keyframes towBeacon{
  0%,36%   {opacity:1}
  48%,88%  {opacity:.06}
  100%     {opacity:1}
}
.tow .flash-a, .tow .flash-b{animation:towBeacon .9s ease-in-out infinite}
.tow .flash-b{animation-delay:-.45s}

/* A car on its way up the ramp leaves the showroom floor behind: its pool of
   floor light is part of its own drawing, and one rising with it onto the
   deck is a spotlight bolted to the car. */
.car > ellipse{transition:opacity .4s ease}
.car.towed > ellipse{opacity:0}

/* ---------------------------------------------------------------- TYRES --
   A pressed tyre is a control like the tail lamp, so it has the same pointer
   and the same focus ring. It has no hover glow: the tyres are found, not
   advertised. */
.car .wheel[role="button"]{cursor:pointer}
.car .wheel[role="button"]:focus{outline:none}
.car .wheel[role="button"]:focus-visible{outline:2px solid var(--brand,#f0c05a);outline-offset:4px;border-radius:50%}

/* The rubber squashes and the bulge spreads about the bottom of the tyre,
   where it meets the floor; car.js sets how far, these set the pivot. */
.car .tyre-rubber, .car .tyre-bulge{transform-box:fill-box;transform-origin:50% 100%}
.car .tyre-bulge{scale:.9 0}

/* Air takes a moment to leave a tyre and the car settles as it goes; a flat
   that snapped into place would read as a different drawing. The same
   transition is the pump a car with a flat gets before it moves or turns
   round, and car.js waits for it tyre by tyre: the duration must match
   PUMP_MS there. */
.car .sag, .car .wheel, .car .tyre-rubber, .car .tyre-rim, .car .tyre-bulge{
  transition:translate .34s cubic-bezier(.3,.6,.35,1), rotate .34s cubic-bezier(.3,.6,.35,1),
             scale .34s cubic-bezier(.3,.6,.35,1);
}

/* --------------------------------------------------- THE HELD-DOWN KEYS --
   Shift on the headlight sends for the tow truck, Alt sends for the police,
   and the two together send for the back-and-forth. Shift with a press is
   also how a browser extends a text selection. With a caret anywhere on the
   page, the tow started and left the page's words highlighted from there to
   the car. Nothing inside a car is text somebody would copy, so the car
   cannot be selected and the page around it still can. Alt's own gestures
   are answered in car.js, where the press is. */
.stage .car{-webkit-user-select:none;user-select:none}

/* ============================================================== THE CHASE ===
 *
 * The police motorcycle that now and then follows a car off the stage: in the
 * chase on the car's only pass, in the back-and-forth on its way back and on
 * its way through again. car.js places it and moves it — where it stands, how
 * far it goes and when are all measured there, in the car's own drawing units
 * — so what is here is the same half a stylesheet owns for the truck: how it
 * looks, and how long a change of state takes.
 *
 * Every rule hangs off .chase, because the names inside the drawing —
 * .flash-red, .bike-wheel, .bike-helmet — are plain words a later rule could
 * reach, and the bike would be the last place anybody looked.
 */

/* An inline <svg> sits on the text baseline with a descender's worth of gap
   under it, which would stand the bike that far off the floor. car.js pins the
   box; this is the part a stylesheet has to say. */
.stage .chase{display:block; translate: var(--drive, 0px) 0;}

/* THE CROSSING, and it is the one movement on this page that is meant to look
   fast. Linear, and the same curve for the car and the bike: a car already
   flat out does not accelerate into frame, and two different curves would open
   or close the gap between them on the way past. Every pass uses this curve,
   each of the back-and-forth's included, and car.js times a pass with the bike
   by the same number. The back-and-forth's lone pass is the exception: it
   stops once the car alone is off the stage, so crossStage() sets a shorter
   duration on the car itself, cut in proportion so the speed is the same, and
   times what follows from that. */
.car.crossing, .chase.crossing{
  transition: translate 1600ms linear;    /* must match CHASE_CROSS_MS */
}

/* The lamps, one colour then the other, for as long as the bike is on the
   page. Quicker than the truck's beacons and with less dark in the cycle: a
   working vehicle parked in a showroom and a machine at a chase are not the
   same rhythm. NO SOUND — the horn belongs to the tail lamp, and a siren is
   the version of this that gets switched off. */
@keyframes chaseFlash{
  0%,40%   {opacity:1}
  50%,90%  {opacity:.04}
  100%     {opacity:1}
}
.chase .flash-red, .chase .flash-blue{animation:chaseFlash .56s ease-in-out infinite}
.chase .flash-blue{animation-delay:-.28s}

@media (prefers-reduced-motion: reduce){
  /* No tow is started under this preference at all — car.js swaps instead —
     so these only matter to a truck already on screen when it changes. A
     chase on screen when it changes is finished by car.js. Until that
     happens its cars keep their places, for the reason given beside
     [data-chase] .car above, and the lamps stop. */
  .tow .flash-a, .tow .flash-b{animation:none}
  .chase .flash-red, .chase .flash-blue{animation:none}
  .car > ellipse{transition:none}
  /* The tyre still goes flat, because flat is the point; it simply is flat.
     And a car about to move or turn is simply full again: car.js does not
     wait for a pump here. */
  .car .sag, .car .wheel, .car .tyre-rubber, .car .tyre-rim, .car .tyre-bulge{transition:none}
}
