/* bubbleup.gg — "Like what you see?": the corner pill and the sheet behind it.
 *
 * Loaded by BOTH pages, and it is the only stylesheet that is. style.css is
 * the landing's (a locked viewport with a physics field in it); demo.css is the
 * demo's (a document that scrolls). This file may assume neither, so the few
 * things it needs from demo.css — the engraved micro-legend, chiefly — are
 * restated here under their own names rather than borrowed. The palette, the
 * mono stack and the `.chip` it extends all come from style.css, which both
 * pages do load.
 *
 * The visual argument: a form on this site is a PANEL, not a modal. It carries
 * the reader card's exact signature — 1px hairline, a 3px purple spine on the
 * left, the asymmetric 3px/9px radius that makes the shape read as a flag, the
 * same two-stop gradient over a backdrop blur — so opening it looks like a
 * panel sliding out of the instrument, not a dialog landing on top of it. */

/* ---------- the pill ---------- */
/* Bottom-right, on both pages, always. The landing's one existing call to
   action lives top-right (`.chip.go`), the demo's live-state chip rides in its
   header; the bottom-right corner is unclaimed on both, and the bubbles
   gravitate to the middle of the landing's field, so a corner stays legible.
   `.chip` gives it the shape and the type; this gives it the corner and the
   pressability. */
.cu-pill {
  position: fixed;
  z-index: 40;
  right: max(18px, env(safe-area-inset-right));
  bottom: max(18px, env(safe-area-inset-bottom));
  /* FINDING 94: on `--ui` (style.css), for every page that carries this pill.
     Its own proportions are unchanged — 0.9/1.6 of padding, a 0.9 gap and 1.2
     of type, which at the old 10px floor read 9/16, 9 and 12 exactly. What is
     gone is the reason those numbers used to be flat here: the landing reached
     them from `style.css` with a `body.home` of extra specificity and the demo
     page from `demo.css` with a `body`, so the pill was one size on two pages
     and another size on the other two. There is nothing left to reach past. */
  padding: calc(var(--ui) * 0.9) calc(var(--ui) * 1.6);
  gap: calc(var(--ui) * 0.9);
  border: 1px solid var(--line-hi);
  color: var(--fg-2);
  font-family: var(--mono);
  /* A step up from chip scale — it is a control, not a label — and a step down
     from the landing's `.chip.go`, which is that page's primary door. */
  font-size: calc(var(--ui) * 1.2);
  letter-spacing: 0.1em;
  cursor: pointer;
  box-shadow: 0 10px 30px rgba(8, 8, 11, 0.5);
  transition:
    color 160ms ease-out,
    border-color 160ms ease-out,
    box-shadow 160ms ease-out,
    transform 160ms ease-out;
}
/* The product's own three-dot mark at chip scale — the bubbles, small. It is
   what keeps this from being an anonymous "contact us" tab. */
.cu-pill .mark {
  width: calc(var(--ui) * 1.3);
  height: calc(var(--ui) * 1.3);
}
/* The dots are hand-placed in px inside the box, so the box cannot be grown on
   its own. Restated as multiples: 0.7/0.4/0.3 wide, at 0.4/0/0.9 across and
   0.5/0.1/0 down — which at the old 10px floor is the 13px mark with 7/4/3 dots
   this file shipped, to the pixel. */
.cu-pill .mark i:nth-child(1) {
  width: calc(var(--ui) * 0.7); height: calc(var(--ui) * 0.7);
  left: calc(var(--ui) * 0.4); top: calc(var(--ui) * 0.5);
}
.cu-pill .mark i:nth-child(2) {
  width: calc(var(--ui) * 0.4); height: calc(var(--ui) * 0.4);
  left: 0; top: calc(var(--ui) * 0.1);
}
.cu-pill .mark i:nth-child(3) {
  width: calc(var(--ui) * 0.3); height: calc(var(--ui) * 0.3);
  left: calc(var(--ui) * 0.9); top: 0;
}
.cu-pill:hover,
.cu-pill:focus-visible {
  color: var(--fg);
  border-color: var(--purple);
  box-shadow:
    0 10px 30px rgba(8, 8, 11, 0.55),
    0 0 18px rgba(145, 71, 255, 0.18);
  transform: translateY(-1px);
}
.cu-pill:focus-visible {
  outline: 2px solid var(--purple);
  outline-offset: 2px;
}
.cu-pill[hidden] {
  display: none;
}

/* FINDING 101 (Kevin, walk 9, 2026-09-13): …unless the page has declared a HOME
   for it. `contact.js` mounts the pill into `[data-cu-pill]` where one exists,
   and there it is ORDINARY CONTENT: in flow, laid out by whatever row it landed
   in, with `right`/`bottom`/`z-index` all inert because a static box has no
   inset and takes no layer.

   The three lines above it are the whole of what changes. Everything the pill
   LOOKS like — the `.chip` shape, the `--ui` padding and type, the mark, the
   hover lift, the focus ring — is size and ink, not position, so it is the same
   control in either place; that is what lets `site-scale.spec.ts` keep reading
   one pill height across all four pages.

   `/demo` is the only page that declares one today, on its OBS row: the page is
   one screen tall, a fixed pill buys nothing on it, and the corner it was fixed
   to already belonged to `Add to OBS`. `demo.css` owns the row. */
[data-cu-pill] > .cu-pill {
  position: static;
}

/* The landing's reader card follows the reigning bubble on every screen, and on
   a small one it rides below the leader — anywhere the field takes it, which
   includes this corner. The card is the thing that yields, so main.js measures
   THIS pill's rect every frame and keeps the card clear of it. Nothing to
   declare here: a rule that moved the card would be a second opinion about a
   position the field already owns, and the two would fight.  */

/* The demo page scrolls; the landing does not (style.css locks it). While the
   sheet is up, neither does — a backdrop over a page that still scrolls under
   the finger is the most common way a bottom sheet feels broken. */
body.cu-open {
  overflow: hidden;
}

/* ---------- backdrop ---------- */
.cu-backdrop {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: rgba(8, 8, 11, 0.62);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity 180ms ease-out;
}
body.cu-open .cu-backdrop {
  opacity: 1;
}
.cu-backdrop[hidden] {
  display: none;
}

/* ---------- the sheet ---------- */
.cu-sheet {
  position: fixed;
  z-index: 50;
  right: max(18px, env(safe-area-inset-right));
  bottom: max(18px, env(safe-area-inset-bottom));
  width: min(390px, calc(100vw - 36px));
  max-height: min(760px, calc(100vh - 36px));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 22px 22px 20px;
  border: 1px solid var(--line);
  border-left: 3px solid var(--purple);
  border-radius: 3px 9px 9px 3px;
  background: linear-gradient(180deg, rgba(16, 16, 20, 0.94), rgba(10, 10, 13, 0.94));
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.04),
    0 22px 60px rgba(8, 8, 11, 0.7);
  color: var(--fg);
  font-family: var(--mono);
  /* Rises out of the corner the pill is in — the panel comes from the control,
     not from nowhere. */
  transform: translateY(10px) scale(0.985);
  transform-origin: 100% 100%;
  opacity: 0;
  transition:
    opacity 180ms ease-out,
    transform 220ms cubic-bezier(0.16, 1, 0.3, 1);
}
body.cu-open .cu-sheet {
  transform: none;
  opacity: 1;
}
.cu-sheet[hidden] {
  display: none;
}
/* The sheet itself takes focus when it opens (contact.js). A dialog container
   is not a control, and the sheet arriving IS the indication — a ring around
   the whole panel would only say the browser agrees. */
.cu-sheet:focus {
  outline: none;
}

/* ---------- head ---------- */
.cu-head {
  position: relative;
  padding-right: 26px;
  margin-bottom: 20px;
}
/* The engraved micro-legend. demo.css has the same device under `.legend`;
   this file cannot reach it (the landing does not load demo.css) and a
   stylesheet that only half-applies on one of two pages is worse than one
   repeated declaration. */
.cu-legend {
  display: block;
  color: var(--fg-3);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.cu-title {
  margin-top: 8px;
  font-size: 18px;
  font-weight: 700;
  letter-spacing: 0.01em;
}
.cu-close {
  position: absolute;
  top: -4px;
  right: -4px;
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--fg-3);
  font-family: var(--mono);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}
.cu-close:hover {
  color: var(--fg);
  background: rgba(58, 58, 71, 0.35);
}
.cu-close:focus-visible {
  outline: 2px solid var(--purple);
  outline-offset: 1px;
}

/* ---------- fields ---------- */
.cu-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
/* `display: flex` above out-specifies the browser's own `[hidden]` rule, so the
   form would still be laid out after a successful submit hid it. */
.cu-form[hidden] {
  display: none;
}
.cu-set {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}
.cu-set > .cu-legend {
  /* `<legend>` arrives with the browser's own inline padding. */
  padding: 0;
  margin-bottom: 9px;
  /* A question is a sentence; the 0.14em tracking that suits a one-word legend
     turns a sentence into a ransom note. */
  letter-spacing: 0.06em;
  line-height: 1.5;
  text-transform: none;
  font-size: 11px;
  color: var(--fg-2);
}

/* The segmented answer: one hairline track, two halves, the chosen half lit.
   Real radios underneath, so arrow keys and screen readers get the behaviour
   for free and none of it is reimplemented. */
.cu-seg {
  display: grid;
  grid-template-columns: 1fr 1fr;
  border: 1px solid var(--line-hi);
  border-radius: 7px;
  overflow: hidden;
}
.cu-seg input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.cu-seg label {
  padding: 9px 0;
  text-align: center;
  color: var(--fg-3);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  background: linear-gradient(180deg, rgba(35, 35, 44, 0.5), rgba(22, 22, 28, 0.5));
  transition:
    color 140ms ease-out,
    background 140ms ease-out;
}
.cu-seg label + input + label {
  border-left: 1px solid var(--line-hi);
}
.cu-seg label:hover {
  color: var(--fg-2);
}
.cu-seg input:checked + label {
  color: #fff;
  background: linear-gradient(180deg, rgba(145, 71, 255, 0.32), rgba(145, 71, 255, 0.16));
}
/* `:focus`, not `:focus-visible`. The radio itself is clipped to a 1px box, so
   its label is the only thing that can show focus at all, and this ring is now
   only ever shown for a half somebody actually ARRIVED at — nothing focuses a
   radio programmatically any more. Neither the open (that is the dialog) nor an
   unanswered submit (that is the group, below): a ring on one half of a
   segmented control in which nothing is chosen reads as a choice. */
.cu-seg input:focus + label {
  outline: 2px solid var(--purple);
  outline-offset: -2px;
}

/* Unanswered, on submit. The refusal is drawn around the whole rectangle, in
   the same amber the invalid inputs use — one half in amber would still be one
   half LIT, and the form would be pointing at an answer nobody gave in the act
   of saying it needs one. `overflow: hidden` above clips the two labels, not
   this shadow, so the ring is unbroken across the seam between them. */
.cu-seg.cu-bad {
  border-color: var(--amber);
  box-shadow: 0 0 0 3px rgba(255, 176, 46, 0.16);
}
/* The group is what takes that focus, and it is deliberately silent about it:
   the amber rectangle inside it is the whole indication, and the fieldset's own
   ring would only draw a second box around the first. It is `tabindex="-1"` —
   nobody can tab to it, so this hides no keyboard affordance. */
.cu-set:focus {
  outline: none;
}

/* The three usernames: revealed by the yes half, and not present in the tab
   order or the accessibility tree until then. `hidden` does the second part;
   the grid does the first. */
.cu-handles {
  display: grid;
  gap: 10px;
}
.cu-handles[hidden] {
  display: none;
}

/* Twitch is the field; YouTube and Kick are one press each. The row that
   offers them is a hint plus two chips on one line — the same engraved legend
   and the same pill shape the rest of the site uses, at the smallest scale it
   uses them, because this is an aside inside a question and not a question. */
.cu-more {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
}
/* The row goes away once it has nothing left to offer, and `display: flex`
   above out-specifies the browser's own `[hidden]` rule. Same for `.cu-row`,
   which is the whole mechanism for a field that has not been asked for. */
.cu-more[hidden],
.cu-row[hidden] {
  display: none;
}
.cu-add {
  padding: 4px 10px;
  border: 1px solid var(--line-hi);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(35, 35, 44, 0.5), rgba(22, 22, 28, 0.5));
  color: var(--fg-3);
  font-family: var(--mono);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    color 140ms ease-out,
    border-color 140ms ease-out;
}
.cu-add:hover {
  color: var(--fg-2);
  border-color: var(--fg-3);
}
.cu-add:focus-visible {
  outline: 2px solid var(--purple);
  outline-offset: 2px;
}

.cu-row {
  display: block;
}
.cu-row .cu-legend {
  margin-bottom: 6px;
}
/* "kept private", "optional" — riding on the label's own line, and it has to be
   audibly quieter than the label or the two read as one four-word field name. */
.cu-hint {
  color: #5c5c68;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: lowercase;
}
.cu-input,
.cu-area {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: rgba(8, 8, 11, 0.6);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.4;
  transition:
    border-color 140ms ease-out,
    box-shadow 140ms ease-out;
}
.cu-area {
  min-height: 68px;
  resize: vertical;
}
.cu-input::placeholder,
.cu-area::placeholder {
  color: #55555f;
}
.cu-input:focus,
.cu-area:focus {
  outline: none;
  border-color: var(--purple);
  box-shadow: 0 0 0 3px rgba(145, 71, 255, 0.16);
}
.cu-input[aria-invalid="true"],
.cu-area[aria-invalid="true"] {
  border-color: var(--amber);
}

/* The honeypot. Off-screen rather than `display:none` or `hidden`, because a
   bot worth trapping skips fields it can see are unreachable — and a human
   never reaches it: `tabindex="-1"` and `autocomplete="off"` are on the input
   itself, in the markup. */
.cu-trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* The one checkbox, drawn rather than borrowed: the native control is the only
   thing on this page that would arrive in the browser's own blue. */
.cu-want {
  display: flex;
  align-items: center;
  gap: 10px;
  cursor: pointer;
  user-select: none;
}
.cu-want input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
}
.cu-box {
  flex: none;
  width: 16px;
  height: 16px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line-hi);
  border-radius: 4px;
  background: rgba(8, 8, 11, 0.6);
  transition:
    border-color 140ms ease-out,
    background 140ms ease-out;
}
.cu-box::after {
  content: "";
  width: 8px;
  height: 4px;
  border-left: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(-45deg) translate(1px, -1px) scale(0.6);
  opacity: 0;
  transition:
    opacity 120ms ease-out,
    transform 160ms cubic-bezier(0.16, 1, 0.3, 1);
}
.cu-want input:checked + .cu-box {
  border-color: var(--purple);
  background: var(--purple);
}
.cu-want input:checked + .cu-box::after {
  opacity: 1;
  transform: rotate(-45deg) translate(1px, -1px) scale(1);
}
.cu-want input:focus-visible + .cu-box {
  outline: 2px solid var(--purple);
  outline-offset: 2px;
}
.cu-want span:last-child {
  color: var(--fg-2);
  font-size: 12px;
  letter-spacing: 0.06em;
}

/* Turnstile renders its own iframe at a fixed size; this only reserves the room
   so the sheet does not jump when the widget arrives. */
.cu-check {
  min-height: 65px;
}
.cu-check:empty {
  min-height: 0;
}

/* ---------- submit, and the one line of state ---------- */
.cu-go {
  width: 100%;
  padding: 11px 16px;
  border: 1px solid var(--purple);
  border-radius: 7px;
  background: linear-gradient(180deg, rgba(145, 71, 255, 0.9), rgba(120, 55, 220, 0.9));
  color: #fff;
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  cursor: pointer;
  transition:
    filter 140ms ease-out,
    opacity 140ms ease-out;
}
.cu-go:hover {
  filter: brightness(1.12);
}
.cu-go:focus-visible {
  outline: 2px solid var(--purple);
  outline-offset: 2px;
}
.cu-go[disabled] {
  opacity: 0.55;
  cursor: default;
  filter: none;
}
.cu-note {
  margin: 0;
  min-height: 15px;
  color: var(--amber);
  font-size: 11px;
  letter-spacing: 0.05em;
  line-height: 1.35;
}
.cu-note:empty {
  min-height: 0;
}

/* What the sheet becomes once it has the submission: one line, and then it
   leaves. Nothing to read, nothing to dismiss. */
.cu-done {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 0 2px;
  color: var(--fg);
  font-size: 13px;
  letter-spacing: 0.04em;
}
.cu-done .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--green);
}
.cu-done[hidden] {
  display: none;
}

/* ---------- phones ---------- */
/* Below the width where a 390px panel in a corner stops being a panel and
   starts being the whole screen with a margin, it becomes a proper bottom
   sheet: full width, seated on the bottom edge, spine along the top. */
@media (max-width: 460px) {
  .cu-sheet {
    right: 0;
    left: 0;
    bottom: 0;
    width: 100%;
    max-height: 92vh;
    padding: 20px 18px calc(18px + env(safe-area-inset-bottom));
    border-left: 1px solid var(--line);
    border-top: 3px solid var(--purple);
    border-radius: 12px 12px 0 0;
    transform-origin: 50% 100%;
    transform: translateY(14px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .cu-pill,
  .cu-backdrop,
  .cu-sheet,
  .cu-box,
  .cu-box::after {
    transition: none;
  }
  .cu-sheet {
    transform: none;
  }
}
