/* BonBonBot Console: THE LEFT RAIL, and everything that stands in it.

   Split out of console.css on the console-refresh branch. It was thirteen
   hundred lines of one file, sharing that file with the page header, the
   buttons and the tooltip, which meant every session touching any of those
   four was editing the same file as the other three.

   WHAT IS HERE: the rail box itself and its scrolling region, the brand mark,
   the search row and its keycap, the entrance, the navigation and its counts,
   the shelf tools' lockups, the settings sub-list, the live scan card and its
   stilled wave, a website's own mark, the tool row, the account button and its
   menu, the theme cycle, and the foot the last two sit in.

   WHAT IS NOT, AND WHERE IT IS INSTEAD. The rail's WIDTH and the block of
   colour behind it are set in console.css, in the rule that lays out
   `.cb-console` as two grid columns, because the frame is what needs to know
   how wide the first column is. `--cbc-rail` is the token both read, and it
   lives in console-theme.css. So a change to how wide the rail is, or to what
   colour its column is painted, is a change to two files this one does not
   own. Ask the session that holds them rather than restating either here: a
   second declaration of the width is how the rail and the space reserved for
   it quietly stop matching.

   Loaded immediately after console.css and before every feature sheet, which
   is exactly where these rules sat when they were part of it. */

/* The rail itself no longer scrolls; the list of sections inside it does. It
   was one scrolling column, so on a short window the account button and the
   live scan card slid off the bottom, which is the moment you want them. */
.cb-rail {
  position: sticky;
  top: 0;
  z-index: var(--cbc-z-rail);
  height: 100svh;
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-3xl);
  padding: var(--cb-space-3xl) var(--cb-space-2xl);
  border-inline-end: 1px solid var(--cbc-line);
  overflow: hidden;
}
/* min-block-size is the load-bearing line: a flex item will not shrink below
   its content without it, so the region would grow the rail instead of
   scrolling and push the foot out of view again. */
.cb-rail-scroll {
  flex: 1 1 auto;
  min-block-size: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-3xl);
}

/* ---- The mark ----
   The wordmark is the fallback and the logo file lays over it; whichever theme
   is on picks its own file. See src/brand.ts and assets/README.md. CONSOLE sits
   under it, letterspaced and small, so the rail says what this is without a
   second line of chrome anywhere else on the page. */
.cb-brand {
  display: grid;
  gap: var(--cb-space-xs);
  padding: var(--cb-space-md) var(--cb-space-lg) var(--cb-space-lg);
  text-decoration: none;
  color: inherit;
}
.cb-brand-art {
  position: relative;
  display: flex;
  align-items: center;
  block-size: var(--cb-space-4xl);
}
/* The rail's own lockup is text only, with no candy icon filling out its
   height, so it reads oversized at the icon variant's block-size (used by the
   onboarding lockup, which does carry the icon). Rail-only, by data-variant
   rather than a second class, since brand.ts already knows which it drew. */
.cb-brand-art[data-variant="text"] {
  block-size: var(--cb-space-3xl);
}
.cb-brand-art[data-variant="text"] .cb-brand-logo {
  max-block-size: var(--cb-space-3xl);
}
@keyframes cb-brand-word-in {
  to {
    opacity: 1;
  }
}
.cb-brand-word {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-sm);
  font-size: var(--cb-type-md-size);
  font-weight: var(--cb-font-weight-semibold);
  letter-spacing: var(--cb-type-3xl-letter-spacing);
  /* Invisible until proven needed. A transition cannot do this: it only runs
     on a style CHANGE, and nothing changes between the element's creation and
     its first paint, so a plain "start hidden, fade to shown" rule painted at
     opacity 1 from frame one regardless. An animation runs on its own clock
     from mount, delay included, which is what makes the wait real: the real
     logo loads well inside --cb-motion-slow on every real machine, so the
     fallback is cancelled below before it is ever seen, and the scarlet "Bot"
     that used to paint for that one frame read as the logo itself flashing
     colour before turning white. A slow or missing file still gets a fallback,
     just an honestly delayed one rather than an instant, unconditional flash. */
  opacity: 0;
  animation: cb-brand-word-in var(--cb-motion-fast) var(--cbc-spring) var(--cb-motion-slow) forwards;
}
.cb-brand-word b {
  color: var(--cbc-accent);
  font-weight: var(--cb-font-weight-semibold);
}
/* Once a real logo has loaded, the type steps aside rather than being removed:
   it stays in the accessibility tree as the accessible name of the link. This
   also cancels the delayed reveal above outright, at whatever point it is in,
   rather than letting an already-running animation paint over the logo. */
.cb-brand-art[data-has-logo] .cb-brand-word {
  animation: none;
  opacity: 0;
}
.cb-brand-logo {
  position: absolute;
  inset-inline-start: 0;
  max-block-size: var(--cb-space-4xl);
  max-inline-size: 100%;
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cbc-spring);
}
/* Ready means it actually loaded. A file that 404s never fires load, so its
   image stays invisible and the wordmark is what remains.

   THESE THREE SELECTED data-variant AND NOTHING HAS EVER WRITTEN ONE. brand.ts
   sets data-for, so neither theme rule matched, both images sat at opacity 1
   stacked on each other, and the scarlet-on-white file is appended second and
   therefore painted on top: the dark rail has been wearing the light lockup
   since the day the attribute was renamed. The pairing was inverted as well,
   so fixing only the attribute name would have shipped a white wordmark on a
   white ground. The name each file is FOR is the theme it belongs to. */
.cb-brand-logo[data-ready] {
  opacity: 1;
}
[data-theme="light"] .cb-brand-logo[data-for="dark"] {
  opacity: 0;
}
[data-theme="light"] .cb-brand-logo[data-for="light"][data-ready] {
  opacity: 1;
}
:root:not([data-theme="light"]) .cb-brand-logo[data-for="light"] {
  opacity: 0;
}
.cb-brand-sub {
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-medium);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-sm);
  color: var(--cbc-ink-faint);
}

/* ---- Search ----
   A button that looks like a field. A real field here would be a second place
   to type that behaves differently from the palette's own box; this is one
   search with two ways in, and it shows the keystroke so the second way is
   learnable rather than hidden. */
.cb-search {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  width: 100%;
  padding: var(--cb-space-md) var(--cb-space-lg);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-surface);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-sm-size);
  text-align: start;
  cursor: pointer;
  transition:
    border-color var(--cb-motion-fast) var(--cbc-spring),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-search:hover {
  border-color: var(--cbc-line-strong);
  color: var(--cbc-ink-muted);
  background: var(--cbc-hover);
}
.cb-search .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
.cb-search-label {
  margin-inline-end: auto;
}
.cb-search-keys {
  display: flex;
  align-items: center;
  gap: var(--cb-space-xxs);
  padding: 0 var(--cb-space-sm);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-sm);
  font-size: var(--cb-type-xs-size);
  font-family: var(--cb-font-mono);
}
.cb-search-keys .cb-icon {
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}

/* ---- A keycap ----
   One drawing of one thing: every <kbd> that keycap.ts hands out
   (admin/src/keycap.ts). It was drawn in four places before this, the palette's
   footer legend, the palette's own esc, the walkthrough's legend, and nowhere
   at all in the assistant, and the four disagreed on padding, radius and ink.
   The walkthrough teaches the search shortcut with a keycap and the palette it
   opens shows the same shortcut in a differently drawn one seconds later.

   Bare element selectors scoped by an ancestor are invisible to check:classes,
   which is how four of them stayed green. Classed, the component is under that
   guard for the first time.

   Ink at full strength, and the words beside it left faint by whichever row
   holds them: the cap is the thing being taught, so it should not be the
   quieter half of its own legend. */
.cb-keycap {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-xxs);
  padding: var(--cb-space-xxs) var(--cb-space-sm);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-sm);
  color: var(--cbc-ink);
  font-family: var(--cb-font-mono);
  font-size: var(--cb-type-xs-size);
}
/* The Command glyph inside one, sized off the type rather than off a spacing
   token so it sits on the letter's own line. */
.cb-keycap-mark {
  inline-size: var(--cb-type-xs-size);
  block-size: var(--cb-type-xs-size);
}

/* ---- How the rail arrives ----
   Four beats, read down the column: the mark, the way to find anything, where
   you can go, and what the console has to say about itself. One per direct
   child, so the queue is the rail's own reading order and shell.ts hands out
   the seats in the order it appends them.

   THE GROUND IS NOT ONE OF THE BEATS. The rail's colour is painted by
   .cb-console::before, not by .cb-rail, so the panel is already there before
   anything is in it. All that is left to do is settle the contents into a
   container that has arrived, which is the shape every entrance here is meant
   to have.

   What this replaced: the six nav links sliding in one at a time on a 28ms
   literal, while the mark, the search, the live card, the bell and the account
   all snapped into place around them. Two things wrong with it. Half a panel
   moving is worse than none of it, because the still half reads as the page and
   the moving half reads as a fault laid over it. And the links are what a rail
   is FOR, so the one part that animated was the part that arrived last.

   Six delays became four on purpose. The rail is a menu, not a list of
   findings: the rows are the same six every time anybody signs in, and
   unrolling them makes a person wait to read something they already know. New
   information earns a stagger. Navigation does not.

   No transform survives this. cbc-slide-in is opacity and a few pixels, the
   fill is backwards, and the bell's panel below is position: fixed. A transform
   left on an ancestor would quietly become its containing block. */
.cb-rail > * {
  animation: cbc-slide-in var(--cb-motion-slow) var(--cbc-spring) backwards;
  animation-delay: var(--cbc-step);
}

/* ---- Navigation ----
   TWO PILES UNDER TWO HEADINGS. It was one list under none, and that was right
   while it was true: a heading earns its place when it tells you which of two
   piles to look in. In the box is the tool; on the shelf is the named things
   with an audience outside this console (nav.ts holds the rule and the list).
   The gap between the piles is the rail's own section spacing, so the two read
   as two sections of the rail rather than as one list with words in it. */
/* minmax(0, 1fr), NOT the implicit auto track, and this file has paid for that
   lesson once already: see the note over `.cb-live`, where a grid's automatic
   column sized to one long domain's max-content and made a group 283px wide
   inside a 199px rail. An `auto` track will not shrink below its content, so
   any row that wants a couple of pixels more than the rail has takes the whole
   column with it, and `.cb-rail-scroll` is a scroll container (it has to be,
   for the vertical overflow) so the overshoot arrives as a HORIZONTAL SCROLLBAR
   on a vertical menu. Measured: every row laying out at 201 in a 199 box.
   A `1fr` track floored at zero cannot do that. */
.cb-nav-piles {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--cb-space-lg);
}
.cb-nav {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--cb-space-xxs);
}
/* The first heading sits directly under the search, where the list used to
   start, so it takes back the top padding .cb-nav-group carries for the gap
   between a list and the heading of the next one. */
.cb-nav-pile:first-child > .cb-nav-group {
  padding-block-start: 0;
}
.cb-nav-group {
  padding: var(--cb-space-lg) var(--cb-space-lg) var(--cb-space-sm);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
}
.cb-nav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  padding: var(--cb-space-md) var(--cb-space-lg);
  border-radius: var(--cb-radius-lg);
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-medium);
  color: var(--cbc-ink-muted);
  text-decoration: none;
  /* The fill and the words move at different speeds on purpose: the background
     answers immediately, the label and its icon follow a beat behind on the
     overshoot curve. That difference is most of what makes a pointer feel like
     it is pushing something rather than toggling a class. */
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-base) var(--cbc-spring);
}
.cb-nav a .cb-icon {
  transition: transform var(--cb-motion-base) var(--cbc-spring);
}
/* Named so the narrow rail can hide the words without hiding the marks. It was
   `.cb-nav a span`, and `icon()` returns a span, so under 1024px the rail was
   six empty rows. */
.cb-nav-label {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-nav a:hover {
  color: var(--cbc-ink);
  background: var(--cbc-hover);
}
.cb-nav a:hover .cb-icon {
  transform: translateX(var(--cb-space-xxs));
}
/* Where you are is the filled row and the accent glyph, and nothing else.
   There was an accent bar down its left edge; see docs/10 on rails. */
.cb-nav a[aria-current="page"] {
  color: var(--cbc-ink);
  background: var(--cbc-surface);
  font-weight: var(--cb-font-weight-semibold);
  border: 1px solid var(--cbc-line);
}
.cb-nav a[aria-current="page"] .cb-icon {
  color: var(--cbc-accent);
}
/* The other rows carry the same border in nothing, so the fill does not shift
   the label by a pixel when it arrives. */
.cb-nav a {
  border: 1px solid transparent;
}
.cb-nav svg {
  width: 100%;
  height: 100%;
  flex: none;
}
.cb-nav-count {
  margin-inline-start: auto;
  padding: 0 var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent-wash);
  color: var(--cbc-accent);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
}

/* ---- The shelf tools' lockups ----
   In the box is the tool and nobody outside the agency has heard of any of it,
   so those rows are a mark and a word. On the shelf is the named things, and a
   named thing has a logo: Momentum and Grapevine are what an account lead puts
   in front of a client, so those rows draw the lockup instead. src/tool-marks.ts
   builds them and assets/README.md says what to call the files.

   THE FALLBACK IS WHAT GIVES THE ROW ITS HEIGHT, which is the whole reason the
   images are taken out of flow. Six lockups drawn at their own aspect ratios
   would be six different row heights, and a rail whose rows are different sizes
   depending on which artwork has arrived is not a rail. The typed name sets one
   height for all of them, every image is hung on it, and a tool with no file yet
   simply keeps the type. The brand mark at the top of the rail has always worked
   this way; this is the same mechanism with two numbers per mark on it.

   AN ARTBOARD IS NOT AN OPTICAL SIZE. Momentum is a light wordmark at 10.4:1
   and Grapevine a bold condensed one at 4.2:1, so one width or one height makes
   one of them half again the size of the other. --cbc-mark-fit is each file's
   own share of the width, tuned so the LETTERS match rather than the boxes, and
   --cbc-mark-lift is how far to raise it so those letters land on the same line
   as everything else's. Both are measured in tool-marks.ts, which is also where
   the argument for measuring them lives. */
/* ONE WIDTH FOR EVERY MARK, AND IT IS WHY THE RAIL NO LONGER SCROLLS SIDEWAYS.
   This was `100% * fit`, and the four marks with no artwork carried a fit of 1,
   so their lockup asked for the WHOLE row and the chevron beside it pushed the
   flex line past the rail: `.cb-rail-scroll` grew a horizontal scrollbar, on a
   vertical menu, permanently. A percentage of a flex line is not the space
   left in that line.

   --cbc-mark-span is Momentum's width, which every mark now shares (the six are
   drawn as one set; src/tool-marks.ts). It is under the row by enough to clear
   the gap and the chevron, so the line cannot overflow by construction rather
   than by everybody remembering. */
.cb-lockup {
  position: relative;
  display: block;
  flex: none;
  min-inline-size: 0;
  /* Only as wide as the word, until there is a mark to be wide FOR. Held at the
     mark's width unconditionally, "Bonbonnière" came out as "BONBONNI..." on
     the one theme that has no artwork for it: a shared width is a promise about
     six drawings, and it says nothing about a piece of type standing in for
     one. */
  inline-size: auto;
}
.cb-lockup[data-has-light],
.cb-lockup[data-has-dark] {
  inline-size: var(--cbc-mark-span);
}
/* Set as a wordmark, not as a nav label: this is standing in for a logo, so it
   is the name in small caps with the rail heading's tracking on it rather than
   the same sentence case as the rows above. */
.cb-lockup-word {
  display: block;
  font-size: var(--cb-type-md-size);
  font-weight: var(--cb-font-weight-bold);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  line-height: var(--cb-type-sm-line-height);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* CENTRED ON ITS LETTERS, NOT ON ITS BOX. A percentage in translateY resolves
   against the element's OWN height, which is exactly the height the lift is a
   share of, so the whole correction is this one term and the stylesheet needs
   to know nothing about any particular mark (tool-marks.ts measures it).

   The overhang costs no height. These are out of flow and the typed name is
   what holds the row open, so Grapevine's grapes rise into the padding above
   without moving a row, a header or anything beside them. */
.cb-lockup-logo {
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 50%;
  transform: translateY(calc(-50% - var(--cbc-mark-lift, 0) * 100%));
  inline-size: 100%;
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cbc-spring);
}
/* Once a real lockup has loaded the type steps aside rather than being removed:
   it is what holds the row open, and taking it out of flow would collapse it.
   Either file counts, because either can be drawn (see below). */
.cb-lockup[data-has-light] .cb-lockup-word,
.cb-lockup[data-has-dark] .cb-lockup-word {
  visibility: hidden;
}

/* ---- WHICH OF THE TWO FILES IS DRAWN ----
   Ready means it actually fired load. A file that 404s never does, so a tool
   with artwork for one theme only still has exactly one image to show.

   THE THEME'S OWN FILE WINS, AND THE OTHER ONE STANDS IN WHEN THERE IS NONE.
   The pair exists so a mark never has to sit on a ground it was not drawn for,
   and that is still the goal; but most of these tools have one file so far, and
   a logo slightly off its ground is a better answer than no logo at all. The
   moment the missing half is dropped into assets/ it takes over here with no
   other change.

   --shown, rather than the literal 1, is what lets the hover dim work. Setting
   opacity on the images directly meant the hover rule out-specified the two
   rules that HIDE the wrong-theme file, so hovering Snackbar (the one tool with
   both files) faded its other logo in on top of the first and the mark appeared
   to change. One custom property, set on the lockup, cannot do that: a hidden
   image is opacity 0 and stays 0 whatever --shown says. */
.cb-lockup {
  --shown: 1;
}
.cb-lockup-logo[data-for] {
  opacity: 0;
}
[data-theme="light"] .cb-lockup-logo[data-for="light"][data-ready],
:root:not([data-theme="light"]) .cb-lockup-logo[data-for="dark"][data-ready] {
  opacity: var(--shown);
}
[data-theme="light"] .cb-lockup:not([data-has-light]) .cb-lockup-logo[data-for="dark"][data-ready],
:root:not([data-theme="light"])
  .cb-lockup:not([data-has-dark])
  .cb-lockup-logo[data-for="light"][data-ready] {
  opacity: var(--shown);
}

/* A SHELF ROW IS ITS LOGO, AND IT STILL CARRIES THE GLYPH. Below the lg
   breakpoint the rail is eighty pixels wide, every label is hidden and each row
   is one mark in the middle of it; a wordmark cannot be that mark. Both are
   built (rail-nav.ts) and this is what swaps them, so the swap cannot come
   apart from the breakpoint that hides the labels. The lockup takes the row's
   full width because that is what makes it as large as it can be: a logo
   indented to where a label would start is a logo made small to line up with
   type that is no longer there. */
.cb-nav a[data-shelf] > .cb-icon {
  display: none;
}
.cb-nav a[data-shelf] {
  padding-block: var(--cb-space-lg);
}
/* NO FILL UNDER A LOGO. Every other row answers a pointer with a wash, and
   that is right for a word in the console's own voice; under somebody's mark it
   puts a coloured plate behind artwork that was drawn to sit on the rail's own
   ground, and the two-file light/dark pair exists precisely so a mark never has
   to sit on a surface it was not drawn for. The chevron is the answer here
   instead, which is the better one anyway: it says where the row goes as well
   as that it is live. The row you are ON keeps its fill, because that is state
   rather than hover and it has to be legible without a pointer. */
.cb-nav a[data-shelf]:hover {
  background: none;
}
/* The mark itself takes the pointer, a tenth of the way out of the ground. It
   is the smallest thing that can stand in for the fill this row does not have:
   much less and the row reads as inert, much more and the logo reads as loading
   rather than as answering. Set on the lockup rather than on the image, so a
   file this theme is not drawing stays at zero (see --shown above). Only the
   image dims: the typed fallback is real text with a contrast floor to keep. */
.cb-nav a[data-shelf]:hover .cb-lockup {
  --shown: 0.9;
}
/* THE CHEVRON, AND WHY ONLY THESE ROWS HAVE ONE. Every other row is a word in
   the console's own voice beside a glyph, and a word in a menu is plainly a
   destination. A shelf row is a piece of somebody's branding sitting in a
   menu, which reads as a badge or a heading until something points; the mark
   at the end is what says it goes somewhere.

   IT IS ALSO THE ROW'S ANSWER TO A POINTER. Every other row nudges its glyph a
   couple of pixels under the fill, and a shelf row has no glyph on screen to
   nudge, so the hover was the background alone and the shelf read as the one
   part of the rail that had stopped responding. The chevron moves rather than
   the lockup, which is the right way round: a logo that slides on hover is a
   logo being animated, and the affordance is what should advance. */
.cb-nav-on {
  margin-inline-start: auto;
  display: flex;
  flex: none;
  /* Its own size rather than `.cb-icon`'s, because it is not the row's mark and
     must not be counted as one (rail-nav.ts). Smaller than a mark on purpose:
     it is punctuation at the end of the row, not a second thing to read. */
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
  color: var(--cbc-ink-muted);
  /* NOT THERE UNTIL YOU GO NEAR IT. A control shows its affordance when you
     approach it and not before (rule 14): six permanent chevrons down a rail
     are six marks nobody reads, and they turn a column of logos back into a
     column of furniture. It slides the last few pixels in as it appears, which
     is what makes it read as answering the pointer rather than blinking on.

     Opacity rather than display, so the space stays reserved and no logo moves
     when it arrives. Keyboard focus counts as approaching: without that clause
     a person tabbing the rail is the one person who never sees it. */
  opacity: 0;
  transform: translateX(calc(var(--cb-space-xs) * -1));
  transition:
    opacity var(--cb-motion-fast) var(--cb-motion-ease),
    transform var(--cb-motion-base) var(--cbc-spring);
}
.cb-nav a[data-shelf]:hover .cb-nav-on,
.cb-nav a[data-shelf]:focus-visible .cb-nav-on {
  opacity: 1;
  transform: none;
}
.cb-nav a[data-shelf][aria-current="page"] .cb-nav-on {
  color: var(--cbc-accent);
}
/* A row that goes nowhere may not promise that it does. */
.cb-nav a[data-shelf][aria-disabled="true"]:hover .cb-nav-on {
  opacity: 0;
}
/* The lockup stops filling the row once something sits at the end of it. */
.cb-nav a[data-shelf] {
  gap: var(--cb-space-md);
}
.cb-nav-on svg {
  inline-size: 100%;
  block-size: 100%;
}

/* A ROW FOR SOMETHING NOT BUILT YET, turned off rather than left out (rule 15).
   Taking it away would move every row beside it the week the screen lands, and
   would leave nobody anywhere to ask what happened to a thing they were told
   about. It keeps its href so a keyboard can still reach the sentence that
   explains it; what is taken away is the arrival and the hover, because a row
   that answers a pointer is a row that says it will do something.

   Dimmed rather than recoloured: a disabled control is exempt from WCAG 1.4.3
   (inactive components), which is why this may use opacity where nothing else
   carrying words in this file may. */
.cb-nav a[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}
.cb-nav a[aria-disabled="true"]:hover {
  background: none;
  color: var(--cbc-ink-muted);
}
.cb-nav a[aria-disabled="true"]:hover .cb-icon {
  transform: none;
}

/* ---- The settings sub-list ----
   Its own section under the navigation, headed by the word "Settings", and only
   present while you are in there. It used to hang off the Settings row,
   indented, which is what said "these belong to the item above": that row is a
   mark at the foot of the rail now, so the indent belonged to nothing and the
   heading does that job instead. It used to be a rule down the left before
   that; see docs/10 on rails. Each link arrives a beat after the one above it,
   so opening the section reads as a list unrolling. */
.cb-nav-settings[hidden] {
  display: none;
}
.cb-nav-sub {
  display: grid;
  gap: 1px;
  margin-block-end: var(--cb-space-md);
}
.cb-nav-sub-link {
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border-radius: var(--cb-radius-md);
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  text-decoration: none;
  transition:
    color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cb-motion-ease);
  animation: cbc-slide-in var(--cb-motion-base) var(--cbc-spring) backwards;
  /* --cbc-step, not a hand-typed step. The 22ms literal here was the same
     mistake the comment ninety lines above records deleting at 28ms, and it had
     the worse half of it too: no cap. The token stops at --cb-motion-base,
     which is the point past which the eye reads a stagger as slow rather than
     as sequence, and the settings list is long enough to reach it. */
  animation-delay: var(--cbc-step);
}
.cb-nav-sub-link:hover {
  color: var(--cbc-ink);
  background: var(--cbc-hover);
}
.cb-nav-sub-link[aria-current="page"] {
  color: var(--cbc-ink);
  font-weight: var(--cb-font-weight-medium);
  background: var(--cbc-surface);
}

/* ---- What is running right now ----
   A labelled group in the rail, not a card on it.

   The rail is flat rows under small-caps headings. Wrapping this in a tinted
   box made it the third different surface in a corner that already had two, and
   in the light theme the tint on a tinted rail read as a slab of peach. Nothing
   here is filled: the heading matches the two above it, the rows match the
   navigation rows, and the only colour is on the one word that carries meaning.

   Stalled says stalled in amber and the bars stop. That is the whole signal. */
/* minmax(0, 1fr), not the implicit auto track.
   A grid's automatic column sizes to its content's max-content width and will
   not shrink below it, so one long domain made the whole group 283px wide
   inside a 199px rail: the elapsed clock sat outside the window and the rail
   grew a horizontal scrollbar. This is the entire reason the bottom of the rail
   looked like it was falling out of its own column. */
/* SHUT IS A COLLAPSED ROW, NOT `hidden`. This group appears the moment the
   first poll answers, and the bell and the account sit directly under it: with
   display:none the bottom of the rail grew a heading and three rows in one
   frame, and whatever the pointer was travelling towards was somewhere else by
   the time it arrived. Opening the track pushes the corner instead.

   The recipe is the notice fold's, and the reason the clipping has to be on the
   inner element rather than on this grid is written out over
   .cb-notice-fold-body in console-notices.css. In short: a `1fr` row is
   minmax(auto, 1fr), and an item that is both clipped and floored at zero
   contributes nothing, so the open track resolves to nothing too.

   `inert` is what shut means, again the same as the fold: the rows leave the
   tab order and the accessibility tree without leaving the document, which is
   what leaves the group something to shrink. */
.cb-live {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 1fr;
  transition:
    grid-template-rows var(--cb-motion-base) var(--cbc-spring),
    margin-block-end var(--cb-motion-base) var(--cbc-spring);
}
.cb-live[inert] {
  grid-template-rows: 0fr;
  /* The rail's foot is a grid with a gap of its own, and a collapsed row is
     still a row: without this the bell sat one gap lower whenever nothing was
     scanning, which is the same displacement this rule exists to stop, only
     permanent. It travels with the track so the two never disagree. */
  margin-block-end: calc(var(--cb-space-lg) * -1);
}
.cb-live-in {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--cb-space-xxs);
  min-block-size: 0;
  overflow: hidden;
}
.cb-live-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-lg) var(--cb-space-lg) var(--cb-space-sm);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
}
/* Four bars running as a wave rather than blinking together: work being done,
   not an alarm going off. */
.cb-live-bars {
  display: flex;
  align-items: flex-end;
  gap: var(--cb-space-xxs);
  block-size: var(--cb-space-lg);
  flex: none;
}
.cb-live-bars i {
  inline-size: var(--cb-space-xxs);
  block-size: 100%;
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent);
  transform-origin: bottom;
  animation: cbc-wave var(--cb-motion-work-loop) var(--cb-motion-ease) infinite;
  animation-delay: calc(var(--i, 0) * var(--cb-motion-work-phase));
}
/* Nothing is running that is going to finish, so the bars stop. A card
   animating away while claiming a dead scan is working is worse than one that
   says nothing. */
.cb-live[data-stranded] .cb-live-bars i {
  background: var(--cbc-warn);
  animation: none;
  transform: scaleY(0.4);
}
/* ---- The wave, stilled ----
   ON .cb-live-bars, not on one caller's copy of it. This override lived on
   .cb-scoop-wave, so the assistant went still under reduced motion and the rail's
   live card and the command palette did not: the global reduce block cuts the
   animation but leaves block-size at 100%, which draws four solid full-height
   accent bars. That is not a still wave, it is a meter reading full, on the two
   surfaces whose whole job in that moment is to say "still working".

   AFTER the rule it overrides. A media query adds no specificity, so placed
   above it this lost to the very declaration it exists to replace. */
@media (prefers-reduced-motion: reduce) {
  .cb-live-bars i {
    animation: none;
    transform: scaleY(0.55);
    opacity: 0.7;
  }
  /* A group that opens in a millisecond is still a thing that moved. Asked to
     stop animating, it simply is open or is not, the same answer the notice
     fold gives. */
  .cb-live {
    transition: none;
  }
}
/* AND THE CONSOLE'S OWN SWITCH, which is a separate question from the system's:
   this is for the person who wants their photos to animate and their work tool
   to sit still. The blanket in console-theme.css cuts durations to a
   millisecond, which is not the same as this: the bars here have to STOP at a
   readable height rather than sprint to the end of their loop.
   After the media query for the reason written above it: a media query adds no
   specificity, so both of these have to sit below the rules they replace. */
:root[data-motion="reduced"] .cb-live-bars i {
  animation: none;
  transform: scaleY(0.55);
  opacity: 0.7;
}
:root[data-motion="reduced"] .cb-live {
  transition: none;
}

.cb-live-rows {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1px;
}
.cb-live-row {
  display: flex;
  align-items: center;
  min-width: 0;
}
.cb-live-open {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  flex: 1 1 auto;
  min-width: 0;
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border-radius: var(--cb-radius-md);
  text-decoration: none;
  color: inherit;
  transition: background var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-live-open:hover {
  background: var(--cbc-hover);
}
.cb-live-site {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The clock and the way to stop the scan, sharing one slot of a fixed width.
   Genuinely sharing it: they sit on top of each other and trade places on
   hover, rather than sitting side by side with both widths reserved. Side by
   side the slot had to be eighty pixels for two things only one of which is
   ever wanted, and in a rail this narrow those pixels come straight out of the
   domain, which is the one part of the row worth reading.

   Stacked in a grid cell was the first attempt and it only worked by luck of
   how wide the words happened to be. A fixed width and absolute placement is
   the same idea with the luck taken out. */
.cb-live-slot {
  position: relative;
  flex: none;
  inline-size: var(--cb-space-7xl);
  block-size: var(--cb-space-3xl);
}
.cb-live-time {
  position: absolute;
  inset-block: 0;
  inset-inline-end: var(--cb-space-xs);
  display: flex;
  align-items: center;
  font-size: var(--cb-type-xs-size);
  font-variant-numeric: tabular-nums;
  color: var(--cbc-ink-faint);
  white-space: nowrap;
  transition: opacity var(--cb-motion-fast) var(--cbc-spring);
}
/* The clock steps aside for the button rather than shuffling along beside it. */
.cb-live-row:hover .cb-live-time,
.cb-live-row:focus-within .cb-live-time {
  opacity: 0;
}
.cb-live-time[data-tone="warn"] {
  color: var(--cbc-warn-text);
  font-weight: var(--cb-font-weight-semibold);
}
/* A stalled scan is the one case where both belong on screen at once: the word
   is the signal and the cross is what you came to press. So the clock moves
   over rather than fading, and keeps its place whether the pointer is on the
   row or not. */
.cb-live[data-stranded] .cb-live-time {
  inset-inline-end: var(--cb-space-3xl);
}
.cb-live[data-stranded] .cb-live-row:hover .cb-live-time,
.cb-live[data-stranded] .cb-live-row:focus-within .cb-live-time {
  opacity: 1;
}
.cb-live-more {
  padding: var(--cb-space-xs) var(--cb-space-lg) 0;
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
}

/* Stopping a scan from the rail. The list is where you find out something is
   stuck, so it is where you should be able to deal with it. Hidden until the
   row is hovered, because a column of crosses reads as a list of things to
   delete rather than as a list of what is happening. */
.cb-live-stop {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  border: 0;
  border-radius: var(--cb-radius-md);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity var(--cb-motion-fast) var(--cbc-spring),
    color var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-live-stop .cb-icon {
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}
.cb-live-row:hover .cb-live-stop,
.cb-live-stop:focus-visible {
  opacity: 1;
}
.cb-live-stop:hover {
  color: var(--cbc-poor-text);
  background: var(--cbc-accent-wash);
}
/* A stalled scan is the one you almost certainly want to stop, so its cross is
   not waiting to be discovered. */
.cb-live[data-stranded] .cb-live-stop {
  opacity: 1;
}

/* ---- A website's own mark ----
   Its favicon if it serves one at the root, its first letter otherwise. The
   letter draws first and is the normal case, because plenty of sites declare
   their icon in markup and serve nothing at /favicon.ico. */
.cb-site-icon {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
  border-radius: var(--cb-radius-sm);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
  overflow: hidden;
  /* One hue stepped through, so a site keeps its colour without the rail
     turning into six unrelated brand palettes. */
  background: color-mix(
    in srgb,
    var(--cbc-accent) calc(22% - var(--tint, 0) * 2%),
    var(--cbc-sunken)
  );
  color: var(--cbc-ink-muted);
}
.cb-site-icon[data-tint="1"] {
  --tint: 1;
}
.cb-site-icon[data-tint="2"] {
  --tint: 2;
}
.cb-site-icon[data-tint="3"] {
  --tint: 3;
}
.cb-site-icon[data-tint="4"] {
  --tint: 4;
}
.cb-site-icon[data-tint="5"] {
  --tint: 5;
}
.cb-site-icon img {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cbc-spring);
}
.cb-site-icon[data-has-icon] img {
  opacity: 1;
}
/* Once a real icon is showing, the tinted disc behind it would be a coloured
   square around somebody else's logo. */
.cb-site-icon[data-has-icon] {
  background: var(--cbc-sunken);
  color: transparent;
}

/* ---- The rail's tool row ----
   Four marks: appearance, settings, activity, the bell. They are not places to
   go, which is why they are not rows. The rail above is a list of what the tool
   has found, one named row each, and Settings and Activity used to sit in it
   under a heading reading "Running it": two more rows of the same weight as
   Submissions, for two screens somebody opens once a week. Together at the
   foot they take one row's height between them, and the difference in weight
   between a word and a mark is most of what makes a rail readable at a glance.

   Four equal cells rather than four centred squares, so the row reads as one
   thing and every target is the same size wherever the pointer lands in it.
   Each carries the same transparent border every rail row does, so the fill
   that arrives with hover or with the open state shifts nothing. */
.cb-rail-tools {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--cb-space-xs);
}
.cb-rail-tool {
  position: relative;
  display: grid;
  place-items: center;
  block-size: var(--cb-space-5xl);
  border: 1px solid transparent;
  border-radius: var(--cb-radius-lg);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-base) var(--cbc-spring);
}
.cb-rail-tool .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
.cb-rail-tool:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
/* Where you are, marked exactly as a navigation row is: the surface, the edge
   and the accent glyph. Being inside settings lights the gear for every screen
   underneath it, which is what makes the settings list appearing above it read
   as belonging to something. */
.cb-rail-tool[aria-current="page"] {
  background: var(--cbc-surface);
  border-color: var(--cbc-line);
  color: var(--cbc-accent);
}

/* ---- The account ----
   A secondary button, not a bare row. It is the one thing in the rail that
   opens something rather than going somewhere, and at the bottom of a column of
   flat navigation rows a flat row is invisible: it read as a caption of who was
   signed in rather than as a control.

   No rule above it. The button's own border already separates it from the
   navigation, and a hairline plus a border a few pixels apart is two lines
   doing one job, which is most of what made this corner look stacked. */
.cb-account {
  position: relative;
}
.cb-account-trigger {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  width: 100%;
  /* Symmetric. The chevron is a 16px box and the avatar a 32px disc, so equal
     padding on both sides is what puts equal air at both ends of the button. */
  padding: var(--cb-space-md);
  border: 1px solid var(--cbc-line);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-surface);
  text-align: start;
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-account-trigger:hover,
.cb-account-trigger[aria-expanded="true"] {
  background: var(--cbc-raised);
  border-color: var(--cbc-line-strong);
}
.cb-account-trigger > .cb-icon {
  margin-inline-start: auto;
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
  color: var(--cbc-ink-faint);
}
.cb-account-who {
  display: grid;
  min-width: 0;
}
.cb-account-name {
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-medium);
  color: var(--cbc-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-account-role {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Somebody's face, or their initials. Never an empty grey disc. */
.cb-avatar {
  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-wash);
  color: var(--cbc-accent);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
  overflow: hidden;
}
.cb-avatar-photo {
  position: absolute;
  inset: 0;
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--cb-motion-base) var(--cbc-spring);
}
.cb-avatar[data-has-photo] .cb-avatar-photo {
  opacity: 1;
}

/* ---- The account menu ----
   Rendered into <body>, beside the trigger: the rail scrolls, and anything
   positioned inside a scrolling box is clipped by it.

   Six rows, one height, and none of them can wrap. It was 256px, which fitted
   "Keys and connections" with ten pixels to spare and therefore did not fit it
   at all on a machine rendering type a shade larger. A menu one font-size bump
   from breaking is a menu that will break on somebody's screen and not on
   yours, so the labels are told not to wrap and the panel is given room. */
.cb-account-panel {
  position: fixed;
  z-index: var(--cbc-z-account);
  inline-size: max-content;
  min-inline-size: var(--cb-width-xxs);
  max-inline-size: var(--cb-width-xs);
  padding: var(--cb-space-xs);
  border-radius: var(--cb-radius-xl);
  background: var(--cbc-raised);
  border: 1px solid var(--cbc-line);
  box-shadow: var(--cbc-shadow);
  animation: cbc-pop var(--cb-motion-base) var(--cbc-spring) backwards;
}
.cb-account-group + .cb-account-group {
  margin-block-start: var(--cb-space-xs);
  padding-block-start: var(--cb-space-xs);
  border-top: 1px solid var(--cbc-line);
}

/* ---- Who you are, at the head of the menu ----
   The menu used to open on its rows, and the argument for that was sound while
   it held five destinations: the identity is on the button you pressed to get
   here. What is left is about the person, so the person goes first. It is not
   the trigger twice over either: this carries the ADDRESS, which is the one
   fact that settles which account you are signed in as. */
.cb-account-me {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  padding: var(--cb-space-lg);
}
.cb-account-me .cb-avatar {
  inline-size: var(--cb-space-5xl);
  block-size: var(--cb-space-5xl);
  font-size: var(--cb-type-sm-size);
}
.cb-account-me-who {
  display: grid;
  min-inline-size: 0;
}
.cb-account-me-name {
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-account-me-mail {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- The company, over the rows about other people ----
   The same small caps the rail puts over a list, because it is doing the same
   job: it says what the rows under it belong to. The seat sits at the far end
   because this is the line it answers a question about. */
.cb-account-org {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-md);
  padding: var(--cb-space-md) var(--cb-space-lg) var(--cb-space-xs);
  font-size: var(--cb-type-xs-size);
}
.cb-account-org-name {
  font-weight: var(--cb-font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--cb-tracking-xxs);
  color: var(--cbc-ink-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cb-account-org-seat {
  margin-inline-start: auto;
  color: var(--cbc-ink-faint);
  white-space: nowrap;
}

/* One row height for everything in here. A menu whose rows are different
   heights reads as two menus. */
.cb-account-link {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
  width: 100%;
  block-size: var(--cb-space-5xl);
  padding-inline: var(--cb-space-lg);
  border: 0;
  border-radius: var(--cb-radius-md);
  background: none;
  color: var(--cbc-ink-muted);
  font-size: var(--cb-type-sm-size);
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
}
.cb-account-link {
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-account-link:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
.cb-account-link .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
  color: var(--cbc-ink-faint);
}
.cb-account-link:hover .cb-icon {
  color: currentColor;
}
/* Signing out sits under a rule, in the same ink as everything else. Red at
   rest would make the last item in the menu the loudest thing in it, and it is
   not a warning: it is the way out. It turns red under the pointer, which is
   the moment it is about to happen. */
.cb-account-link[data-tone="danger"]:hover {
  color: var(--cbc-poor-text);
}
.cb-account-link[data-tone="danger"]:hover .cb-icon {
  color: var(--cbc-poor-text);
}

/* Two icon buttons in a well. Both states visible, the current one lifted out
   of the well onto the card surface, which is the whole of the affordance. */
.cb-theme-pick {
  display: flex;
  gap: var(--cb-space-xxs);
  padding: var(--cb-space-xxs);
  border-radius: var(--cb-radius-md);
  background: var(--cbc-sunken);
}
.cb-theme-option {
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-3xl);
  border: 0;
  border-radius: var(--cb-radius-sm);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-theme-option .cb-icon {
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}
.cb-theme-option:hover {
  color: var(--cbc-ink);
}
.cb-theme-option[aria-checked="true"] {
  background: var(--cbc-surface);
  color: var(--cbc-ink);
  box-shadow: var(--cbc-shadow);
}

/* ---- The theme, as one button ----
   THE MENU SHOWS THREE AND THIS SHOWS ONE, and the difference is the room. In
   the account menu all three fit side by side and both alternatives are visible,
   which is what that menu is for. In the foot of the first-run questions there
   is one seat going and it is shared with a caption, so it becomes one button
   that moves to the next of the three. What keeps that honest is words:
   appearance.ts gives it the state it is in and the state one press gives, as
   its tooltip and as its accessible name, so nobody presses it to find out.

   QUIET AT REST, because of the company it keeps. The row it sits in is a way
   back, a way out and a line of small grey type, and none of them is the thing
   to do next: a filled chip in that row would be the loudest thing in the foot
   of a screen whose whole point is the question above it. It takes a ground
   when the pointer arrives, which is the console's hover everywhere else, and
   it never moves anything. */
.cb-theme-cycle {
  display: grid;
  place-items: center;
  /* 32 round a 16 mark, so there is eight all round it. At 24 the icon sat four
     from every edge and the ground that comes up under the pointer was a collar
     on the glyph rather than a target behind it: the same shape the keycaps
     beside it in that row have, minus their room. The two keycaps are the thing
     to match down there, not the icon. */
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-sm);
  background: none;
  color: var(--cbc-ink-faint);
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-theme-cycle .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
.cb-theme-cycle:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}

/* ---- The foot of the rail ----
   Pushed to the floor. Its two children are a labelled group and an account
   row, both ordinary rail furniture, so the spacing between them is the rail's
   own rather than a gap between cards.

   This rule has now been deleted twice by edits to neighbouring blocks, and
   both times the only symptom was the account sitting two hundred pixels above
   the bottom of the screen with nothing under it. */
/* ---- The bottom of the rail ----
   Pushed to the floor, and holding the two things that belong there: what is
   running right now, and who is signed in. The gap is the account button's own
   breathing room, so the group reads as a footer rather than as the navigation
   continuing to the bottom of the window. */
.cb-rail-foot {
  /* Pinned by the flex layout rather than pushed by an auto margin: the
     scrolling region above takes the free space, so there is none left to
     push with. */
  flex: none;
  padding-block-start: var(--cb-space-xl);
  border-block-start: 1px solid var(--cbc-line);
  display: grid;
  /* Same reason as .cb-live: an automatic grid track will not shrink below its
     content, and everything in this corner has a domain or an address in it. */
  grid-template-columns: minmax(0, 1fr);
  gap: var(--cb-space-lg);
}


/* ---- EIGHTY PIXELS OF RAIL ----
   Below the lg breakpoint the rail narrows to its glyphs. Nothing in it is
   taken away (rule 15): the words go, the marks stay, and the two rows in the
   foot get smaller rather than vanishing. The account button in particular has
   been `display: none` down here once before, which is how the only way to
   sign out came to be unreachable on a narrow window.

   1024px mirrors tokens.breakpoint.lg. The literal is unavoidable: CSS cannot
   read a var() in a media prelude.

   THE COLUMN ITSELF NARROWS IN console.css, in the matching prelude there,
   because the grid track and the paint behind the rail belong to the frame.
   Both preludes have to agree, and there is no way for either file to know
   that the other one changed. */
@media (max-width: 1024px) {
  .cb-rail {
    padding-inline: var(--cb-space-md);
  }
  .cb-brand span,
  .cb-nav-label,
  .cb-nav-group {
    display: none;
  }
  .cb-nav a {
    justify-content: center;
  }
  /* The shelf's lockups go the way the labels go, and their glyphs come back.
     Eighty pixels of rail cannot hold a wordmark, and a row with nothing in it
     is worse than a row wearing a lucide mark for the afternoon. */
  .cb-nav a[data-shelf] > .cb-lockup {
    display: none;
  }
  /* `grid`, which is what `.cb-icon` is; not `block` and not `revert`. The
     glyph centres its svg with place-items, so restoring it as a block would
     leave the mark in the corner of its own twenty pixels, and `revert` goes to
     the user agent rather than to the rule above. */
  .cb-nav a[data-shelf] > .cb-icon {
    display: grid;
  }
  .cb-nav a[data-shelf] {
    padding-block: var(--cb-space-md);
  }
  /* AND THE FILL COMES BACK DOWN HERE, because down here there is no logo and
     no chevron: the row is one glyph in eighty pixels of rail, exactly like
     every other row, and without the wash it would be the only part of the rail
     that does not answer a pointer at all. */
  .cb-nav a[data-shelf]:hover {
    background: var(--cbc-hover);
  }
  /* The chevron goes with the lockup it belongs to: eighty pixels of rail has
     room for one mark, and the one worth keeping is the row's own. */
  .cb-nav-on {
    display: none;
  }
  /* The words and the keystroke go with everything else's; what is left is the
     magnifier, which is the mark this control is known by. It used to keep all
     three in eighty pixels of rail, so the field read "Sear" with the keycaps
     off the edge. */
  .cb-search {
    justify-content: center;
  }
  .cb-search-label,
  .cb-search-keys {
    display: none;
  }
  /* Four across does not fit eighty pixels of rail, so the row folds into two
     by two rather than being taken away. It was `.cb-account { display: none }`
     down here for the account, which is how the only way to sign out came to be
     unreachable on a narrow window; nothing in this corner is allowed to
     vanish, it just gets smaller (rule 15). */
  .cb-rail-tools {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  /* The name and the seat go, the face and the chevron stay: the button is
     still there, still opens the same menu, and the menu still says who you
     are on its first line. */
  .cb-account-who {
    display: none;
  }
  .cb-account-trigger {
    justify-content: center;
  }
}
