/* ---- Sign in ----
   The console's front door: two planes of one page, no card.

   It was a bordered box in the middle of the ground, which is the shape of a
   dialog, and a dialog is a thing that opened on top of something. There is
   nothing behind this. Two full-height planes with a hairline between them are
   the page itself, and they say the same thing a card was there to say (this
   is one small thing to do) without pretending to float above a console
   nobody has reached yet.

   The left plane is static markup in index.html, so it paints with the
   stylesheets rather than after two megabytes of JavaScript. Everything on the
   right is written by src/signin/. See signin/frame.ts for why the copy of the
   left plane is taken at module load.

   NO GRADIENT, NO ATMOSPHERE, NO RAIL. The plane is two occupied corners and
   an empty middle, which cannot be mistaken for a failed alignment. docs/10
   names gradients specifically, and a scarlet hairline down the seam would be
   the accent rail it also names, wearing a different hat.

   Every colour here is a --cbc-* semantic, with ONE named exception at the
   bottom of the file: the Google button, which is a vendor's control appearing
   inside ours and is specified in fixed values by the people who own it. That
   is the only pair of theme-conditional rules here, and it is marked as such
   where it sits. Everything else still flips on its own. */

/* ---- The one number that opens the room ----

   THE WALL'S SHARE, AND THE ONLY THING THAT MOVES. Registered rather than a
   plain custom property because an unregistered one cannot be transitioned at
   all: the browser has no idea a string like "20%" is a length, so it cuts. A
   registered `<length-percentage>` interpolates, including from the clamp
   below, so the two lines that describe the layout are also the two lines that
   describe the opening. Nothing else has to know the number: the grid reads it,
   the picture is simply told to fill whatever it ends up as. */
@property --cb-signin-wall {
  syntax: "<length-percentage>";
  inherits: false;
  initial-value: 0%;
}

.cb-signin {
  /* EXACTLY ONE WINDOW, NOT AT LEAST ONE. It was `min-block-size`, which is the
     right rule for a page and the wrong one for this: two full-height planes
     with a photograph down one of them are a screen, and a screen that is
     taller than the window is one the reader has to scroll to find the button
     on. Measured, the door's own content came to 566, so any window shorter
     than that scrolled the whole page, photograph and all.
     The height ladder at the bottom of this file is what makes the content fit
     instead of being clipped, and the measure is the one thing allowed to
     scroll if a window is shorter than any of it was designed for. */
  block-size: 100svh;
  display: grid;
  /* The door never falls under 480, which is what keeps a 384 measure inside
     64 of padding from ever being squeezed: 806/634 at 1440, 645/507 at 1152.
     Stated as the WALL'S width rather than the door's, which is the same
     layout said the other way round, because the wall is the half that moves
     and a transition needs both ends of it written in the same property. */
  --cb-signin-wall: calc(100% - clamp(var(--cb-width-sm), 44%, var(--cb-width-lg)));
  grid-template-columns: var(--cb-signin-wall) minmax(0, 1fr);
  /* Only ever visible if one of the two planes fails to paint. */
  background: var(--cbc-base);
}

/* ---- The room opening ----

   THE WALL IS A CONTAINER AND THE PICTURE IS A PLAIN FILL OF IT. That sentence
   is the whole of this now, and it replaces about a hundred lines of FLIP.

   WHAT WAS HERE, AND WHY IT WAS WRONG. The columns were set to their final
   values in one go and both planes were transformed back to where they had
   been and released, so that no frame of the move cost a layout. A plane being
   scaled stretches its contents, so the contents were counter-scaled; a
   counter-scaled holder renders at the width it will END at, so the picture
   inside it was pinned at the width it STARTED at and slid sideways under the
   counter-scale by half the distance the seam travels.

   Every one of those endpoints is correct and the middle is not. A counter
   scale animated as a linear pair does not cancel the scale it is undoing: the
   plane went 2.8 to 1 while the holder went 0.357 to 1, and at the halfway
   frame that is 1.9 times 0.68, or a photograph twenty-nine per cent too big.
   Measured on a 1440 window, frame by frame, the picture went 806, 861, 934,
   1011, 1039, 1010, 934, 856, 806 wide. It swelled and shrank back over the
   whole move. Nothing was dropping frames, every one was drawn eight
   milliseconds after the last; it was drawing the wrong thing, and no easing
   curve fixes a geometry error.

   So the machinery is gone and the browser does the arithmetic. One track
   width transitions, the wall genuinely narrows, and `object-fit: cover` on
   `object-position: center center` re-covers the photograph as it goes: the
   render stays the size it was and the crop closes in on the middle of it,
   which is what a container narrowing around a landscape frame does. There is
   no second clock to fall out of step with, because there is no second thing
   moving.

   IT IS A LAYOUT ANIMATION AND THAT IS FINE HERE, which is worth stating
   because this file used to argue the opposite. It is two grid tracks and one
   image, on a screen with nothing else on it, at the one moment in the product
   where nothing else is happening. The cost was measured before this was
   written and it holds the display's frame budget.

   20% mirrors --ask-wall-share in console-ask.css, which is the shape being
   opened into. The two are the same number twice and want to stay that way. */
/* AND `:root[data-room]` IS THE SAME SHAPE WITHOUT THE MOVE. It is stamped in
   the head of index.html when this browser was part way through the first run
   when it last unloaded, so a reload paints the opened room at frame one rather
   than painting the door and then playing the opening at somebody who has not
   just signed into anything. There is no transition on this branch and there
   must not be: it is not an animation, it is where the screen already was. */
.cb-signin[data-wide],
:root[data-room] .cb-signin {
  --cb-signin-wall: 20%;
}

/* ON A MARK OF ITS OWN, NOT ON `[data-wide]`, and the reload ground is why.
   `[data-wide]` is also the shape a refresh paints at frame one, and a
   transition declared there would be armed for every later change of that
   track: a window resize during the second step would slide the seam across
   like a curtain. signin/widen.ts puts this on for the length of the opening
   and takes it off again, so the only thing that ever animates is the opening.

   --cbc-carry rather than --cbc-spring: this is a plane changing size, which is
   the one thing that curve exists for (console-theme.css). */
.cb-signin[data-opening] {
  transition: --cb-signin-wall calc(var(--cb-motion-slow) + var(--cb-motion-base)) var(--cbc-carry);
}

/* The words that belong to signing in, gone before the room starts moving.
   The mark stays: it is the one thing on this screen that is also on the next
   one, and it is already the top-centre of the door, so widening the door is
   the whole of its move. Held rather than removed, so nothing reflows. */
.cb-signin[data-hush] .cb-signin-measure,
.cb-signin[data-hush] .cb-signin-line,
.cb-signin[data-hush] .cb-signin-foot,
.cb-signin[data-hush] .cb-brand-art,
:root[data-room] .cb-signin-measure,
:root[data-room] .cb-signin-line,
:root[data-room] .cb-signin-foot,
:root[data-room] .cb-brand-art {
  opacity: 0;
  transition:
    opacity var(--cb-motion-base) var(--cb-motion-ease),
    transform var(--cb-motion-base) var(--cb-motion-ease);
  pointer-events: none;
}

/* ---- The lockup is handed over, not held ----

   IT USED TO STAY, and the argument for that was good: the mark is the one
   thing on this screen that is also on the next one, it is already the door's
   top-centre, so widening the door was the whole of its move and it needed no
   correction. What that misses is that there are two of them. This screen's
   lockup and the next screen's lockup are different elements in different
   trees, and holding the first one up until the second fades in over it means
   a quarter of a second of one logo dissolving into another logo half a pixel
   away from it, which is the flicker every cross-fade of near-identical things
   produces.

   So it leaves, upwards, with the rest of the words, and the next screen's
   lockup comes down into the space it left (cbc-drop, console-ask.css). Up and
   away, then down and in: one movement across two screens, and nothing is ever
   dissolving into a copy of itself.

   ON `.cb-brand-art` AND NOT ON `.cb-signin-mark`, and it stays there. It had
   to be, back when signin/widen.ts counter-scaled the door's only element child
   to stop the plane's own scale stretching its contents: a transform written on
   the mark would have been the one that transform replaces. Nothing is scaled
   any more, so this is no longer forced, but it is still the right element. The
   mark is the block that holds the lockup and the product line, and the lockup
   is what is being handed over; lifting the block would take the line with it,
   and the line leaves by fading with the rest of the words. */
.cb-signin[data-hush] .cb-brand-art,
:root[data-room] .cb-brand-art {
  transform: translate3d(0, calc(var(--cb-space-2xl) * -1), 0);
}

/* ---- The wall ----
   position: relative is for the cover below, and for nothing else. The plane's
   own background-color stays a --cbc-* semantic whatever is hung on it, which
   is what keeps "the two planes are different colours in both themes" a thing
   a test can read off getComputedStyle rather than off a screenshot. */
.cb-signin-brand {
  position: relative;
  overflow: hidden;
  background: var(--cbc-sunken);
  border-inline-end: 1px solid var(--cbc-line);
}

/* ---- The picture, and nothing on it and nothing done to it ----
   Drop a file into assets/covers and it lands here (see that folder's README,
   scripts/build-covers.ts and src/signin/cover.ts). WITH THE FOLDER EMPTY NONE
   OF THIS IS ON THE PAGE AT ALL: cover.ts adds no holder, so the wall is the
   plain plane it has always been.

   IT IS THE PHOTOGRAPH, AS IT WAS TAKEN. That is the whole specification now,
   and getting to it took two rounds of removal worth writing down, because both
   were arguments this file used to make at length in the other direction.

   The first round was the layers ON it. There were three: a solid block of the
   plane's own colour at the top, another at the bottom, and a flat wash across
   the whole thing, all so that a lockup, a product line, a colophon, a build
   stamp and a theme control could each sit on their own ground rather than on a
   photograph. Every one of those is in the door beside it now. A photograph
   covered at both ends to make room for words was not really a photograph.

   The second round is the things done TO it, and they are gone for the same
   reason one sentence further on. There was a five-function camera grade, a
   turbulence grain blended in overlay, a warm lift under the shadows and a
   corner fall-off, and each was argued for well: film held less colour than a
   sensor does, a negative has a base density so its darkest tone is never the
   ink of the page, a lens is dimmer at the edge of its circle, and grain is what
   makes an eye read "captured" rather than "processed". All true about
   photography, and all of it beside the point here. The wall is one picture,
   chosen deliberately, and five layers of interpretation over it are the console
   having an opinion about somebody else's photograph. A photograph covered at
   both ends was not really a photograph; neither is one put through a stack.

   So: fill the plane, keep its own proportions, and do not appear until it has
   actually decoded. Three lines and a fade, and the file that used to be needed
   to explain the treatment is the treatment being gone.

   IT ALSO COSTS NOTHING NOW, which is a consequence rather than a reason. The
   grain was drawn at the size of its box, so on a plane whose width was
   animating the browser re-ran feTurbulence at a new size on every frame; that
   was eight frames of sixty over budget during the opening, and it was the only
   thing on this screen that ever was. */
.cb-signin-cover {
  position: absolute;
  inset: 0;
  overflow: hidden;

  /* ---- Nothing here moves ----

     IT USED TO SETTLE OUT OF A 1.06 SCALE over most of a second, and to carry a
     `will-change: transform` for the length of it. Both are gone with the FLIP
     above them. A scale on this box is the picture moving independently of the
     container it fills, which is the one thing the opening must not do: the
     photograph is a fill of a plane, and a fill that is 6% too big and easing
     back is a second animation for the eye to notice underneath the first.

     What is left is the fade, which is not motion but arrival: the file lands
     whenever it lands, and a photograph that pops in is worse than one that
     comes up. See cover.ts on why there is no deadline on that. */
  opacity: 0;
  transition: opacity var(--cb-motion-slow) var(--cb-motion-ease);
}
/* Hidden until it has decoded, and it fades rather than appears. An image that
   404s never fires load, never gets the attribute, and costs a photograph
   rather than leaving a broken frame on the console's front door.

   NO FILTER. There was a five-function grade here and the header above says why
   it went. What is worth keeping from it is the one line that turned out to
   argue against itself: the frame should look like it was taken on something,
   not like it was put through something. */
.cb-signin-cover-art {
  inline-size: 100%;
  block-size: 100%;
  /* THE WHOLE OF THE OPENING'S GEOMETRY IS THESE TWO LINES. The plane narrows
     and the browser re-covers the frame inside it: for a landscape photograph
     in a full-height box the render never changes size at all, because the
     height is what binds at both ends, so what the reader sees is the crop
     closing evenly on the middle of the picture. `center center` is stated
     rather than left to the default for exactly that reason: it is the thing
     that keeps the middle in the middle while the box moves around it. */
  object-fit: cover;
  object-position: center center;
}
/* The <picture> is a wrapper and nothing else: it exists so the browser can
   take AVIF where it can and WebP where it cannot (cover.ts), so it has to
   pass its box straight through to the image inside it.

   IT USED TO BE PINNED AND DRIFTED. It was given the wall's starting width and
   translated sideways by half the distance the seam travels, on a clock of its
   own that was longer than the planes' and started a beat after them, so that
   it trailed the edge that was moving. That lag was described as depth and it
   was really the picture disagreeing with its own container: the box it was
   supposedly filling had already arrived and the photograph was still on its
   way. Both are gone. The container narrows, the fill follows it exactly, and
   there is one clock. */
.cb-signin-cover > picture {
  display: block;
  block-size: 100%;
}
/* The holder follows the picture: `data-ready` is on the <img>, because only
   the image knows when it has decoded, and the thing that moves is its parent.
   One selector rather than a second attribute for cover.ts to remember. */
.cb-signin-cover:has(.cb-signin-cover-art[data-ready]) {
  opacity: 1;
}

/* ---- And the picture leaving, which is the other half of the opening ----

   THE WALL DISSOLVES TO ITS OWN GROUND AS THE ROOM OPENS. The photograph does
   not travel to the next screen any more: it fades to `--cbc-sunken`, which is
   the plane's own colour underneath it, and the room that opens next brings ITS
   picture up out of that same dark (console-ask.css, onboarding/wall.ts). Image,
   dark, image, with the dark as the hinge, which is one gesture rather than two
   photographs trading places.

   IT USED TO BE A CROSSFADE BETWEEN THE TWO PICTURES, with the door's kept
   alive underneath the room's for a second and a half. That was written when
   both walls wore the same photograph and nothing about the swap was meant to
   be visible; they are two pictures now (cover-roles.ts), and a slow dissolve
   from one to the other is the one thing a reader IS meant to notice, so it is
   better done through the ground than on top of it.

   IT RUNS FROM FORTY-FIVE PER CENT OF THE WINDOW TO SEVENTY-EIGHT, WHICH IS NOT
   THE END OF THE MOVE. The door travels from forty-four to eighty, so the
   picture begins going almost as the seam does and is finished just before it
   lands: the last sliver of the travel sweeps over the plane rather than over a
   photograph. Ending it at the settle instead was the first version and it read
   wrong for a reason worth writing down: --cbc-carry spends two thirds of the
   wall's travel in its first fifth, so by the time any of the fade was visible
   the seam had all but stopped, and the picture appeared to dim after the move
   rather than during it.

   SO THE LENGTH IS TWO BEATS OF --cb-motion-fast AND THAT IS ARITHMETIC, not a
   preference. Measured off a filmed sign-in at 1440, the door reaches
   forty-five per cent eight milliseconds into a six hundred and fifty
   millisecond move and seventy-eight per cent at three hundred. Three hundred
   is --cb-motion-fast twice, and eight milliseconds is no delay, so the fade
   starts with the move and is done on the frame the seam reaches seventy-eight.
   Written as a multiple of a token rather than as a number, the way the room's
   own fade is one screen along.

   --cbc-dim rather than --cbc-carry: an ease-OUT on a fade takes the picture
   away in the first fifth, which at this length is a cut. This one holds for an
   instant and then goes (console-theme.css), which reads as the seam taking the
   picture with it, and it is the exact mirror of the --cbc-lift the room's own
   picture comes up on.

   AND IT DOES NOT COME BACK. `visibility` is switched at the end of the fade,
   not transitioned, so once the picture is gone the holder is out of the paint
   entirely and nothing can raise it again while the room is open: not a file
   that finishes decoding late and claims `data-ready` after the fade, not the
   arrival transition above, not a repaint. The two places that genuinely know
   the door is back up take `[data-wide]` off (signin/widen.ts), and only those.
   Opacity alone left an invisible photograph sitting over the plane waiting for
   any of that to put it back.

   ON `[data-wide]` FOR THE VALUE AND `[data-opening]` FOR THE CLOCK, which is
   the same split the wall's own width uses one screen up and for the same
   reason: a reload paints `[data-wide]` at frame one and must not animate. With
   no clock the two properties simply cut, which is also the reduced-motion
   path. The `:has()` is specificity and nothing more. It has to out-rank the
   arrival fade directly above, which carries one of its own. */
.cb-signin[data-wide] .cb-signin-cover:has(.cb-signin-cover-art[data-ready]),
:root[data-room] .cb-signin .cb-signin-cover:has(.cb-signin-cover-art[data-ready]) {
  opacity: 0;
  visibility: hidden;
}
.cb-signin[data-opening] .cb-signin-cover {
  transition:
    opacity calc(var(--cb-motion-fast) * 2) var(--cbc-dim),
    visibility 0s linear calc(var(--cb-motion-fast) * 2);
}

/* ---- What used to be over the picture, and is not ----

   NOTHING IS DRAWN ON IT ANY MORE, and this note is here so that the next
   person to think of adding something knows it has been tried.

   There were two pseudo-elements. `::after` carried a warm lift and a corner
   fall-off: a flat wash of blackcurrant at six per cent so the shadows never
   reached the ink of the page, which is what a negative's base density does and
   what `contrast` and `brightness` cannot do because they move the whole curve;
   and a radial gradient taking twenty per cent out of the very corners, which is
   about what a decent prime does wide open. `::before` carried grain, from an
   SVG turbulence at eight per cent in overlay blend, on the argument that a
   graded frame with no texture in it reads as a clean photograph made to look
   old rather than as one that was captured.

   All three are defensible as photography and none of them is this console's
   business. The wall carries ONE picture, picked on purpose, and a grade, a lift
   and a vignette over it are the product having an opinion about it. Take them
   off and it is the photograph, which is what was wanted in the first place.

   Two smaller things went with them, both worth knowing. The fall-off followed
   the plane rather than the picture, so once the wall had narrowed to a fifth of
   the window it was drawing a dark edge down the two long sides of a strip,
   which is not what a lens does. And the grain was the only thing on this screen
   that ever cost a frame: it was drawn at the size of its box, so a box whose
   width was animating made the browser re-run feTurbulence sixty times a second.

   The plane's own `background: var(--cbc-sunken)` is what shows where there is
   no photograph, and it is not an effect: it is the wall. */

/* ---- The identity, at the top of the door ----
   The lockup with the product line under it, as one block. They were at
   opposite ends of the wall before, a lockup at the top and the line at the
   bottom with a photograph between them, which is two halves of one signature
   in two places.

   THE LINE IS SMALLER THAN IT WAS, and that is not a loss. At 3xl it was the
   largest thing on the screen because it was the only thing on its plane; on a
   plane that also carries "Sign in" it would be a second heading arguing with
   the first. At lg it reads as what it is, which is the product saying its own
   name under its own mark. */
.cb-signin-mark {
  display: grid;
  justify-items: center;
  gap: var(--cb-space-lg);
  text-align: center;
}
/* The lockup arrives from the top, on the same keyframes and tempo the room's
   masthead uses (console-ask.css, cbc-mast-drop): two screens, one gesture, so
   the hand-off between them reads as the same object coming down twice rather
   than two different logos with two different manners. Plays when the door is
   actually shown: under the returning ground the plane is display none, and a
   sign-out revealing it restarts the entrance, which is right for a door that
   has just been arrived at. */
.cb-signin-mark .cb-brand-art {
  --mast-drop: var(--cb-space-3xl);
  animation: cbc-mast-drop var(--cb-motion-slow) var(--cbc-spring) var(--cb-motion-base) backwards;
}
@media (prefers-reduced-motion: reduce) {
  .cb-signin-mark .cb-brand-art {
    animation: none;
  }
}
:root[data-motion="reduced"] .cb-signin-mark .cb-brand-art {
  animation: none;
}
/* Descendant rules only. .cb-brand-art and .cb-brand-logo belong to the rail's
   mark (console.css) and are worn here at a different size; a bare rule for
   either would be this file quietly redefining somebody else's component.

   inline-size on the holder is load-bearing, not tidying. Both logo files are a
   viewBox with no width or height, so an <img> of one takes its size from
   whatever its box allows: in the rail that is the typed wordmark's own width,
   which is why the mark there is 25 tall inside a 32 box. Given a width to work
   with, the height cap is what binds. */
.cb-signin-mark .cb-brand-art {
  block-size: var(--cb-space-6xl);
  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 the holder (console.css), and the holder is the
   full width of the plane for the reason above, so justify-items on the block
   around it does nothing at all and the mark sat against the left edge with the
   line centred under it. Both insets to zero and the inline margins to auto is
   what centres an absolutely positioned element that has a width of its own,
   and the height cap above is what gives it one. */
.cb-signin-mark .cb-brand-logo {
  max-block-size: var(--cb-space-6xl);
  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-signin-mark .cb-brand-word {
  font-size: var(--cb-type-xl-size);
}
.cb-signin-line {
  font-size: var(--cb-type-lg-size);
  line-height: var(--cb-type-lg-line-height);
  letter-spacing: var(--cb-type-lg-letter-spacing);
  font-weight: var(--cb-font-weight-medium);
  color: var(--cbc-ink-muted);
}
.cb-signin-stamp {
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
  font-variant-numeric: tabular-nums;
}

/* ---- The bottom row ----
   Small print at one end, the one control at the other, sharing a baseline.
   They are together because they are the same kind of thing: everything on this
   row is about the console rather than about getting into it, which is what
   keeps the door plane opposite down to a single action.

   align-items: end and not center. The credit is three lines and the control is
   one, and a control floating level with the middle of a paragraph reads as
   having been dropped there. */
/* ---- The bottom of the door ----
   The one control, and one line of small print under it, both on the middle.

   THE CONTROL IS ABOVE THE PRINT, not beside it. Side by side they were a row
   with a pressable thing at one end and a copyright at the other, which reads
   as a toolbar; stacked, the thing you can press is a control and the line
   under it is a footnote, which is what each of them actually is. It is also
   the order they are in the markup, so a keyboard reaches the control on the
   way down rather than after the last word on the page. */
.cb-signin-foot {
  display: grid;
  justify-items: center;
  gap: var(--cb-space-xl);
  text-align: center;
}
/* One line: who made it, whose it is, and which build. Wrapping is allowed and
   is not a failure, because at 320 three facts do not fit across a phone; what
   is not allowed is three lines by construction, which is what a grid of rows
   made it before. */
.cb-signin-colophon {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: var(--cb-space-sm);
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
}
.cb-signin-sep {
  opacity: 0.5;
}

/* ---- Picking a theme before there is anybody to remember it for ----
   The control itself is the account menu's, unchanged: .cb-theme-pick and
   .cb-theme-option are console.css's and this only re-grounds them. In the menu
   the well is --cbc-sunken inside a --cbc-raised panel, which is a recess. This
   wall IS --cbc-sunken, so the same well is invisible here and the three
   options read as three loose icons nobody would take for a control. A hairline
   says the same thing the recess was saying. The current option still lifts to
   --cbc-surface with the shadow, which is a step up from the wall in both
   themes, so the marked state needs nothing from this file. */
.cb-signin-appearance .cb-theme-pick {
  background: none;
  border: 1px solid var(--cbc-line);
}
/* And the lift comes down with it. --cbc-shadow is tuned to hold a dialog off
   the page; under a twenty-four pixel chip on a flat wall it is a soft dark
   blob a third of the control's width below it, which on the narrow layout is
   the only mark on an otherwise empty strip. The card shadow is the same idea
   at the size of the thing casting it, and the step in lightness underneath is
   doing most of the work in either theme anyway. */
.cb-signin-appearance .cb-theme-option[aria-checked="true"] {
  box-shadow: var(--cb-shadow-card);
}

/* ---- The door ----
   Optically centred with two padding values rather than a magic offset: the
   block end carries 80 more than the other three, so the auto margins split
   what is left and the column lands above the mathematical centre at every
   window height.

   margin: auto and NOT align-content: center. Auto margins collapse to zero
   when there is no free space; a centring keyword pushes the top of content
   taller than its box off the top edge, where it cannot be scrolled to. A
   refusal callout on a five-hundred-pixel window is exactly that case. */
/* ---- The door, in three bands ----

   THE MARK, THE ACT, THE SMALL PRINT, and the middle one takes what is left.
   Everything on this plane used to be on the wall opposite, which left the door
   as a heading floating in an empty half-screen with the identity of the
   product on the other side of a hairline from the button that signs you into
   it. Together they read in the order they matter: whose console this is, the
   one thing to do, and then the things that are about the console rather than
   about getting into it.

   A grid rather than space-between, and the reason is the middle row. The act
   has to sit at the optical centre of the plane whether the small print under
   it is one line or three, and `1fr` on that row alone does exactly that: the
   two ends take the height they need and the middle keeps the rest, so the
   button does not move when a build stamp gains a character.

   `minmax(0, 1fr)` and `margin: auto` on the measure rather than a centring
   keyword: a refusal callout on a five-hundred-pixel window is taller than its
   row, and `center` pushes the top of anything taller than its box off the top
   edge where it cannot be scrolled to. */
.cb-signin-door {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  gap: var(--cb-space-7xl);
  padding: var(--cb-space-7xl);
  background: var(--cbc-surface);
}
.cb-signin-measure {
  margin: auto;
  inline-size: 100%;
  /* NO OVERFLOW HERE, AND THAT IS A CORRECTION. This row briefly carried
     `overflow-y: auto` as a last resort for a window shorter than the ladder at
     the bottom of this file can compress to. It was the wrong place for it and
     it never paid for itself.

     A box with `overflow: auto` is a scroll container, and a scroll container
     clips everything its children draw outside their own boxes. The one control
     on this screen has a focus ring that does exactly that, and the entrance
     every element here arrives on is a transform, so the row had a scrollbar
     down it before anything had even settled: a scrollbar inside a heading, a
     sentence and one button, on a screen with nothing to scroll to.

     The ladder is what makes this fit, and it does: measured on the real page,
     nothing scrolls at any height from 440 up. Below that the plane overflows
     its own cap and the page grows, which is the ordinary way a page too small
     for its content behaves, and it is a better answer than a scrollbar inside
     the sign-in box. */
  /* CENTRED, ALL OF IT. One column, one axis: the mark, the heading, the
     sentence under it, the button and the small print all share the middle of
     the plane rather than a left edge. A door is one thing to do, and a single
     column of centred type is the shape that says so; a ragged left edge with
     one wide button on it reads as a form with the fields taken out. */
  text-align: center;
  /* 384, and the line length is unchanged from the card: its 480 included 48
     of padding on either side, so the measure was already this. */
  max-inline-size: var(--cb-width-xs);
}
.cb-signin-head {
  font-size: var(--cb-type-2xl-size);
  line-height: var(--cb-type-2xl-line-height);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-ink);
  margin-block-end: var(--cb-space-md);
}
.cb-signin-lede {
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink-faint);
  margin-block-end: var(--cb-space-3xl);
}

/* ---- The one thing saying something ----
   THE BORDER IS --cbc-line IN EVERY TONE. --cbc-poor resolves to the brand
   scarlet in both themes, and a scarlet hairline around a scarlet wash is two
   scarlets arguing over a box with a scarlet button under it. Tone is carried
   by the heading, the glyph and a seven-percent ground, which is three
   channels, one of which is words.

   Hidden until it has a tone rather than removed from the page: a live region
   has to be in the document before it is written to, or half the screen
   readers in use announce nothing. say.ts sets the tone in one frame and the
   words in the next for the same reason. */
.cb-signin-say {
  display: grid;
  grid-template-columns: var(--cb-space-2xl) minmax(0, 1fr);
  gap: var(--cb-space-lg);
  align-items: start;
  margin-block-end: var(--cb-space-2xl);
  padding: var(--cb-space-lg) var(--cb-space-xl);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-sunken);
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  color: var(--cbc-ink);
}
.cb-signin-say:not([data-tone]) {
  display: none;
}
.cb-signin-say .cb-icon {
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
}
.cb-signin-say[data-tone="stopped"] {
  background: color-mix(in srgb, var(--cbc-warn) 7%, var(--cbc-surface));
}
.cb-signin-say[data-tone="stopped"] .cb-icon {
  color: var(--cbc-warn-text);
}
.cb-signin-say[data-tone="refused"] {
  background: color-mix(in srgb, var(--cbc-poor) 7%, var(--cbc-surface));
}
.cb-signin-say[data-tone="refused"] .cb-icon {
  color: var(--cbc-poor-text);
}
.cb-signin-say[data-tone="note"] .cb-icon {
  color: var(--cbc-ink-faint);
}
.cb-signin-say[data-tone="waiting"] {
  color: var(--cbc-ink-muted);
}
.cb-signin-say[data-tone="waiting"] .cb-icon {
  color: var(--cbc-ink-faint);
}
.cb-signin-sentence {
  color: inherit;
}
/* The aside and the account line are the second read, and only ever the second
   read: --cbc-ink-faint is the measured 57% floor and nothing here goes under it. */
.cb-signin-aside,
.cb-signin-who {
  margin-block-start: var(--cb-space-sm);
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
}
/* The one place a command may appear (docs/09, and the copy sheet's error
   voice). Monospace so `npm run dev` is something to copy rather than to read. */
.cb-signin-cmd {
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink);
}

/* ---- The way in ----
   GOOGLE'S BUTTON, TO GOOGLE'S RULES, and this is the one place in the console
   where a component is not ours to design. Their Sign-In branding guidelines
   govern the mark, the wording, the ground it sits on and the room around it,
   because the whole point of the thing is that somebody recognises it before
   they read it.

   What was here was the console's scarlet primary with a monochrome G tinted by
   currentColor, which breaks the single rule those guidelines have no exception
   for: the G is never recoloured. It was tinted because the ground was scarlet,
   and Google's approved grounds are neutral, so the two faults were one fault.
   Both are fixed by moving the ground.

   WHAT IS FOLLOWED
   - The mark is the official full-colour G, unmodified (src/icons/google.svg).
     Nothing in this file sets a fill or a colour on it.
   - The ground is one of Google's two neutrals, picked to suit the plane behind
     it: white with a grey stroke and near-black type in the light theme, their
     near-black with a grey stroke and light type in the dark one.
   - The wording is "Continue with Google", which is on their approved list, in
     every state including in flight (see index.ts: twenty-three specs match on
     that accessible name, and a button that renames itself mid-sign-in breaks
     all of them somewhere else).
   - The proportions: at least their 40px minimum height, the mark at 20 in a
     button of about 47, at least 8 of clear space around the mark, and 12
     between the mark and the words. The pill radius is their "rounded" variant
     and it is also what docs/09 says every button in this product is.

   WHAT IS NOT, AND WHY
   - ROBOTO. Google asks for it. This console cannot have it: rule 13 forbids a
     literal font stack anywhere that ships, the token set is the official
     Candybox one and is not ours to add a family to, and check:tokens enforces
     both. Loading a second webfont for the label of one button on one internal
     screen would also put a typeface on this control that appears nowhere else
     in the product. Google's own guidance is that the button should sit
     naturally in the surface around it and to fall back to the interface's font
     where Roboto is not available, so it wears Poppins at the weight the rest
     of the console's buttons wear. That is a deliberate, written-down
     deviation, which is the only kind worth having.
   - THEIR EXACT NEUTRALS. Their light ground is #FFFFFF, which is a token here
     and is used. Their dark ground is #131314, their strokes #747775 and
     #8E918F: none of those three is in the official Candybox set and rule 13
     does not allow adding an off-scale value for them, so each is the nearest
     thing the set already holds. The dark ground is the brand's own near-black
     and the two strokes are mixed to land within a couple of percent of the
     specified greys. Nobody looking at the button can tell, and the alternative
     was three raw hex values in a stylesheet that fails the build for one.

   AND A SIDE EFFECT WORTH NAMING. .cb-btn is white on --cbc-accent, which
   measures 3.37:1 in the dark theme against the 4.5:1 that normal-sized text
   needs. Every primary button in this console owes that, and this one no longer
   does: it is now near-black on white and ceramic grey on near-black. The axe
   allowance in tests/signin.spec.ts is written against ".cb-signin-google" by
   name and can come off the day somebody checks.

   The mark is sized by its box and never by the glyph inside it. A rule setting
   the svg directly is what console.css's own icon comment forbids, and it is
   how the old sheet's `.cb-signin-google svg { width: 24px }` overhung its 16px
   box. This file loads after console.css, so one rule at the same specificity
   as `.cb-btn .cb-icon` wins with nothing marked important. */
.cb-signin-google {
  inline-size: 100%;
  padding-block: var(--cb-space-lg);
  /* Google asks for 8 to 12 between the mark and the words; .cb-btn's own gap
     is the 8 at the bottom of that range and 12 is what the mark wants at 20. */
  gap: var(--cb-space-lg);
  background: var(--cb-color-black);
  color: var(--cb-color-ceramic-grey);
  border-color: color-mix(in srgb, var(--cb-color-ceramic-grey) 52%, transparent);
}
/* Their state layer, which is 8% of the type colour over the ground. */
.cb-signin-google:hover:not([aria-disabled="true"]) {
  background: color-mix(in srgb, var(--cb-color-ceramic-grey) 8%, var(--cb-color-black));
}
/* The light button, on the one plane in this console that is genuinely white.
   These two rules and their two companions below are the file's only
   theme-conditional ones; see the note at the top. */
[data-theme="light"] .cb-signin-google {
  background: var(--cb-color-white);
  color: var(--cb-color-text-primary);
  border-color: color-mix(in srgb, var(--cb-color-text-tertiary) 80%, var(--cb-color-white));
}
[data-theme="light"] .cb-signin-google:hover:not([aria-disabled="true"]) {
  background: color-mix(in srgb, var(--cb-color-text-primary) 8%, var(--cb-color-white));
}
/* .cb-btn[aria-disabled] sends the ground to `inherit` on hover, which is right
   for a scarlet pill going neutral and wrong for a button whose ground is the
   vendor's: it would drop the white out from under the G for as long as the
   pointer was on it while the Google window is open. Restored, per theme, at a
   specificity that beats it. */
.cb-signin-google[aria-disabled="true"]:hover,
.cb-signin-google[aria-disabled="true"]:active {
  background: var(--cb-color-black);
}
[data-theme="light"] .cb-signin-google[aria-disabled="true"]:hover,
[data-theme="light"] .cb-signin-google[aria-disabled="true"]:active {
  background: var(--cb-color-white);
}
/* 20 in a button of 47, which holds Google's 18-in-40 proportion, and the
   negative start margin .cb-btn gives its glyphs is taken back: their guidelines
   ask for clear space around the mark rather than for it to hang into the
   padding. */
.cb-signin-google .cb-icon {
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
  margin-inline-start: 0;
}
/* The ring override that used to be here is gone with the scarlet. It existed
   because --cbc-accent was ringing a control already filled with --cbc-accent,
   the only place in the console where the ring was the hue of the thing it rang.
   On a neutral ground the standard ring is the strongest one available, and one
   fewer local rule is one fewer thing to keep in step. */

/* ---- Arriving ----
   One entrance, one direction, six steps. cbc-emerge and not cbc-rise: twenty
   pixels is for a thing that IS the arrival, and here that is the plane, which
   does not move. These settle onto planes that were already correct at frame
   one.

   --i is set here rather than from JavaScript because these are six fixed
   seats rather than a list of unknown length, and because the left plane
   animates before any script has run. Seat 4 twice on purpose: the button and
   the product line land together, one on each side of the seam.

   No `to` keyframe is authored anywhere, so every element ends at its own
   resting value and nothing is pinned out of its hover state. prefers-reduced-
   motion needs no rule here: the global block neutralises duration AND delay,
   and the delay is the half that is usually missed. */
.cb-signin-mark,
.cb-signin-head,
.cb-signin-lede,
.cb-signin-say,
.cb-signin-google,
.cb-signin-line,
.cb-signin-foot {
  animation: cbc-emerge var(--cb-motion-slow) var(--cbc-spring) backwards;
  animation-delay: var(--cbc-step);
}
.cb-signin-mark {
  --i: 0;
}
.cb-signin-head {
  --i: 1;
}
.cb-signin-lede {
  --i: 2;
}
.cb-signin-say {
  --i: 3;
}
.cb-signin-google {
  --i: 4;
}
.cb-signin-line {
  --i: 4;
}
/* The last seat is the whole bottom row rather than the build stamp alone. It
   is now the small print AND the appearance control, and three things arriving
   one after another in the corner of a wall is a queue nobody is watching; as
   one seat they land as the one object they read as. */
.cb-signin-foot {
  --i: 5;
}
/* The picture is not a seat. It cannot be: it arrives whenever the file does,
   which is after the entrance on a cold load and before it on a warm one, and
   a stagger built around something that might already be there is a stagger
   with a hole in it. It has its own fade, on load, in cover.ts. */

/* ---- Below the split ----

   ONE COLUMN, AND IT IS ALREADY THE ONE COLUMN. This used to be most of the
   work in this file: the mark was on one plane and the way in was on the other,
   so a narrow window meant hiding half a wall, restacking what was left and
   lifting the appearance control out of the flow to keep it level with a lockup
   it no longer sat beside. The door carries all of it now, in the order it
   should be read, so a narrow window is the same column with less room.

   The wall goes entirely rather than emptying: there is nothing in it but a
   picture, and a photograph behind a thirty-two pixel lockup is a banner. What
   is left is one plane on --cbc-surface, so the page has one background rather
   than a centred slab with two hard vertical edges down it.

   The small print stays, and used to be hidden here. It was three stacked lines
   under a door that is one thing to do, which is a paragraph of footnote on a
   phone; as one wrapping line it costs a row or two and there is no longer a
   reason to take it away. The appearance control stays for a stronger reason:
   it is the only thing down here anybody can press, and hiding it would put the
   bug straight back for somebody on a laptop at a narrow window.

   The mark steps down to the rail's own size, because a 48 lockup over a 384
   column is a hat. */
/* 1024px mirrors tokens.breakpoint.lg */
@media (max-width: 1024px) {
  .cb-signin {
    grid-template-columns: minmax(0, 1fr);
    background: var(--cbc-surface);
    /* AND IT MAY GROW AGAIN HERE, which is the one place the cap above is
       wrong. Capped, this is a screen: two planes, a photograph, and a door
       that has to hold its own shape. Stacked, the photograph is gone and what
       is left is a column of type on a phone, which is a document. A document
       taller than the window scrolls, and making it scroll INSIDE a 380-pixel
       middle row instead would be the page pretending to be a screen. */
    block-size: auto;
    min-block-size: 100svh;
  }
  .cb-signin-brand {
    display: none;
  }
  .cb-signin-mark .cb-brand-art {
    block-size: var(--cb-space-4xl);
  }
  .cb-signin-mark .cb-brand-logo {
    max-block-size: var(--cb-space-4xl);
  }
  .cb-signin-mark .cb-brand-word {
    font-size: var(--cb-type-md-size);
  }
  .cb-signin-door {
    background: none;
    gap: var(--cb-space-5xl);
    padding: var(--cb-space-5xl) var(--cb-space-4xl) var(--cb-space-5xl);
    inline-size: 100%;
    margin-inline: auto;
    max-inline-size: calc(var(--cb-width-xs) + var(--cb-space-4xl) + var(--cb-space-4xl));
  }
  .cb-signin-measure {
    margin-inline: 0;
  }
}
/* 768px mirrors tokens.breakpoint.md */
@media (max-width: 768px) {
  .cb-signin-door {
    padding-inline: var(--cb-layout-container-padding-mobile);
    max-inline-size: calc(
      var(--cb-width-xs) +
      var(--cb-layout-container-padding-mobile) +
      var(--cb-layout-container-padding-mobile)
    );
  }
}

/* ---- Below the fold, which there is not one of ----

   THE SAME LADDER THE QUESTIONS NEXT DOOR ALREADY HAD, and this screen never
   had one. console-ask.css compresses its rhythm at 1024 and again at 768 on
   the height axis, with the reasoning written out there: the air goes first,
   then the standfirst, and the heading is the last thing to give way, because a
   screen whose whole idea is one thing at a time and a heading that has quietly
   shrunk to the size of a panel title has given away the idea to save forty
   pixels. The sign-in door had none of that, so it simply grew past short
   windows: 310 of content inside 256 of air and padding, which is 566, and
   anything shorter scrolled.

   THE LOCKUP DOES NOT STEP DOWN, and that is the same decision made in the same
   words next door. It is the one element that survives the handoff into the
   second step and the four questions, at the same size in the same place, and a
   mark that shrank here would be the one thing that carried across changing
   size the moment it arrived. It costs 48 pixels and the air around it is worth
   more.

   The breakpoints mirror the token set on the other axis, which is the same
   thing the width queries below do and for the same reason: CSS cannot read a
   var() in a media query, so each one names the token it stands for. */
/* 1024px mirrors tokens.breakpoint.lg, on the height axis */
@media (max-height: 1024px) {
  .cb-signin-door {
    gap: var(--cb-space-5xl);
    padding-block: var(--cb-space-5xl);
  }
}
/* 768px mirrors tokens.breakpoint.md, on the height axis */
@media (max-height: 768px) {
  .cb-signin-door {
    gap: var(--cb-space-4xl);
    padding-block: var(--cb-space-4xl);
  }
  /* The product line closes up under the lockup rather than being taken away:
     it is the one line on this screen that says what the thing is. */
  .cb-signin-mark {
    gap: var(--cb-space-md);
  }
  .cb-signin-lede {
    margin-block-end: var(--cb-space-2xl);
  }
  /* The control and the small print stop being two separate objects and become
     one block in the corner, which is what they read as anyway. */
  .cb-signin-foot {
    gap: var(--cb-space-lg);
  }
}
/* 480px mirrors tokens.breakpoint.sm, on the height axis. A laptop with every
   toolbar open, or a phone turned sideways. */
@media (max-height: 480px) {
  .cb-signin-door {
    gap: var(--cb-space-3xl);
    padding-block: var(--cb-space-3xl);
  }
  /* Now the standfirst gives way, one step, and the heading still does not. */
  .cb-signin-line {
    font-size: var(--cb-type-base-size);
    line-height: var(--cb-type-base-line-height);
    letter-spacing: normal;
  }
}

/* ---- The line that says it worked ----
   Between the callout and the button, which is where somebody is already
   looking when they press it. It fills the second or two between Google
   closing its window and this console being drawn: without it, a screen that
   has just accepted somebody sits there saying nothing, which reads as a click
   that did not land.

   Good rather than accent: this is the one thing on the sign-in wall that is
   neither a refusal nor a brand mark, and it should read as a door opening. */
.cb-signin-greeting {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  margin-block-end: var(--cb-space-xl);
  padding: var(--cb-space-lg) var(--cb-space-xl);
  border-radius: var(--cb-radius-xl);
  background: color-mix(in srgb, var(--cbc-good) 12%, transparent);
  color: var(--cbc-ink);
  font-size: var(--cb-type-sm-size);
  line-height: var(--cb-type-sm-line-height);
  text-wrap: pretty;
}
/* The dot, so the line reads as a state and not as a sentence somebody left
   there. Drawn rather than an icon: it is one element and one colour. */
.cb-signin-greeting::before {
  content: "";
  flex: none;
  inline-size: var(--cb-space-md);
  block-size: var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-good);
  /* AND IT BEATS, because for the second it is up something genuinely is
     happening: the console is being built behind this line and the reader has
     nothing to look at while it is. A still dot says "that worked" and then
     says nothing for a second and a half, which is the part of this moment
     that reads as the console having stopped.

     The live card's pulse, exactly (console-board.css): same size, same
     keyframe, same depth. The console has one way of saying work is going on
     right now and this is a second place that means it, not a second language
     for it. The rule that pulse is written under is the one that makes this
     honest rather than decorative: a pulse only beats while something is
     actually running, and a decoration that looks like activity is worse than
     none. This line is on screen for exactly as long as the boot it stands for.

     --cbc-loop and not --cbc-loop-calm, which is the one number that is this
     surface's rather than borrowed. The calm loop is 2.2s, and the greeting is
     up for 1.6 on a first sign-in and about 1.0 on every one after it: the dot
     would dim once, never come back, and read as fading out rather than as
     beating. At the ordinary loop a returning reader sees one whole breath and
     a new one sees half again, which is the difference between a pulse and a
     thing that went dark. */
  animation: cbc-breathe var(--cbc-loop) var(--cb-motion-ease) infinite;
  --cbc-breath: 0.3;
}
/* It arrives rather than appearing: the whole point is that somebody notices
   it in a moment when nothing else on the screen is moving. */
.cb-signin-greeting {
  animation: cb-greeting-in var(--cb-motion-base) var(--cbc-spring) both;
}
@keyframes cb-greeting-in {
  from {
    opacity: 0;
    transform: translateY(var(--cb-space-sm));
  }
  to {
    opacity: 1;
    transform: none;
  }
}
