/* ---- The four questions ----

   A PAGE, AND STILL A DIALOG, and every rule at the top of this file exists to
   hold those two facts together. It was a 560-pixel card floating over a dimmed
   console, which is the shape of a confirmation: the console asked somebody
   what they do for a living inside the same box it uses to check whether they
   really meant to delete a scan. So it is the whole viewport now, on an opaque
   ground, with a backdrop the same colour so nothing of the console shows
   through and nothing of the old shape survives.

   It stays a real <dialog> opened with showModal(), and src/onboarding/dialog.ts
   carries the full argument. In short: the browser's top layer, focus trap and
   background inertness come free, and three other systems already stand down
   for `.cb-dialog` by name (scoop's TOP list, history.ts's covered(),
   walkthrough/over.ts's blocked()). A hand-rolled overlay would break all three
   silently. So the class is kept for the stand-downs and EVERY VISUAL PROPERTY
   IT BRINGS IS OVERRIDDEN HERE: the width, the padding, the radius, the shadow,
   the entrance and the veil.

   IT DOES NOT CONTINUE THE SIGN-IN WALL, IT IS THE SIGN-IN WALL, WITH THE DOOR
   THROWN OPEN. That screen is a picture on the left and a plane with two
   occupied corners on the right; this opens at exactly that split, on the very
   same picture and the very same door, and then takes the questions' plane to
   eighty per cent while the picture settles into the twenty that is left.
   Nothing is copied and nothing is resized: every node came off the screen
   before this one, both planes are laid out where they END, and the whole move
   is `transform` (src/onboarding/stage.ts). Nothing here is a card, a box or a
   border: the screen is the container.

   NO CANDY MOTIF ANYWHERE ON IT, which docs/09 allows one of. This surface
   already has the brand's mark at the top and the brand's scarlet on the
   eyebrow, the chosen row and the line along the bottom. A sweet on top of that
   would be the fourth thing saying whose product this is on a screen whose one
   job is to ask four questions.

   NO ACCENT RAIL. The chosen option is a filled row with a check in its number
   slot, which is how `.cb-lib-state` already marks a card that is on the board.
   The progress hairline runs along the BOTTOM edge, which is the one edge
   docs/10 does not rule out and the one that reads as a page's own footing.

   EVERY COLOUR IS A ROLE TOKEN, so light and dark both resolve out of
   console-theme.css and there is not one prefers-color-scheme block in this
   file. There is no local reduced-motion block either: the global one in
   console-theme.css already cuts duration AND delay to nothing, and the one
   part of the sequence a stylesheet cannot cut, the steps held apart by
   timers, is not run at all for somebody who asked for less (stage.ts). */

/* ---- The viewport ----
   `inset: 0` and explicit sizes together, on purpose. The sizes are what the
   element is; the insets are what pins it once `.cb-dialog`'s auto margins are
   taken away. max-inline-size caps the vw against the initial containing block,
   because 100vw counts a scrollbar the console behind may still be showing and
   an extra fifteen pixels of width would push the two planes' seam off the one
   the sign-in screen has under it.

   dialog.cb-ask, not .cb-ask: an element plus a class beats `.cb-dialog`
   whatever order the two stylesheets happen to load in. */
dialog.cb-ask {
  inset: 0;
  margin: 0;
  inline-size: 100vw;
  max-inline-size: 100%;
  block-size: 100svh;
  max-block-size: 100svh;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: var(--cbc-surface);
  color: var(--cbc-ink);
  box-shadow: none;
  /* The panel inside does the scrolling. A dialog that scrolls would take the
     progress line at the bottom edge with it, and the picture's drift would
     paint outside the wall. */
  overflow: hidden;
  /* cbc-fade and not cbc-rise. Twenty pixels of travel is for a box arriving
     over a page; this IS the page, and a page does not slide in from below. */
  animation: cbc-fade var(--cb-motion-base) var(--cb-motion-ease) backwards;
}
/* The one place in the console where the veil is deliberately not the veil.
   Everything else that opens dims what it opened over because the reader is
   coming back to it; nothing is coming back here until this is answered or
   declined, and a blurred console showing through would say otherwise. */
dialog.cb-ask::backdrop {
  background: var(--cbc-surface);
  backdrop-filter: none;
  animation: cbc-fade var(--cb-motion-base) var(--cb-motion-ease) backwards;
}
/* AND THE ONE THAT WALKED IN OFF THE WALL DOES NOT FADE IN AT ALL, because
   there is nothing for it to fade in over: what is behind it at frame zero is
   the same picture, the same door and, in two cases, the same nodes. A fade
   here would be the screen dissolving into an identical copy of itself, which
   costs a quarter of a second and shows as a flicker on any machine that drops
   a frame doing it. */
dialog.cb-ask[data-mode="morph"],
dialog.cb-ask[data-mode="morph"]::backdrop {
  animation: none;
}

/* ---- Where the seam is, and where it was ----

   Every length in this file comes out of these five, and they are declared on
   the surface itself rather than on either plane, because both planes now need
   them and a custom property only travels downwards. `.cb-ask` and not
   `dialog.cb-ask`: the second-step lock wears the same room on a plain div
   (src/mfa/surface.ts), and it is entitled to the same arithmetic.

   THE RATIOS ARE RATIOS AND NOT PERCENTAGES on purpose. The drift below has to
   be a length, because a percentage inside a transform resolves against the
   element's own width and the element in question is a photograph wider than
   the plane holding it. One arithmetic, in one unit, for all of it.

   TWENTY AND NOT THIRTY, which is a smaller strip than it sounds: at 1440 it is
   288 pixels and at 1280 it is 256, both still wide enough for a landscape
   frame to read as a photograph rather than as a texture. It also gives the
   question back a fifth of the screen, which is where the whole surface's
   attention is supposed to be.

   The three offsets underneath are the FLIP, written by stage.ts at the moment
   the surface opens and left at nothing for a surface that did not walk in off
   a wall. Their zeroes are declared here so that a rule reading one of them is
   never invalid, which is what an unset var() would make it. */
.cb-ask {
  /* How wide the plane the sign-in split was measured against actually is.
     100vw is only the fallback; stage.ts writes the measured width here before
     anything paints and keeps it current through a resize, because the two
     stop being the same number the moment a scrollbar is showing. */
  --ask-span: 100vw;
  --ask-door-share: 0.44;
  --ask-wall-share: 0.2;
  /* The sign-in wall's own width, which is the width the picture keeps. Where
     there is a wall on the page, wall.ts overrides this with what it measured;
     the clamp is the sign-in grid's own, kept here for the restart path, which
     has no wall to measure. */
  --ask-wall-from: calc(
    var(--ask-span) -
    clamp(var(--cb-width-sm), var(--ask-span) * var(--ask-door-share), var(--cb-width-lg))
  );
  --ask-wall-to: calc(var(--ask-span) * var(--ask-wall-share));
  /* How far the questions' plane is pushed off to the right at frame zero, how
     far back the two things centred on it are pulled, and how far down the
     lockup starts. All three animate to nothing and none of them is layout. */
  --ask-shut: 0px;
  --ask-lift: 0px;
  --ask-rise: 0px;
}

/* ---- The two planes ----

   THE PICTURE IS A FIXED PLANE AND THE QUESTIONS ARE A SHUTTER OVER IT, and
   that one sentence is the difference between this and the version that
   juddered. Neither plane's width changes, ever. The picture plane is laid out
   at the SIGN-IN wall's width and stays there; the questions' plane is laid out
   at its settled eighty per cent, is opaque, and covers everything to the right
   of the seam whatever the picture is doing underneath. Moving the seam is then
   one transform on one element, and what the reader reads as "the door opening"
   is the shutter sliding left off a photograph that was always that wide.

   IT USED TO ANIMATE `inline-size` ON BOTH, which is a layout animation: every
   frame the browser re-solved the flex row, re-covered the photograph, re-laid
   out the frame, the measure, the foot and the hairline, and re-resolved the
   panel's top padding, all on the main thread. No easing curve can rescue that,
   which is why the curve was not the first thing fixed.

   Absolutely positioned rather than a flex row, because the two planes no
   longer divide anything between them: one is pinned to the leading edge at a
   width of its own and the other is pinned to the seam and to the far edge.
   `overflow: clip` and not `hidden`, because the shutter overhangs the far edge
   by its own shut at frame zero and a hidden overflow would still be a scroll
   container, which a focused control inside can scroll. Clip cannot. */
.cb-ask-stage {
  position: relative;
  block-size: 100%;
  overflow: clip;
}

/* ---- The picture ----

   IT IS THE SIGN-IN WALL'S WIDTH AND IT NEVER CHANGES. Not because the strip
   beside the questions is that wide, but because the shutter beside it is
   opaque: at frame zero the whole plane is showing, which is the sign-in
   screen, and once the shutter has slid left only the leading fifth of it is,
   which is this screen. Nothing was resized to get from one to the other, so
   `object-fit: cover` is solved once, on the frame the surface opens, and never
   again. The plane it is pinned to is measured off the wall itself where there
   is one on the page (wall.ts).

   NO HAIRLINE ON THIS PLANE ANY MORE, because the seam is not this plane's edge
   any more: it is the shutter's leading edge, which is the only edge that
   moves. Drawing it here would put a line down the middle of the photograph at
   frame zero and hide it behind the shutter at the end. The questions' plane
   carries it now, on exactly the pixel the sign-in wall drew it on. */
.cb-ask-wall {
  /* HALF THE DISTANCE THE SEAM TRAVELS, and this is the whole parallax in one
     line. The photograph is rendered once at the wall's width and never
     re-covered, so its centre is fixed at half of that; the strip it has to end
     up centred on is half of twenty per cent. The difference is half as far as
     the seam moves, which is what the shutter above it moves.

     A layer that lags is what reads as depth; a layer that keeps pace reads as
     a curtain. Applied as a transform on a box that is never re-laid-out, so it
     can be given a longer duration than the shutter and fall behind it. */
  --ask-drift: calc((var(--ask-wall-to) - var(--ask-wall-from)) / 2);
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  /* THE STRIP IT ENDS ON, not the width the sign-in wall started at.
     It used to be the latter, because this plane did the shrinking: it opened
     at the sign-in wall's width and the morph took it down to twenty per cent.
     The sign-in screen does that itself now (signin/widen.ts), so this surface
     is put up at the finished shape and this is simply what that shape is.
     Left at --ask-wall-from it sat at fifty-six per cent of the window, which
     is the sign-in screen's proportion showing up on a screen that had already
     moved past it. */
  inline-size: var(--ask-wall-to);
  overflow: hidden;
  background: var(--cbc-sunken);
  /* THIS PLANE'S LAYERS ARE ITS OWN BUSINESS, and that is load-bearing now that
     the plane is wider than the strip of it anybody sees. The shade over the
     photograph sits at z-index 3; without a stacking context here that 3 is
     counted against the questions' plane, which is a later sibling at auto, and
     the shade paints over the first five hundred pixels of the questions. It
     used to be harmless: the plane was only ever as wide as the picture, so
     there was nothing of the other plane underneath it to darken. */
  isolation: isolate;
}

/* ---- The one layer that moves ----

   IT CARRIES A TRANSFORM AND NOTHING ELSE, and that is the whole reason it
   exists. The photograph used to be moved by translating the <img> itself, and
   at the time that element carried a five-function camera grade as a CSS
   `filter`, sat under a grain pseudo that blended with what was beneath it, and
   had a corner fall-off painted over it. Moving it meant re-running the grade
   and re-blending the grain at a new position on every frame, full screen, at
   the display's real pixel count, and it juddered on every machine it was tried
   on. console-signin.css hit the same wall one screen earlier and wrote the
   answer down: the movement goes on the holder, not on the picture.

   ALL THREE OF THOSE LAYERS HAVE SINCE GONE (console-signin.css: the wall is
   the photograph, with nothing done to it), so the judder they caused cannot
   come back through this element. The separation stays anyway, and not out of
   sentiment: a box whose only job is a transform is a box a reader can be sure
   about, and the next thing anybody hangs on the picture lands INSIDE it rather
   than on the layer that moves.

   IT FILLS ITS PLANE, which is now a plane that never changes size, so the
   picture inside it is laid out once and moved as a finished texture. It is
   wider than the strip it ends on, and offset by half the difference, so the
   photograph stays centred on what the shutter leaves showing.

   IT IS THE ONE THING THAT LAGS, and the lag is the whole reason it has a
   duration of its own. The shutter takes --cb-motion-slow plus base; this takes
   another base on top, so when the plane arrives the picture is still a few
   pixels short of home and comes in under it. A layer that keeps pace with the
   thing in front of it reads as a curtain rather than as depth. Both are on
   --cbc-carry, so they are the same gesture at two speeds and not two gestures.

   `will-change` only where something is going to change: the morph is the one
   run where this moves at all.

   IT USED TO SAY wall.ts TOOK THE PROMOTION BACK OFF when the move ended, and
   that was never true. The release rode on a `[data-still]` flag written by a
   `holdStill()` nobody ever called, and which could not have worked if they
   had: it waited for a `transitionend` for `transform` on `.cb-ask-drift`, and
   this plane has no transition at all (the rule below says so twice). Both the
   dead function and the two rules keyed on the flag are gone. What is left is
   honest and is a small debt: on the morph run the promotion is held for the
   life of the surface, which docs/10 rules out. Releasing it needs a listener
   on a real transitionend of a plane that really transitions, which is the
   panel further down, and that is a change with a visual check on it. */
.cb-ask-drift {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  /* PINNED AT THE WIDTH THE PICTURE WAS RENDERED AT, not at the strip it ends
     up showing through, and this line is load-bearing arithmetic rather than
     layout taste.

     --ask-drift above is `(to - from) / 2`: half the distance between the
     sign-in wall's width and the strip this plane settles at, which is how far
     the photograph has to move for its own centre to land on the centre of the
     strip. On a 1600 window that is (320 - 960) / 2, or minus three hundred and
     twenty. Apply that to a box that is itself only three hundred and twenty
     across and the picture is not offset, it is gone: shifted exactly its own
     width off the leading edge, leaving the plane's own ground showing, which
     reads as an install with no photographs in it.

     That is what `inset: 0` did here. It is the same mistake wall.ts's header
     warns about from the other end ("pinning the picture to it is how a refresh
     lost its photograph"), arriving through the stylesheet instead of through a
     measurement. The box is the width the picture was covered at; the plane
     above it is the strip; overflow does the rest. */
  inline-size: var(--ask-wall-from);
  /* No transition, and no starting pose. Both were for a surface that opened at
     the sign-in wall's width and shrank; this one is put up at the finished
     shape, so a "from" here is a move it has no business making. See the rule
     further down that states the offset. */
}
.cb-ask[data-mode="morph"] .cb-ask-drift {
  will-change: transform;
}
/* NOT `[data-open]`, AND NO TRANSITION. The drift is where the picture ENDS,
   and by the time this surface exists the sign-in screen has already settled
   its own photograph on exactly that crop: the wall next door narrows to the
   same share and lets `object-fit: cover` do the arithmetic, so both screens
   show the middle strip of one render (console-signin.css). Where there IS a
   wall on the page, wall.ts measures it and this offset comes out at nothing,
   because the two are already the same box. Gating it on the open mark meant
   this plane opened at nothing and slid to the offset, so the picture that was
   sitting still behind the fade started moving again the moment the fade landed:
   the same photograph, in the same place, travelling 257 pixels for no reason
   anybody could name. Stated flat, the two screens' pictures are identical and
   nothing about the swap is visible. */
.cb-ask-drift {
  transform: translate3d(var(--ask-drift), 0, 0);
}

/* The picture itself is console-signin.css's `.cb-signin-cover`, moved across
   whole rather than rebuilt, so the one file that owns the photograph goes on
   owning it here. It fills the box above and is told nothing else: no width of
   its own, no transform and no transition, which is the point. Its own fade-in
   on load is left alone for the run that has no wall to take a picture off,
   where the file really is arriving late. */
.cb-ask-drift .cb-signin-cover {
  inset: 0;
}

/* ---- The plate, and the picture coming up out of the dark ----

   The drift holds its picture on a PLATE: a full-bleed box whose only animated
   property is opacity, so the entrance below never touches the transform the
   drift owns or the layout the plane is mid-way through. wall.ts says when it
   is armed; this says what it does.

   WHAT IS BEHIND IT AT FRAME ONE IS THE PLANE'S OWN GROUND. The door's
   photograph has already dissolved to --cbc-sunken by the time this surface
   exists (console-signin.css fades it out over the length of the opening), and
   this plane's background is that same --cbc-sunken. So the room's picture is
   not replacing anything: it comes up out of the dark the last one went into.

   IT USED TO BE A CROSSFADE, with the door's photograph mounted a second time
   underneath this one and kept alive for a second and a half. That was right
   while both walls wore the same picture and the swap was meant to be
   invisible. They are two pictures now (cover-roles.ts), the dark between them
   is the hinge, and a second full-size photograph held on the page to be faded
   through was paying for the entrance twice.

   A SECOND AND NOT THREE BEATS. `--cb-motion-slow * 2.5` is a thousand
   milliseconds, which is off the duration scale on purpose and says so: this is
   the one fade in the product that is meant to be WATCHED rather than got out
   of the way, and every value on the scale is short enough to read as the
   picture simply being there. No delay, so it starts as the room lands and the
   two are one arrival rather than a pause and then an effect.

   --cbc-lift, which is --cbc-dim read backwards: the same curve the door's
   picture left on, running the other way (console-theme.css). */
.cb-ask-plate {
  position: absolute;
  inset: 0;
}
.cb-ask-wall[data-dark] .cb-ask-plate-over {
  opacity: 0;
}
.cb-ask-wall[data-dark][data-lit] .cb-ask-plate-over {
  opacity: 1;
  transition: opacity calc(var(--cb-motion-slow) * 2.5) var(--cbc-lift);
}

/* ---- The slight dark over it ----
   THE CONSOLE'S ONE VEIL, TAKEN DOWN TO A FRACTION OF ITSELF, rather than a
   second dark invented for this screen. --cbc-veil at full strength is for
   hiding a page somebody is coming back to; at forty per cent of itself it is
   about a fifth of a stop, which is enough to sit the picture behind the
   questions without making it a silhouette.

   Over a photograph or not at all. On an install with no covers in the folder
   the wall is the plain plane it has always been, and a wash over that is only
   the plane in a slightly different grey: an effect nobody asked for, on the
   one install that has nothing to put it over.

   A SIBLING OF THE MOVING LAYER, NOT A CHILD OF IT. Inside, the dark would
   travel with the photograph and slide off the edge of the plane; over the top
   it stays put while the picture drifts under it, which is the only way round
   that reads as one thing lit rather than two things sliding.

   AND AN ELEMENT WITH A COLOUR ON IT, NEVER A FILTER. A `brightness()` on the
   plane would be a second filtered layer to composite, over the one the box
   above just finished taking a filter off, and it would darken the plain ground
   too on an install with no photographs in it. This costs one composited quad
   and applies only where there is something to apply it to.

   THE SHADE IS THE ONE THING STILL DRAWN OVER A PHOTOGRAPH ANYWHERE IN THIS
   PRODUCT, and it earns it by belonging to this screen rather than to the
   picture: the sign-in wall shows the frame as it was taken, and here the same
   frame is sat behind four questions that have to be readable. It comes off with
   the surface. z-index 3 is left over from a grain and a fall-off the cover used
   to paint at 1 and 2; there is nothing at either any more, and it is kept
   because the shade genuinely does have to be above the picture. */
.cb-ask-shade {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background: color-mix(in srgb, var(--cbc-veil) 40%, transparent);
  /* On the shutter's clock rather than the picture's, because it is the reason
     the picture is being put behind something: it should be finished when the
     questions' plane is, not still deepening under a screen that has settled. */
  transition: opacity calc(var(--cb-motion-slow) + var(--cb-motion-base)) var(--cb-motion-ease)
    var(--cb-motion-fast);
}
/* AND IT NEVER COMES ON. Left as it was, the wash faded up to forty per cent
   the moment this surface opened, so the photograph the reader was already
   looking at got visibly darker as the screen arrived over it: the same
   picture, in the same place, changing brightness for no reason they could
   name. It had one when the questions plane was a shutter sliding across this
   photograph and needed it held back underneath. That plane is opaque now and
   sits beside the picture rather than over it, so there is nothing left for a
   wash to protect, and the sign-in wall this is supposed to be continuing
   carries no wash at all.

   The RULE goes and the element stays. It costs one composited quad, and the
   day somebody does want that strip held back it wants holding back on the
   sign-in screen too, in the same terms, from here. What must never come back
   is a darkening only one of the two screens does. */

/* ---- The questions' plane, which is the shutter ----

   THE ONE ELEMENT THAT MOVES, AND IT ONLY EVER TRANSLATES. It is laid out at
   its settled width from the first frame, pushed off to the right by --ask-shut
   so its leading edge lands on the sign-in seam, and slid back to nothing. No
   scale, so nothing inside it is distorted and nothing has to be counter-scaled
   to undo a distortion; it is opaque, so the far edge hanging off the screen at
   frame zero costs a clip and nothing else.

   WHAT THE READER READS IS THE DOOR GROWING, and the arithmetic is why. A plane
   sliding left uncovers the picture at twice the rate its own contents travel,
   because its contents are centred and a centre moves half as far as an edge
   when a box grows on one side. So the seam runs at one speed, the lockup and
   the door at half of it, the photograph behind at half again: three depths out
   of one transform each, which is what the eye reads as a room opening rather
   than a panel arriving.

   THE SEAM HAIRLINE IS ITS LEADING EDGE. It used to be the wall's trailing
   border, but the wall no longer has a moving edge to hang it on. As a border
   here it lands on the pixel the sign-in wall drew it on, because --ask-shut
   lines up the two CONTENT edges and the border sits just outside this one.

   overflow: hidden is for the hairline along the bottom of it: pinned to this
   plane rather than across the viewport, it travels with the plane and never
   crosses the photograph. */
.cb-ask-panel {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  inset-inline-start: var(--ask-wall-to);
  border-inline-start: 1px solid var(--cbc-line);
  overflow: hidden;
  background: var(--cbc-surface);
  transform: none;
  transition: transform calc(var(--cb-motion-slow) + var(--cb-motion-base)) var(--cbc-carry)
    var(--cb-motion-fast);
}
.cb-ask[data-mode="morph"]:not([data-open]) .cb-ask-panel {
  transform: translate3d(var(--ask-shut), 0, 0);
}
/* Promoted for the morph, and NOT yet released, which is a debt rather than a
   decision. The release was a sibling selector on a `[data-still]` flag the
   wall was said to write when the picture stopped; nothing wrote it, because
   the writer (`holdStill()`) had no caller and waited on a transition the
   drift plane does not have. That rule is gone with it. This panel is the one
   plane here that genuinely transitions transform (the rule above), so it is
   also the one that can carry a real release: a listener on its own
   transitionend, which is a change with a visual check on it rather than a
   sweep item. Until then docs/10's objection to a promotion held for the life
   of a surface stands against this line, on the morph run only. */
.cb-ask[data-mode="morph"] .cb-ask-panel {
  will-change: transform;
}

/* ---- The confetti on the last card ----
   IN FRONT OF THE CARD AND UNTOUCHABLE. Paper that fell behind the words would
   be a pattern on the wallpaper rather than something happening in the room,
   and paper that could be clicked would put a four-second dead zone over the
   two buttons this card exists to offer. `pointer-events: none` is the whole
   of that second half.

   It clips to the panel because the panel already hides its overflow, which is
   also what makes the top edge a real edge: the pieces are born above it and
   come over it, rather than appearing out of nowhere a little way down.

   No colour, no size and no motion here on purpose. It is a canvas: what is on
   it is drawn frame by frame in onboarding/confetti.ts, which reads its five
   brand colours off the document so this one surface cannot hold a copy of
   them (rule 13). */
.cb-ask-cheer {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  pointer-events: none;
  z-index: 3;
}

/* ---- Three bands ----
   The mark, the question, the foot, and the middle one takes what is left.
   This is the sign-in door's grid for the sign-in door's reason: the question
   has to sit at the optical centre of the plane whether the foot under it is
   one line or two, and `1fr` on the middle row alone does exactly that.

   `1fr` and NOT `minmax(0, 1fr)`, which is the difference between a screen that
   scrolls and one that overlaps itself. A zero minimum lets the row be shorter
   than what is in it, so a five-option question on a nine-hundred-pixel window
   ran straight through the legend underneath it: the row was capped and the
   card simply carried on past the bottom of it. A plain `1fr` keeps the
   content-based minimum, so the grid grows, the frame scrolls, and the hairline
   at the bottom edge stays where it is because it hangs off the plane rather
   than off this.

   ITS PADDING IS ITS SETTLED PADDING FROM THE FIRST FRAME. The top padding used
   to animate from the door's own value to this one, to carry the lockup down,
   and animating padding on a grid means re-laying-out the grid and everything
   in it on every frame of the move. The lockup now starts where it started by
   being pushed back up there (--ask-rise, below), which is a transform on one
   small element instead of a reflow of the whole plane. */
.cb-ask-frame {
  /* POSITIONED SO THAT IT PAINTS ABOVE THE DOOR IT REPLACES. The door leaving is
     an absolutely positioned layer and this is in flow, and a positioned box
     paints over an in-flow one whatever the source order says. Positioned too,
     the two go back to being read in the order they are written: the door
     first, this over it, the hairline and the paper over both. Nothing else
     moves, because everything inside here that is absolute already has a
     positioned parent of its own (the slot, an option's number). */
  position: relative;
  block-size: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--cb-space-4xl);
  padding: var(--cb-space-5xl) var(--cb-space-7xl) var(--cb-space-6xl);
  overflow-y: auto;
}

/* ---- The mark ----
   Top-centre, at the size it is on the sign-in screen, and it is usually the
   very same element: stage.ts lifts the lockup off the door rather than
   building a second one, so nothing about it can arrive, load or flicker. It
   is the one thing here that neither fades in nor fades out. It ends up
   somewhere else only because the plane under it got wider.

   IT IS THE ONLY PART OF THE FRAME THAT IS PUSHED BACK. Everything else in
   there is held at nothing until the planes have stopped (further down), so the
   only thing on this plane that has to be somewhere else at frame zero is the
   lockup: across by --ask-lift, which is the same correction the door gets so
   the two cannot come apart, and up by --ask-rise, which is the difference
   between the door's top padding and this plane's. Both animate to nothing on
   the shutter's own clock, so the lockup is carried by the plane rather than
   travelling beside it.

   Descendant rules only. .cb-brand-art and .cb-brand-logo are console.css's
   component, worn here at the sign-in's size, and a bare rule for either would
   be this file quietly redefining somebody else's. The three values below are
   console-signin.css's own for the same three properties, which is not a copy
   of a decision so much as the same lockup at the same size in the same place. */
.cb-ask-top {
  /* Block, not grid. Its one child asks for 100% width, and a grid's auto
     track answers a percentage child by shrinking to its content, which here
     is the INVISIBLE wordmark under the absolutely positioned logo: the whole
     masthead resolved to 123px and the logo then fit itself inside that.
     Measured, twice, before the track was the suspect. */
  display: block;
}
/* THE MASTHEAD IS NOT A CARD MARK, AND THE NAME IS THE FIX: both wore
   `.cb-page-mark` once, and the card marks' fixed box further down wins that
   cascade, so every size and every entrance written here silently lost. One
   class, one component (stage.ts wears this one). */
.cb-ask-mast {
  display: flex;
  align-items: center;
  justify-content: center;
  inline-size: 100%;
  /* IT COMES DOWN INTO THE SPACE THE OTHER ONE LEFT. The sign-in screen's
     lockup lifts away upwards as the room opens (console-signin.css), and this
     one arrives from the same direction a beat later, so the two read as one
     movement handed between two screens rather than as two logos cross-fading
     through each other half a pixel apart.

     The delay is what makes it a hand-off rather than a collision: the surface
     itself is still fading up over its first --cb-motion-base, and the mark
     waits that out before it starts. It is also the ONLY thing on this surface
     that moves. The panel does not travel, the picture does not travel, and the
     room is at its final geometry before it is on screen at all: what arrives
     is opacity, and then the things inside it in order. */
  /* Its own beat, not a stowaway on the room's fade: it starts as the fade
     ends and travels far enough to be seen arriving (--mast-drop below), which
     is what makes the door-to-room hand-off read as one lockup coming down
     rather than two logos swapped while nobody watched. The door's own mark
     rides the same keyframes at the same tempo (console-signin.css). */
  --mast-drop: var(--cb-space-3xl);
  animation: cbc-mast-drop var(--cb-motion-slow) var(--cbc-spring) var(--cb-motion-base) backwards;
}
@keyframes cbc-mast-drop {
  from {
    opacity: 0;
    transform: translate3d(0, calc(var(--mast-drop) * -1), 0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .cb-ask-mast {
    animation: none;
  }
}
:root[data-motion="reduced"] .cb-ask-mast {
  animation: none;
}
.cb-ask[data-mode="morph"]:not([data-open]) .cb-ask-mast {
  transform: translate3d(var(--ask-lift), var(--ask-rise), 0);
}
/* AND IT DROPS IN ON A RELOAD TOO, which an earlier cut of this rule got
   wrong. The reload's ground paints the opened door shape with its lockup
   HUSHED to nothing (console-signin.css hides .cb-brand-art under
   [data-room], because a held logo dissolving into its twin is the flicker
   that rule exists to kill). So at the moment the morph room appears there is
   no lockup anywhere on screen, and a mast with its animation stripped popped
   in at full opacity from empty air: the exact flash the drop exists to
   replace. The one thing a morph arrival rightly skips is the ROOM fading in
   over its own ground; the mast has nothing on the ground to be continuous
   with, so it arrives the way it always does. */
.cb-ask-mast .cb-brand-art {
  /* A step up from the door's 6xl, deliberately: the door's lockup shares its
     panel with a heading, a lede and a button, and this one is the only thing
     in its band on a plane with one job. At the door's size it read as a
     leftover rather than a masthead. The drop animation is what carries the
     hand-off between the two sizes. */
  block-size: var(--cb-space-7xl);
  inline-size: 100%;
  justify-content: center;
}
/* CENTRED BY ITS INSETS, not by the box it is in: the lockup is absolutely
   positioned at the start of its holder (console.css) and the holder is the
   full width of the plane, so a centring keyword on anything around it does
   nothing at all. */
.cb-ask-mast .cb-brand-logo {
  /* A real height, not a cap: max-block-size only ever shrinks, and the file's
     intrinsic size is smaller than this band, so under a cap the mark rendered
     at whatever size the asset happened to be exported at. The width follows
     the aspect on its own; the cap below is the escape for a very narrow
     window. */
  block-size: var(--cb-space-7xl);
  /* Raised WITH the height: console.css caps every .cb-brand-logo at the
     rail's size, and a cap outranks any height under it. Measured before this
     line existed: block-size 64, rendered 32. */
  max-block-size: var(--cb-space-7xl);
  inline-size: auto;
  max-inline-size: 100%;
  inset-inline: 0;
  margin-inline: auto;
}
/* The typed fallback scales with the mark it stands in for, or it reads as the
   rail's small wordmark stranded under a lockup six times its size. */
.cb-ask-mast .cb-brand-word {
  font-size: var(--cb-type-xl-size);
}

/* ---- Who this is ----
   Under the choices, on the first card of a run, and screen.ts carries the
   argument for it being neither in the foot nor under the mark: the foot is two
   decisions about the QUESTIONS and this is a decision about the ACCOUNT, one
   press away from the control it is most easily mistaken for; and at frame zero
   the top of this plane is a still photograph of the sign-in door, which has a
   line of its own in that exact spot.

   QUIET, BUT A CONTROL AT THE CONSOLE'S OWN SIZE. Nobody who signed in as the
   right person should read this line at all; the one person who signed in as
   the wrong one is looking for exactly it. So the ink is the faintest on the
   surface and the words are small. The button beside it is NOT shrunk to match,
   and that is deliberate: it is the same quiet control, at the same height, as
   the two in the foot, because it is the same kind of thing as them. A way out
   drawn smaller than the other ways out is a way out somebody cannot find.

   It fades in with the room rather than standing at frame zero (further down),
   because at frame zero there is no room for it to stand in yet. */
/* QUIET, AND ONLY THERE FOR SOMEBODY WHO HAS MADE A MISTAKE. It is not part of
   the question and must never compete with the answers: the address is the
   smallest type on the card, the way out beside it is a link rather than a
   button, and neither is stronger than the foot note underneath them. Somebody
   in the right account should be able to answer four questions without ever
   reading this; somebody in the wrong one finds it exactly where they look. */
.cb-ask-who {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: var(--cb-space-sm);
  margin-block-start: var(--cb-space-3xl);
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
}
/* The address, and only the address: on an install that cannot name one the
   span is simply not built and the control stands on its own (screen.ts). It
   is the one string here nobody chose the length of, so it is allowed to break
   inside itself rather than push the button off the plane. */
.cb-ask-whom {
  overflow-wrap: anywhere;
}
/* And the control is not. Four words wrapped to two lines under a lockup reads
   as something that has gone wrong, and there is always room for it: the
   address gives way first. */
/* A LINK, NOT A BUTTON, whatever the markup says. It was reading as a third
   answer sitting under two: same weight, same face, same size as the things the
   card is actually asking somebody to choose between. Underlined and set in the
   same small type as the address it follows, it reads as the aside it is, and
   it still answers to a keyboard and still shows the console's focus ring. */
/* The selector carries the class twice over on purpose. `.cb-btn[data-variant]`
   is two selectors' worth of weight and a bare `.cb-ask-swap` is one, so the
   padding and the size below lost to console.css and this drew as a
   forty-two-pixel pill wearing an underline. It is the hazard the top of this
   file already names for `.cb-dialog`, met a second time. */
.cb-ask-who .cb-btn.cb-ask-swap {
  white-space: nowrap;
  padding: 0;
  border: 0;
  background: none;
  min-block-size: 0;
  font-size: inherit;
  line-height: inherit;
  font-weight: var(--cb-font-weight-medium);
  color: var(--cbc-ink-muted);
  text-decoration: underline;
  text-underline-offset: var(--cb-space-xxs);
  transition: color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-ask-who .cb-btn.cb-ask-swap:hover {
  color: var(--cbc-ink);
}

/* ---- The measure ----
   768 rather than the console's own 1440. This is one question and a short list
   of answers, not a table, and a heading at the top of the type scale needs a
   line length it can wrap on rather than the width of a monitor. */
.cb-ask-slot {
  position: relative;
  margin: auto;
  inline-size: 100%;
  max-inline-size: var(--cb-width-xl);
  /* Written by screen.ts, between one card's height and the next. A fixed
     height sized to the tallest card leaves the three-option question in a
     hole, and a jump is the worst thing this surface could do. */
  transition: block-size var(--cb-motion-base) var(--cbc-spring);
}
/* Only while the box is moving. Left on, it would clip the two-pixel focus ring
   off an option that runs the full width of it, which is the one thing here a
   keyboard reader cannot do without.

   `clip`, never `hidden`, and the difference is the double-jump this box once
   drew: `hidden` is still a scroll container, so the incoming card's autofocus
   landing on a control below the pinned height SCROLLED the box to reach it,
   teleporting both cards upward, and the glide then clamped the scroll back to
   nought so everything slid down again. `clip` is pure paint clipping: the box
   cannot scroll, focus stays where it lands, and the growing card is revealed
   by the glide alone. */
.cb-ask-slot[data-growing] {
  overflow: clip;
}

/* ---- One card in, one card out ----
   THE OUTGOING ONE IS OUT OF FLOW. Both in flow for a single frame is the page
   jumping by the height of a question and coming back. Out of flow it lies over
   the space its replacement is about to take, travelling and fading, and
   contributes nothing at all to the layout underneath, which is what lets the
   box glide between the two heights while the old card is still on screen.

   Vertically, and this is the one place the direction is a decision rather than
   a default. Going on, the card leaves upwards and the next arrives from below,
   which is the direction a list of questions runs. Going back reverses it, so
   Back reads as going back rather than as another question. */
.cb-ask-page[data-leaving] {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  /* Fast, and faster than the arrival that follows it. The two ran the same
     length at first and crossed at half opacity in the same place on the
     screen, so for a moment there were two headings printed over each other and
     neither was readable. A card being answered has already been read; the one
     coming has not. */
  animation: cbc-ask-gone var(--cb-motion-fast) var(--cb-motion-ease) forwards;
}
.cb-ask-page[data-leaving="back"] {
  animation-name: cbc-ask-gone-back;
}

/* ---- Arriving ----
   Six seats, in the order they are read, which is the sign-in wall's entrance
   worn on the screen after it. Not the card as one block: the eyebrow says
   where you are, the question is the thing to read, and the answers are what to
   do about it, so they land in that order and the eye follows.

   AFTER THE LAST CARD HAS GONE, not over the top of it. The delay starts at one
   --cb-motion-fast, which is exactly how long the exit above takes, so the two
   cards never share the screen and the whole thing reads as a page turning
   rather than as a dissolve.

   --cbc-step-letter and not --cbc-step, and the token's name undersells it: it
   is the console's finest sequence that still reads as a sequence rather than
   as a blur. Six seats land inside a hundred and twenty-five milliseconds on
   it, where the row stagger would take the full quarter second it caps at and
   put the answers arriving a beat after somebody had already read the question.

   No `to` keyframe anywhere, so every element ends at its own resting value.
   `backwards` and a delay is what holds each part before its turn; the global
   reduced-motion block cuts both, which is why there is no local one. */
.cb-ask-page > * {
  animation: cbc-ask-on var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: calc(var(--cb-motion-fast) + var(--cbc-step-letter));
}
/* THE FIRST CARD OF A RUN THAT WALKED IN OFF THE WALL WAITS FOR THE ROOM.
   Written on the page by dialog.ts and never taken off again, because an
   animation-delay changed on an element already partway through its entrance
   snaps it to the end. The wait is the two steps in front of it added up: the
   door's words leaving, then the planes moving and the picture settling, which
   is when there is somewhere for a question to arrive into. */
.cb-ask-page[data-wait] > * {
  animation-delay: calc(var(--cb-motion-slow) + var(--cb-motion-base) + var(--cbc-step-letter));
}
/* AND SO DOES THE ROOM AROUND IT. The foot and the hairline are drawn once and
   never rebuilt (screen.ts says why), so without this they are on screen from
   frame zero: two controls and a progress line printed over a door that has not
   finished leaving, on a surface whose whole claim is that it is still the
   screen behind it. Held on the same clock as the card's six seats, so the
   plane fills as one thing. Opacity only, because these are the room and not
   the content: the card travels, the room does not.

   THEY WAIT FOR THE PLANE TO HAVE ARRIVED, NOT TO HAVE STOPPED, which is the
   difference --cbc-carry made. That curve is within a per cent of home well
   before its duration is up, so a wait of slow-plus-base lands these on a plane
   that has settled while its last few pixels are still being spent. Content
   settling into a container that is already there is the point; waiting for the
   transition to END would leave a beat of nothing in the middle of the move.

   Only for a run that walked in off the wall. On the restart path there is
   nothing to wait for and the room is simply there. */
.cb-ask[data-mode="morph"] .cb-ask-base,
.cb-ask[data-mode="morph"] .cb-ask-who,
.cb-ask[data-mode="morph"] .cb-ask-rail {
  animation: cbc-fade var(--cb-motion-base) var(--cb-motion-ease) backwards;
  animation-delay: calc(var(--cb-motion-slow) + var(--cb-motion-base));
}
.cb-ask-page[data-from="back"] > * {
  animation-name: cbc-ask-back;
}
/* And a card's parts stop arriving the moment the card starts leaving. Answered
   inside half a second, it would otherwise have rows still fading in while the
   card holding them fades out, which is two gestures fighting over one object.
   Last, so it beats the direction rule above it at the same specificity. */
.cb-ask-page[data-leaving] > * {
  animation: none;
}
.cb-ask-page > *:nth-child(1) {
  --i: 0;
}
.cb-ask-page > *:nth-child(2) {
  --i: 1;
}
.cb-ask-page > *:nth-child(3) {
  --i: 2;
}
.cb-ask-page > *:nth-child(4) {
  --i: 3;
}
.cb-ask-page > *:nth-child(5) {
  --i: 4;
}
.cb-ask-page > *:nth-child(n + 6) {
  --i: 5;
}

/* ---- The mark at the top of a card ----

   THREE SCREENS OF ONE RUN, ONE MARK, AND IT USED TO BE THREE. The second
   step's shield, the profile stop's signature and the team stop's pair of
   people are three drawings of three different things, which is right. They
   were also three sizes, three stroke weights and three gaps to the heading,
   which is not: on an ordinary laptop the shield drew at 128, the signature at
   96 and the pair at 64, with sixteen pixels of air under the first, none at
   all under the second and twelve under the third. So pressing the button
   between two stops made the largest thing on the screen jump size, weight AND
   position while every band underneath it cross-faded politely. Each of the
   three files had written its own height ladder, each ladder carried a good
   argument, and no two of them agreed.

   SO THE BOX IS HERE AND THE DRAWING STAYS THERE. This owns the size, the
   ladder, the gap to the heading, the colour and the stroke. Each mark's own
   file owns what it is a picture of: which paths draw, in what order, and the
   one settle it takes as it lands, named through --mark-settle so this can run
   it without knowing what it is.

   HALF A UNIT, AND THAT IS THE WHOLE OF "THINNER". lucide draws at 2 units in a
   24 box, which at 128 is a ten-pixel outline and reads as a sticker. Half a
   unit is about two and a half pixels there, which is a line somebody drew.
   Anything drawn SMALLER inside a mark scales its own weight back up to meet
   it, which is what the shield's glyph has always done and is now the rule
   rather than one file's local decision.

   AND IT ARRIVES WITH THE CARD IT IS ON, which it did not. `.cb-ask-page > *`
   above hands every band of a card an entrance; each mark's own `animation`
   shorthand landed later in the cascade at exactly the same weight and quietly
   replaced it. Every other band was held invisible for one --cb-motion-fast and
   then rose into place, while the mark appeared at full opacity on frame zero,
   over the top of the card that had not finished leaving. Both animations are
   listed here instead.

   ENTRANCE LAST, AND THAT ORDER IS LOAD-BEARING. Two animations on one element
   do not composite a transform: the last one in the list wins outright. The
   entrance has to win for the quarter second it runs, and it fills backwards
   rather than forwards, so the frame it ends on is the frame the settle
   underneath it takes the transform back. Written the other way round, the
   mark would fade in without ever travelling. */
.cb-page-mark {
  --mark-size: var(--cb-space-10xl);
  /* When the mark starts: the card's own first seat. Read by the entrance and
     the settle below AND by every drawing beat in the three mark files, so the
     whole object is one clock. */
  --mark-in: var(--cb-motion-fast);
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--mark-size);
  block-size: var(--mark-size);
  /* CENTRED BY ITS OWN MARGINS. A mark is a direct child of the page (page.ts
     gave them their own slot), and `text-align: center` does nothing for a box
     that sets its own width. */
  margin-block-end: var(--cb-space-xl);
  margin-inline: auto;
  color: var(--cbc-accent);
}
/* THE ICON FILLS THE BOX, AND IT MUST BE A DIRECT-CHILD RULE. `icon()` puts
   `cb-icon` on everything it builds (dom.ts), and the profile stop's mark has a
   second one INSIDE it riding the signature's path: a descendant rule here
   sized that pen at 100% of its own track, which drew it at the full width of
   the mark it was supposed to be writing on. */
.cb-page-mark > .cb-icon {
  inline-size: 100%;
  block-size: 100%;
}
.cb-page-mark svg {
  stroke-width: 0.5;
}
.cb-ask-page > .cb-page-mark {
  animation:
    var(--mark-settle, none) var(--cbc-mark-run) var(--cbc-spring),
    cbc-ask-on var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: var(--mark-in, 0s), var(--mark-in, 0s);
}
/* `both`, NEVER BARE `forwards`, ON ANYTHING RIDING --mark-in. A delay shows
   the element's BASE state until the clock starts, and for a dash-drawn path
   the base state is the finished figure: every mark flashed fully drawn for
   the length of its own wait, blinked out at the first keyframe, and then drew
   itself. `both` holds the 0% frame through the delay, which is the missing
   half of the one-clock rule. */

/* AND EVERY BEAT OF THE DRAWING WAITS WITH IT, which is what --mark-in is for
   and why it is a property rather than a length written out twice.

   A mark is several parts on one clock: an outline draws, a thing arrives
   inside it, the whole object settles on the frame those two meet, and a ring
   goes out of it. Those beats are written as stops INSIDE each keyframe
   (console-gate.css says why at length), which only holds together while every
   part of the mark starts at the same instant. Delaying the box so it arrives
   with its card and leaving the drawing at zero pulls that apart: on the team
   stop the ring came out of a pair of figures a fifth of a second before they
   settled, which is a flourish arriving before the thing it is a flourish for.

   So the delay is one number, declared on the mark, and every construction rule
   in the three mark files reads it. Nothing is written twice and nothing can
   drift.

   THE FIRST CARD OF A RUN THAT WALKED IN OFF THE WALL WAITS FOR THE ROOM, and
   because the whole mark reads --mark-in it waits as one thing. Its own rule
   because the one above is at the same weight and comes first. */
.cb-ask-page[data-wait] > .cb-page-mark {
  --mark-in: calc(var(--cb-motion-slow) + var(--cb-motion-base));
}
/* AND THE DRAWING STOPS WHEN THE CARD STARTS LEAVING, which the rule above the
   nth-child list does not do on its own: `.cb-ask-page[data-leaving] > *`
   reaches direct children, so it cancels the mark's box and stops at it. Every
   beat of the drawing is a DESCENDANT of that box, or a pseudo-element on it,
   and all of them carried on: the signature went on being written and the pen
   went on travelling across a card that was sliding up and fading out, and the
   team stop's ring went on expanding out of a card that had already gone. That
   is the "two gestures fighting over one object" the leaving rule exists to
   prevent, met one level down. */
.cb-ask-page[data-leaving] .cb-page-mark *,
.cb-ask-page[data-leaving] .cb-page-mark::before,
.cb-ask-page[data-leaving] .cb-page-mark::after {
  animation: none;
}
.cb-ask-page[data-from="back"] > .cb-page-mark {
  animation-name: var(--mark-settle, none), cbc-ask-back;
}
.cb-ask-page[data-leaving] > .cb-page-mark {
  animation: none;
}

@keyframes cbc-ask-on {
  from {
    opacity: 0;
    transform: translate3d(0, var(--cb-space-3xl), 0);
  }
}
@keyframes cbc-ask-back {
  from {
    opacity: 0;
    transform: translate3d(0, calc(var(--cb-space-3xl) * -1), 0);
  }
}
/* An exit is the one animation that HAS to say where it ends, because where it
   ends is gone. The entrances above are all `from` only for the opposite
   reason: they end wherever the element rests. */
@keyframes cbc-ask-gone {
  to {
    opacity: 0;
    transform: translate3d(0, calc(var(--cb-space-4xl) * -1), 0);
  }
}
@keyframes cbc-ask-gone-back {
  to {
    opacity: 0;
    transform: translate3d(0, var(--cb-space-4xl), 0);
  }
}
/* Used by two things that leave without going anywhere: the still frame of the
   door, and this whole surface once it is finished with. Neither travels,
   because both are being replaced in place by what is already underneath. */
@keyframes cbc-ask-adieu {
  to {
    opacity: 0;
  }
}

/* ---- The door that was here a moment ago ----

   AND IT IS THE DOOR, NOT A PICTURE OF IT. This used to be a clone pinned in
   viewport coordinates over the top of both planes, and that clone is what made
   the handoff read as two containers: a second copy of the screen standing in
   front of the first, free to disagree with it by a font, a scrollbar or half a
   pixel. stage.ts moves the real element in here now, at the width it already
   had, and it leaves the way it came: carried by the plane it is standing on,
   fading over a quarter of a second, then taken off the page.

   CENTRED ON THE PANEL AND PUSHED BACK BY --ask-lift, which is the same offset
   the lockup gets. That is what makes the two of them travel together and at
   half the speed of the seam, so what leaves is the door's contents leaving
   with the door rather than being slid off the side of it.

   BEHIND THE FRAME AND NOT OVER IT, which is the panel's first child plus one
   `position: relative` on the frame (above). The real lockup standing in the
   frame then simply paints on top of the hole its own row left. The clone had
   to go over everything, and then have a hole punched in IT to let the same
   lockup through. */
.cb-ask-door {
  position: absolute;
  inset-inline: 0;
  margin-inline: auto;
  pointer-events: none;
  animation: cbc-ask-adieu var(--cb-motion-base) var(--cb-motion-ease) forwards;
  transition: transform calc(var(--cb-motion-slow) + var(--cb-motion-base)) var(--cbc-carry)
    var(--cb-motion-fast);
}
.cb-ask[data-mode="morph"]:not([data-open]) .cb-ask-door {
  transform: translate3d(var(--ask-lift), 0, 0);
}
/* AND NOTHING INSIDE IT ARRIVES AGAIN. Every part of the sign-in door carries
   the wall's own staggered entrance (console-signin.css's six seats), and an
   element removed from the document and put back somewhere else starts its
   animations over: the words this is holding steady would fade IN, one after
   another, over a screen that was already showing them. Six overrides would
   each be a copy of somebody else's decision; one line saying "this door has
   already arrived" is the actual rule. */
.cb-ask-door * {
  animation: none;
}
/* It fills the layer it was moved into, and it brings no ground with it.

   THE HEIGHT: on the sign-in screen the door is a grid item stretched to the
   full height of the page; lifted out of that grid it is a plain block that
   shrinks to its own content, and its three bands collapse to the top of the
   plane with the small print halfway up it.

   THE GROUND: the door paints --cbc-surface and so does the plane it is now
   standing on, so taking its ground away changes not one pixel and turns it
   into words on the panel rather than words on a lid. */
.cb-ask-door > * {
  block-size: 100%;
  background: none;
}
/* CONSOLE-FORMS.CSS CATCHES THE DOOR'S PARAGRAPHS THE MOMENT THEY LAND IN HERE,
   and it is the one thing about moving a real element into a dialog that a
   clone did not escape either. `.cb-dialog p` sets a size and a colour, and a
   descendant selector beats a bare class whatever the load order says, so the
   product line arrived at the dialog's small size and the small print at its
   large one. The colophon then wrapped to a second line, the foot grew by a
   row, and everything centred in the row above it moved up half of that: a
   quarter of the door out of place on the one frame that is supposed to be
   indistinguishable from the screen before.

   Most of what that rule steals is what the door had anyway. These are the two
   declarations that are not, and they are console-signin.css's own values for
   the same two properties rather than a second opinion about them. */
.cb-ask-door .cb-signin-line {
  font-size: var(--cb-type-lg-size);
  color: var(--cbc-ink-muted);
}
.cb-ask-door .cb-signin-colophon {
  font-size: var(--cb-type-xs-size);
}
/* THE HOLE THE LOCKUP LEAVES, HELD OPEN. stage.ts lifts the real lockup out of
   the door's top row and into the panel's own mark, because it is the one thing
   on this screen that survives the move. Its row is a two-item grid, so with
   the lockup gone the product line under it would ride up by the height of a
   lockup and the gap above it, on the very frame that is supposed to be
   indistinguishable from the screen before. Which is exactly what the padding
   below adds back, in the two tokens the row was built out of. */
.cb-ask-door .cb-signin-mark {
  padding-block-start: calc(var(--cb-space-6xl) + var(--cb-space-lg));
}

/* ---- Where you are ----
   Written "1 of 4" in cards.ts and shouted here, because ALL-CAPS is a thing
   this console draws and never a thing it writes down (docs/09).

   Scoped under .cb-ask, and every paragraph rule below it is, because
   console-forms.css declares `.cb-dialog p` and a descendant selector beats a
   bare class whatever the load order says. */
.cb-ask .cb-ask-eyebrow {
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  font-weight: var(--cb-font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-accent-text);
  margin-block-end: var(--cb-space-xl);
}

/* ---- The question ----
   The largest thing on the screen, and by a distance. Everything else here is
   in service of it: one line of standfirst, a short list of answers, and two
   quiet controls at the foot.

   text-wrap: balance, because a forty-eight pixel heading that drops one word
   onto a second line reads as a mistake rather than as a wrap. */
.cb-ask-title {
  font-size: var(--cb-type-4xl-size);
  line-height: var(--cb-type-4xl-line-height);
  letter-spacing: var(--cb-type-4xl-letter-spacing);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-ink);
  text-wrap: balance;
  margin-block-end: var(--cb-space-xl);
}
/* ONE LINE, WHEREVER IT CAN BE ONE LINE, which is why this runs the full
   measure while the result card's list below is held to 640. Capped at 640 it
   broke after "bothers", and a standfirst that wraps to carry two words onto a
   second line reads as an overflow rather than as a sentence. A list of
   findings is read down and wants the shorter line; a single line under a
   heading wants to stay a single line. */
.cb-ask .cb-ask-lede {
  font-size: var(--cb-type-lg-size);
  line-height: var(--cb-type-lg-line-height);
  color: var(--cbc-ink-muted);
  text-wrap: pretty;
  margin-block-end: var(--cb-space-6xl);
}
/* ---- The note about the run: how long, where you are, what you are changing ----

   A CHIP AND NOT A THIRD PARAGRAPH, and that is the whole of the difference.
   The words were right and the drawing was not: grey type at a smaller size
   directly under the standfirst reads as a sentence somebody could not fit into
   the paragraph above, which is exactly what an afterthought looks like. What
   it actually is is metadata about the run rather than about the question, so
   it is drawn the way this console draws metadata: its own ground, its own
   round, a mark in front of it, sized to the words rather than to the measure.

   INLINE-FLEX, so the chip is as wide as what it says. Stretched to the measure
   it would be a banner, and a banner is a thing that has gone wrong.

   Its own space above and below rather than a negative margin pulling it up
   under the standfirst. The lede already carries the gap before the answers;
   this sits inside that gap as its own object, which is what stops it reading
   as a fourth line of the paragraph. */
.cb-ask-about {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-sm);
  padding: var(--cb-space-xs) var(--cb-space-lg) var(--cb-space-xs) var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-sunken);
  color: var(--cbc-ink-muted);
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  font-weight: var(--cb-font-weight-medium);
  margin-block-start: calc(var(--cb-space-5xl) * -1);
  margin-block-end: var(--cb-space-6xl);
}
/* The mark carries the accent and the words do not. One coloured thing on a
   chip is a chip with a mark on it; two is a badge, and a badge is louder than
   a note about how long something takes. */
.cb-ask-about-mark {
  display: inline-flex;
  flex: none;
  color: var(--cbc-accent);
}
.cb-ask-about-mark .cb-icon {
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
}

/* The result card's list. Ink rather than the muted standfirst colour: these
   are the findings, not the line above them. */
.cb-ask .cb-ask-line {
  font-size: var(--cb-type-md-size);
  line-height: var(--cb-type-md-line-height);
  color: var(--cbc-ink);
  max-inline-size: var(--cb-width-lg);
  margin-block-end: var(--cb-space-lg);
}
/* The gap above the answers is the same however the card got there: after a
   standfirst, after a list of findings, or after nothing but the question. */
.cb-ask-line + .cb-ask-opts,
.cb-ask-done + .cb-ask-opts,
.cb-ask-title + .cb-ask-opts {
  margin-block-start: var(--cb-space-6xl);
}

/* ---- What the run actually did ----

   The last card of the five is the one moment where the console has done
   something rather than asked something. As three sentences of grey type it
   read exactly like the three cards before it, so each line is now a thing that
   happened and is marked as done.

   THE MARK IS MADE, THEN THE SENTENCE FOLLOWS IT. That order is the whole
   effect: both arriving together reads as a list appearing, and one after the
   other reads as work completing. Four rows, each one a small sequence of its
   own, and the four of them a beat apart.

   THE TICK USED TO DRAW ITSELF BACKWARDS, and that is worth writing down
   because the mistake is invisible until somebody names it. lucide's check is
   `M20 6 9 17l-5-5`: the path STARTS at the top right and ends at the bottom
   left. `stroke-dashoffset: 30` to `0` reveals a path from its start, so the
   mark was being drawn from the top of the long arm downwards and finishing at
   the short one. Nobody has ever drawn a check that way. A negative offset
   reveals from the END instead, which walks the same path in reverse: up the
   short arm from the bottom left, then out along the long one. Same icon, same
   two lines of CSS, opposite gesture, and it is the difference between a mark
   being made and a shape being wiped onto the screen.

   THE BEAT IS THE CARD'S OWN, not --cbc-step. That one is min(i * 50ms, 250ms),
   which is right for a board of twelve panels and far too tight here: four rows
   fifty milliseconds apart is four rows arriving at once. This is the finale of
   a run somebody has just spent a minute on, there are never more than five of
   them, and a hundred milliseconds is the difference between a stagger you
   register and one you only measure.

   IT STARTS AFTER THE CARD HAS. The list holds until the heading above it has
   landed, for the reason console-theme.css gives at --cbc-inside: a sequence
   that runs underneath something still arriving is work being done where
   nobody can watch it. The container itself does not animate at all now, for
   the other half of the same rule (rule 14): content settles into a container
   that has already arrived, and a box fading in over its own rows was the
   thing hiding them.

   Everything is over inside about a second, which is comfortably ahead of
   somebody reading four lines. */
.cb-ask {
  /* One beat between rows, and where the first one starts. Both off the motion
     scale rather than typed, so the whole sequence moves together if the scale
     ever does. */
  --tick-beat: calc(var(--cb-motion-fast) * 2 / 3);
  --tick-from: calc(var(--cb-motion-fast) * 2);
}
.cb-ask-done {
  display: grid;
  gap: var(--cb-space-xl);
  margin: 0 0 var(--cb-space-lg);
  padding: 0;
  list-style: none;
  max-inline-size: var(--cb-width-lg);
  /* The container is simply there. Its rows are the entrance: see the header. */
  animation: none;
}
/* Each row's turn, computed once and read by all three of its parts, so the
   disc, the mark and the words cannot drift apart. --i is page.ts's. */
.cb-ask-did {
  --tick-at: calc(var(--tick-from) + var(--i, 0) * var(--tick-beat));
  /* THE SIZE LIVES ON THE ROW, not on the words inside it, because the words
     are two different elements: a plain span on a line that only reports
     something, and a button on a line that is also the way back to the answer
     it reports. A button has to shake off the user agent's own font, `font:
     inherit` is how, and that shorthand resets font-size and line-height too.
     Declared on the words, the size was therefore won by whichever rule came
     later and the list rendered at two sizes: three sentences at fourteen and
     one at eighteen, for no reason a reader could see. Declared here, both
     kinds of line inherit the same one and neither can override it by accident. */
  font-size: var(--cb-type-md-size);
  line-height: var(--cb-type-md-line-height);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  /* Centred on the FIRST LINE, not on the block. A mark centred against a
     sentence that wraps to two lines floats in the middle of the paragraph
     instead of sitting against the words it belongs to. */
  align-items: start;
  gap: var(--cb-space-lg);
}
.cb-ask-did-said {
  align-self: center;
}
.cb-ask-did:has(.cb-ask-did-said) {
  align-items: center;
}
/* The words, arriving behind their own mark. cbc-emerge and not cbc-rise:
   twenty pixels is what a whole panel travels, and this is a line inside one. */
.cb-ask-did-said {
  color: var(--cbc-ink);
  animation: cbc-emerge var(--cb-motion-base) var(--cbc-spring) backwards;
  animation-delay: calc(var(--tick-at) + var(--cb-motion-fast) * 1.6);
}
/* ---- And most of those lines are the way back to the answer ----

   QUIET UNTIL A POINTER COMES NEAR IT, which is rule 14's affordance arriving
   with the hand rather than sitting on the page. At rest it is the sentence,
   indistinguishable from the two lines that are not answers, because the card
   is a summary first and a control second: four underlined links in a list of
   five would turn the finale into a form.

   The underline is on the text and not a border, so it hugs the words and wraps
   with them, and the ground stays out of it: a filled row here would look like
   an option, and the options are the two buttons below. */
.cb-ask-did-go {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  /* Shakes off the user agent's own button font, all of it. It is safe to take
     the size with it because the size is on the row above (`.cb-ask-did`), so
     what this inherits is what the plain lines are already using. It was not
     safe while the size was declared on this same element, and that was the
     bug: the shorthand quietly won and the pressable lines came out four pixels
     smaller than the ones that are not. */
  font: inherit;
  color: inherit;
  text-align: start;
  cursor: pointer;
  text-decoration: underline solid transparent;
  text-underline-offset: var(--cb-space-xs);
  transition: text-decoration-color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-ask-did-go:hover {
  text-decoration-color: var(--cbc-line-strong);
}
.cb-ask-did-go:focus-visible {
  outline: var(--cbc-focus);
  outline-offset: var(--cbc-focus-gap);
  border-radius: var(--cb-radius-sm);
}
/* The mark itself: a disc that lands, a ring off it, and the tick drawn inside.

   SOLID, NOT A WASH. It was a pale ring with a small mark in it, which at the
   size of a line of type reads as a bullet somebody has styled rather than as
   something being ticked off. Filled, at the same size as the type it sits
   beside, it is the one confident thing on a card whose whole job is to say
   the work is done.

   It is a check and not a rail, which is the distinction docs/10 draws: a
   coloured bar down a left edge is decoration pretending to be structure, and
   these are four marks that each mean the sentence beside them happened. */
.cb-ask-tick {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent);
  color: var(--cb-color-white);
  animation: cbc-tick-pop calc(var(--cb-motion-base) * 1.4) var(--cbc-spring) backwards;
  animation-delay: var(--tick-at);
}
/* The ring, which is the only part of this that is purely for the pleasure of
   it. One expanding outline per row, gone in under half a second, and it is
   what turns four marks appearing into four marks being STAMPED.

   Resting invisible, so the animation ends where the element already is
   (console-theme.css on backwards vs both). Two pixels because a ring off a
   disc is a hairline by any other name, which is the guard's own exemption. */
.cb-ask-tick::after {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid var(--cbc-accent);
  border-radius: var(--cb-radius-full);
  opacity: 0;
  animation: cbc-tick-ping calc(var(--cb-motion-slow) * 1.2) var(--cb-motion-ease) backwards;
  animation-delay: calc(var(--tick-at) + var(--cb-motion-fast) / 3);
}
.cb-ask-tick .cb-icon {
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
  /* Thicker than the console's usual stroke. A hairline tick inside a filled
     disc disappears at this size, and the mark is the point of the row. */
  stroke-width: 3;
}
/* THE STROKE IS DRAWN, not faded in, and it is drawn the way a hand draws it.
   The header explains the negative offset; thirty is comfortably longer than
   the path, so the dash covers it whatever the icon's exact length is, and the
   line is cut square while it travels so the leading end does not bulge.

   `forwards` here and nowhere else in this block: this is the one animation
   whose end state is not the element's resting one, because the resting one is
   an undrawn line. */
.cb-ask-tick svg {
  stroke-dasharray: 30;
  stroke-dashoffset: -30;
  stroke-linecap: square;
  animation: cbc-tick-draw calc(var(--cb-motion-fast) * 1.6) var(--cb-motion-ease) forwards;
  animation-delay: calc(var(--tick-at) + var(--cb-motion-fast));
}
/* AND WHAT COMES NEXT WAITS FOR THE LAST MARK. The two buttons and the quiet
   line under them are on the page's own stagger, which is twenty-five
   milliseconds a child: they used to be sitting there finished while the list
   above them was still on its second row, which reads as a card that has
   already moved on from the thing it is showing you. --n is the number of rows,
   written by page.ts, because CSS cannot count its own children into a delay. */
.cb-ask-page:has(.cb-ask-done) > .cb-ask-opts,
.cb-ask-page:has(.cb-ask-done) > .cb-ask-foot {
  animation-delay: calc(var(--tick-from) + var(--n, 0) * var(--tick-beat) + var(--cb-motion-base));
}
/* Lands with a touch of overshoot, which is the whole of the pop: a disc that
   scales straight to size is a shape resizing, and one that goes a hair past
   and comes back is a thing being put down. */
@keyframes cbc-tick-pop {
  from {
    opacity: 0;
    transform: scale(0.2);
  }
  60% {
    opacity: 1;
    transform: scale(1.12);
  }
  to {
    transform: scale(1);
  }
}
@keyframes cbc-tick-ping {
  from {
    opacity: 0.5;
    transform: scale(1);
  }
  to {
    opacity: 0;
    transform: scale(2);
  }
}
@keyframes cbc-tick-draw {
  to {
    stroke-dashoffset: 0;
  }
}
.cb-ask .cb-ask-foot {
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-faint);
  margin-block-start: var(--cb-space-3xl);
}

/* ---- The answers ---- */
.cb-ask-opts {
  display: grid;
  gap: var(--cb-space-lg);
}

/* One answer, and a large one: this is the only thing on the screen anybody is
   meant to press, so it is the size of the decision rather than the size of its
   longest word.

   NOTHING HERE MOVES THE ROW. Ground and border only. A hover that shifts
   layout under a list somebody is reading down is the one hover this surface
   must not have, and a row that lifts on approach makes the four below it look
   like they sank. */
.cb-ask-opt {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--cb-space-xl);
  align-items: center;
  inline-size: 100%;
  padding: var(--cb-space-2xl) var(--cb-space-3xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-3xl);
  /* Transparent, not raised. In the light theme --cbc-raised and --cbc-surface
     are both white, so a "lifted" row on this ground would be a row with no
     ground at all in exactly one theme. A hairline on the page and a wash on
     hover say the same thing in both. */
  background: none;
  color: var(--cbc-ink);
  text-align: start;
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-ask-opt:hover {
  background: var(--cbc-hover);
  border-color: var(--cbc-line-strong);
}
.cb-ask-opt:focus-visible {
  outline: var(--cbc-focus);
  outline-offset: var(--cbc-focus-gap);
}
/* Chosen, and held for one --cb-motion-fast before the card leaves so the press
   is acknowledged before the screen changes. Unmistakable on purpose: the whole
   row fills, the border goes to the accent itself, and the number slot turns
   into a check.

   [data-on] IS THE SAME PICTURE FOR THE SAME FACT. On the one card taking
   several answers a row stays marked instead of leaving, and a second visual
   language for "this one is chosen" on one card out of five would be the
   surface teaching two things where it had one. The only difference is how long
   it lasts, which is not a thing a colour can say. */
.cb-ask-opt[data-taken],
.cb-ask-opt[data-on] {
  background: var(--cbc-accent-wash);
  border-color: var(--cbc-accent);
}
.cb-ask-opt[aria-disabled="true"] {
  color: var(--cbc-ink-faint);
  cursor: default;
}
.cb-ask-opt[aria-disabled="true"]:hover {
  background: none;
  border-color: var(--cbc-line);
}

/* ---- The number ----
   NOT DECORATION: it is the key that chooses this row, and it is the only
   reason the numbers are on screen. It answers when the pointer approaches the
   row rather than when it reaches the chip, which is the affordance arriving
   with the hand. The check underneath is what the chosen row swaps to. */
.cb-ask-num {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  inline-size: var(--cb-space-5xl);
  block-size: var(--cb-space-5xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-lg);
  background: var(--cbc-sunken);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-medium);
  font-variant-numeric: tabular-nums;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-ask-opt:hover .cb-ask-num {
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink);
}
.cb-ask-num .cb-icon {
  position: absolute;
  opacity: 0;
  transition: opacity var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-ask-opt[data-taken] .cb-ask-num,
.cb-ask-opt[data-on] .cb-ask-num {
  background: var(--cbc-accent);
  border-color: var(--cbc-accent);
  color: var(--cbc-on-accent);
}
.cb-ask-opt[data-taken] .cb-ask-num .cb-icon,
.cb-ask-opt[data-on] .cb-ask-num .cb-icon {
  opacity: 1;
}
.cb-ask-opt[data-taken] .cb-ask-num > span:last-child,
.cb-ask-opt[data-on] .cb-ask-num > span:last-child {
  opacity: 0;
}

/* ---- The way off a card that takes several answers ----
   The only primary button on this surface, and the only card that has one. Four
   of the five are answered by pressing a row, where a Next button would be a
   second decision nobody is making; on the fifth the rows are marks, so there
   has to be something that means "that is all of them".

   RIGHT ALIGNED, UNDER THE ROWS. The list is read down its left edge and the
   button is the end of it, so it sits at the far end of the last line rather
   than under the first word of every row, where it would read as a fifth
   option. It is full width where the rows stop being a list and start being a
   column of taps, which is the same breakpoint everything else here uses. */
.cb-ask-next {
  /* Laid out rather than aligned, because the card is a plain block and there
     is nothing here to align against. A flex box the width of its own words,
     pushed to the end by its own margin. */
  display: flex;
  inline-size: fit-content;
  margin-inline-start: auto;
  margin-block-start: var(--cb-space-3xl);
}

.cb-ask-words {
  display: grid;
  gap: var(--cb-space-xs);
}
.cb-ask-label {
  font-size: var(--cb-type-md-size);
  line-height: var(--cb-type-md-line-height);
  font-weight: var(--cb-font-weight-medium);
}
.cb-ask-note {
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-faint);
}

/* ---- The foot ----
   The way back at one end, the way out at the other, and what the keyboard does
   between them. All three are quiet: none of them is the thing to do next, and
   the thing to do next is the list above them.

   THREE SEATS IN ONE ROW rather than a control in each corner of the plane. The
   two things anybody can press down here are the same kind of thing, they cost
   the same, and a reader deciding between "back" and "not now" should not have
   to look at opposite ends of the screen to find the second one. The legend
   sits between them because it is a caption on the list above, not a control.

   `1fr auto 1fr` and not space-between: the legend stays on the plane's centre
   line whether Back is turned off or not, which space-between cannot promise. */
.cb-ask-base {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--cb-space-3xl);
}
/* Placed rather than left to auto flow. The legend takes itself off the page on
   a card that has no keys (`:empty` below), and two items in a three-column
   grid flow into the first two columns: the way out would drift to the middle
   of the row on exactly the cards where it means something different. */
.cb-ask-back {
  grid-column: 1;
  justify-self: start;
}
.cb-ask-quit {
  grid-column: 3;
  justify-self: end;
}
/* ---- The middle seat ----
   The legend and the theme, together, holding the centre line between them.
   ONE SEAT AND NOT TWO: the row is `1fr auto 1fr` precisely so the middle stays
   on the plane's centre whether Back is there or not, and a fourth column would
   be the end of that. So the pair is centred rather than each of them.

   The legend still takes itself off a card with no keys (`:empty` below) and
   the theme stays, which is what it means for one of these to be a caption and
   the other to be furniture. */
.cb-ask-mid {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: var(--cb-space-2xl);
}
.cb-ask-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--cb-space-2xl);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
}
.cb-ask-legend:empty {
  display: none;
}
.cb-ask-legend > span {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-sm);
}

/* ---- The cards that are not questions ----
   The result, the read-back and the one about a board somebody has already
   arranged all have two buttons and both of them are answers. A third way out
   on those would mean "Not now" meaning something different there than it means
   on the four questions.

   HIDDEN, NOT REMOVED, and that is the difference from taking them out of the
   markup: the foot keeps its height, so the question does not jump down the
   screen on the one card that has no Back. It is the same reasoning rule 15
   uses for a button in a row of buttons, applied to a whole band. */
.cb-ask[data-foot="off"] .cb-ask-base {
  visibility: hidden;
}

/* ---- And the account line, on the cards it is not for ----

   HIDDEN, FOR THE RULE DIRECTLY ABOVE. It sits in the frame's own flow between
   the choices and the foot, so `display: none` would take a whole row out of a
   three-row grid on every card but the first: the middle row would grow by the
   line and the gap above it, and the question, which is centred in that row,
   would drop half of it the moment somebody answered the first one. A surface
   whose entire idea is one question at a time cannot move the question between
   two of them.

   So the band keeps its height on every card and only its contents go, which is
   exactly what the foot does two rules up and for exactly the same reason. What
   is left on cards two, three and four is air above the foot, in the same place
   every time.

   It is on the first card only, and dialog.ts says what "first" means: the
   bottom of the stack, not question one. Somebody who has answered two
   questions has already decided whose account this is. */
.cb-ask:not([data-who="on"]) .cb-ask-who {
  visibility: hidden;
}

/* ---- How far through ----
   A hairline along the bottom edge of the plane the questions are on. It agrees
   with the eyebrow to the digit (screen.ts says why), and it fades out entirely
   on a card that is not a step in anything rather than sitting there full and
   claiming to be finished.

   ALONG THE PANEL AND NOT THE VIEWPORT, which the picture decided. A quarter of
   the way through four questions puts the whole filled part of a full-width
   line over the photograph, where scarlet on a graded image is either invisible
   or a scratch. On the panel it is still the bottom edge of the screen, and it
   travels with the plane it belongs to. Below the breakpoint there is no
   picture and it is the full width again, on its own.

   Quiet is the whole brief: two pixels at the edge, findable when looked for
   and invisible when not. */
.cb-ask-rail {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  block-size: var(--cb-space-xxs);
  background: var(--cbc-line);
  transition: opacity var(--cb-motion-base) var(--cb-motion-ease);
}
.cb-ask-rail[data-track="off"] {
  opacity: 0;
}
.cb-ask-fill {
  display: block;
  block-size: 100%;
  background: var(--cbc-accent);
  transform: scaleX(var(--at, 0));
  transform-origin: 0 50%;
  transition: transform var(--cb-motion-slow) var(--cbc-spring);
}
/* Where a run OPENS is not a move, so it is not animated. A resumed run used to
   sweep the line from nothing to half way, drawing two steps somebody took
   yesterday as though they were being taken now. screen.ts sets this for one
   frame around the first position it is ever told, and every move after it
   animates as before. */
.cb-ask-rail[data-jump] .cb-ask-fill {
  transition: none;
}

/* ---- Leaving ----

   IT CLOSES FIRST AND FADES SECOND, and dialog.ts carries the argument. In
   short: every animation under a modal is paused while one is open, so the
   dashboard behind this has been sitting at frame zero of its own entrance the
   whole time and the board the last card rearranged is sitting at frame zero of
   its. Closing releases the top layer and starts both; this then fades off the
   top of a page that is arriving underneath it, which is the payoff being shown
   rather than announced.

   A closed <dialog> is display:none in every UA stylesheet, and an author rule
   beats one without needing !important. Out of the top layer it needs a layer
   of its own, and --cbc-z-gate is the right one for the same reason the
   security gate uses it: a full-bleed surface that owns the screen for as long
   as it is on it. Inert and pointer-events:none because for these four hundred
   milliseconds the only thing it still does is be opaque.

   Last of the entrance rules on purpose. It carries the same specificity as the
   morph's `animation: none` above and has to win on source order. */
dialog.cb-ask[data-going] {
  display: block;
  position: fixed;
  z-index: var(--cbc-z-gate);
  pointer-events: none;
  animation: cbc-ask-adieu var(--cb-motion-slow) var(--cb-motion-ease) forwards;
}

/* ---- Smaller windows, and shorter ones ----

   TWO AXES, BECAUSE THEY FAIL DIFFERENTLY, and the second one matters more than
   it looks. Narrow, the question runs out of line length and the type has to
   come down the scale. Short, there is nothing wrong with the type and
   everything wrong with the air around it, so the padding, the gaps and the row
   height give way first and the question keeps as much of its size as the
   window allows.

   AND SHORT IS THE ORDINARY CASE. A laptop advertised as 1440 by 900 hands a
   browser about eight hundred pixels once its own chrome is out of the way, and
   the tallest card here is a question with five answers on it. So the roomy
   version above is for a real monitor and the first height step below is what
   most people will actually see: not a degraded layout, just a less generous
   one. The alternative was a screen that scrolls on the machine everybody has,
   which for a surface whose whole idea is one question at a time would be the
   whole idea gone. */
/* 1024px mirrors tokens.breakpoint.lg */
@media (max-width: 1024px) {
  /* THE PICTURE GOES, exactly as it does on the sign-in screen at this width
     and for the same reason: the door there has a 480 floor, so below this the
     split stops being a split and the wall is hidden rather than squeezed. This
     surface has to agree with it to the pixel, because at this width frame zero
     is a full-width door and there is no wall for a plane to grow out of.

     SO THERE IS NO SEAM AND NOTHING SLIDES. The questions' plane is the whole
     screen in both states, which is what the hairline and the shut are taken
     off for: a border with no picture beside it is a line down the leading edge
     of a phone, and a shut measured against a door that is a centred column
     rather than a plane would push the whole screen sideways by its margin.
     stage.ts measures the seam off the wall and reads its absence as a zero, so
     --ask-lift and --ask-rise come out at nothing here on their own; only the
     two things this rule cannot leave to arithmetic are written down. */
  .cb-ask-wall {
    display: none;
  }
  .cb-ask-panel {
    inset-inline-start: 0;
    border-inline-start: 0;
  }
  .cb-ask-mast .cb-brand-art {
    block-size: var(--cb-space-4xl);
  }
  .cb-ask-mast .cb-brand-logo {
    max-block-size: var(--cb-space-4xl);
  }
  .cb-ask-mast .cb-brand-word {
    font-size: var(--cb-type-md-size);
  }
  .cb-ask-frame {
    padding-inline: var(--cb-space-5xl);
  }
  /* The lockup steps down with the door's own, so the hole it leaves in the
     door's top row is a smaller one (the rule this mirrors is above). */
  .cb-ask-door .cb-signin-mark {
    padding-block-start: calc(var(--cb-space-4xl) + var(--cb-space-lg));
  }
  .cb-ask-title {
    font-size: var(--cb-type-3xl-size);
    line-height: var(--cb-type-3xl-line-height);
    letter-spacing: var(--cb-type-3xl-letter-spacing);
  }
}
/* 768px mirrors tokens.breakpoint.md */
@media (max-width: 768px) {
  .cb-ask-frame {
    padding-inline: var(--cb-space-3xl);
  }
  /* The legend takes its own row above the two controls. Side by side with them
     at this width it was three things sharing about three hundred pixels, and
     the one that wrapped was the one teaching the keys. */
  .cb-ask-base {
    grid-template-columns: 1fr 1fr;
    row-gap: var(--cb-space-xl);
  }
  .cb-ask-mid {
    grid-row: 1;
    grid-column: 1 / -1;
  }
  .cb-ask-back {
    grid-row: 2;
    grid-column: 1;
  }
  .cb-ask-quit {
    grid-row: 2;
    grid-column: 2;
  }
  .cb-ask-title {
    font-size: var(--cb-type-2xl-size);
    line-height: var(--cb-type-2xl-line-height);
    letter-spacing: var(--cb-type-2xl-letter-spacing);
  }
  .cb-ask .cb-ask-lede {
    font-size: var(--cb-type-base-size);
    line-height: var(--cb-type-base-line-height);
  }
}
/* THE QUESTION IS THE LAST THING TO GIVE WAY. Everything around it comes in
   first: the air at the top and bottom, the standfirst, the room inside a row
   and the size of its label. The heading holds its 48 down to a window that
   genuinely cannot take it, because a screen whose whole idea is one question at
   a time and a question that has quietly shrunk to the size of a panel title is
   a screen that has given away the idea to save forty pixels. */
/* 1024px mirrors tokens.breakpoint.lg, on the other axis */
@media (max-height: 1024px) {
  .cb-ask-frame {
    gap: var(--cb-space-3xl);
    padding-block: var(--cb-space-4xl) var(--cb-space-5xl);
  }
  .cb-ask .cb-ask-eyebrow {
    margin-block-end: var(--cb-space-lg);
  }
  .cb-ask-title {
    margin-block-end: var(--cb-space-lg);
  }
  /* THE MARK DOES NOT COME DOWN HERE. This is the height of an ordinary laptop
     and the cards that carry a mark have one heading, one sentence and one
     thing to press under it: 128 pixels of drawing is not what runs a screen
     like that out of room. The air closes up and the mark holds its size until
     the window is genuinely short. */
  .cb-page-mark {
    margin-block-end: var(--cb-space-lg);
  }
  .cb-ask .cb-ask-lede {
    font-size: var(--cb-type-md-size);
    line-height: var(--cb-type-md-line-height);
    margin-block-end: var(--cb-space-4xl);
  }
  .cb-ask-line + .cb-ask-opts,
  .cb-ask-title + .cb-ask-opts {
    margin-block-start: var(--cb-space-4xl);
  }
  .cb-ask-opts {
    gap: var(--cb-space-md);
  }
  .cb-ask-opt {
    padding: var(--cb-space-lg) var(--cb-space-3xl);
  }
  .cb-ask-label {
    font-size: var(--cb-type-base-size);
    line-height: var(--cb-type-base-line-height);
  }
}
/* 768px mirrors tokens.breakpoint.md, on the other axis */
@media (max-height: 768px) {
  .cb-ask-frame {
    gap: var(--cb-space-xl);
    padding-block: var(--cb-space-2xl) var(--cb-space-3xl);
  }
  /* AND NOW THE CARD'S OWN MARK COMES DOWN, which is a different element from
     the lockup the note below is about. It is the largest thing on the card and
     the only thing on it that is decoration, so it is what gives way first;
     ninety-six still reads as a mark rather than an icon. */
  .cb-page-mark {
    --mark-size: var(--cb-space-9xl);
  }
  /* The address line closes up under the mark rather than coming off the
     screen: it is twelve-pixel type, it costs a row of eighteen pixels, and it
     is the one thing on this plane somebody in the wrong account is looking
     for. Only the air around it gives way. */
  .cb-ask-top {
    gap: var(--cb-space-md);
  }
  /* THE MARK DOES NOT STEP DOWN ON THIS AXIS, and it used to. It is the login
     screen's lockup at the login screen's size, which is the whole of what
     makes the handoff read as one screen rather than two, and the sign-in door
     has no height steps of its own: shrinking here would be the one element
     that survived the move changing size the moment it arrived. It costs
     twenty-four pixels on a seven-hundred-pixel window, and the tallest card
     there still clears the foot by a couple of hundred. */
  .cb-ask-title {
    font-size: var(--cb-type-3xl-size);
    line-height: var(--cb-type-3xl-line-height);
    letter-spacing: var(--cb-type-3xl-letter-spacing);
  }
  .cb-ask .cb-ask-eyebrow {
    margin-block-end: var(--cb-space-md);
  }
  .cb-ask .cb-ask-lede {
    margin-block-end: var(--cb-space-3xl);
  }
  .cb-ask-line + .cb-ask-opts,
  .cb-ask-title + .cb-ask-opts {
    margin-block-start: var(--cb-space-3xl);
  }
  .cb-ask-opt {
    padding: var(--cb-space-md) var(--cb-space-2xl);
  }
  /* The full width, where the rows have stopped being a list and become a
     column of taps: a button the width of its own words at the end of that
     column is a small target beside four large ones. */
  .cb-ask-next {
    inline-size: 100%;
  }
  .cb-ask-num {
    inline-size: var(--cb-space-4xl);
    block-size: var(--cb-space-4xl);
  }
}
/* 560px mirrors tokens.width.md, on the other axis, the way the width query
   further up this ladder mirrors width.lg. A laptop with every toolbar it has
   open, or a phone turned sideways.

   THE LADDER STOPPED AT 768 AND THE SCREEN STOPPED FITTING. Measured on the
   second step's own offer card: comfortable at 560, thirty-four pixels over at
   520 and seventy-four over at 480, which is a scroll on a screen that is one
   thing to decide. Everything here is air except the last rule, and the heading
   STILL does not move: it stepped once at 768 and that is where it stays.

   The keyboard legend goes, and it is the one thing here that is removed rather
   than tightened. It is a hint about a keyboard, at a height that is a phone
   held sideways or a laptop with no room left; the two controls it describes are
   both on screen and both pressable, so what is lost is a caption for something
   nobody at this size is doing. */
@media (max-height: 560px) {
  .cb-ask-frame {
    gap: var(--cb-space-md);
    padding-block: var(--cb-space-lg) var(--cb-space-xl);
  }
  .cb-ask-top {
    gap: var(--cb-space-xs);
  }
  .cb-page-mark {
    --mark-size: var(--cb-space-7xl);
    margin-block-end: var(--cb-space-md);
  }
  .cb-ask .cb-ask-eyebrow {
    margin-block-end: var(--cb-space-xs);
  }
  .cb-ask-title {
    margin-block-end: var(--cb-space-md);
  }
  .cb-ask .cb-ask-lede {
    margin-block-end: var(--cb-space-xl);
  }
  .cb-ask-line + .cb-ask-opts,
  .cb-ask-title + .cb-ask-opts {
    margin-block-start: var(--cb-space-xl);
  }
  .cb-ask-opts {
    gap: var(--cb-space-sm);
  }
  .cb-ask-opt {
    padding: var(--cb-space-sm) var(--cb-space-xl);
  }
  /* The two lines under the card close up against it. Both are footnotes: the
     small print the card itself carries, and the address line that says which
     account this is. Neither moves, and there is simply less air over them. */
  .cb-ask .cb-ask-foot {
    margin-block-start: var(--cb-space-lg);
  }
  .cb-ask-who {
    margin-block-start: var(--cb-space-md);
  }
  .cb-ask-legend {
    display: none;
  }
}
/* 480px mirrors tokens.breakpoint.sm, on the height axis, and it is the last
   rung there is. The mark takes its final step, to the size the lockup at the
   top of the plane is drawn at, which is as small as a mark can go and still be
   one. Nothing pressable shrinks here: a 480-pixel window is a phone held
   sideways as often as it is a laptop with too many toolbars, and a control
   shaved under the touch target is a worse outcome than a card that scrolls. */
@media (max-height: 480px) {
  .cb-page-mark {
    --mark-size: var(--cb-space-6xl);
  }
}
