/* Self-hosted rather than loaded from Google. An overlay that reaches out to
 * a CDN at load renders in a fallback face whenever the network is slow or
 * down, and that shows up on stream as the title suddenly being the wrong
 * font. 64KB for both, latin subset only. */
@font-face {
  font-family: 'Bangers';
  src: url(fonts/bangers.woff2) format('woff2');
  font-weight: 400;
  font-display: block;
}

@font-face {
  font-family: 'Nunito';
  src: url(fonts/nunito.woff2) format('woff2');
  font-weight: 400 700;
  font-display: block;
}

:root {
  /* Primary palette. Five, and the wedges cycle through all of them.
     Named for what they are rather than for a role, because the roles moved
     around more than once while this was being picked. */
  --k-plum:   #723148;   /* the RIM now, no longer a wedge fill */
  /* Took plum's place in the wedge cycle. Being a yellow-green, this is the
     colour that ruled out keying against green - see --key-color at the end of
     this block, which is why the key is cyan. */
  --k-olive:  #b5c545;
  --k-ink:    #241317;
  --k-gold:   #fdbf37;
  --k-orange: #f6812b;
  --k-pink:   #f3a6b1;

  --text: #fff4ea;

  /* The scene's own width, mirrored here so things OUTSIDE the scene can be
     measured against it. The 48px is .stage's left+right padding, the 140px is
     the same allowance .scene subtracts from the viewport height, and the 16/9
     is its aspect ratio. If any of those change in .scene, they change here. */
  --scene-w: min(calc(100vw - 48px), calc((100vh - 140px) * 16 / 9));

  /* ---- the history column's width ---------------------------------------
   * Derived, never chosen. The scene is centred and its width is capped by the
   * viewport HEIGHT, so how much room is left beside it changes with the window
   * - and at 1920x1080, the size a browser source is usually set to, it is only
   * about 124px per side.
   *
   * That is why this is a calculation rather than a number. A fixed 240px
   * column would sit inside the keyed area at that size and go out on stream,
   * and it would look perfectly fine in a wide dev window while doing it.
   * max(0px, ...) is what makes the column vanish instead of overlapping when
   * the window is too narrow to have a margin at all.
   *
   * The outline is 9px because .scene's dashed edge is an `outline` - 6px of
   * offset plus a 3px stroke - which paints OUTSIDE the box and takes no layout
   * space, so CSS thinks the scene ends 9px before it visibly does. */
  --scene-outline: 9px;
  --hist-gap: 12px;
  --hist-inset: 10px;
  --side-space: max(0px, calc(
    (100vw - var(--scene-w)) / 2
    - var(--scene-outline) - var(--hist-gap) - var(--hist-inset)
  ));
  --hist-w: min(var(--side-space), 260px);

  /* THE CHROMA KEY BED. Cyan, and chosen by measurement rather than by taste.
     Green, red and white were each tried on a real setup and each leaked.
     Do not change this without redoing the measurement below.
     ---------------------------------------------------------------------
     OBS keys on distance in the UV chroma plane, NOT on hue angle. That
     distinction is what settled it: hue ignores saturation, so it treats the
     charm's dark blue butterfly as a serious rival to cyan when its chroma
     magnitude is actually small.

     Clearance from each candidate to the nearest thing on screen - the wheel's
     six colours plus the charm's real pixels - bigger being more room for the
     key filter to work in:

       cyan      #00ffff   0.632   <-- this one
       green     #00ff00   0.533
       magenta   #ff00ff   0.458
       blue      #0000ff   0.447
       OBS green #00b140   0.393
       red       #ff0000   0.307

     A full sweep of the RGB cube at step 5 found nothing better than cyan. It
     wins because the palette runs warm-red to yellow-green and the blue
     butterfly is dark and muted, so little competes with it.

     What is nearest to cyan now is only the NEUTRALS - the charm's white centre,
     its greys, and ink. Those sit at zero chroma and are equidistant from every
     fully saturated key, so this is the practical ceiling: no coloured thing on
     screen is closer than the greys already are. */
  --key-color: #00ffff;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  /* Fixed-canvas overlay — scrolling is never intended. */
  overflow: hidden;
}

body {
  /* Dark, for the operator. This is outside the crop, so it never goes out;
     it is here so the controls and the history column have something readable
     to sit on. Only .scene's background is keyed. */
  background: var(--k-ink);
  color: var(--text);
  font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif;
  letter-spacing: 0.01em;
}

.stage {
  position: relative;
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 24px;
  padding: 24px;
}

/* ---------------- Scene (the keyable 16:9 region) ----------------
 * Crop the OBS source to this area and key its background. Anything outside the
 * dashed outline (controls, history, padding) is the operator's and won't be on
 * stream.
 *
 * It defaults to the key colour, so the overlay is stream-ready the moment it
 * loads. It used to default to white and need a button press, which is a step
 * that only ever gets missed in one direction: nobody notices the wheel is
 * white until it is already on stream sitting in a white box.
 * ---------------------------------------------------------------- */

.scene {
  position: relative;
  aspect-ratio: 16 / 9;
  /* Pick whichever constraint is smaller so 16:9 always holds inside the viewport. */
  width: min(100%, calc((100vh - 140px) * 16 / 9));
  max-height: calc(100vh - 140px);
  background: var(--key-color);
  outline: 3px dashed var(--k-pink);
  outline-offset: 6px;
  overflow: hidden;
  border-radius: 4px;
}

/* Review beds, cycled by the Bed button. Neither is what goes out - they exist
   so edges can be checked against something other than the key. White finds
   dark fringing and any pale wedge text; clear shows the page behind and is
   where light haloing shows. */
.scene.bed-white { background: #ffffff; }
.scene.bed-clear { background: transparent; }

/* ---------------- The drop group (everything that goes on stream) -----------
 * The wheel and its string, dropping in and snapping away as one thing.
 *
 * NOT called `.show`, which is what it was first, and which was a bug: the
 * result card's visible state is `.result.show`, so a bare `.show` rule matched
 * the card too and threw it 150% above the scene the moment it opened. The card
 * was opening correctly and landing off screen, which looked like the popup was
 * broken rather than like a selector collision. Keep this name distinct from any
 * state class.
 * -------------------------------------------------------------------------- */

.drop-group {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;

  /* Parked above the crop until Trigger Drop. The WHOLE group moves, string
     included, so hiding it leaves the scene as flat key colour with nothing left
     in frame to key around - not even a cord running off the top. */
  transform: translateY(-150%);
  /* Low, so a squash reads as the group landing on something rather than
     shrinking towards its own middle. */
  transform-origin: 50% 78%;
  will-change: transform;
}

/* The settled state the drop animation hands over to. */
.drop-group.down {
  transform: none;
}

.drop-group.dropping { animation: drop-in 1150ms both; }
.drop-group.hiding   { animation: snap-away 620ms both; }

/* Gravity on the way down, then two decreasing bounces.
 *
 * The squash sits on the SAME keyframe as each landing rather than after it -
 * that is what reads as impact. Put it a frame later and the wheel looks like
 * it is wobbling on a spring instead of hitting something. */
@keyframes drop-in {
  0% {
    transform: translateY(-150%) scale(1, 1);
    animation-timing-function: cubic-bezier(0.55, 0, 0.85, 0.35);   /* falling */
  }
  42%  { transform: translateY(0) scale(1, 1);          animation-timing-function: ease-out; }
  52%  { transform: translateY(0) scale(1.10, 0.88);    animation-timing-function: ease-out; }
  64%  { transform: translateY(-15%) scale(0.95, 1.06); animation-timing-function: ease-in; }
  76%  { transform: translateY(0) scale(1.06, 0.94);    animation-timing-function: ease-out; }
  86%  { transform: translateY(-5%) scale(0.98, 1.02);  animation-timing-function: ease-in; }
  94%  { transform: translateY(0) scale(1.02, 0.98);    animation-timing-function: ease-out; }
  100% { transform: translateY(0) scale(1, 1); }
}

/* Anticipation, then launch. The squat before the exit is what sells the
 * stretch: leaving straight away just looks like the wheel was deleted. */
@keyframes snap-away {
  0%   { transform: translateY(0) scale(1, 1);          animation-timing-function: ease-out; }
  26%  { transform: translateY(5%) scale(1.12, 0.86);   animation-timing-function: ease-in; }
  46% {
    transform: translateY(-14%) scale(0.94, 1.12);
    animation-timing-function: cubic-bezier(0.5, 0, 1, 0.5);        /* accelerating out */
  }
  100% { transform: translateY(-160%) scale(0.78, 1.38); }
}

/* ---------------- Wheel ---------------- */

.wheel-frame {
  position: relative;
  /* Driven off the scene's HEIGHT, not its width. The scene is 16:9 and the
     wheel is the tallest thing in it, so height is the constraint that binds -
     sizing on width would let the wheel grow past the crop on a wide window. */
  height: min(64%, 64vh);
  aspect-ratio: 1;
  border-radius: 50%;
  /* Where it hangs from: the point at which the cord LEAVES FRAME, not the far
     end of the string element.

     This was -197.5% (the string's actual end, far off screen) and it looked
     wrong. With a lever that long, the rotation needed to move the wheel a
     visible distance tilts the visible run of cord by almost nothing - so the
     cord stayed vertical and the wheel appeared to slide sideways rather than
     swing under it.

     -28.125% is the top edge of .scene, exactly. The frame is height:min(64%,
     64vh) and .scene is always shorter than the viewport, so 64% always wins and
     the gap above the wheel is a fixed 18% of the scene = 28.125% of the frame's
     own height. The cord is clipped there, so pivoting at that line means it
     appears anchored precisely where it vanishes, and now visibly tilts. */
  transform-origin: 50% -28.125%;
  /* The design's warm bloom.
   *
   * WORTH KNOWING ON STREAM: this is a soft, semi-transparent glow, and it sits
   * on the chroma key. Wherever it is partly transparent the key colour shows
   * through it, so OBS keys some of the glow away and can leave a fringe. It
   * looks correct on the white review bed and against a dark scene; judge it on
   * the green one before going live, and drop this one declaration if it
   * fringes. */
  box-shadow: 0 0 44px 6px rgba(253, 191, 55, 0.28);
}

/* ---- The sway ----
 *
 * TWO keyframes, extreme to extreme, with `alternate` - not four keyframes
 * around a full cycle.
 *
 * This matters more than it looks. A keyframe timing function applies BETWEEN
 * each pair of keyframes, so the first version of this - 0 -> +A -> 0 -> -A -> 0
 * with ease-in-out - eased at every one of those points, including the two where
 * the angle is 0. That put the slowest moment at the BOTTOM of the arc and the
 * quickest near the ends, which is precisely backwards: a pendulum is fastest as
 * it passes through the bottom and momentarily still at each end. It read as a
 * hesitation in the middle followed by a lurch.
 *
 * Written as a single sweep from one extreme to the other, `ease-in-out` lands
 * where it should on its own - slow at both ends, quick through the middle -
 * because the ends of the swing are now the ends of the segment. One iteration
 * is half a cycle, so the period below is half the full swing.
 *
 * The negative delay starts it mid-sweep at 0deg rather than parked at one
 * extreme, so it picks up from where the drop leaves the wheel instead of
 * snapping sideways the instant the animation is allowed to run. Half of the
 * duration, because ease-in-out is symmetric and its midpoint is the midpoint of
 * the travel.
 *
 * On .wheel-frame rather than .drop-group because .drop-group's transform is
 * already spoken for by the drop and the snap-away. Two animations cannot share
 * one property; nesting them costs nothing.
 *
 * The spin is unaffected: .wheel rotates inside this frame, and the pointer
 * sways WITH the frame, so which wedge sits under the pointer never changes.
 * winningIndex reads the rotation figure rather than the DOM, so it could not be
 * confused by the sway even if that were not true. */
.drop-group.down:not(.dropping):not(.hiding) .wheel-frame {
  animation: sway 2.9s ease-in-out infinite alternate;
  animation-delay: -1.45s;
  will-change: transform;
}

/* The period is deliberately NOT reduced along with the angle. A pendulum's
   period is set by its length, not by how far it swings, so a smaller arc over
   the same 2.9s is what actually happens - it just moves more slowly. Speeding
   it up to match the smaller travel is what would make it look wrong. */
@keyframes sway {
  from { transform: rotate(-0.65deg); }
  to   { transform: rotate(0.65deg); }
}

/* The suspension string. Runs up from behind the pointer and off the top of the
 * frame, so the wheel reads as hung from something rather than floating.
 *
 * A sibling of .wheel, not a child - a string that rotated with the face would
 * wind itself round the rim. */
.suspension {
  position: absolute;
  left: 50%;
  /* Bottom edge 2.5% BELOW the frame's top, which puts it inside the pointer's
     body: the pointer paints over it (z-index 3 against 1) and hides the join,
     so the cord appears to end at the triangle rather than behind it. */
  bottom: 97.5%;
  width: 1.1%;
  /* Far longer than the gap above the wheel ever is - the frame is at most 64%
     of the scene, so that gap is only about 28% of the frame's own height.
     .scene clips the excess, so the string always leaves frame rather than
     stopping in mid air, including at the top of the drop's bounce. */
  height: 200%;
  transform: translateX(-50%);
  /* Across the width, not down the length: dark edges either side of a lighter
     core is what makes a flat rectangle read as a round cord. */
  background: linear-gradient(90deg,
    var(--k-ink) 0%,
    var(--k-plum) 42%,
    var(--k-plum) 62%,
    var(--k-ink) 100%);
  z-index: 1;
}

/* The charm on the cord.
 *
 * A sibling of .wheel like the string and the pointer, so it does not turn with
 * the face - but a CHILD of .wheel-frame, which is the element that sways, so it
 * swings with the cord it is threaded on rather than hanging still while the
 * string moves under it.
 *
 * bottom is measured from the frame's bottom edge, so 104% puts it 4% of the
 * frame's height above the frame's top. The gap between the wheel and the top of
 * the scene is a fixed 28.125% of the frame's height, and the charm is 20% tall,
 * which leaves it centred in the visible run of cord with about 24px of air
 * either side at a 1920x1080 source.
 *
 * The offset has to move whenever the width does - they are two halves of one
 * measurement. At 20% the charm nearly fills the gap, so there is not much room
 * left to grow: 30% would be 180px against a 169px run of cord and would either
 * clip at the top of the scene or sit on the wheel.
 *
 * z-index 2: over the string (1) so it reads as threaded on, under the pointer
 * (3) so it can never cover the thing that says which wedge won. */
.charm {
  position: absolute;
  left: 50%;
  bottom: 104%;
  width: 20%;
  height: auto;
  transform: translateX(-50%);
  z-index: 2;
  pointer-events: none;
  /* The source art is 112x112 and will usually be drawn smaller than that, so
     the browser is downscaling - which it does well. Sitting on a coloured bed
     rather than a dark one, a hard edge would show, so let it interpolate. */
  image-rendering: auto;
}

/* The only thing that rotates. */
.wheel {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.wheel svg {
  width: 100%;
  height: 100%;
  display: block;
}

.wheel svg text {
  pointer-events: none;
  user-select: none;
}

/* The hub, a sibling of .wheel rather than a child, so it does not turn. Its
   size is bounded by LABEL_INNER in script.js: the labels stop at 26 of the
   viewBox's 114 units, which is 22.8% of the face, so anything under that
   clears them. */
.hub {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 18%;
  height: 18%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, var(--k-plum) 0%, var(--k-ink) 72%);
  border: 0.5vmin solid var(--k-gold);
  box-shadow: 0 0 0 0.25vmin var(--k-ink);
  display: grid;
  place-items: center;
  z-index: 2;
}

/* The gold pupil at dead centre. */
.hub::after {
  content: '';
  width: 24%;
  height: 24%;
  border-radius: 50%;
  background: var(--k-gold);
  box-shadow: 0 0 0 0.2vmin var(--k-ink);
}

/* The pointer, also a sibling: one that rotated would never leave the wedge it
   started on. Two stacked pseudo-elements rather than a stroke, because
   clip-path has no outline of its own - the ink one is drawn slightly larger
   and behind, which is what reads as the outline. */
.pointer {
  position: absolute;
  top: 0;
  left: 50%;
  width: 6.5%;
  aspect-ratio: 1 / 1.2;
  transform: translate(-50%, -26%);
  z-index: 3;
}

.pointer::before,
.pointer::after {
  content: '';
  position: absolute;
  inset: 0;
  clip-path: polygon(50% 100%, 0% 40%, 14% 0%, 86% 0%, 100% 40%);
}

.pointer::before {
  background: var(--k-ink);
  transform: scale(1.2);
}

.pointer::after {
  background: var(--k-gold);
}

/* ---------------- History (operator only) ----------------
 * In the margin to the LEFT of the scene, never inside it. Absolutely
 * positioned rather than placed in .stage's flow, so it cannot push the scene
 * off centre or change its size - the OBS crop is framed against that scene,
 * and a panel that resized it would move the crop every time a spin was logged.
 * -------------------------------------------------------- */

.history {
  position: absolute;
  left: var(--hist-inset);
  top: 50%;
  transform: translateY(-50%);
  width: var(--hist-w);
  max-height: 78vh;
  /* Collapses to nothing rather than overlapping when the window is too narrow
     to have a margin. No border or padding on the box itself, for the same
     reason: at width 0 they would still paint a sliver. */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 6px;
  color: var(--text);
}

.history-title {
  margin: 0;
  font-family: Nunito, Segoe UI, system-ui, sans-serif;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-indent: 0.24em;
  text-transform: uppercase;
  color: var(--k-gold);
  opacity: 0.8;
}

.history-list {
  margin: 0;
  padding: 0;
  list-style: none;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-family: Nunito, Segoe UI, system-ui, sans-serif;
  font-size: 10px;
  line-height: 1.3;
}

.history-list li {
  display: flex;
  gap: 6px;
  padding: 3px 6px;
  border-radius: 4px;
  background: rgba(243, 166, 177, 0.06);
  border-left: 2px solid var(--k-plum);
}

/* Newest first, so the one that just landed is the one at the top. */
.history-list li:first-child {
  border-left-color: var(--k-gold);
  background: rgba(253, 191, 55, 0.1);
}

.history-when {
  flex: none;
  color: var(--k-pink);
  opacity: 0.65;
  font-variant-numeric: tabular-nums;
}

.history-what {
  /* Long entries wrap rather than being clipped: the point of the column is
     reading back what came up, and a truncated label can be ambiguous between
     two entries that share a prefix. */
  min-width: 0;
  overflow-wrap: anywhere;
}

.history-empty {
  margin: 0;
  font-family: Nunito, Segoe UI, system-ui, sans-serif;
  font-size: 10px;
  opacity: 0.4;
}

.history-empty[hidden] { display: none; }

/* ---------------- Controls ---------------- */

.controls {
  display: flex;
  gap: 14px;
  z-index: 10;
}

.btn {
  font: inherit;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 12px 24px;
  border-radius: 10px;
  border: none;
  cursor: pointer;
  transition: transform 0.12s ease, box-shadow 0.2s ease, background 0.2s ease, opacity 0.2s ease;
  background: linear-gradient(180deg, var(--k-pink) 0%, var(--k-orange) 100%);
  color: var(--text);
  box-shadow: 0 4px 14px rgba(211, 69, 49, 0.45);
}

/* The same control, as a link. An anchor does not inherit the button reset, so
   it needs the layout and the underline suppressed to sit level with the rest. */
.btn-link {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

.btn:hover:not(:disabled) {
  transform: translateY(-1px);
  background: linear-gradient(180deg, #e8a08c 0%, #df5740 100%);
  box-shadow: 0 6px 20px rgba(211, 69, 49, 0.6);
}

.btn:active:not(:disabled) {
  transform: translateY(0);
}

.btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* Suppress the default focus ring on mouse click, keep it for keyboard nav. */
.btn:focus { outline: none; }
.btn:focus-visible {
  outline: 2px solid var(--k-gold);
  outline-offset: 2px;
}

/* ---------------- Result pop-out ----------------
 * Covers the whole of .scene — which is the region OBS crops to, so the card
 * lands over the wheel on stream rather than beside it. It stays up until the
 * close button is pressed: the wheel is not the readout at 43 entries (a wedge
 * is 8.4deg wide and its label is squeezed to fit), so the card is what chat
 * actually reads, and a card that faded on its own would be gone before it had
 * been read out.
 *
 * No scrim: the wheel behind stays at full brightness, and the card carries its
 * own background and border so it reads against whatever it lands on. The layer
 * still spans the scene rather than hugging the card, because that is what
 * swallows clicks on the wheel while the result is up.
 * ------------------------------------------------ */

.result {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 4%;
  opacity: 0;
  /* Non-interactive while hidden, so it never swallows a click on the wheel. */
  pointer-events: none;
  transition: opacity 240ms ease;
  z-index: 10;
}

.result.show {
  opacity: 1;
  pointer-events: auto;
}

.result-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: clamp(20px, 3.4vw, 44px) clamp(34px, 6vw, 82px);
  max-width: 100%;
  border-radius: 20px;
  /* The design's card: plum at the top corner falling to ink, so the gold type
     has a dark field under it wherever it lands on the wheel. */
  background: linear-gradient(168deg,
    rgba(114, 49, 72, 0.94) 0%,
    rgba(73, 33, 46, 0.96) 50%,
    rgba(36, 19, 23, 0.98) 100%);
  border: 2px solid var(--k-gold);
  box-shadow:
    0 0 48px rgba(253, 191, 55, 0.2),
    0 22px 60px rgba(0, 0, 0, 0.65);
  text-align: center;
  transform: scale(0.92);
  transition: transform 240ms cubic-bezier(0.34, 1.45, 0.55, 1);
}

.result.show .result-card {
  transform: scale(1);
}

.result-label {
  font-family: Bangers, Impact, Haettenschweiler, sans-serif;
  font-weight: 400;
  font-size: clamp(20px, 3.6vw, 46px);
  line-height: 1.15;
  letter-spacing: 0.1em;
  text-indent: 0.1em;
  color: var(--k-gold);
  text-wrap: balance;
}

/* Sits on the card's corner rather than inside the text flow, so a long entry
   wrapping to three lines cannot push it off. */
.result-close {
  position: absolute;
  top: 8px;
  right: 12px;
  width: clamp(28px, 2.6vw, 40px);
  height: clamp(28px, 2.6vw, 40px);
  display: grid;
  place-items: center;
  padding: 0;
  font-size: clamp(20px, 2vw, 28px);
  line-height: 1;
  font-family: inherit;
  color: var(--k-gold);
  background: transparent;
  border: 2px solid var(--k-gold);
  border-radius: 50%;
  cursor: pointer;
  transition: background 140ms ease, color 140ms ease, transform 140ms ease;
}

.result-close:hover {
  background: var(--k-orange);
  border-color: var(--k-orange);
  color: var(--text);
  transform: scale(1.08);
}

.result-close:focus { outline: none; }
.result-close:focus-visible {
  outline: 2px solid var(--k-gold);
  outline-offset: 3px;
}

/* The winning wedge, so the card and the wheel can be checked against each
   other. The wedge itself only brightens - the outline is a separate path that
   script.js appends last.

   An SVG stroke straddles the edge it is drawn on, half inside the shape and
   half outside, and wedges are painted in order - so a stroke on the wedge had
   its outer half covered by the NEXT wedge on one side and not the other, and
   came out visibly thicker on one edge than the other. */
.seg.win { filter: brightness(1.15); }

.seg-outline {
  fill: none;
  stroke: #fff1dc;
  stroke-width: 2.5;
  stroke-linejoin: round;
  pointer-events: none;
}


/* The operator's warning strip. Outside .scene, so it is never on stream. */
.notice {
  max-width: var(--scene-w);
  padding: 10px 14px;
  border-radius: 8px;
  border: 1px solid var(--k-gold);
  background: rgba(253, 191, 55, 0.12);
  color: var(--text);
  font-size: 13px;
  line-height: 1.4;
}

.notice[hidden] { display: none; }
/* The pinned zone, beside the History heading. Dimmer than the heading: it is a
   qualifier on the times below, not a second heading. */
.history-zone {
  opacity: 0.6;
  letter-spacing: 0.12em;
}
