/* ═══ THE PICTURE ═════════════════════════════════════════════════════════
   The ink-wash scene in partials/scene.html, and nothing else. Its own file
   since 2026-09-05, because it is on two hosts now: the landing page, where it
   is the page, and the Teaching Platform's sign-in, where it is what the card
   floats on. The platform does not load site.css and never should — that is
   sixty kilobytes of marketing-site layout for one drawing.

   WHAT IS IN HERE IS INTRINSIC TO THE DRAWING: how it crops, how the birds
   beat, how the haze drifts. What is NOT is anything about what sits on top of
   it. The landing page's two questions and their leader lines are in site.css;
   the sign-in card is in platform.css; and the landing page's phone layout —
   where the picture stops being a background and becomes a block with the
   questions stacked under it — stayed in site.css too, because on the platform
   a background stays a background at every width.

   The palette is tokens.css on both hosts. Nothing here names a colour. */

/* TWO BOXES, and the reason is the landing page's: its two questions are
   positioned in percentages, and a percentage is only trustworthy if the box
   it measures against has the picture's own aspect ratio.
     .scene   what the reader sees — full width, exactly the viewport tall,
              and it CLIPS.
     .frame   the drawing's own 16:9, sized to COVER the scene and centred in
              it. The <svg> and both questions live in here together, so
              whatever the frame does to one it does to the other, and the
              line from a question always lands on its person.
   This is object-fit:cover written out, because object-fit does not apply to
   inline SVG and the questions must be cropped by the same rule the drawing
   is. --h has to be a real length, not a percentage, for the max() to work. */
.scene {
  --h: 100svh;
  --fw: max(100vw, calc(var(--h) * 16 / 9));
  --fh: calc(var(--fw) * 9 / 16);
  position: relative; width: 100%; height: var(--h); overflow: hidden;
}
/* THE CROP IS BIASED, NOT CENTRED. When the frame is taller than the scene,
   something has to go. Centring took it off both ends, and the top end is the
   wash — the one thing in this picture that is the brand. Weighting the loss
   32/68 keeps it and spends the paper under the ledge instead, which is the
   part that is there to be empty. On a screen taller than 16:9 the frame is
   WIDER instead, --fh equals --h, and this resolves to zero. */
.frame {
  position: absolute; left: 50%; translate: -50% 0;
  top: calc((var(--h) - var(--fh)) * .32);
  width: var(--fw); height: var(--fh);
}
.art { display: block; width: 100%; height: 100%; }

/* The birds. Three things at once, each on its own element so none of them
   fight over `transform`: the flock crosses, the bird bobs, the wings beat.
   The travel is in px, and inside an SVG a CSS px IS a user unit — 2440 of
   them is the 1600-wide frame plus clearance at both ends, at any scale. A
   percentage would be measured against each flock's own bounding box, which
   is three birds wide and means nothing.

   The beat scales Y through NEGATIVE, so the wings swing from up-swept,
   through flat, to down-swept and back. Its duration rides in on a custom
   property because a <use> clones its target into a shadow tree that an inline
   style cannot reach — which is how this animation ran with a duration of
   zero, and no wing ever moved.

   THE WINGS NEVER STOP, AND THE CALM IS IN THE STROKE ITSELF. There was a
   glide here for a while — a burst of fast strokes and then a stretch of the
   cycle riding, on the theory that a gull beats a few times and then holds. It
   is true of a gull and it did not work here. Nine hundredths of a second per
   downstroke is not a wing swinging, it is a twitch; and the ride after it does
   not read as rest, it reads as the twitch STOPPING. Flap-flap-flap-stop, seven
   birds over, was exactly the abrupt little motion the sky was accused of.
   So the pause is gone and the stroke carries the calm instead: slower (a
   downstroke is a fifth of a two-and-a-bit-second cycle, about half a second,
   and you can follow it), shallower (36° of swing, not 54°, so the wing moves
   WITHIN the shape of a bird rather than snapping between two of them), and
   eased at both ends so nothing arrives anywhere hard.
   What survives from the old rhythm is the asymmetry — the downstroke does the
   work and is half again as fast as the recovery — and the refusal to be a
   metronome. That now comes from the two strokes in a cycle being unequal and
   from every bird running its own `--beat`, rather than from a hole in the
   middle of the animation. `--beat` is still the WHOLE cycle, two strokes, not
   one, which is why the numbers in home.html are a bit over two seconds.

   AND IT IS A PIVOT NOW, NOT A SQUASH. The glyph is two paths, one per wing
   (see the note over `#bird` in home.html), and each turns about the body
   rather than the pair being scaled through flat. Scaling made the bird
   disappear twice a beat at this size; turning keeps ink on the screen at
   every instant, and a wing that swings also gets visibly shorter as it goes,
   which is the foreshortening the scale was faking.

   THE CROSSING BOWS. Three keyframes between the ends, with x kept exactly
   linear and y lifted off the straight line by a few units — the flight path
   is a shallow arc instead of a rail. And the birds no longer hold a rigid
   triangle: each one drifts a few units against its neighbours on its own slow
   clock (`.bd`), so the formation opens and closes the way a real one does. */
.flock { animation-timing-function: linear; animation-iteration-count: infinite; }
.flock--r { animation-name: door-fly-r; }
.flock--l { animation-name: door-fly-l; }
.bird { animation: door-bob ease-in-out infinite; transform-origin: center; transform-box: fill-box; }
/* The drift sits on the <use> because every other element in the stack already
   owns a transform: the flock crosses, the bird bobs, the wings beat. Four
   animations, four elements, none of them fighting. */
.bd { animation: door-drift var(--drift, 12s) ease-in-out infinite alternate; animation-delay: var(--driftd, 0s); }
/* Each wing turns about the body — `100% 53%` of the LEFT wing's own box is
   its right-hand end at the height the two paths meet, and mirrored for the
   right. `fill-box` so the origin is the wing's geometry and not the 1600-unit
   stage the flock is flying across. */
.wing { animation-duration: var(--beat, 1.9s); animation-delay: var(--beatd, 0s); animation-timing-function: linear; animation-iteration-count: infinite; transform-box: fill-box; }
.wing--l { animation-name: door-beat-l; transform-origin: 100% 53%; }
.wing--r { animation-name: door-beat-r; transform-origin: 0% 53%; }
.haze { animation: door-haze linear infinite; }
/* x is dead linear across all five stops (-420 → 190 → 800 → 1410 → 2020, the
   flock's own timing function is linear); only y leaves the straight line. */
@keyframes door-fly-r {
  0%   { transform: translate(-420px, 24px); }
  25%  { transform: translate(190px, 4px); }
  50%  { transform: translate(800px, -8px); }
  75%  { transform: translate(1410px, -20px); }
  100% { transform: translate(2020px, -24px); }
}
@keyframes door-fly-l {
  0%   { transform: translate(2020px, -18px); }
  25%  { transform: translate(1410px, -2px); }
  50%  { transform: translate(800px, 10px); }
  75%  { transform: translate(190px, 18px); }
  100% { transform: translate(-420px, 22px); }
}
/* Up quickly, down slowly, and a shade of sideways in it: a bird rides a
   pocket of air rather than bouncing on a spring. The lift is three and a half
   units over a six-to-eight-second clock — shallower and slower than it was,
   because a bob you can time is a bob you can see working. */
@keyframes door-bob {
  0%   { transform: translate(0, 0); }
  34%  { transform: translate(.5px, -3.4px); }
  66%  { transform: translate(-.3px, 1.1px); }
  100% { transform: translate(0, 0); }
}
@keyframes door-drift { from { transform: translate(0, 0); } to { transform: translate(5px, -3.5px); } }
/* Two strokes, no glide, per wing and mirrored. The timing function on each
   keyframe governs the interval that STARTS there: the downstroke gets 20% of
   the cycle and the recovery 30%, so the stroke that does the work is half
   again as fast as the one that resets — which is the difference between a bird
   and a windscreen wiper. The two strokes are not identical (25° then 21°), and
   that plus each bird's own `--beat` is what keeps a continuous swing from
   reading as a metronome. */
@keyframes door-beat-l {
  0%   { transform: rotate(11deg);  animation-timing-function: cubic-bezier(.45, 0, .55, 1); }
  20%  { transform: rotate(-25deg); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  50%  { transform: rotate(10deg);  animation-timing-function: cubic-bezier(.45, 0, .55, 1); }
  70%  { transform: rotate(-21deg); animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  100% { transform: rotate(11deg); }
}
@keyframes door-beat-r {
  0%   { transform: rotate(-11deg); animation-timing-function: cubic-bezier(.45, 0, .55, 1); }
  20%  { transform: rotate(25deg);  animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  50%  { transform: rotate(-10deg); animation-timing-function: cubic-bezier(.45, 0, .55, 1); }
  70%  { transform: rotate(21deg);  animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  100% { transform: rotate(-11deg); }
}
@keyframes door-haze { from { transform: translateX(-2.5%); } to { transform: translateX(2.5%); } }

/* The birds hold still, and holding still is not the same as stopping. */
@media (prefers-reduced-motion: reduce) {
  .flock, .bird, .bd, .wing, .haze { animation: none; }
  /* Stopping the animation would leave every flock stacked on its own origin
     at the left edge, which is not "the birds hold still", it is a pile. */
  .flock { transform: translateX(var(--rest, 0px)); }
}

@media (max-width: 900px) {
  /* The birds are drawn for a 1600-unit stage. On a phone that stage renders
     about 700 CSS px wide, so a 20-unit bird is nine pixels of grey — present
     in the DOM and invisible to a reader. Scaling the flocks is what keeps
     them birds rather than dust. `scale:` and not `transform:`, because the
     crossing animation owns transform and the two compose in that order. */
  .flock { scale: 1.9; opacity: .8; }
}
