/* BonBonBot Console: the bell, the badge and the inbox panel.
   ===================================================================
   The bell is one of the four marks in the rail's tool row, with the
   other three controls that are about running the console rather than
   about reading what it found. It was a full-width row with a word on
   it, under the navigation, which read as one more place to go; it is
   not a place to go, it is the console speaking. It stays there at
   zero, because "nothing new" is a fact worth telling somebody and a
   control that disappears leaves nowhere to go and look at last week
   (rule 15).

   The panel is a popover in the top layer, for the same reason the
   menu and the account are: the rail scrolls, and anything positioned
   inside a scrolling box is clipped by it.

   Two things earn their movement here and nothing else does. The badge
   settles once when a count rises, and the panel comes up out of the
   bell rather than fading in the middle of the screen. Every other
   change is a colour answering the pointer immediately. */

/* ---- The bell ----
   The SHAPE is `.cb-rail-tool`, which the other three in that row wear too
   (console.css). What is left here is what is the bell's own: the state of
   being open, and the count sitting on the mark. Nothing about size, padding
   or layout, because two files each laying out one element is one component
   silently wearing the other's, decided by load order. */
.cb-bell[aria-expanded="true"] {
  color: var(--cbc-ink);
  background: var(--cbc-surface);
  border-color: var(--cbc-line);
}
.cb-bell[aria-expanded="true"] .cb-icon {
  color: var(--cbc-accent);
}

/* ---- The dot ----
   THERE IS NO NUMBER ON IT ANY MORE. It was an accent pill carrying the count,
   which at two digits was wide enough to sit over most of the bell it was
   reporting on: the mark that says which control this is lost an argument to a
   figure nobody reads off a corner. What a bell is asked is "is there
   anything"; how many is in the tooltip, and spelled out in full the moment the
   panel opens.

   Hung off the bell's top right corner, clipping it by a few pixels, so it
   reads as sitting on the mark rather than beside it. The ring is in the rail's
   own ground, which is what keeps the dot legible where it crosses the glyph.

   IT BREATHES, SLOWLY, and only while there is something in the inbox. This
   file used to say one settle and never a loop, and it was right about the pill
   it was describing: a two-digit figure pulsing at the edge of vision is a
   thing people put a window over. A four-pixel dot at the calm cadence is the
   opposite problem solved, because a dot that never moves in a corner of a rail
   somebody has had open since nine in the morning is a dot nobody sees at all.
   Off entirely under reduced motion, where it is simply a dot. */
.cb-bell-badge {
  position: absolute;
  /* Against the MARK, not against the button: the tool cells are wider than
     they are tall, so a corner of the cell is a long way off on its own. This
     puts the dot's centre on the bell's top right corner. */
  inset-block-start: var(--cb-space-md);
  inset-inline-end: calc(50% - var(--cb-space-lg));
  inline-size: var(--cb-space-md);
  block-size: var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent);
  box-shadow: 0 0 0 2px var(--cbc-sunken);
  animation: cbc-bell-breathe var(--cb-motion-work-loop-calm) var(--cb-motion-ease) infinite;
}
.cb-bell-badge[hidden] {
  display: none;
}
/* The halo, and the reason it is a pseudo-element: the dot itself holds a ring
   in the rail's ground, and an element cannot expand a second ring out of the
   same box without taking the first one with it. */
.cb-bell-badge::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: var(--cbc-accent);
  animation: cbc-bell-halo var(--cb-motion-work-loop-calm) var(--cb-motion-ease) infinite;
}
@keyframes cbc-bell-breathe {
  0%,
  70%,
  100% {
    transform: scale(1);
  }
  35% {
    transform: scale(1.12);
  }
}
@keyframes cbc-bell-halo {
  0% {
    opacity: 0.5;
    transform: scale(1);
  }
  60%,
  100% {
    opacity: 0;
    transform: scale(2.2);
  }
}
/* One settle when a count rises, over the breathing: something arriving while
   you are looking at the rail is worth a movement of its own. Backwards, and no
   `to`. It held `both` and ended on a written-out `transform: none`, which is
   the mistake console-theme.css spends a paragraph on: a held final value
   outranks every ordinary declaration on the element, so the badge was pinned
   to whatever the keyframe decided rather than to whatever it says about
   itself. */
.cb-bell-badge[data-arrived] {
  animation:
    cbc-bell-settle var(--cb-motion-slow) var(--cbc-spring) backwards,
    cbc-bell-breathe var(--cb-motion-work-loop-calm) var(--cb-motion-ease) infinite;
}
@keyframes cbc-bell-settle {
  from {
    transform: scale(0.5);
  }
  60% {
    transform: scale(1.14);
  }
}

/* ---- The panel ---- */
.cb-notices,
.cb-notices:popover-open {
  position: fixed;
  inset: auto;
  margin: 0;
  z-index: var(--cbc-z-notices);
  display: grid;
  /* Head, list, foot. The middle track is the only one that scrolls, so the
     filter and the way out of here are always reachable. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  inline-size: var(--cb-width-sm);
  max-inline-size: calc(100vw - var(--cb-space-6xl));
  max-block-size: min(var(--cb-width-lg), calc(100vh - var(--cb-space-6xl)));
  padding: 0;
  border: 1px solid var(--cbc-line-strong);
  border-radius: var(--cb-radius-2xl);
  background: var(--cbc-raised);
  box-shadow: var(--cbc-shadow);
  color: var(--cbc-ink);
  overflow: hidden;
  /* It grows out of the bell in the bottom corner, which is what ties it to the
     button rather than to the page. */
  transform-origin: bottom left;
  animation: cbc-pop var(--cb-motion-base) var(--cbc-spring) backwards;
}
/* The fallback for a browser with no popover support. */
.cb-notices:not(:popover-open):not([data-open]) {
  display: none;
}
.cb-notices[data-open] {
  display: grid;
}

.cb-notices-head {
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-lg) var(--cb-space-lg) var(--cb-space-md);
  border-block-end: 1px solid var(--cbc-line);
}
.cb-notices-title {
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-ink);
}

/* One shape for every number that is about the words beside it: the unread
   count in the head, the size of a day, the size of a folded run. A quiet
   pill in a well, never the accent: the bell three inches away is already
   wearing the accent, and two of them is one of them shouting. */
.cb-notices-waiting,
.cb-notices-tally,
.cb-notice-count {
  flex: none;
  padding-inline: var(--cb-space-sm);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-sunken);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-medium);
  font-variant-numeric: tabular-nums;
  letter-spacing: normal;
}
/* Nothing at all at zero. "Notifications 0" is a head reporting that nothing
   happened, which is a thing to read and then throw away. */
.cb-notices-waiting:empty {
  display: none;
}

/* Two states in a well, the current one lifted out of it onto the card
   surface. The same control the appearance switch uses in the account menu.
   The room between the head's two halves is here rather than on the title,
   which used to hold it: the count sits beside the title now, and a margin
   there would open the gap up in the middle of the pair. */
.cb-notices-views {
  margin-inline-start: auto;
  display: flex;
  gap: var(--cb-space-xxs);
  padding: var(--cb-space-xxs);
  border-radius: var(--cb-radius-md);
  background: var(--cbc-sunken);
}
.cb-notices-view {
  padding: var(--cb-space-xxs) var(--cb-space-md);
  border: 0;
  border-radius: var(--cb-radius-sm);
  background: none;
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-medium);
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-notices-view:hover {
  color: var(--cbc-ink);
}
.cb-notices-view[aria-pressed="true"] {
  background: var(--cbc-surface);
  color: var(--cbc-ink);
  box-shadow: var(--cbc-shadow);
}

.cb-notices-tool {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border-radius: var(--cb-radius-md);
  color: var(--cbc-ink-faint);
  text-decoration: none;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cbc-spring);
}
.cb-notices-tool:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
.cb-notices-tool .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}

/* ---- The list ---- */
.cb-notices-list {
  display: grid;
  align-content: start;
  gap: 1px;
  /* No padding along the top edge, and that is load-bearing rather than a
     taste. The day heading below is sticky against this box, so it parks at
     the padding edge and not at the box edge: with a gutter above it, rows
     scrolled up through that gutter and were visible over the heading, which
     reads as the list leaking. The gutter that is left is at the bottom, where
     nothing sticks and nothing can show through. */
  padding: 0 var(--cb-space-xs) var(--cb-space-xs);
  overflow-y: auto;
  /* A list that has hit its end should not start scrolling the page behind it. */
  overscroll-behavior: contain;
}
/* An empty state in here is a panel's worth of room, not a card's. */
.cb-notices-list .cb-nothing {
  block-size: auto;
  padding-block: var(--cb-space-6xl);
}

/* The day stays at the top of the window while its own rows scroll under it,
   so the list never leaves you reading times with nothing to hang them on. */
.cb-notices-day {
  display: flex;
  align-items: center;
  gap: var(--cb-space-sm);
  position: sticky;
  inset-block-start: 0;
  z-index: 1;
  padding: var(--cb-space-lg) var(--cb-space-md) var(--cb-space-xs);
  background: var(--cbc-raised);
  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);
}

/* ---- One row ---- */
/* Tight on purpose. Eight of these filled a popover, so the day above them
   was the only thing a reader could see at once. */
.cb-notice {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
  padding: var(--cb-space-sm) var(--cb-space-md);
  border-radius: var(--cb-radius-lg);
  transition: background var(--cb-motion-fast) var(--cb-motion-ease);
  /* What is behind a row. The row itself is transparent, so this names the
     surface it is sitting on rather than one it paints, and only the strip of
     actions reads it (see below). The panel's rows are on the popover; the
     screen's rows are on a card, and the day heading a few rules down already
     makes the same distinction for the same reason. */
  --cbc-notice-ground: var(--cbc-raised);
}
.cb-notice:hover,
.cb-notice:focus-within {
  background: var(--cbc-hover);
}
/* A SINGLE ROW IS A COLUMN: the line, then the drawer that opens under it. Only
   single rows (not the folded-run summary, which stays a flat flex row) become
   columns; the flex layout moves onto the line so the tick and the actions sit
   exactly where they always did, and the row's own padding wraps both the line
   and the drawer so the hover wash and the fold indent cover the whole thing. */
.cb-notice:not(.cb-notice-run) {
  display: block;
}
.cb-notice-line {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--cb-space-md);
}
/* ---- The mark, and the unread badge on it ----
   The dot had a column of its own at the head of every row. That spent a
   bullet and a gap on each of them, read or not, and started the row with a
   bare coloured disc rather than with the thing the row is about. On the
   corner of the mark it is a badge on its own subject, the way a count sits on
   an app icon, and nothing shifts when it goes: it is positioned over the
   mark, so a read row is the same shape as an unread one. */
.cb-notice-markwrap {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
}
.cb-notice-dot {
  position: absolute;
  /* Onto the corner rather than inside it. Half the dot hangs off the disc,
     which is what makes it read as applied to the mark instead of drawn as
     part of the glyph. */
  inset-block-start: calc(var(--cb-space-xxs) * -1);
  inset-inline-end: calc(var(--cb-space-xxs) * -1);
  inline-size: var(--cb-space-md);
  block-size: var(--cb-space-md);
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent);
  /* A ring in the ground it sits on, so the badge keeps its edge over a
     tinted severity disc and over the hover wash alike. */
  box-shadow: 0 0 0 2px var(--cbc-raised);
}
/* The row's own ground changes on hover, and a ring in the old colour would
   leave a pale halo sitting on the wash. */
.cb-notice:hover .cb-notice-dot,
.cb-notice:focus-within .cb-notice-dot {
  box-shadow: 0 0 0 2px var(--cbc-hover-strong);
}

.cb-notice-open {
  display: flex;
  align-items: flex-start;
  gap: var(--cb-space-md);
  flex: 1 1 auto;
  min-inline-size: 0;
  color: inherit;
  text-decoration: none;
}
/* The link covers the whole row rather than only its own words: a notice you
   have to aim at is a notice that costs more to open than to ignore. The
   actions sit above it on their own layer. */
.cb-notice-open::after {
  content: "";
  position: absolute;
  inset: 0;
}
.cb-notice-open:focus-visible {
  outline: none;
}
.cb-notice-open:focus-visible::after {
  outline: var(--cbc-focus);
  outline-offset: var(--cbc-focus-inset);
  border-radius: var(--cb-radius-lg);
}

/* Severity is the mark and never the row: a list of ordinary events painted
   like a list of alarms is a list nobody reads to the bottom of.
   The mark sits in a disc of its own so it has somewhere to be tinted. */
.cb-notice-mark {
  flex: none;
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  border-radius: var(--cb-radius-full);
  color: var(--cbc-ink-faint);
}
.cb-notice-mark svg {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
.cb-notice[data-severity="good"] .cb-notice-mark {
  color: var(--cbc-good-text);
}
.cb-notice[data-severity="warn"] .cb-notice-mark {
  color: var(--cbc-warn-text);
}
.cb-notice[data-severity="bad"] .cb-notice-mark {
  color: var(--cbc-poor-text);
}

/* A glyph at twenty pixels is invisible in a column of twelve of them, so an
   unread warning or failure carries the tint behind it as well as in it. The
   same sixteen per cent the chips use (console-data.css), one figure rather
   than one per hue, because a disc this small has far less area to be seen in.
   Read ones lose it: a read alarm is history, and history should recede. */
.cb-notice[data-unread][data-severity="warn"] .cb-notice-mark {
  background: color-mix(in srgb, var(--cbc-warn) 16%, transparent);
}
.cb-notice[data-unread][data-severity="bad"] .cb-notice-mark {
  background: color-mix(in srgb, var(--cbc-poor) 16%, transparent);
}

.cb-notice-body {
  display: grid;
  min-inline-size: 0;
}
.cb-notice-said {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-sm);
  min-inline-size: 0;
}
.cb-notice-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--cb-type-sm-size);
  color: var(--cbc-ink-muted);
}
.cb-notice[data-unread] .cb-notice-title {
  color: var(--cbc-ink);
  font-weight: var(--cb-font-weight-semibold);
}
/* Its own layer, so the sheet of link that covers the row does not swallow
   the pointer before this can say what it is short for. */
.cb-notice-count {
  position: relative;
  z-index: 1;
}

/* What happened, and who did it, sharing one line and one baseline. They are
   one fact: "Access taken away" and "by mireille" read as two things only
   because they were drawn as two. */
.cb-notice-under {
  display: flex;
  align-items: baseline;
  gap: var(--cb-space-sm);
  min-inline-size: 0;
}
/* One line, then it is cut. It used to run to two, which is a third of a row
   spent on the part of the notice that is one click away in full. */
.cb-notice-detail {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
}
/* Never clipped, because it is the half of the line worth reading: a notice
   saying access was taken away and not by whom sends somebody round three
   desks asking. Lifted over the row's link sheet so the whole address can be
   had from its tooltip. */
.cb-notice-by {
  position: relative;
  z-index: 1;
  flex: none;
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-faint);
  white-space: nowrap;
}
/* When a sleeping row wakes, at the end of the same line and never clipped:
   on the snoozed view it is the one fact the row is there to carry, so the
   detail gives way to it rather than the other way round. A notch brighter
   than the rest of the line for the same reason, and lifted over the row's
   link sheet so the exact date can be had from its tooltip. */
.cb-notice-back {
  position: relative;
  z-index: 1;
  flex: none;
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
  color: var(--cbc-ink-muted);
  white-space: nowrap;
}

/* ---- What you can do to a row ----
   The actions take the space the time was using rather than a column of their
   own, so nothing on the row moves when they arrive. Same trade the live card
   makes with its clock and its stop button. */
.cb-notice-slot {
  position: relative;
  flex: none;
  display: grid;
  place-items: center end;
  align-self: center;
  /* Wide enough for the actions that cover it, worked out from them rather
     than picked off the scale: three buttons and the two gaps between. The
     third one (snooze) pushed them past a fixed width, and what overflowed
     sat on top of the end of the sentence every time a pointer arrived. The
     deed a few kinds carry is no longer a fourth button here: it moved into the
     drawer below the row, so the strip is always these three. */
  min-inline-size: calc(var(--cb-space-4xl) * 3 + var(--cb-space-xxs) * 2);
}
.cb-notice-when {
  font-size: var(--cb-type-xs-size);
  font-variant-numeric: tabular-nums;
  color: var(--cbc-ink-faint);
  transition: opacity var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-notice-acts {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: var(--cb-space-xxs);
  opacity: 0;
  /* Invisible and unclickable, or the right end of every row is a button
     nobody can see. Keyboard focus still reaches them, which is the point. */
  pointer-events: none;
  transition: opacity var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-notice:hover .cb-notice-when,
.cb-notice:focus-within .cb-notice-when {
  opacity: 0;
}
.cb-notice:hover .cb-notice-acts,
.cb-notice:focus-within .cb-notice-acts {
  opacity: 1;
  pointer-events: auto;
}
.cb-notice-act {
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-md);
  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-notice-act:hover {
  background: var(--cbc-hover-strong);
  color: var(--cbc-ink);
}
.cb-notice-act .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
/* Turned off rather than taken away (rule 15), so it has to look turned off:
   the tooltip carries the reason, and this is what somebody sees before they
   reach for it. It keeps its pointer events, which is how the reason can be
   read at all. */
.cb-notice-act[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}
.cb-notice-act[aria-disabled="true"]:hover {
  background: none;
  color: var(--cbc-ink-faint);
}

/* ---- The drawer under an actionable row ----
   The one action a notice lets you take, and the facts worth seeing before you
   take it, revealed when you approach the row. It used to be a worded button
   crammed into the strip of filing glyphs, the same size as Archive and reaching
   back over the sentence to fit; the drawer gives it room and gives the sign-in
   warning somewhere to say where the sign-in came from, right beside the button
   that answers it.

   0fr to 1fr rather than a height, so it settles to its own content without a
   magic number, and the inside clips while it does. The whole thing lives inside
   the row's padding, so the hover wash and the fold indent already cover it. */
.cb-notice-more {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--cb-motion-base) var(--cbc-spring);
}
/* Opens on approach, but NOT while a selection is live. Picking is a different
   job from acting on one row: during it, hovering a row is aiming at its tick,
   not asking to see its footer, and letting the drawer open then would have it
   flap shut and open again on every pick as the row redraws. So a row that is
   in picking mode keeps its drawer closed whatever the pointer does. */
.cb-notice:not([data-picking]):hover .cb-notice-more,
.cb-notice:not([data-picking]):focus-within .cb-notice-more {
  grid-template-rows: 1fr;
}
.cb-notice-more-in {
  overflow: hidden;
  min-block-size: 0;
}
/* The revealed footer proper: a quiet inset card, so the drawer reads as a panel
   that belongs to the row rather than as loose text that appeared under it. It is
   inset past the mark disc and the gap after it, the same offset at every row
   density because the mark is the same size at every one, so it lines up under
   the sentence. */
.cb-notice-more-pane {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cb-space-md);
  margin: var(--cb-space-xxs) 0 var(--cb-space-xs) calc(var(--cb-space-3xl) + var(--cb-space-md));
  padding: var(--cb-space-sm) var(--cb-space-md);
  border-radius: var(--cb-radius-lg);
  background: var(--cbc-sunken);
}
/* The facts, stacked: where, then when. A column reads as a small record; the
   same two side by side read as a sentence that happens to have an icon in it. */
.cb-notice-facts {
  display: flex;
  flex-direction: column;
  gap: var(--cb-space-xxs);
  min-inline-size: 0;
}
.cb-notice-fact {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-sm);
  min-inline-size: 0;
  color: var(--cbc-ink-muted);
  font-size: var(--cb-type-xs-size);
  line-height: var(--cb-type-xs-line-height);
}
.cb-notice-fact .cb-icon {
  flex: none;
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
  color: var(--cbc-ink-faint);
}
/* Up to two lines, then clipped: a location sentence is longer than a single
   line here and reads better wrapped than cut off in the middle of the place. */
.cb-notice-fact > span {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}
.cb-notice-more-acts {
  display: flex;
  align-items: center;
  gap: var(--cb-space-sm);
  flex: none;
}

/* The action itself, as a real button rather than a fourth glyph. Tinted at
   rest and filled on hover: it is the reason somebody opened the drawer, so it
   is plainly the thing to press, without shouting over the row at rest. */
.cb-notice-deed {
  display: inline-flex;
  align-items: center;
  gap: var(--cb-space-xs);
  flex: none;
  block-size: var(--cb-space-4xl);
  padding-inline: var(--cb-space-md);
  border: 0;
  border-radius: var(--cb-radius-full);
  background: var(--cbc-accent-quiet);
  color: var(--cbc-accent);
  font-size: var(--cb-type-xs-size);
  font-weight: var(--cb-font-weight-semibold);
  /* Two words are two words. A deed that wrapped would be taller than its row. */
  white-space: nowrap;
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cb-motion-ease),
    scale var(--cb-motion-fast) var(--cbc-spring);
}
.cb-notice-deed:hover {
  background: var(--cbc-accent);
  color: var(--cbc-on-accent);
}
.cb-notice-deed:active {
  scale: 0.97;
}
.cb-notice-deed .cb-icon {
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}
/* Off while it runs, because these are real round trips and a second press
   would start a second scan. Turned off rather than taken away (rule 15), so it
   has to look it, and it keeps its pointer events so the reason can be read. */
.cb-notice-deed[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}
.cb-notice-deed[aria-disabled="true"]:hover {
  background: var(--cbc-accent-quiet);
  color: var(--cbc-accent);
}

/* ---- A run of related rows, folded ----
   Twelve invitations withdrawn in one afternoon was twelve rows of the same
   six words. Folded, it is one row saying what they are about, how many, and
   either which kinds are in them or when they happened, and pressing it puts
   the twelve back exactly where they were. The row is the control: there is
   no separate handle to find, and no second thing on the line competing with
   the one thing it does. */
.cb-notice-fold {
  /* A button brings its own padding, and every browser's is different. The row
     around it already pays for the inset, so without this the summary's mark
     sits a few pixels right of every other mark in the column and the one row
     that is meant to stand for twelve is the one row out of line. */
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  text-align: start;
  cursor: pointer;
}
.cb-notice-fold .cb-notice-body {
  flex: 1 1 auto;
}
/* It turns to face down when the run is open, which is the whole animation:
   the rows underneath do the rest by arriving.

   It sits inside the title line rather than out at the row's right edge. Out
   there it lined up with the times, which are not something anybody can press,
   and it was a long way from the words it opens. Here it reads as the end of
   the same phrase: this kind, this many, and here they are. */
.cb-notice-chev {
  flex: none;
  align-self: center;
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
  color: var(--cbc-ink-faint);
  transition: transform var(--cb-motion-base) var(--cbc-spring);
}
.cb-notice-chev svg {
  inline-size: 100%;
  block-size: 100%;
}
.cb-notice-fold[aria-expanded="true"] .cb-notice-chev {
  transform: rotate(90deg);
}

/* The fold opens by growing its own row from nothing, so the rows below it
   are pushed rather than jumped. `inert` is what shut means: it collapses the
   track, and it takes the rows out of the tab order and away from the panel's
   arrow keys without taking them out of the document, which is what leaves
   the fold something to shrink. */
.cb-notice-fold-body {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--cb-motion-base) var(--cbc-spring);
}
.cb-notice-fold-body[inert] {
  grid-template-rows: 0fr;
}
/* The clipping belongs to the inner element, and this is the one part of the
   recipe that cannot be moved. Put `overflow: hidden` on the grid itself and
   the open track resolves to nothing: a `1fr` row is `minmax(auto, 1fr)`, the
   automatic minimum comes from the item's content, and an item that is both
   clipped and floored at zero contributes zero. The fold then flips every
   attribute it is supposed to flip, reports itself open to a screen reader,
   and shows nothing at all, which is how it shipped the first time. */
.cb-notice-fold-in {
  display: grid;
  gap: 1px;
  min-block-size: 0;
  overflow: hidden;
}
/* Indented, never railed: what says these belong to the row above is where
   they start, not a coloured line down their edge (docs/10). */
.cb-notice-fold-in .cb-notice {
  padding-inline-start: var(--cb-space-4xl);
}

/* ---- The foot ---- */
.cb-notices-more {
  display: grid;
  justify-items: center;
  padding: var(--cb-space-md);
}
.cb-notices-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cb-space-md);
  padding: var(--cb-space-md) var(--cb-space-lg);
  border-block-start: 1px solid var(--cbc-line);
}

/* The one line about the view this panel does not have. Snoozed notices are
   listed on the full screen, and this is what stops the panel being a place
   where something can be put and then not found. It is drawn only while some
   are asleep, so it is never furniture reporting that nothing happened, and it
   sits above the foot because it is about what is missing from the list rather
   than about where to go next. Quiet at rest and answering the pointer
   immediately, like every other row in here. */
.cb-notices-asleep {
  display: flex;
  align-items: center;
  gap: var(--cb-space-sm);
  padding: var(--cb-space-sm) var(--cb-space-lg);
  border-block-start: 1px solid var(--cbc-line);
  color: var(--cbc-ink-faint);
  font-size: var(--cb-type-xs-size);
  text-decoration: none;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    color var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-notices-asleep:hover {
  background: var(--cbc-hover);
  color: var(--cbc-ink);
}
.cb-notices-asleep .cb-icon {
  inline-size: var(--cb-space-lg);
  block-size: var(--cb-space-lg);
}

/* The rule in console-theme.css already cuts every duration to nothing. These
   two exist because their whole point is the movement, so they are told not to
   move at all rather than to move instantly. */
@media (prefers-reduced-motion: reduce) {
  .cb-bell-badge,
  .cb-bell-badge::after,
  .cb-bell-badge[data-arrived] {
    animation: none;
  }
  /* The halo is the animation, so with nothing to animate it would sit there as
     a second flat disc twice the size of the dot. */
  .cb-bell-badge::after {
    display: none;
  }
  .cb-notices {
    animation: none;
  }
  /* A fold that opens in a millisecond is still a thing that moved. Asked to
     stop animating, it simply is open or is not. */
  .cb-notice-fold-body,
  .cb-notice-chev {
    transition: none;
  }
}

/* ---- The same rows, on the full screen ----
   The panel is a peek and the screen is the record, so they hold the same row
   at two densities. In the panel a row is a card in a padded list: rounded,
   floating, with air around it. Here it is a line in a ledger, full width with
   a hairline under it, which is what the activity trail next door already
   looks like. Two screens that both list what happened should not be two
   different-looking screens.

   The card these sit in is `.cb-flow > .cb-card:has(.cb-filters)`, padded top
   and bottom and not at the sides, so each child pays for its own inset. */
.cb-notices-results {
  display: block;
}
.cb-notices-page {
  display: block;
}
/* Dense is the row the screen draws: the same line, squarer, taking the
   card's own inset. It is an attribute on the row rather than a rule about
   where the row happens to be, so the row that is asked for tight is the row
   that is tight, wherever it is put. */
.cb-notice[data-dense] {
  padding: var(--cb-space-md) var(--cb-space-3xl);
}
.cb-notices-page .cb-notice {
  border-radius: 0;
  /* Here a row is on a card rather than in a popover, so the strip of actions
     has a different thing to match. Same swap the day heading makes below. */
  --cbc-notice-ground: var(--cbc-surface);
}
.cb-notices-page .cb-notice + .cb-notice {
  border-block-start: 1px solid var(--cbc-line);
}
/* A folded run breaks the run of siblings the hairline above is drawn from,
   so the two edges it makes are drawn back: the first row inside the fold,
   and whatever the fold lands on top of. */
.cb-notices-page .cb-notice-fold-in > .cb-notice:first-child,
.cb-notices-page .cb-notice-fold-body + .cb-notice {
  border-block-start: 1px solid var(--cbc-line);
}
.cb-notices-page .cb-notice-fold-in {
  gap: 0;
}
.cb-notices-page .cb-notice-fold-in .cb-notice {
  padding-inline-start: var(--cb-space-6xl);
}
.cb-notices-page .cb-notice-open:focus-visible::after {
  border-radius: 0;
}

/* The day rides at the top of the window while its own rows pass under it. It
   takes the card's own ground rather than the panel's, because here it is
   sitting on a card and there it is sitting in a popover. */
.cb-notices-page .cb-notices-day {
  padding: var(--cb-space-lg) var(--cb-space-3xl) var(--cb-space-md);
  border-block-end: 1px solid var(--cbc-line);
  background: var(--cbc-surface);
}
/* The heading and the first row under it would otherwise carry two lines
   between them, which reads as a gap rather than as a join. */
.cb-notices-page .cb-notices-day + .cb-notice {
  border-block-start: 0;
}

/* The empty states and the failure get the card's inset too, so a screen with
   nothing on it is not a sentence pressed against the left edge. */
.cb-notices-results > .cb-nothing,
.cb-notices-results > .cb-loading {
  padding-inline: var(--cb-space-3xl);
}

/* ---- PICKING SEVERAL AT ONCE ----

   The tick, and the bar that takes over the head while anything is ticked.
   admin/src/views/notice-pick.ts holds the arithmetic and argues the design;
   this is where it is drawn.

   THE BOX IS NOT ALWAYS THERE. A column of checkboxes down every line makes
   filing look like the point of the screen, and reading is the point of the
   screen. So it takes the kind's mark's place when you approach it, which is
   rule 14's "a control shows its affordance when you approach it and not
   before", and stays up for everything once anything at all is picked, because
   at that point picking IS what is happening.

   NOTHING MOVES WHEN IT APPEARS. The box is the same size as the mark and sits
   on top of it rather than beside it, so a row does not reflow under the
   pointer that is on its way to ticking it. Same reason the row's own actions
   sit in the space the time was using. */

.cb-notice-box {
  position: absolute;
  /* THE MARK'S OWN SPOT AND THE MARK'S OWN SIZE. It stands in for the kind's
     disc, so it sits exactly where that disc sits (the row's inline padding)
     and is exactly as big (3xl, the mark disc), rather than a smaller square
     parked at the row's left edge. A box that is not the size or place of the
     thing it replaces reads as a second column that appeared, which is the one
     thing the swap is designed to avoid. */
  inset-inline-start: var(--cb-space-md);
  /* CENTRED ON THE ROW, not left at its static position. Out of flow it would
     otherwise sit at the top edge of the line, where the sticky day heading
     above it (z-index 1, a few rules up) covers the first row of every day:
     the box was there, and unclickable, on exactly the rows somebody reaches
     for first. */
  inset-block-start: 50%;
  translate: 0 -50%;
  /* And above the row's own link, which is a later sibling and would otherwise
     paint over it. */
  z-index: 2;
  inline-size: var(--cb-space-3xl);
  block-size: var(--cb-space-3xl);
  /* NO PADDING. This is a <button>, and a button carries the user agent's own
     padding (1px 6px in Chromium) unless told otherwise. That 6px each side
     shrank the grid the tick centres in to a sliver narrower than the tick, so
     the check overflowed and landed off to the right, which no amount of
     nudging the glyph inside the svg could fix. Zeroed, the tick centres in the
     whole box. */
  padding: 0;
  border-radius: var(--cb-radius-md);
  border: 1.5px solid var(--cbc-line-strong);
  background: var(--cbc-surface);
  color: var(--cbc-on-accent);
  display: grid;
  place-items: center;
  cursor: pointer;
  opacity: 0;
  transition:
    opacity var(--cb-motion-fast) var(--cb-motion-ease),
    border-color var(--cb-motion-fast) var(--cb-motion-ease),
    scale var(--cb-motion-fast) var(--cbc-spring),
    background var(--cb-motion-fast) var(--cb-motion-ease);
}
/* A HIT AREA BIGGER THAN THE BOX. The disc is 24px, and 24px is a hard target
   with a pointer and a harder one with a thumb, so the button reaches an extra
   ring past its own edge without the drawing growing. The row's own link sits
   below this on z, so the wider grab does not steal an ordinary row click. */
.cb-notice-box::before {
  content: "";
  position: absolute;
  inset: calc(var(--cb-space-sm) * -1);
}
/* Approached, walked to with a keyboard, or picking already under way. */
.cb-notice:hover .cb-notice-box,
.cb-notice:focus-within .cb-notice-box,
.cb-notice[data-picking] .cb-notice-box,
.cb-notice-box:focus-visible {
  opacity: 1;
}
.cb-notice-box:hover {
  border-color: var(--cbc-accent);
}
.cb-notice-box:active {
  scale: 0.9;
}
.cb-notice-box:focus-visible {
  outline: var(--cbc-focus);
  outline-offset: var(--cbc-focus-gap);
}
.cb-notice-box[data-on],
.cb-notice-box[data-partly] {
  opacity: 1;
  background: var(--cbc-accent);
  border-color: var(--cbc-accent);
}
/* The check, shown only when the box is ticked. A real glyph rather than two
   rotated borders: the border trick came out lopsided at this size and read as
   a scratch. Hidden with a scale so it lands with the tick rather than blinking
   on. */
.cb-notice-tick {
  display: grid;
  place-items: center;
  /* Fills most of the 3xl box, the way a real checkbox tick does: a small
     glyph floating in a big box reads as off even when it is centred. */
  inline-size: var(--cb-space-2xl);
  block-size: var(--cb-space-2xl);
  opacity: 0;
  scale: 0.6;
  transition:
    opacity var(--cb-motion-fast) var(--cb-motion-ease),
    scale var(--cb-motion-fast) var(--cbc-spring);
}
.cb-notice-tick svg {
  inline-size: 100%;
  block-size: 100%;
  stroke-width: 3;
  /* Optically centred. The lucide check's bounding box is centred horizontally
     but its ink sits half a unit high in the viewBox, and its mass is up in the
     long right arm, so dead-centre reads as riding high. A hairline nudge
     straight down (never sideways: the box is already centred across) settles
     it onto the eye's centre. */
  transform: translate(0, 0.5px);
}
.cb-notice-box[data-on] .cb-notice-tick {
  opacity: 1;
  scale: 1;
}
/* Half of a folded run picked: one flat dash, the one mark a checkbox is
   allowed to draw for "some". The check is hidden and this takes its place. */
.cb-notice-box[data-partly] .cb-notice-tick {
  opacity: 0;
}
.cb-notice-box[data-partly]::after {
  content: "";
  display: block;
  inline-size: var(--cb-space-sm);
  block-size: 0;
  border-block-end: 2px solid currentColor;
}
/* A single row is a column now (its line, then its drawer), so its tick lives
   inside the line and anchors to the line's own start. The line sits inside the
   row's padding, and that padding is what carries the fold indent, so the box
   lands on the mark at every indent without a rule per depth: 0 from the line is
   the mark, whether the row is flush or pushed in under a run. A run's own box
   is a direct child of the run and keeps the row-padding offset above. */
.cb-notice-line .cb-notice-box {
  inset-inline-start: 0;
}

/* The row makes room only while the boxes are up, and the mark slides out of
   the way rather than the row growing: the width is the same either way. */
.cb-notice:hover .cb-notice-markwrap,
.cb-notice:focus-within .cb-notice-markwrap,
.cb-notice[data-picking] .cb-notice-markwrap {
  opacity: 0;
}
.cb-notice-markwrap {
  transition: opacity var(--cb-motion-fast) var(--cb-motion-ease);
}
.cb-notice[data-picked] {
  background: var(--cbc-accent-quiet);
}
/* A one-shot flash the moment a row is ticked, so a pick lands with a beat of
   feedback. It starts on a stronger accent wash and settles back to the picked
   ground (the row already carries data-picked), which is why the keyframe names
   only the start: the end is the row's own resting colour. It replays every
   time because the row is a fresh element on each pick, which is the point,
   even for a row already hovered or opened. */
.cb-notice[data-flash] {
  animation: cbc-pick-flash var(--cb-motion-base) var(--cb-motion-ease);
}
@keyframes cbc-pick-flash {
  from {
    background: color-mix(in srgb, var(--cbc-accent) 45%, transparent);
  }
}

/* ---- The bar ----
   A floating toolbar just above the foot, over the bottom of the list, rather
   than a band that takes the head's place. Inverted onto the ink ground so it is
   actually seen: drawn in the panel's own raised colour it was the same colour
   as everything behind it. It rises in when a selection starts and is out of the
   flow entirely at rest, so it is never furniture, and the head is left alone so
   ticking a box moves nothing a pointer is aiming at. */
.cb-notices-bar {
  display: none;
  align-items: center;
  justify-content: space-between;
  gap: var(--cb-space-md);
  /* Detached from the panel's edges on three sides so it reads as floating above
     the foot rather than as another band of chrome. */
  margin: 0 var(--cb-space-md) var(--cb-space-md);
  padding: var(--cb-space-xs);
  border-radius: var(--cb-radius-full);
  /* The ink ground and the surface as its text, which inverts cleanly in both
     themes: a dark bar with light marks in the light theme, the reverse in the
     dark one, without a second palette. */
  background: var(--cbc-ink);
  color: var(--cbc-surface);
  box-shadow: var(--cbc-shadow);
}
.cb-notices[data-picking] .cb-notices-bar {
  display: flex;
  animation: cbc-bar-rise var(--cb-motion-base) var(--cbc-spring) backwards;
}
/* Up from just below its resting place, so it lands where it lives rather than
   sliding across anything. */
@keyframes cbc-bar-rise {
  from {
    opacity: 0;
    transform: translateY(var(--cb-space-md)) scale(0.98);
  }
}
.cb-notices-bar-lead {
  display: flex;
  align-items: center;
  gap: var(--cb-space-xxs);
  min-inline-size: 0;
}
.cb-notices-picked {
  padding-inline-end: var(--cb-space-xs);
  font-size: var(--cb-type-sm-size);
  font-weight: var(--cb-font-weight-semibold);
  color: var(--cbc-surface);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.cb-notices-bar-acts {
  display: flex;
  align-items: center;
  gap: var(--cb-space-xxs);
  justify-content: flex-end;
}
/* A hairline between "select all" and the three that act on the selection, in
   the bar's own inverted ink: a faint division so the toolbar reads as two
   groups rather than one run of five taps. */
.cb-notices-bar-sep {
  flex: none;
  inline-size: 1px;
  block-size: var(--cb-space-lg);
  background: color-mix(in srgb, var(--cbc-surface) 24%, transparent);
}
/* Every tap on the bar: an icon on the inverted ground, its word on the tooltip
   (rule 16). Same footprint whether it is the dismissal, select-all, or one of
   the three filing actions, so the toolbar is an even row of taps. */
.cb-notices-barbtn {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--cb-space-4xl);
  block-size: var(--cb-space-4xl);
  border: 0;
  border-radius: var(--cb-radius-full);
  background: none;
  color: var(--cbc-surface);
  cursor: pointer;
  transition:
    background var(--cb-motion-fast) var(--cb-motion-ease),
    scale var(--cb-motion-fast) var(--cbc-spring);
}
.cb-notices-barbtn:hover {
  background: color-mix(in srgb, var(--cbc-surface) 18%, transparent);
}
.cb-notices-barbtn:active {
  scale: 0.92;
}
.cb-notices-barbtn:focus-visible {
  outline: 2px solid var(--cbc-surface);
  outline-offset: -2px;
}
.cb-notices-barbtn .cb-icon {
  inline-size: var(--cb-space-xl);
  block-size: var(--cb-space-xl);
}
.cb-notices-barbtn[aria-disabled="true"] {
  opacity: 0.4;
  cursor: not-allowed;
}
.cb-notices-barbtn[aria-disabled="true"]:hover {
  background: none;
}

@media (prefers-reduced-motion: reduce) {
  .cb-notice-box,
  .cb-notice-tick,
  .cb-notice-more,
  .cb-notice-markwrap {
    transition: none;
  }
  .cb-notices[data-picking] .cb-notices-bar,
  .cb-notice[data-flash] {
    animation: none;
  }
}
:root[data-motion="reduced"] .cb-notice-box,
:root[data-motion="reduced"] .cb-notice-tick,
:root[data-motion="reduced"] .cb-notice-more,
:root[data-motion="reduced"] .cb-notice-markwrap {
  transition: none;
}
:root[data-motion="reduced"] .cb-notices[data-picking] .cb-notices-bar,
:root[data-motion="reduced"] .cb-notice[data-flash] {
  animation: none;
}
