/* BonBonBot Console: THE PAGE HEADER, the block every screen opens with.

   Split out of console.css on the console-refresh branch, where it had been
   sharing a file with the rail, the buttons and the tooltip.

   WHAT IS HERE: the header row itself, the headline and the mask it rises
   through, the lockup that stands in for a headline on a screen with artwork,
   the lede, the provenance line at the far end, the actions row, and the
   staggered entrance that carries all of it.

   WHAT IS NOT. `.cb-main`, the measured column every screen is poured into,
   stays in console.css: it is the page, not the header, and the rail's other
   half of the frame is declared beside it.

   THE ONE THING TO KNOW BEFORE EDITING. `.cb-page-head` is built by shell.ts
   for the WHOLE app, so a rule here lands on every screen in the console.
   console-dossier.css once carried a second, unscoped `.cb-page-head` and
   loads afterwards, so its alignment and gap won everywhere rather than on
   the dossier, and the declarations written here had never applied anywhere
   at all. Nothing about that is visible from inside either file. If a screen
   needs its own header treatment, scope it to that screen rather than
   redeclaring the shared class in that screen's sheet.

   The entrance is deliberately not four entrances. The headline moves and
   everything else arrives on opacity alone, once the headline is underway,
   because a sentence and two buttons travelling as well turns one gesture
   into three and the eye ends up following the last thing to stop. `--i` is
   handed out in shell.ts in the order the parts are appended and `--cbc-step`
   turns a seat into a delay, so this is the same cadence as the rail and the
   board rather than a third one to keep in step with them.

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

/* THE VALUES THAT ACTUALLY SHIPPED, which were not the ones written here.
   console-dossier.css carried a second, UNSCOPED `.cb-page-head` and loads
   afterwards, so its `align-items: baseline` and `gap: sm` won on EVERY page
   header in the console rather than on the dossier's own: shell.ts builds this
   element for the whole app. The `flex-end` and `2xl` gap that used to be here
   had therefore never applied anywhere, and the margin below is the one
   declaration of this rule that was doing anything.
   Composed rather than chosen, so nothing moves. Worth knowing how it reads
   from the outside: a page-specific stylesheet silently restyling a global
   component is invisible until somebody diffs the two files. */
.cb-page-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--cb-space-sm);
  margin-bottom: var(--cb-space-5xl);
}
/* ---- How a page header arrives ----
   Read the way the header is read: the headline, the sentence under it, then
   the controls at the far end one at a time. The whole block used to rise as
   one, which is a header sliding onto the page rather than a header being set.

   --i is handed out in shell.ts in the order the parts are appended, and
   --cbc-step turns a seat into a delay, so this is the same cadence as the rail
   and the board rather than a third one to keep in step with them. */
.cb-page-head h1 {
  font-size: var(--cb-type-3xl-size);
  line-height: var(--cb-type-3xl-line-height);
  letter-spacing: var(--cb-type-3xl-letter-spacing);
  font-weight: var(--cb-font-weight-semibold);
  /* THE HEADING IS THE WINDOW AND THE SPAN IS WHAT RISES THROUGH IT. One
     element cannot clip its own text and move it at the same time.

     The padding is not spacing, it is headroom for descenders: at 36px in a
     44px line box the tail of a "y" sits below the line box, and an
     overflow: hidden that does not allow for it shaves the bottom off
     "Activity". The negative margin gives the space straight back, so the
     header occupies exactly what it did before. */
  overflow: hidden;
  padding-block-end: var(--cb-space-md);
  margin-block-end: calc(var(--cb-space-md) * -1);
}
/* ONE PIECE, THROUGH THE MASK ABOVE. It was one span per letter on its own
   delay, and shell.ts holds the three reasons that stopped being right: the
   header's timing became a function of how many letters somebody wrote, a
   heading became twenty-two inline-blocks with a non-breaking space standing in
   for every real one, and a title that assembles itself is the loudest thing on
   a screen whose point is what is under it.

   Fading as it rises rather than only rising. The mask alone is a hard edge
   wiping upward, which is right for a word being written and too emphatic for a
   word simply arriving; opacity over the same curve is what turns the wipe into
   a settle. */
.cb-h1 {
  display: block;
  animation: cbc-clip-up var(--cb-motion-slow) var(--cbc-spring) backwards;
}
/* Past its own height, not exactly its height: the descender headroom on the h1
   means a hundred percent still leaves the tail of a letter showing at the
   start. */
@keyframes cbc-clip-up {
  from {
    transform: translate3d(0, 115%, 0);
    opacity: 0;
  }
}
/* The lockup where the word would be, on the six screens that have one. The
   h1 is still the mask and the mark still rises through it, so a shelf screen
   and every other screen open the same way; what changes is what is drawn.

   SIZED OFF THE CAP HEIGHT, NOT OFF THE RAIL'S FIT, and that is the whole
   difference between the two surfaces. A rail row holds a mark beside five
   others, so its size is a question about its NEIGHBOURS and `fit` is somebody's
   judgement of mass. A header holds one mark and no neighbours, so its size is a
   question about the HEADLINE it stands in for, and cap height answers that
   exactly: divide the cap this header wants by the mark's own measured
   cap-per-unit-width and the width falls out. Nothing to tune, and a new file is
   right here the first time it is measured.

   It used to be `--cbc-mark-base * fit`, one number serving both, and the two
   surfaces pulled apart the moment Grapevine's rail fit went up for mass: the
   same number would have drawn a 36px cap under a headline whose cap is 25. */
.cb-h1-mark {
  /* Its own window, rather than borrowing the one on `.cb-page-head h1`. Two of
     the six shelf screens head themselves rather than calling pageHead (the
     Momentum pair), and a mark that rose through nothing there would slide in
     from below the header instead of out of it. */
  overflow: hidden;
  line-height: 0;
  /* HEADROOM FOR THE OVERHANG, the same trick and the same reason as the
     descender headroom on `.cb-page-head h1` above. A lifted mark rises above
     the line its letters sit on, and the window that makes the entrance work
     would shave the top off it: Grapevine lost the tips of its grapes by two
     pixels. The negative margin gives the space straight back, so the header
     occupies exactly what it did. */
  padding-block-start: var(--cb-space-lg);
  margin: calc(var(--cb-space-lg) * -1) 0 0;
}
.cb-h1-mark .cb-lockup {
  animation: cbc-clip-up var(--cb-motion-slow) var(--cbc-spring) backwards;
  /* Only as wide as the word, until there is a lockup to be wide for. The rail
     can hold its width open either way because every row there is the same
     width; a heading cannot, since the fallback here is a real headline and a
     three hundred pixel box would cut "Bonbonnière" with an ellipsis while the
     page had room for twice it. */
  inline-size: fit-content;
  max-inline-size: 100%;
}
.cb-h1-mark .cb-lockup[data-has-light],
.cb-h1-mark .cb-lockup[data-has-dark] {
  inline-size: var(--cbc-mark-span-head);
  /* NO PERCENTAGE CEILING, and the reason is a circularity that only shows up
     as a mark quietly coming out too small. `.cb-page-title` is a flex item
     sized by its content, and its content is this heading and the one-line
     sentence under it; the heading's width is what we are computing, so the
     browser settles the parent on the SENTENCE and a `min(100%, …)` here then
     clamps the mark to the length of a blurb. Momentum came out at 194px
     against the 265 its cap height asks for, and would have moved every time
     somebody reworded a lede. The width is a few hundred pixels at most, the
     h1 clips rather than overflows, and the typed fallback above keeps its own
     ceiling because that one is text and should stay inside the column. */
  max-inline-size: none;
}
/* AND WITH NO LOCKUP IT IS SIMPLY THE HEADLINE. The rail sets its fallback as a
   wordmark, because a rail row is not a headline and small caps read as a mark
   there. Here the thing it stands in for IS the heading, so the four screens
   with no artwork yet open exactly as they always have rather than wearing a
   half-designed placeholder for however many weeks it takes. Measured: a lockup
   at these fits lands on a 25px cap, and so does this. */
.cb-h1-mark .cb-lockup-word {
  font-size: var(--cb-type-3xl-size);
  line-height: var(--cb-type-3xl-line-height);
  letter-spacing: var(--cb-type-3xl-letter-spacing);
  font-weight: var(--cb-font-weight-semibold);
  text-transform: none;
}
/* ---- Everything else in the header just fades ----
   No rise and no mask. The headline is doing the moving, and a sentence and two
   buttons travelling as well turns one gesture into three: the eye ends up
   following the last thing to stop rather than the thing that was worth
   reading. They arrive on opacity alone, once the headline is underway.

   The delay is a fixed head start plus each one's own seat. It used to have to
   be fixed, because the headline was one span per letter and how long it ran
   depended on how many letters somebody had written; the headline is one piece
   now (shell.ts) and runs for a known time, so the head start finally means
   what it always claimed to.

   SCOPED TO THE HEADER, and it has to be. .cb-page-actions is also the class on
   the row that holds "Load more" at the foot of a list, so unscoped this
   animated a button halfway down the page with no seat and no delay, and
   replayed it every time the list redrew. */
.cb-page-lede,
.cb-page-head .cb-page-actions > * {
  animation: cbc-fade var(--cb-motion-slow) var(--cb-motion-ease) backwards;
  animation-delay: calc(var(--cb-motion-fast) + var(--cbc-step));
}
/* One line, and the stylesheet is what guarantees it. Copy that outgrows the
   header is cut with an ellipsis and carries the whole sentence in a tooltip;
   it does not wrap and push the first panel down the page. */
.cb-page-lede {
  margin-top: var(--cb-space-md);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-sm-size);
  max-width: var(--cb-layout-paragraph-max-width);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cb-page-title {
  min-width: 0;
}
/* Provenance, at the far end of the title's own row rather than stacked under
   the blurb as a second, smaller grey sentence. */
.cb-page-aside {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--cb-space-2xl);
}
.cb-page-meta {
  font-size: var(--cb-type-xs-size);
  color: var(--cbc-ink-faint);
  text-align: end;
}
/* Centred on one axis rather than left to stretch. Stretch matches heights
   only while every child actually stretches, and a row that mixes buttons with
   a segmented control has one that does not: the pill sets its own height from
   its padding and the buttons sat high against it. */
.cb-page-actions {
  display: flex;
  align-items: center;
  gap: var(--cb-space-lg);
}

/* The headline steps down a size on a narrow window. 768px mirrors
   tokens.breakpoint.md; the literal is unavoidable, CSS cannot read a var() in
   a media prelude. `.cb-main`'s own padding at this width is in console.css,
   with the rest of the page frame. */
@media (max-width: 768px) {
  .cb-page-head h1 {
    font-size: var(--cb-type-2xl-size);
    line-height: var(--cb-type-2xl-line-height);
    letter-spacing: var(--cb-type-2xl-letter-spacing);
  }
}
