/* ---------------------------------------------------------------------------
   Peltify — the suggestion chat and its two cards, 2026-08-29.
   Cards: src/ui/cards.js (taste-check `.tc-*`, suggestion `.sc-*`, the
   compare graph `.cg-*`, the stack `.cd-*`). Chat: src/ui/chat.js (`.ch-*`).

   Boxy like the rest of the app (--r-box); covers keep --r-art. The two big
   answer buttons on the taste card are pills, because Nick pointed at the
   built card with pills and said "It should be a neat card like that".
   --------------------------------------------------------------------------- */

/* ---- a cover shown whole over its own blurred copy ---- */
.cd-hero { position: relative; overflow: hidden; border-radius: var(--r-art); background: var(--paper-deep); display: grid; place-items: center; }
.cd-art-bg { position: absolute; inset: -12%; width: 124%; height: 124%; object-fit: cover; filter: blur(18px) saturate(1.2); opacity: 0.55; }
.cd-art { position: relative; z-index: 1; display: block; max-width: 100%; max-height: 100%; object-fit: contain;
          box-shadow: 0 14px 30px -16px rgba(20, 8, 40, 0.7); border-radius: 4px; }
.cd-art-none { position: relative; z-index: 1; display: grid; gap: 2px; place-items: center; text-align: center; color: var(--ink-faint);
               font-size: 12px; font-weight: 700; }
.cd-art-none small { font-weight: 500; font-size: 11px; }

/* ---- the taste-check card ---- */
.tc-card { display: flex; flex-direction: column; gap: 12px; width: min(560px, 100%); margin: 0 auto;
           padding: 14px 14px 16px; border: 1px solid var(--line); border-radius: var(--r-box);
           background: var(--white); box-shadow: var(--raise); }
.tc-card .cd-hero { height: 260px; padding: 12px; }
.tc-card .cd-art { max-height: 236px; }
.tc-body { display: flex; flex-direction: column; gap: 4px; padding: 2px 6px 0; }
.tc-lead { margin: 0; font-size: 11.5px; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-faint); }
.tc-title { margin: 0; font-family: var(--font-serif); font-size: clamp(24px, 3vw, 32px); font-weight: 700; letter-spacing: -0.01em; line-height: 1.1; }
.tc-name { border: 0; background: none; padding: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; }
.tc-name:hover { color: var(--spine); }
.tc-qm { margin-left: 2px; }
.tc-by { margin: 0; font-size: 16px; color: var(--ink-soft); }
.tc-basis { margin: 4px 0 0; font-size: 11.5px; line-height: 1.45; color: var(--ink-faint); }
.tc-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 4px 6px 0; }
/* 24.1 — the notes bar under a taste card: a clarifier on the answer, quiet
   enough that it never competes with the three buttons above it. */
.tc-noterow { padding: 8px 6px 0; }
.tc-note {
  width: 100%; min-height: 34px; padding: 0 12px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--white); color: var(--ink);
  font: inherit; font-size: 13px;
}
.tc-note::placeholder { color: var(--ink-faint); }
.tc-note:focus-visible { outline: 2px solid var(--violet); outline-offset: 1px; border-color: var(--violet); }

/* 24.1 — the skippable star row on a card that just entered the library. */
.sc-rate { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 6px 0; }
.sc-rate-l { font-size: 12.5px; font-weight: 700; color: var(--ink-soft); }
.sc-rate-skip {
  border: 0; background: none; padding: 0 4px; font: inherit; font-size: 12.5px;
  color: var(--ink-faint); text-decoration: underline; cursor: pointer;
}
.sc-rate-skip:hover { color: var(--ink); }

/* 24.1 — the deck's two controls, and deliberately only two: lock the anchor,
   or ask for a different direction. Everything else is said in the composer. */
.ch-deckacts { display: flex; align-items: center; justify-content: center; gap: 8px; padding-top: 6px; flex-wrap: wrap; }
.ch-lock, .ch-diff {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 30px; padding: 0 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--white); color: var(--ink-soft); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.ch-lock:hover, .ch-diff:hover { border-color: var(--ink); color: var(--ink); }
.ch-lock.is-on { background: var(--ink); border-color: var(--ink); color: var(--white); }
.tc-yes, .tc-no { height: 48px; border-radius: var(--r-pill); font: inherit; font-size: 15px; font-weight: 700; cursor: pointer; }
.tc-yes { border: 1px solid var(--violet); background: var(--violet); color: var(--white); }
.tc-yes:hover:not(:disabled) { background: var(--spine); border-color: var(--spine); }
.tc-no { border: 1px solid var(--line-strong); background: var(--white); color: var(--ink); }
.tc-no:hover:not(:disabled) { border-color: var(--ink); }
.tc-yes:disabled, .tc-no:disabled { opacity: 0.5; cursor: default; }
.tc-progress { display: flex; align-items: center; justify-content: center; gap: 10px; font-size: 12px; font-weight: 700; color: var(--ink-soft); }
.tc-dots { display: inline-flex; gap: 5px; }
.tc-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--line-strong); }
.tc-dot.is-now { width: 18px; border-radius: 4px; background: var(--violet); }
.tc-dot.is-done { background: var(--violet); opacity: 0.5; }
.tc-foot { display: flex; justify-content: center; }
.tc-skip { height: 32px; padding: 0 14px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--paper);
           font: inherit; font-size: 12.5px; font-weight: 700; color: var(--ink-soft); cursor: pointer; }
.tc-skip:hover:not(:disabled) { color: var(--ink); border-color: var(--ink); }

/* ===========================================================================
   19.1 (`taste-check-card/SPEC.md` entry 5) — IT FITS THE CHATBOX.
   Nick: "'Have you heard' cards are too big and dont structurly fit in the
   chatbox on certain aspect ratios, specifically laptop and phone." Measured
   at 605px against a 430px thread on a 13" laptop and a 553px thread on a
   phone. The same ladder the suggestion card got in 17.2 — the artwork tracks
   the viewport, then the quiet lines and the button heights give — except the
   artwork's cap is a LENGTH, not `100%`, because a percentage on a centred
   grid item never resolved against the hero (the 19.1 fault on the other
   card: the cover was cropped rather than scaled).
   =========================================================================== */
.tc-card { --tc-hero: clamp(96px, 22dvh, 260px); }
.tc-card .cd-hero { height: var(--tc-hero); }
.tc-card .cd-art { max-height: calc(var(--tc-hero) - 24px); }
@media (max-height: 760px) {
  .tc-card { gap: 8px; padding: 12px; }
  .tc-basis { display: none; }
  .tc-yes, .tc-no { height: 40px; font-size: 14px; }
  .tc-seen-only { height: 34px; }
  .tc-actions { gap: 8px; padding-top: 0; }
  .tc-title { font-size: clamp(20px, 2.4vw, 26px); }
  .tc-by { font-size: 14px; }
}
@media (max-width: 640px) {
  .tc-card { gap: 9px; padding: 12px; }
  .tc-yes, .tc-no { height: 44px; }
  .tc-actions { gap: 9px; padding-top: 0; }
}

/* ---- the suggestion card: two columns, the left a little smaller ---- */
.sc-card { position: relative; display: flex; flex-direction: column; gap: 14px; width: 100%;
           padding: 18px; border: 1px solid var(--line); border-radius: var(--r-box);
           background: var(--white); box-shadow: var(--raise); user-select: text; }
.sc-split { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: 22px; align-items: start; }
.sc-work { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
.sc-work .cd-hero { height: 220px; padding: 14px; }
.sc-work .cd-art { max-height: 192px; }
.sc-meta { display: flex; flex-direction: column; gap: 4px; }
.sc-kind { align-self: flex-start; padding: 2px 8px; border-radius: var(--r-box); background: var(--violet-wash-strong); color: var(--spine);
           font-size: 10.5px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; }
.sc-title { margin: 2px 0 0; font-family: var(--font-serif); font-size: clamp(24px, 2.6vw, 32px); font-weight: 700; line-height: 1.08; letter-spacing: -0.01em; }
.sc-title-open { border: 0; background: none; padding: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; }
/* A LONG TITLE CLAMPS (lane 12.5, message 14: "the UI is terrible when the
   title name / description gets long"): two lines on desktop, three on a
   phone (HANDOFF-12 open question 5, default taken). The whole name is in
   the title attribute and on the Details popup. */
.sc-title-open { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
/* ABOUT IS A FIXED NUMBER OF LINES, WITH "more.." (lane 12.5). */
.sc-about-p { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
.sc-about-more { border: 0; background: none; padding: 0; margin-top: 2px; font: inherit; font-size: 12.5px; font-weight: 700; color: var(--spine); cursor: pointer; }
.sc-about-more:hover { text-decoration: underline; }
/* The whole media column opens the pill (lane 12.5). */
.sc-work[data-pill] { cursor: pointer; }
.sc-work[data-pill] .sc-desc, .sc-work[data-pill] .sc-meta { cursor: pointer; }
.sc-title-open:hover { color: var(--spine); }
.sc-creator { margin: 0; font-size: 15px; color: var(--ink-soft); display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px; }
.sc-creator-open { border: 0; background: none; padding: 0; font: inherit; font-weight: 700; color: var(--ink); cursor: pointer; }
.sc-creator-open:hover { color: var(--spine); }
.sc-sep { color: var(--ink-faint); padding: 0 2px; }
.sc-year { font-variant-numeric: tabular-nums; }
/* NOT TOO SQUEEZED: the description keeps the whole column, at a readable measure. */
.sc-desc { display: flex; flex-direction: column; gap: 4px; }
.sc-desc p { margin: 0; font-family: var(--font-serif); font-size: 15px; line-height: 1.55; color: var(--ink); }
.sc-desc .faint { font-family: var(--font-sans); font-size: 13px; color: var(--ink-faint); }
.sc-prov { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-sans) !important; font-size: 11.5px !important; color: var(--ink-faint) !important; }
/* Unverified ([3B-1]): the check was refused, and the card says so — amber,
   not red, because nothing is known to be wrong. */
.sc-unverified p { font-family: var(--font-sans); font-size: 12.5px; line-height: 1.5;
                   color: var(--warn); display: inline-flex; align-items: flex-start; gap: 5px; }
.sc-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.sc-tag { padding: 4px 9px; border: 1px solid var(--line); border-radius: var(--r-box); background: var(--white); font-size: 12.5px; font-weight: 600; }
.sc-tag-from { border-style: dashed; color: var(--ink-soft); }

/* THE GOOGLE-STYLE RATINGS ROW ([SUGG-7], `…2tjx7iHIeC.png`): score big,
   source small under it, hairlines between cells. Only provider-stated
   numbers ever land here; with none, the row does not exist. */
.sc-ratings { display: flex; align-items: stretch; }
.sc-rating { display: flex; flex-direction: column; align-items: center; gap: 1px; flex: 1 1 0;
             padding: 2px 10px; border-left: 1px solid var(--line); text-align: center; }
.sc-rating:first-child { border-left: 0; padding-left: 0; }
.sc-rating-n { font-size: 15px; font-weight: 800; font-variant-numeric: tabular-nums; color: var(--ink); }
.sc-rating-s { font-size: 11px; color: var(--ink-soft); }

.sc-why { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
/* the description, ABOVE "What this has" ([SUGG-7]) — catalogue prose */
.sc-about { }
.sc-about-p { margin: 0; font-family: var(--font-serif); font-size: 14.5px; line-height: 1.55; color: var(--ink); }
.sc-returned { margin: 0; display: flex; align-items: center; gap: 4px; font-size: 12.5px; color: var(--coral-dark); }
.sc-why-box { padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--r-box); background: var(--paper); }
.sc-why-h { margin: 0 0 6px; font-size: 11px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); }
.sc-why-p { margin: 0; font-size: 14.5px; line-height: 1.55; color: var(--ink); }
.sc-why-p.faint { color: var(--ink-faint); }
/* "Open in Spotify" … for the apps that ARE connected, under the why (entry 2).
   Wrapping links, each its own pill; on a phone they take a full row each. */
.sc-open { display: flex; flex-wrap: wrap; gap: 8px; }
.sc-open-l { display: inline-flex; align-items: center; gap: 6px; height: 36px; padding: 0 14px;
             border: 1px solid var(--line-strong); border-radius: var(--r-pill); background: var(--white);
             color: var(--ink); font-size: 13px; font-weight: 700; text-decoration: none;
             transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease); }
.sc-open-l:hover { border-color: var(--ink); background: var(--paper); }
.sc-open-l svg { color: var(--ink-faint); }
@media (max-width: 520px) { .sc-open-l { flex: 1 1 100%; justify-content: center; } }

/* the simple graph, side by side */
.cg { display: flex; flex-direction: column; gap: 8px; }
.cg-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
.cg-col { min-width: 0; }
.cg-h { margin: 0 0 6px; font-size: 11px; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); }
.cg-theirs .cg-h { color: var(--spine); }
.cg-rows { margin: 0; padding: 0; list-style: none; display: grid; gap: 6px; }
/* WORDS ARE NEVER CUT OFF (entry 2): the name has the room, the bar takes
   what is left, and a long genre wraps rather than ending in "…". */
.cg-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(56px, 34%) auto; align-items: center; gap: 10px; font-size: 13px; }
.cg-l { font-weight: 700; overflow-wrap: anywhere; line-height: 1.25; }
.cg-track { display: block; height: 8px; border-radius: 2px; background: var(--paper-deep); overflow: hidden; }
.cg-fill { display: block; height: 100%; background: var(--violet); border-radius: 2px; }
/* related to you = purple, in the name and the bar (entry 2) */
.cg-row.is-shared .cg-fill { background: var(--violet); }
.cg-row.is-shared .cg-l { color: var(--violet); }
.cg-row.is-new .cg-l { color: var(--ink); }
.cg-n { font-variant-numeric: tabular-nums; min-width: 2ch; text-align: right; font-weight: 800; }
.cg-new { font-size: 11px; color: var(--ink-faint); font-style: italic; }
.cg-empty { margin: 0; font-size: 12.5px; color: var(--ink-faint); }
.cg-foot { margin: 0; font-size: 12px; color: var(--ink-soft); }
/* the side-by-side ([SUGG-8]): the work's genres against how much of that
   list is already yours — two bars, one meaning per length */
.cg-compare { display: grid; gap: 4px; padding: 8px 10px; margin-bottom: 4px;
              border: 1px solid var(--line); border-radius: var(--r-box); background: var(--paper); }
.cg-cmp-row { display: grid; grid-template-columns: minmax(52px, auto) minmax(0, 1fr) auto; align-items: center; gap: 10px; font-size: 12px; }
.cg-cmp-l { font-weight: 700; color: var(--ink-soft); }
.cg-cmp-n { font-variant-numeric: tabular-nums; font-weight: 800; }
.cg-cmp-row .cg-fill { background: var(--ink-soft); }
.cg-cmp-row.is-you .cg-fill { background: var(--violet); }
.cg-cmp-row.is-you .cg-cmp-l, .cg-cmp-row.is-you .cg-cmp-n { color: var(--spine); }

/* THE MARKS (13.8, suggestion-card answer 14). A row of small tokens in the
   corner — one per mark, because Already seen and Pelt STACK with Keep or Not
   for me. Each arrives with a short pop, which is the "small animated token"
   Nick asked for; nothing greys out and nothing leaves. */
.sc-said { position: absolute; top: 10px; right: 12px; margin: 0; display: inline-flex; align-items: center;
           gap: 5px; z-index: 2; }
.sc-token { display: inline-flex; align-items: center; gap: 4px;
            padding: 3px 9px; border-radius: var(--r-box); font-size: 11.5px; font-weight: 800;
            letter-spacing: 0.06em; text-transform: uppercase;
            background: var(--violet); color: var(--white);
            animation: sc-token-in 260ms var(--ease) both; }
.sc-token[data-v="no"] { background: var(--ink-soft); }
.sc-token[data-v="seen"] { background: var(--ink); }
.sc-token[data-v="pelt"] { background: var(--spine, #4c1d95); }
.sc-token[data-v="ignored"] { background: var(--line-strong); color: var(--ink); }
@keyframes sc-token-in { from { opacity: 0; transform: scale(.7) translateY(-3px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .sc-token { animation: none; } }

/* 13.8 REPLACED THE GREY-AND-MOVE-ON (2026-08-30, entries 3-5) AND 11.10's
   fly-out: "a tap marks, it never removes". A marked card keeps its full
   colour — it is a card you have an opinion about, not a card that is done —
   and the way on is the viewer's arrows, ← / →, or a swipe. */
.sc-card[data-said] .sc-actions { opacity: 1; }
.ch-viewer:focus-visible { outline: 2px solid var(--violet); outline-offset: 4px; border-radius: var(--r-box); }

/* The third taste answer (13.8, answer 15) sits under the first two: it is a
   longer sentence and a rarer press, so it does not compete for the row. */
.tc-seen-only { grid-column: 1 / -1; height: 40px; border: 1px solid var(--line);
                border-radius: var(--r-pill); background: none; font: inherit; font-size: 13px;
                font-weight: 600; color: var(--ink-soft); cursor: pointer; }
.tc-seen-only:hover:not(:disabled) { border-color: var(--ink); color: var(--ink); }
.tc-seen-only:disabled { opacity: 0.5; cursor: default; }
@media (prefers-reduced-motion: reduce) { .sc-card[data-said] { transition: none; } }

.sc-actions { display: flex; justify-content: space-around; gap: 8px; padding-top: 12px; border-top: 1px solid var(--line-soft); }
.sc-act { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 6px 10px; border: 0; background: none;
          font: inherit; font-size: 12.5px; font-weight: 700; color: var(--ink-soft); cursor: pointer; }
.sc-act-g { display: grid; place-items: center; width: 46px; height: 46px; border: 1px solid var(--line-strong); border-radius: 50%; background: var(--white); color: var(--ink); transition: transform 140ms var(--ease), border-color 140ms var(--ease); }
.sc-act:hover:not(:disabled) .sc-act-g { transform: translateY(-2px); border-color: var(--ink); }
.sc-act-keep .sc-act-g { border-color: var(--violet); color: var(--violet); }
.sc-act-keep .sc-act-l { color: var(--spine); }
.sc-act.is-on .sc-act-g { background: var(--violet); border-color: var(--violet); color: var(--white); }
.sc-act:disabled { opacity: 0.45; cursor: default; }

@media (max-width: 760px) {
  .sc-split { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .sc-card { padding: 14px; gap: 10px; }
}
/* A PHONE SEES THE WHOLE CARD (2026-08-30, entry 3: "impossible to see the
   full card on mobile devices"): everything one step smaller and tighter —
   the cover a short band, the title down a size, the actions a single row
   that never wraps its words. */
@media (max-width: 520px) {
  .sc-title-open { -webkit-line-clamp: 3; }
  .sc-work .cd-hero { height: 120px; padding: 8px; }
  .sc-work .cd-art { max-height: 104px; }
  .sc-title { font-size: 21px; }
  .sc-creator { font-size: 13.5px; }
  .sc-desc p { font-size: 13.5px; line-height: 1.45; }
  .sc-tags { gap: 4px; }
  .sc-tag { padding: 3px 7px; font-size: 11.5px; }
  .sc-why-box { padding: 10px 12px; }
  .sc-why-p { font-size: 13px; line-height: 1.45; }
  .sc-open-l { flex: 1 1 auto; height: 32px; font-size: 12px; padding: 0 10px; }
  .sc-actions { gap: 4px; padding-top: 8px; }
  .sc-act { padding: 4px 2px; font-size: 11px; flex: 1 1 0; }
  .sc-act-l { white-space: nowrap; }
  .sc-act-g { width: 38px; height: 38px; }
  .cg-row { font-size: 12px; gap: 8px; }
  .cg-h, .sc-why-h { font-size: 10px; }
  .sc-rating-n { font-size: 13.5px; }
  .sc-about-p { font-size: 13px; line-height: 1.45; }
}

/* ---- the stack: one card on top, blank panels behind, the flight ---- */
.cd-stack { position: relative; padding-bottom: 18px; }
.cd-behind { position: absolute; left: 4%; right: 4%; top: 0; bottom: 0; border-radius: var(--r-box);
             background: var(--white); border: 1px solid var(--line); box-shadow: var(--lift);
             transform: translateY(calc(var(--d, 1) * 10px)) scale(calc(1 - var(--d, 1) * 0.03)); opacity: 0.6; z-index: 0; }
.cd-behind[data-depth="1"] { --d: 1; }
.cd-behind[data-depth="2"] { --d: 2; left: 8%; right: 8%; opacity: 0.35; }
.cd-swipe { position: relative; z-index: 1; outline: none; }
.cd-swipe:focus-visible { outline: 3px solid var(--violet); outline-offset: 4px; border-radius: var(--r-box); }
.cd-swipe.is-back { animation: cd-return 360ms var(--ease) both; }
@keyframes cd-return {
  from { transform: translateY(14px) scale(0.985); opacity: 0.4; }
  to   { transform: none; opacity: 1; }
}

/* ===========================================================================
   THE CHAT (src/ui/chat.js) — REDRAWN 2026-08-29 (suggestions/RAW.md entry 5).
   "bigger… an actually encapsulating chat… top to bottom"; "bubbly just like
   chatgpt… without any boxes at all… Notice how my message has the box
   however"; "basic smooth and not too complicated and fluffy"; "I dont like
   the purple highlight". So: no frame around the chat, no box around a
   Peltify turn, the person's words in a soft neutral bubble on the right, a
   plain composer whose focus is a darker line, and a typing animation.
   =========================================================================== */
.ch { display: flex; flex-direction: column; min-height: 420px;
      /* TOP TO BOTTOM, below the topbar: the offset is measured at mount
         (`--ch-top`) so the box ends at the bottom of the window and the
         thread scrolls inside it, never the page. */
      height: calc(100dvh - var(--ch-top, 170px)); max-height: none;
      border: 0; border-radius: 0; background: none; box-shadow: none; overflow: hidden; }
.ch-thread { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 18px;
             padding: 6px 4px 18px; overflow-anchor: none; scrollbar-width: thin; }
/* 20.3 (`suggestions/SPEC.md` entry 34) — THE COMPOSER IS AT THE BOTTOM AND
   HISTORY STACKS UPWARD. Nick: "I also want the chatbox to type at the bottom
   and have history scroll up. Right now it starts at the top then comes down."
   The composer was always last in the column; what was wrong is that a SHORT
   thread grew downwards from the top, leaving the first message stranded at
   the ceiling with a gap above the composer.

   `margin-top: auto` on the first turn, NOT `justify-content: flex-end` on the
   scroller: flex-end on an overflow container makes the overflowing top
   unreachable in Chrome. This pushes the stack down only when there is room
   to spare, and does nothing once the thread is taller than the box. */
.ch-thread > *:first-child { margin-top: auto; }
.ch-turn { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ch-turn.is-you { align-items: flex-end; }
/* the person's bubble — like ChatGPT's: soft, neutral, rounded, on the right */
.ch-you { max-width: min(72%, 560px); padding: 10px 16px; border-radius: 20px; border-bottom-right-radius: 6px;
          background: var(--paper-deep); color: var(--ink); font-size: 15px; line-height: 1.5; overflow-wrap: anywhere;
          /* 20.2 — A LINE BREAK IN WHAT SOMEBODY WROTE IS A LINE BREAK. The
             taste-check artifact is one answer per line ("@media1 - heard it
             (LINEBREAK) @media2 - skip"), and every one of them ran together
             into a paragraph because HTML collapses newlines. It applies to
             any typed message too, which is what a chat bubble should do. */
          white-space: pre-wrap; }
.ch-you-inc { display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-end; }
.ch-inc { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--white); font-size: 12.5px; font-weight: 600; color: var(--ink); }
.ch-inc img { width: 18px; height: 18px; object-fit: cover; border-radius: 3px; }
/* AN INLINE MENTION (phase6, 2026-09-04): `[MF DOOM - Madvillainy]` written
   into the sentence itself. `.ig-mention` in ig.css owns the look — weight,
   colour, hover — and this only makes it sit right inside a bubble whose
   background is already tinted. Nothing here redefines it. */
.ch-you .ig-mention { color: var(--you-mention, var(--spine)); }
/* 26.1 — the have-you-heard record ("Pete Rock - The Auditorium Vol. 1 – skip"):
   "the ONLY case where it shouldnt line break on the media itself". A name
   stays whole; the line may wrap before its "– skip", never inside the name. */
.ch-you.is-artifact .ig-mention { white-space: nowrap; }
.ch-you .ig-mention:hover { background: color-mix(in srgb, var(--white) 55%, transparent); }
.ch-mention.lib-mine { text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; }
/* an attached piece INSIDE the message ([SUGG-12]): inline with the words,
   clickable to its metadata card */
.ch-inc-in { font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; vertical-align: -0.35em;
             margin: 0 2px; transition: border-color var(--fast) var(--ease); }
.ch-inc-in:hover { border-color: var(--ink); }
.ch-inc-in.lib-mine { border-style: dotted; border-color: var(--violet); }
/* a Peltify answer: plain words, no box, spanning the width */
.ch-card { width: 100%; padding: 0 2px; border: 0; border-radius: 0; background: none; }
.ch-card-t { margin: 0; font-size: 15.5px; line-height: 1.55; color: var(--ink); max-width: 70ch; }
.ch-card-t b { font-weight: 800; }
/* the caret while the words are being typed */
.ch-card-t.is-typing::after { content: ""; display: inline-block; width: 2px; height: 1em; margin-left: 2px; vertical-align: -0.15em;
                              background: var(--ink); animation: ch-caret 900ms steps(2, start) infinite; }
@keyframes ch-caret { to { opacity: 0; } }
.ch-note { margin: 0; font-size: 12.5px; color: var(--ink-soft); display: flex; align-items: center; gap: 6px; }
.ch-note.is-bad { color: var(--coral-dark); }
/* the quick buttons: horizontal, easy to hit, quiet until hovered */
.ch-opts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; transition: opacity 260ms var(--ease), transform 260ms var(--ease); }
/* 14.8 (_global/mobile/SPEC.md entry 2): "if on small device it should be a
   left right gallery of questions to save space" — the kind chips wrapped
   onto four rows on an iPhone 7 and pushed everything else off the screen.
   One row that scrolls sideways inside itself; the page still never does. */
@media (max-width: 640px) {
  .ch-opts { flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
             scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
             padding-bottom: 4px; scrollbar-width: none; }
  .ch-opts::-webkit-scrollbar { display: none; }
  .ch-opts > .ch-opt { flex: none; scroll-snap-align: start; }
  .ch-hints { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; justify-content: flex-start; }
  .ch-hints::-webkit-scrollbar { display: none; }
  .ch-hints > * { flex: none; }
}
.ch-opts.is-pending { opacity: 0; transform: translateY(4px); pointer-events: none; }
.ch-opt { height: 40px; padding: 0 16px; border: 1px solid var(--line-strong); border-radius: var(--r-pill); background: var(--white);
          font: inherit; font-size: 14px; font-weight: 600; color: var(--ink); cursor: pointer;
          transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease); }
.ch-opt:hover:not(:disabled) { border-color: var(--ink); background: var(--paper); }
.ch-opt.is-custom { border-style: dashed; color: var(--ink-soft); }
.ch-opt.is-picked { background: var(--ink); border-color: var(--ink); color: var(--white); }
.ch-opt:disabled { opacity: 0.55; cursor: default; }
.ch-count .ch-opt { min-width: 56px; font-size: 16px; }
/* the taste card sits centred in its turn — and so does its stack, so the
   panels behind it are card-wide, not pane-wide (a 1456px pane made them
   two flat sheets the width of the screen) */
.ch-turn.is-taste .cd-stack { width: min(560px, 100%); margin: 0 auto; }
.ch-turn.is-taste .tc-card { margin: 0 auto; }
/* the picks: a viewer with arrows and dots, like the reference build */
.ch-picks { display: flex; flex-direction: column; gap: 10px; }
/* "I want the cards to be a little more boxed in. On this wide monitor its
   really stretched out" → "For the suggestion card ONLY… Similar feel /
   dimensions to a mini tablet… Its just gotta have a real CARD feel to it"
   (2026-08-30, entries 3–5). The thread and the composer keep the full width;
   the card is capped at a mini-tablet's measure and centred. */
/* 15.3: the viewer is the card alone; the arrows moved down to the dots row. */
.ch-viewer { display: grid; grid-template-columns: minmax(0, 1fr); align-items: center;
             width: min(100%, var(--sc-max, 860px)); margin: 0 auto; }
.ch-picks .ch-note, .ch-dots { width: min(100%, var(--sc-max, 860px)); margin-left: auto; margin-right: auto; }
/* 20.5 (`suggestion-card/SPEC.md` entry 10) — THE ARROWS GET BIGGER. Nick,
   over the screenshot: "Notice how small the arrows are?" 44px is the hit
   target a thumb needs and the one the strips used to stand in for. */
.ch-arrow { width: 44px; height: 44px; font-size: 14px; border: 1px solid var(--line); border-radius: 50%; background: var(--white); color: var(--ink); cursor: pointer; display: grid; place-items: center; font: inherit; }
.ch-arrow:hover:not(:disabled) { border-color: var(--ink); }
.ch-arrow:disabled { opacity: 0.3; cursor: default; }
.ch-dots { display: flex; align-items: center; justify-content: center; gap: 6px; }
.ch-dots .ch-arrow { width: 44px; height: 44px; font-size: 14px; }
.ch-dots .ch-prev { margin-right: 8px; }
.ch-dots .ch-next { margin-left: 8px; }
.ch-dot { width: 8px; height: 8px; padding: 0; border: 0; border-radius: 50%; background: var(--line-strong); cursor: pointer; }
.ch-dot.is-now { width: 20px; border-radius: 4px; background: var(--ink); }
.ch-dot.is-done { background: var(--ink-soft); }
.ch-dots-n { font-size: 12px; font-weight: 700; color: var(--ink-soft); margin-left: 6px; }
/* THE THINKING INDICATOR — three dots, like every AI chat; the words beside
   them are the real stage the build is at, never a made-up one. */
.ch-typing { display: flex; align-items: center; gap: 10px; margin: 0; font-size: 13.5px; color: var(--ink-soft); }
.ch-typing-d { display: inline-flex; align-items: center; gap: 4px; height: 18px; }
.ch-typing-d i { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-soft); opacity: 0.35;
                 animation: ch-bounce 1.2s ease-in-out infinite; }
.ch-typing-d i:nth-child(2) { animation-delay: 160ms; }
.ch-typing-d i:nth-child(3) { animation-delay: 320ms; }
@keyframes ch-bounce { 0%, 60%, 100% { transform: translateY(0); opacity: 0.35; } 30% { transform: translateY(-4px); opacity: 1; } }
/* the usage limit: like most AI chatboxes */
.ch-limit { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border: 1px solid rgba(191, 62, 47, 0.45);
            border-radius: var(--r-box); background: var(--coral-wash); color: var(--coral-dark); font-size: 14px; }
.ch-limit svg { flex: none; color: var(--coral-dark); }
/* the composer, pinned below, with the + — plain, no purple */
.ch-composer { flex: none; display: flex; flex-direction: column; gap: 8px; padding: 10px 0 4px; background: none; }
.ch-included { display: flex; flex-wrap: wrap; gap: 6px; }
.ch-included:empty { display: none; }
.ch-inc-x { border: 0; background: none; padding: 0 0 0 4px; color: var(--ink-faint); cursor: pointer; display: grid; place-items: center; }
.ch-inc-x:hover { color: var(--ink); }
.ch-row { display: flex; align-items: center; gap: 8px; min-height: 52px; padding: 0 8px 0 8px; border: 1px solid var(--line-strong);
          border-radius: 26px; background: var(--white); box-shadow: var(--raise);
          transition: border-color var(--fast) var(--ease); }
.ch-row:focus-within { border-color: var(--ink); outline: none; }
.ch-plus { width: 36px; height: 36px; flex: none; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 50%;
           background: var(--white); color: var(--ink); cursor: pointer; transition: border-color var(--fast) var(--ease); }
.ch-plus:hover:not(:disabled) { border-color: var(--ink); background: var(--paper); }
.ch-input { flex: 1 1 auto; min-width: 0; border: 0; background: none; font: inherit; font-size: 15.5px; color: var(--ink); outline: none; }
.ch-input:focus, .ch-input:focus-visible { outline: none; box-shadow: none; }
.ch-input::placeholder { color: var(--ink-faint); }
.ch-send { width: 36px; height: 36px; flex: none; display: grid; place-items: center; border: 0; border-radius: 50%; background: var(--ink); color: var(--white); cursor: pointer; }
.ch-send:hover:not(:disabled) { background: var(--ink-soft); }
.ch-send:disabled, .ch-plus:disabled { opacity: 0.4; cursor: default; }
/* only the server's count remains under the box — the footnote is gone (entry 5) */
.ch-foot { display: flex; justify-content: flex-end; margin: 0; font-size: 11.5px; color: var(--ink-faint); min-height: 1em; }
/* 26.2 — the round-size slider, left of the allowance. */
.ch-foot-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
/* GREYED OUT when the allowance is spent: unclickable, and it looks it. */
.ch.is-limited .ch-composer { opacity: 0.55; pointer-events: none; }
/* [SUGG-17] only NEW ASKS are off when the allowance is spent: the option
   pills. Taste answers, verdicts, Keep, Pelt and share stay live. */
.ch.is-limited .ch-thread .ch-opt { opacity: 0.5; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  .ch-typing-d i, .ch-card-t.is-typing::after { animation: none; }
}
/* Under the phone's bottom rail AND the Dashboard's bottom tabs (2026-08-30):
   the box stops above both. */
/* The height is MEASURED (ui/chat.js `measureBox`) against the real fixed bars,
   because a fixed subtraction is right at one width and wrong at every other
   (2026-08-30, entry 7). These stay as the fallback for the frames before the
   first measurement, and for a layout that cannot be measured at all. */
@media (max-width: 900px) {
  .ch { height: calc(100dvh - var(--ch-top, 110px) - 72px); }
}
@media (max-width: 720px) {
  .ch { height: calc(100dvh - var(--ch-top, 96px) - 122px); }
}
@media (max-width: 720px) {
  .ch { min-height: 360px; }
  .ch-thread { padding: 4px 0 12px; gap: 14px; }
  .ch-you { max-width: 88%; }
  .ch-viewer { grid-template-columns: minmax(0, 1fr); }
}

/* ---- the + menu: Media / Friends ([PELT-5], 2026-09-01) ------------------ */
.ch-row { position: relative; }
.ch-plusmenu {
  position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 30;
  display: flex; flex-direction: column; min-width: 240px;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--white); box-shadow: var(--shadow-md); padding: 4px;
}
.ch-plusitem {
  display: flex; align-items: center; gap: 10px; text-align: left;
  padding: 9px 10px; border: 0; border-radius: var(--r-box);
  background: none; cursor: pointer; color: var(--ink);
}
.ch-plusitem:hover { background: var(--violet-wash); }
.ch-plusitem-t { display: flex; flex-direction: column; gap: 1px; }
.ch-plusitem-t b { font-size: 13.5px; }
.ch-plusitem-t span { font-size: 11.5px; color: var(--ink-faint); }

/* 28.7 — the + popover, claude.ai's attach-menu shape: small labelled
   sections, then the rows. Picks per round as pills (or a stepper on Plus),
   the kinds as chips, a hairline before the add-to-ask rows. */
.ch-plusmenu { min-width: 272px; max-width: min(320px, calc(100vw - 32px)); gap: 2px; padding: 6px; }
.ch-pop-sec { display: grid; gap: 6px; padding: 8px 10px 6px; }
.ch-pop-l { font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-faint); }
.ch-size-pills { display: flex; gap: 6px; }
.ch-size-pill {
  min-width: 40px; height: 30px; padding: 0 12px; border-radius: 999px; border: 1px solid var(--line);
  background: none; font: inherit; font-size: 13.5px; font-weight: 700; color: var(--ink); cursor: pointer;
}
.ch-size-pill:hover { border-color: var(--ink); }
.ch-size-pill[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--white); }
.ch-step { display: inline-flex; align-items: center; justify-self: start; border: 1px solid var(--line); border-radius: 999px; }
.ch-step button { width: 32px; height: 30px; border: 0; background: none; font: inherit; font-size: 16px; color: var(--ink); cursor: pointer; border-radius: 999px; }
.ch-step button:hover { background: var(--violet-wash); }
.ch-step-n { min-width: 2ch; text-align: center; font-size: 13.5px; }
.ch-kindchips { display: flex; flex-wrap: wrap; gap: 6px; }
.ch-kindchip {
  padding: 5px 11px; border-radius: 999px; border: 1px solid var(--line); background: none;
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--ink); cursor: pointer;
}
.ch-kindchip:hover { background: var(--violet-wash); border-color: var(--violet-wash-strong); }
.ch-pop-hr { margin: 4px 6px; border: 0; border-top: 1px solid var(--line); }

/* a friend chip in the composer or a sent bubble ([PELT-4/5]) */
.ch-inc.is-friend {
  background: var(--violet-wash);
  border-color: var(--violet-wash-strong);
  font-weight: 700;
}
.ch-friendrow { width: 100%; cursor: pointer; }
.ch-friendrow:hover { border-color: var(--line-strong); }

/* what travels with a pelted card, shown before it is sent (F5) */
.pelt-why {
  border: 1px solid var(--line); border-radius: var(--r-box);
  background: var(--paper); padding: 10px 12px;
}
.pelt-why b { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-faint); }
.pelt-why p { margin: 4px 0 0; font-size: 12.5px; line-height: 1.45; }

/* the Peltify-authorship line under a card's why ([F5]) */
.dm-card-inf { display: block; margin-top: 4px; font-size: 10.5px; color: var(--ink-faint); font-style: italic; }

/* [SUGG-18] 2026-09-01 — a turn that arrives after the first paint rises in
   once, on insertion, like ChatGPT's reply. The first paint's turns use
   `.rise` under the pane's `is-fresh` stagger (motion.css). */
.ch-turn.ch-new { animation: rise-in 420ms var(--soft, cubic-bezier(0.22, 0.68, 0.35, 1)) backwards; }
@media (prefers-reduced-motion: reduce) { .ch-turn.ch-new { animation: none; } }

/* [SUGG-15] a loose match says how it was matched — amber, like unverified. */
.sc-loose { font-family: var(--font-sans); font-size: 12.5px; line-height: 1.5; color: var(--warn);
            display: inline-flex; align-items: flex-start; gap: 5px; margin: 0 0 4px; }

/* ---- the @ picker ([SUGG-16], 2026-09-01): a card list above the input ---- */
.ch-mention {
  position: absolute; bottom: calc(100% + 8px); left: 44px; right: 44px; z-index: 31;
  max-height: 300px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px;
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--white);
  box-shadow: var(--shadow-md); padding: 6px;
}
.ch-mention-empty { margin: 0; padding: 10px 12px; font-size: 12.5px; color: var(--ink-faint); }
.ch-mrow { display: flex; align-items: center; gap: 12px; text-align: left; padding: 8px 10px;
           border: 0; border-radius: var(--r-box); background: none; cursor: pointer; color: var(--ink); }
.ch-mrow:hover, .ch-mrow.is-active { background: var(--violet-wash); }
.ch-mart { width: 40px; height: 40px; flex: none; border-radius: 6px; object-fit: cover; background: var(--paper-deep);
           display: grid; place-items: center; color: var(--ink-faint); }
.ch-mtext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ch-mtext b { font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-mtext span { font-size: 12px; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* 18.1 / `searchable-library` entry 13 — the picker row is the same selectable
   BOX as Search: image + title + description. Two lines of the source's own
   sentence, never a composed one. */
.ch-mrow { align-items: flex-start; }
.ch-mtext span.ch-mdesc {
  font-size: 11.5px; line-height: 1.35; color: var(--ink-soft, var(--ink-faint));
  white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
/* 19.3 (`composer/SPEC.md` entry 9) — AND IT FLIPS. Above by default (the
   chat composer is at the foot of the screen); below when there is more room
   there, which is the post dialog, where the field is near the top and the
   panel ran off the screen. `mention-picker.js` measures and sets the class
   and the height, because neither is knowable from a stylesheet. */
.ch-mention.is-below { bottom: auto; top: calc(100% + 8px); }
@media (max-width: 720px) { .ch-mention { left: 0; right: 0; max-height: 240px; } }

/* ===========================================================================
   2026-09-06 — "futuristic … floaty and less basic bubbles … sophisticated
   and cutting edge" (Nick, suggestions/RAW.md entry 11). The same parts,
   lifted off the page: the person's words in a translucent glass bubble with
   a soft drop shadow; the option pills as light chips that rise on hover;
   the composer floating on a blurred sheet; the limit notice quieter. No
   new boxes — "simplify it down a TON".
   =========================================================================== */
.ch-you {
  background: color-mix(in srgb, var(--white) 72%, transparent);
  border: 1px solid color-mix(in srgb, var(--white) 60%, var(--line));
  box-shadow: 0 10px 30px -14px rgba(36, 20, 46, 0.35), inset 0 1px 0 rgba(255, 255, 255, 0.8);
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  border-radius: 22px; border-bottom-right-radius: 8px;
}
.ch-card-t { font-size: 15.5px; letter-spacing: -0.005em; }
.ch-opt {
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--white) 86%, transparent);
  box-shadow: 0 4px 14px -10px rgba(36, 20, 46, 0.45);
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease),
              transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.ch-opt:hover:not(:disabled) { border-color: var(--violet); background: var(--white);
                               transform: translateY(-1px); box-shadow: 0 10px 22px -12px rgba(139, 31, 209, 0.45); }
.ch-opt.is-picked { background: var(--ink); border-color: var(--ink); color: var(--white); box-shadow: none; transform: none; }
.ch-opt.is-custom { border-style: dashed; background: transparent; box-shadow: none; }
.ch-composer { position: relative; padding-top: 14px; }
.ch-composer::before {
  content: ""; position: absolute; inset: 0 -8px -8px;
  background: linear-gradient(to bottom, transparent, var(--paper) 40%);
  pointer-events: none; z-index: 0;
}
.ch-composer > * { position: relative; z-index: 1; }
.ch-row {
  border: 1px solid color-mix(in srgb, var(--white) 50%, var(--line));
  background: color-mix(in srgb, var(--white) 88%, transparent);
  -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px);
  box-shadow: 0 18px 40px -22px rgba(36, 20, 46, 0.5), 0 2px 6px -2px rgba(36, 20, 46, 0.12);
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
/* the focus stays a darker line, never purple (entry 5, kept) */
.ch-row:focus-within { border-color: var(--ink); box-shadow: 0 18px 44px -20px rgba(36, 20, 46, 0.5); }
.ch-send { background: var(--violet); box-shadow: 0 6px 16px -8px rgba(139, 31, 209, 0.8); }
.ch-send:hover:not(:disabled) { background: var(--spine); }
.ch-plus { border-color: transparent; background: var(--paper); }
.ch-limit { border: 1px solid var(--line); background: var(--white); color: var(--ink-soft); border-radius: 12px;
            box-shadow: var(--shadow-sm); }
.ch-limit svg { color: var(--warn); }
.ch-typing-d i { background: var(--violet); }
.ch-dot.is-now { background: var(--violet); }
.ch-arrow { border-color: transparent; box-shadow: var(--shadow-sm); }
.ch-arrow:hover:not(:disabled) { border-color: var(--violet); color: var(--spine); }
.sc-offkind { color: var(--ink-soft); }
@media (prefers-reduced-motion: reduce) { .ch-opt { transition: none; } .ch-opt:hover:not(:disabled) { transform: none; } }

/* 2026-09-06, the suggestion card in the same language as the chat: a
   rounder, lifted card; the kind as an outline pill; the why-box on the
   card's own paper with a violet spine; the four actions as light chips. */
.sc-card { border-radius: 14px; box-shadow: 0 24px 50px -30px rgba(36, 20, 46, 0.55), 0 2px 8px -4px rgba(36, 20, 46, 0.12); border-color: var(--line-soft); }
.sc-kind { background: transparent; border: 1px solid var(--violet-wash-strong); border-radius: 999px; }
.sc-work .cd-hero { border-radius: 12px; }
.sc-why-box { border: 0; border-radius: 10px; box-shadow: inset 3px 0 0 var(--violet); }
.sc-tag { border-radius: 999px; }
.sc-act-g { border-color: var(--line); box-shadow: var(--shadow-sm); }
.sc-act:hover:not(:disabled) .sc-act-g { border-color: var(--violet); box-shadow: 0 10px 20px -12px rgba(139, 31, 209, 0.5); }
.tc-card { border-radius: 14px; box-shadow: 0 24px 50px -30px rgba(36, 20, 46, 0.55); border-color: var(--line-soft); position: relative; }
/* 2026-09-15 — the close on the card, over the artwork's corner */
.tc-x { position: absolute; top: 8px; right: 8px; z-index: 3; width: 34px; height: 34px; border-radius: 50%; border: 0;
        background: rgba(20, 12, 28, .55); color: #fff; font-size: 22px; line-height: 1; display: grid; place-items: center; cursor: pointer; }
.tc-x:hover { background: rgba(20, 12, 28, .8); }
.tc-scrim { display: none; }
.tc-card .cd-hero { border-radius: 12px; }

/* 2026-09-06, entry 3: "Im not super much a fan of the roundedness … I would
   like more concreteness especially for album metadata stuff". The
   suggestion and taste-check cards ARE album metadata: 6px corners, boxy
   tags and pills; the lift stays. */
.sc-card, .tc-card { border-radius: 6px; }
.sc-work .cd-hero, .tc-card .cd-hero { border-radius: 4px; }
.sc-kind, .sc-tag { border-radius: 3px; }
.sc-why-box { border-radius: 4px; }
.sc-open-l { border-radius: 3px; }

/* 2026-09-06, entry 12 — the card "explains itself": three labelled blocks
   (About · What this has · Why Peltify picked it) on hairlines, one label
   style; the cover flat on paper with no blurred copy behind it; the why
   with a violet spine and no fill. Nothing about the content changed. */
.sc-work .cd-hero { background: var(--paper); }
.sc-work .cd-art-bg { display: none; }
.sc-work .cd-art { border-radius: 3px; box-shadow: 0 16px 30px -18px rgba(36, 20, 46, 0.7); }
.sc-kind { border-radius: 2px; }
.sc-why { gap: 0; }
.sc-why > * + * { border-top: 1px solid var(--line-soft); padding-top: 12px; margin-top: 12px; }
.sc-why > .sc-returned + * { border-top: 0; margin-top: 4px; }
.sc-about-h, .cg-h, .sc-why-h { margin: 0 0 6px; font-size: 10.5px; font-weight: 800; letter-spacing: 0.14em;
                                 text-transform: uppercase; color: var(--ink-faint); }
.cg-theirs .cg-h { color: var(--ink-faint); }
.sc-why-h { color: var(--spine); }
.sc-about-p { font-size: 14.5px; line-height: 1.55; }
.sc-why-box { padding: 0 0 0 14px; border-radius: 0; background: transparent; box-shadow: inset 3px 0 0 var(--violet); }
.sc-why-p { font-size: 14.5px; }
.cg-compare { border-radius: 3px; background: var(--paper); border: 0; }
.sc-open { padding-top: 12px; }
.sc-open-l { border-radius: 3px; }
.sc-actions { border-top: 1px solid var(--line); }


/* ---- the media pill (2026-09-07) — Apple Messages' effect pill: a small
   vertical pill that rises from where you tapped, one row per connected app,
   Details, and an × underneath. */
.mp-wrap { position: fixed; z-index: 80; transform: translate(-50%, 0) scale(.86); opacity: 0; transition: transform 180ms var(--ease, cubic-bezier(.2,.7,.2,1)), opacity 140ms; display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; }
.mp-wrap.is-above { transform: translate(-50%, -100%) scale(.86); }
.mp-wrap.is-in { opacity: 1; transform: translate(-50%, 0) scale(1); pointer-events: auto; }
.mp-wrap.is-above.is-in { transform: translate(-50%, -100%) scale(1); }
.mp-pill { position: relative; background: var(--white); color: var(--ink); border-radius: 22px; padding: 10px 6px 8px; box-shadow: 0 10px 30px rgba(20,12,28,.22), 0 1px 3px rgba(20,12,28,.12); min-width: 168px; max-width: 260px; }
.mp-pill::before { content: ""; position: absolute; left: 50%; width: 14px; height: 14px; background: var(--white); transform: translateX(-50%) rotate(45deg); }
.mp-wrap.is-below .mp-pill::before { top: -6px; }
.mp-wrap.is-above .mp-pill::before { bottom: -6px; }
.mp-title { margin: 0 12px 6px; font-size: 12px; font-weight: 700; color: var(--mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: .02em; text-transform: uppercase; }
.mp-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.mp-app { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 12px; border: 0; background: none; border-radius: 16px; font: inherit; font-size: 15px; color: var(--ink); text-decoration: none; cursor: pointer; text-align: left; }
.mp-app:hover, .mp-app:focus-visible { background: var(--paper-deep, #efe7dc); outline: none; }
.mp-app-i { width: 24px; height: 24px; display: grid; place-items: center; }
.mp-app-n { flex: 1; }
.mp-app-c { font-size: 12px; color: var(--mute); }
.mp-app.is-connected .mp-app-n { font-weight: 700; }
.mp-pelt { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 4px; }
.mp-pelt .mp-app-n { font-weight: 700; color: var(--spine); }
.mp-details { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 4px; }
.mp-x { width: 34px; height: 34px; border-radius: 50%; border: 0; background: var(--white); color: var(--ink); box-shadow: 0 2px 8px rgba(20,12,28,.2); display: grid; place-items: center; cursor: pointer; }
.sc-art-open { display: block; width: 100%; padding: 0; border: 0; background: none; cursor: pointer; text-align: inherit; font: inherit; color: inherit; }
.ch-mention.has-art { display: inline-flex; align-items: center; gap: 6px; }
.ch-mention .ig-mention-art { width: 22px; height: 22px; border-radius: 5px; object-fit: cover; }
@media (prefers-reduced-motion: reduce) { .mp-wrap { transition: none; } }

/* =============================================================================
   THE STAGE — 2026-09-07, `suggestions/SPEC.md` › The shape (entries 13–16).
   "it is not a scrolling chat, instead a fixed chat where the responses and
   feedback are scrolling to the background, not UP." The composer never
   moves; words rise above it as a fading gallery; the live card holds the
   centre; a rail on the right keeps what happened.
   ============================================================================= */
.ch.is-thread { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 56px; gap: 0 12px; overflow: hidden; isolation: isolate; }
.ch-bg { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; border-radius: 16px; opacity: 0; transition: opacity 900ms ease; }
.ch-bg.is-on { opacity: 1; }
.ch-bg-grid { position: absolute; inset: -8%; display: grid; grid-template-columns: repeat(8, 1fr); gap: 6px; transform: rotate(-6deg) scale(1.08); filter: blur(10px) saturate(0.7); opacity: 0.13; }
.ch-bg-grid img { width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: 6px; }
.ch-bg.is-drift .ch-bg-grid { animation: ch-drift 90s ease-in-out infinite alternate; }
@keyframes ch-drift { from { transform: rotate(-6deg) scale(1.08) translateY(0); } to { transform: rotate(-5deg) scale(1.12) translateY(-3%); } }
.ch-body { position: relative; z-index: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; }

/* 13.7 — a card is a message in the thread; it takes the height it needs
   and the thread scrolls, so nothing is capped and nothing overlaps. */
.ch-thread .ch-turn { width: 100%; min-width: 0; animation: ch-card-in 520ms cubic-bezier(.2,.7,.2,1) both; }
@keyframes ch-card-in { from { opacity: 0; transform: translateY(18px) scale(0.97); } to { opacity: 1; transform: none; } }
/* 12.13: once the card has arrived it stays — a re-parent of the chat must not replay its entrance. */
.ch-thread .ch-turn.is-settled { animation: none; }
.ch-thread .ch-turn .sc-card, .ch-thread .ch-turn .tc-card { user-select: text; }

/* the cold open */
.ch-hello { display: grid; gap: 12px; place-items: center; text-align: center; padding: 34px 24px 20px; }
.ch-hello-t { display: flex; align-items: center; gap: 12px; margin: 0; font-size: clamp(26px, 4vw, 38px); font-weight: 800; letter-spacing: -0.03em; color: var(--ink); }
.ch-hello-s { margin: 0; font-size: 15px; color: var(--ink-soft); }
.ch-hints { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px 14px; padding: 14px 12px 6px; }
.ch-hint { display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 14px; border: 1px solid var(--line); border-radius: var(--r-pill); background: var(--white); color: var(--ink-soft); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; box-shadow: var(--lift); }
.ch-hint:hover { border-color: var(--ink); color: var(--ink); }

/* the buddy's mark — a small orb before every Peltify line (_global/voice) */
.ch-mark { flex: none; width: 14px; height: 14px; border-radius: 50%; background: radial-gradient(circle at 35% 35%, #d9b3ff, var(--violet) 55%, var(--spine)); box-shadow: 0 0 0 3px rgba(139, 31, 209, 0.12); }
.ch-hello .ch-mark { width: 22px; height: 22px; }
.ch-line.is-say .ch-card, .ch-line.is-note .ch-note, .ch-line.is-limit .ch-limit { display: flex; align-items: flex-start; gap: 10px; }
.ch-line .ch-mark { margin-top: 4px; }

/* the gallery: newest at the bottom, big; older ones rise, shrink, grey, curl */
.ch-line .ch-picked { display: inline-block; margin-top: 6px; padding: 3px 10px; border-radius: var(--r-pill); background: var(--ink); color: var(--white); font-size: 12px; font-weight: 700; }
.ch-line.is-you { display: flex; justify-content: flex-end; }
/* `backwards`, NOT `both` (2026-09-07, chrome_KI6t7oJKjB: "it has the fade
   away thing, but above it it strangely becomes normal"): with `both` the
   arrival keyframe's opacity:1 / transform:none stayed pinned on every line
   added this session, so those never faded while re-drawn ones did. */
.ch-line.ch-new { animation: ch-line-in 640ms cubic-bezier(.2,.7,.2,1) backwards; }
@keyframes ch-line-in { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
/* 15.5: the white ink here was for a mention on a VIOLET bubble. 13.7 made
   the person's bubble cream, so white on cream was a mention nobody could see
   — and once `renderBody` drew the kind glyph in `currentColor`, an invisible
   glyph beside visible words. The mention keeps `--spine` in the chat, the
   same purple it wears in a post: one renderer, one colour. */

/* the composer stays put; the allowance sits under it as a function */
.ch.is-thread .ch-composer { flex: none; }
.ch-usage { display: flex; align-items: center; justify-content: flex-end; gap: 8px; font-size: 11.5px; color: var(--ink-faint); }
.ch-usage-bar { width: 72px; height: 4px; border-radius: 2px; background: var(--line-soft); overflow: hidden; }
.ch-usage-bar i { display: block; height: 100%; width: calc(var(--u, 0) * 100%); background: var(--violet); border-radius: 2px; }

/* the rail: what happened, and where it went */
.ch-rail { position: relative; z-index: 2; display: flex; flex-direction: column; gap: 8px; align-items: center; padding: 10px 0; }
.ch-rail-b { position: relative; width: 44px; height: 44px; border: 1px solid var(--line); border-radius: 14px; background: var(--white); color: var(--ink-soft); display: grid; place-items: center; cursor: pointer; font: inherit; box-shadow: var(--lift); transition: transform 200ms var(--soft, ease), border-color 200ms ease; }
.ch-rail-b:hover, .ch-rail-b.is-open { border-color: var(--violet); color: var(--spine); transform: translateY(-1px); }
.ch-rail-b .ch-rail-n { position: absolute; top: -6px; right: -6px; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 9px; background: var(--violet); color: var(--white); font-size: 11px; font-weight: 800; display: grid; place-items: center; transition: transform 240ms var(--soft, ease); }
.ch-rail-b .ch-rail-n:empty { display: none; }
.ch-rail-b.is-bump .ch-rail-n { transform: scale(1.35); }
.ch-rail-b .ch-rail-l { position: absolute; right: 52px; top: 50%; transform: translateY(-50%); padding: 4px 8px; border-radius: 8px; background: var(--ink); color: var(--white); font-size: 11.5px; font-weight: 700; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 160ms ease; }
.ch-rail-b:hover .ch-rail-l, .ch-rail-b:focus-visible .ch-rail-l { opacity: 1; }
/* THE FLIGHT IS SLOW AND SOFT (Nick, 2026-09-07: "flies in a sort of ugly
   manner. Very fast and rapid and not smooth"): a full second, eased out,
   fading and blurring as it shrinks toward the rail. */

/* a drawer slides in over the body */
.ch-drawer-x { width: 30px; height: 30px; border: 0; border-radius: 50%; background: var(--paper); color: var(--ink); cursor: pointer; display: grid; place-items: center; }
.ch-drow { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px; border: 1px solid var(--line-soft); border-radius: 12px; background: var(--white); text-align: left; font: inherit; color: var(--ink); cursor: pointer; }
.ch-drow:hover { border-color: var(--violet); }
.ch-drow img, .ch-drow .ch-drow-art { width: 40px; height: 40px; flex: none; border-radius: 8px; object-fit: cover; background: var(--paper-deep); }
.ch-drow-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.ch-drow-t b { font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-drow-t span { font-size: 12px; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ch-drow-v { flex: none; font-size: 11.5px; font-weight: 700; color: var(--ink-soft); }
.ch-drow.is-asked { flex-direction: column; align-items: stretch; gap: 4px; cursor: default; }
.ch-drow.is-asked .ch-drow-q { font-weight: 700; font-size: 13.5px; }
.ch-drow.is-asked .ch-drow-a { font-size: 13px; color: var(--ink-soft); }
.ch-drawer-empty { margin: 0; padding: 20px 8px; text-align: center; font-size: 13px; color: var(--ink-faint); }

/* the sample button on a cover */
.cd-hero { --p: 0; }
.cd-play { position: absolute; left: 10px; bottom: 10px; z-index: 3; display: inline-flex; align-items: center; gap: 7px; height: 32px; padding: 0 12px 0 8px; border: 0; border-radius: var(--r-pill); background: rgba(20, 12, 30, 0.72); color: #fff; font: inherit; font-size: 12px; font-weight: 700; cursor: pointer; backdrop-filter: blur(6px); box-shadow: 0 6px 18px -8px rgba(0,0,0,.7); }
.cd-play-g { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; background: conic-gradient(var(--violet) calc(var(--p) * 360deg), rgba(255,255,255,.25) 0); }
.cd-play-g svg { width: 12px; height: 12px; }
.cd-play-l { max-width: 160px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cd-play-t { opacity: 0.7; font-variant-numeric: tabular-nums; }
.cd-play.is-none { opacity: 0.55; cursor: default; }
/* 19.6 — a sample that exists but would not play: still pressable, and it
   says which of the two happened. */
.cd-play.is-blocked { background: rgba(120, 20, 20, 0.78); }
.cd-play.is-loading .cd-play-g { animation: ch-spin 900ms linear infinite; }
@keyframes ch-spin { to { transform: rotate(360deg); } }
.cd-trailer { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%; object-fit: cover; background: #000; }
/* 13.16: the cover fades back in as the trailer fades out — never a cut. */
.cd-art { transition: opacity 700ms ease; }
.cd-hero.is-sampling .cd-art { opacity: 0; }
.cd-trailer.is-fading { pointer-events: none; }

/* 13.12 — DISCOVER FOLLOWS THE THEME (`_global/design-language/SPEC.md`
   2026-09-08, answer 32: "One token set; Discover follows the theme (no dark
   island in light mode)").

   This block used to hard-code a dark palette onto Discover — twenty-odd token
   overrides plus a near-black radial gradient — so the room stayed dark on a
   light machine and, worse, would have stayed exactly as dark when the rest of
   the app went dark, which is two dark schemes rather than one. It now sets
   NO colours: the room is the theme's own paper, one shade deeper, and every
   token comes from `styles/tokens.css`. The covers still sit behind it. */
.srch .ch.is-thread {
  padding: 14px 18px 12px; border-radius: 0;
  color: var(--ink);
  background: var(--paper-deep);
}
.srch .ch.is-thread .ch-bg { border-radius: 0; }
.srch .ch.is-thread .ch-opt { background: var(--white); border-color: var(--line-strong); color: var(--ink); }
.srch .ch.is-thread .ch-opt:hover:not(:disabled) { background: var(--violet-wash); border-color: var(--ink); }
.srch .ch.is-thread .ch-opt.is-picked { background: var(--violet); border-color: var(--violet); }
.srch .ch.is-thread .ch-you { background: var(--violet-wash-strong); color: var(--ink); }
.srch .ch.is-thread .ch-row { background: var(--white); border-color: var(--line-strong); }
.srch .ch.is-thread .ch-composer::before { display: none; }
.srch .ch.is-thread .ch-input { color: var(--ink); }
.srch .ch.is-thread .ch-hint, .srch .ch.is-thread .ch-rail-b, .srch .ch.is-thread .ch-drow { background: var(--white); }
.srch .ch.is-thread .tc-card, .srch .ch.is-thread .sc-card { background: var(--white); border-color: var(--line); color: var(--ink); }
.srch .ch.is-thread .ch-bg-grid { opacity: 0.16; filter: blur(12px) saturate(0.7); }

@media (prefers-reduced-motion: reduce) {
  .ch-thread .ch-turn, .ch-line.ch-new { animation: none; }
  .ch-line { transition: opacity 200ms ease; }
  .ch-bg.is-drift .ch-bg-grid { animation: none; }
}
@media (max-width: 720px) {
  .ch.is-thread { grid-template-columns: minmax(0, 1fr); }
  .ch-rail { flex-direction: row; justify-content: center; order: 2; padding: 6px 0 0; }
  .ch-rail-b .ch-rail-l { display: none; }
  .ch-hello-t { font-size: 26px; }
}

/* ---- 2026-09-07, Nick's second look: the room, the phone, the strip, the drawer column, the loading card, the running send ---- */

/* THE WORDS SIT NEAR THE CARD ("The text seems rather far away"): the body is
   a centred column, not the whole width of a wide monitor. */
.ch-body { width: min(1180px, 100%); margin: 0 auto; }
/* 13.7: the deck panel is pulled out OVER the thread, on the left, and the
   thread keeps scrolling behind it — so the grid no longer opens a column. */

/* the limit strip, its own band, not a buddy line */
.ch-limit-slot { flex: none; }
.ch-limit-slot[hidden] { display: none; }
.ch-limit { display: flex; align-items: center; gap: 10px; margin: 4px 0 8px; padding: 10px 14px; border: 1px solid var(--line); border-radius: 12px; background: var(--white); color: var(--ink-soft); font-size: 13px; }
.ch-limit svg { flex: none; color: var(--warn); }
.ch-limit-t { flex: 1 1 auto; min-width: 0; }
.ch-limit-n { flex: none; font-size: 11.5px; font-weight: 700; color: var(--ink-faint); white-space: nowrap; }
/* 13.3: with no account the strip carries the way out of it, not a number
   about an allowance the visitor does not have. */
.ch-limit-cta { flex: none; padding: 7px 13px; border: 1px solid var(--ink); border-radius: 10px;
  background: var(--ink); color: var(--paper); font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer; }
/* THE SOFT STOP CARD (13.7, answer 18): what you got, when it comes back, and
   the way to more. It FADES IN, because it arrives after the last suggestion
   rather than in front of it. No box around anything untappable (rule 6) —
   one quiet card, serif head, one purple button. */
/* 2026-09-14 — the stop card, quieter (Nick: "Really take inspiration from
   claude.ai"): a hairline card, plain sans, one dark button. */
.ch-stop { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 8px;
  padding: 18px 20px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--white); }
.ch-stop-h { margin: 0; font-size: 15px; font-weight: 600; letter-spacing: -.01em; color: var(--ink); }
.ch-stop-p { margin: 0; font-size: 14px; line-height: 1.5; color: var(--ink-soft); max-width: 60ch; }
.ch-stop-acts { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.ch-stop-cta { padding: 8px 14px; border: 1px solid var(--ink); border-radius: 8px;
  background: var(--ink); color: var(--white); font: inherit; font-size: 13.5px;
  font-weight: 600; cursor: pointer; }
.ch-stop-cta:hover { opacity: .88; }
.ch-stop-soon { font-size: 12.5px; color: var(--ink-faint); }
.ch-stop-n { margin: 2px 0 0; font-size: 11.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-faint); }
.ch-limit-cta:hover { background: var(--violet, #6d28d9); border-color: var(--violet, #6d28d9); }
.srch .ch.is-thread .ch-limit { background: rgba(255,255,255,.06); }

/* the loading card on the stage */
.ch-load { display: grid; place-items: center; min-height: 240px; }
/* 20.3 (`suggestions/SPEC.md` entry 33) — NO BORDERED BOX. Nick: "Id like the
   'finding suggestions' UI to not have a bordered box." The mark, the words
   and the stage line, on the thread's own ground. */
.ch-load-card { display: grid; gap: 8px; place-items: center; text-align: center; width: min(420px, 100%); padding: 28px 24px; border: 0; border-radius: 0; background: none; box-shadow: none; }
/* 18.5: the orb is gone — the app has ONE loading mark (`styles/app.css`
   `.ldg-mark`), and the loading card draws it large. */
.ch-load-t { margin: 4px 0 0; font-size: 16px; font-weight: 800; letter-spacing: -0.01em; color: var(--ink); }
.ch-load-w { margin: 0; font-size: 13px; color: var(--ink-soft); }
.srch .ch.is-thread .ch-load-card { background: none; }

/* a card already on the stage does not re-enter on a repaint */
.ch-thread .ch-turn.is-settled { animation: none; }

/* the send button runs, and stops */
.ch-send { position: relative; }
.ch-send-run { display: none; position: absolute; inset: 0; border-radius: 50%; }
.ch-send.is-running .ch-send-go { display: none; }
.ch-send.is-running .ch-send-run { display: block; }
.ch-send.is-running .ch-send-run::before { content: ""; position: absolute; inset: 4px; border-radius: 50%; border: 2px solid rgba(255,255,255,.35); border-top-color: #fff; animation: ch-spin 900ms linear infinite; }
.ch-send.is-running .ch-send-run::after { content: ""; position: absolute; left: 50%; top: 50%; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 2px; background: #fff; }
.ch-send.is-running { cursor: pointer; }

/* 13.12 — the suggestion card is the theme's own card. It used to force cream
   ("Im thinking the suggestion cards can be cream / white similar to the 'have
   you heard'"), which was the right call INSIDE a permanently dark room; with
   the room following the theme, cream-on-cream is invisible in light mode and
   a second white in dark mode. The contrast Nick asked for is kept by the
   card sitting on --white against the room's --paper-deep. */
.srch .ch.is-thread .sc-card {
  background: var(--white); border-color: var(--line); color: var(--ink); }

/* ONE FEEL FOR BOTH CARDS (2026-09-07, entry 18: "The suggestion cards
   especially dont have an overall feel. The 'have you heard' cards are neat,
   just how I like it"). The suggestion card takes the taste card's skin —
   the same 6px corner, the same lift, the same cream — and its own width cap:
   "nice and boxed in. Similar feel / dimensions to a mini tablet" (suggestion-
   card entry 5), so it never stretches across a wide monitor. The stage card
   is a size container so the two columns fold on the CARD's width, not the
   window's: a drawer that takes 360px from a laptop must fold it too. */
.ch-thread .ch-turn { container-type: inline-size; }
.ch-thread .ch-turn .sc-card { width: min(760px, 100%); margin: 0 auto; border-radius: 6px; box-shadow: var(--raise); padding: 16px; gap: 12px; }
.ch-thread .ch-turn .sc-split { gap: 18px; }
.ch-thread .ch-turn .sc-work .cd-hero { height: 190px; padding: 12px; }
.ch-thread .ch-turn .sc-work .cd-art { max-height: 166px; }
.ch-thread .ch-turn .sc-about-p, .ch-thread .ch-turn .sc-why-p { font-size: 14px; line-height: 1.5; }
.ch-thread .ch-turn .sc-actions { padding-top: 10px; }
@container (max-width: 620px) {
  .ch-thread .ch-turn .sc-split { grid-template-columns: minmax(0, 1fr); gap: 12px; }
  .ch-thread .ch-turn .sc-card { padding: 14px; gap: 10px; }
  .ch-thread .ch-turn .sc-work .cd-hero { height: 150px; padding: 8px; }
  .ch-thread .ch-turn .sc-work .cd-art { max-height: 134px; }
}
/* THE PHONE SEES THE WHOLE CARD (suggestion-card must-not: "no card taller
   than a phone can show"; entry 18: "Mobile aspect ratio is practically
   unusable"). Measured 2026-09-07: the full card ran 907px in a 346px slot.
   On a phone the stage takes more of the box and the card keeps what decides
   a pick — cover, name, tags, why, the four actions. About, "What this has"
   and the Open-in row step aside; the cover's own button still opens the
   work on the person's apps. Overturnable in one line. */
@media (max-width: 720px) {
  .ch-thread .ch-turn .sc-card { padding: 12px; gap: 8px; }
  .ch-thread .ch-turn .sc-split { gap: 8px; }
  .ch-thread .ch-turn .sc-work { gap: 8px; }
  .ch-thread .ch-turn .sc-work .cd-hero { height: 104px; padding: 6px; }
  .ch-thread .ch-turn .sc-work .cd-art { max-height: 92px; }
  .ch-thread .ch-turn .sc-title { font-size: 20px; }
  .ch-thread .ch-turn .sc-about, .ch-thread .ch-turn .cg, .ch-thread .ch-turn .sc-open,
  .ch-thread .ch-turn .sc-desc, .ch-thread .ch-turn .sc-ratings { display: none; }
  .ch-thread .ch-turn .sc-why > * + * { border-top: 0; margin-top: 0; padding-top: 0; }
  .ch-thread .ch-turn .sc-why-box { padding-left: 10px; }
  .ch-thread .ch-turn .sc-why-p { font-size: 13px; line-height: 1.45; }
  .ch-thread .ch-turn .sc-actions { padding-top: 8px; }
}
/* A SHORT WINDOW (a laptop with the bar open, a landscape phone): the cover
   gives up height before the words do, so the card resolves instead of
   scrolling inside its slot. */
@media (max-height: 760px) {
  .ch-thread .ch-turn .sc-work .cd-hero { height: 140px; padding: 8px; }
  .ch-thread .ch-turn .sc-work .cd-art { max-height: 124px; }
  .ch-thread .ch-turn .sc-card { gap: 10px; padding: 14px; }
}

/* THE PHONE (Nick: "Mobile aspect ratio is practically unusable"): the rail
   is a small row at the top, the card takes at most half the box and scrolls
   inside itself, the words keep their room. The one-icon rail is LATER. */
@media (max-width: 720px) {
  .ch.is-thread, .ch.is-thread.has-drawer { grid-template-columns: minmax(0, 1fr); }
  .ch-rail { order: -1; flex-direction: row; justify-content: flex-end; gap: 6px; padding: 2px 0 6px; }
  .ch-rail-b { width: 34px; height: 34px; border-radius: 10px; }
  .ch-rail-b .ch-rail-n { min-width: 15px; height: 15px; font-size: 10px; top: -5px; right: -5px; }
  .ch-thread .ch-turn { border-radius: 14px; }
  .ch-hello { padding: 14px 8px 8px; }
  .ch-hints { gap: 6px; }
  .ch-hint { height: 34px; font-size: 12.5px; padding: 0 12px; }
  .srch .ch.is-thread { padding: 8px 10px 8px; }
}

/* ===========================================================================
   HANDOFF-11 §11.3–11.5 (2026-09-07) — the card's final shape.
   LEFT: the hero bolder and edge-to-edge in its column, kind · title ·
   creator · year, the scores, then About with its source. RIGHT: the genre
   chips at the very top, tinted by relatedness, then why, then Open in.
   No scroll box: the card is as tall as it is and the page scrolls.
   =========================================================================== */
.ch-thread .ch-turn .sc-work .cd-hero { height: 236px; padding: 0; border-radius: 4px; }
.ch-thread .ch-turn .sc-work .cd-art { max-height: 236px; }
.sc-desc .sc-about-p { font-family: var(--font-sans); }
.sc-desc .sc-prov { margin-top: 2px; }
.sc-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* ONE NUMBER, TWO COLOURS (11.4): the fill is Peltify purple by --rel, the
   text goes white once the fill passes the middle; never grey. */
.sc-chips .sc-tag { border-radius: 3px; font-size: 12.5px; font-weight: 600; color: var(--ink); background: var(--white); border: 1px solid var(--line); }
.sc-tag[style*="--rel"] {
  background: color-mix(in srgb, var(--violet) calc(var(--rel, 0) * 100%), var(--white));
  border-color: color-mix(in srgb, var(--violet) calc(var(--rel, 0) * 100%), var(--line));
  color: color-mix(in srgb, #fff calc(clamp(0, (var(--rel, 0) - 0.45) * 10, 1) * 100%), var(--ink));
}
.sc-chips .sc-tag-from { color: var(--ink-soft); border-style: dashed; background: transparent; }
/* 13.7 — the box is still the viewport and the composer is still pinned at
   the bottom; the THREAD is what scrolls now, so a card is never capped. */

/* ===========================================================================
   13.7 (2026-09-08) — the rail is ONE icon, Choices, and it pulls the deck
   panel out on the left. History and its modal are gone; the thread is the
   history. Cream, 4px, hairlines.
   =========================================================================== */
.ch-rail-b { border-radius: 4px; box-shadow: none; }
.ch-rail-b .ch-rail-n { border-radius: 4px; }
.ch-drow-acts { display: flex; gap: 6px; flex: none; }
.ch-drow-b { height: 30px; padding: 0 12px; border: 1px solid var(--line); border-radius: 3px; background: var(--white);
             color: var(--ink); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.ch-drow-b:hover { border-color: var(--ink); }
.ch-drow-b.is-change { background: var(--ink); color: var(--white); border-color: var(--ink); }
.ch-drow-b:disabled { opacity: .45; cursor: default; }
/* 11.7 — only the current line carries the buddy's mark */
/* 11.8 — the phone: the rail row never sits on the card; the card sizes to
   what it shows, the page scrolls if it must */
@media (max-width: 720px) {
  .ch-rail { position: static; }
}

/* ===========================================================================
   2026-09-07, late — Nick's second look.
   =========================================================================== */
.srch /* the rail is always reachable */
.ch.is-thread .ch-rail { z-index: 8; pointer-events: auto; }
/* THE CARD MUST NOT SWALLOW THE CHAT (chrome_GOFG81XjLD): the stage card is
   capped to leave the prompt line and the composer their room; its About
   folds to four lines on the stage (the whole text is on the media card),
   the hero is a little shorter, and the gallery keeps a floor. */
.ch-thread .ch-turn .sc-card { width: min(720px, 100%); }
.ch-thread .ch-turn .sc-work .cd-hero { height: 196px; }
.ch-thread .ch-turn .sc-work .cd-art { max-height: 196px; }
.ch-thread .ch-turn .sc-about-p { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
/* ENTERING DISCOVER — ONCE. The fade is keyed on `is-entering`, which the
   chat sets when it is built and drops on animationend. Keyed on the page
   (`.srch .ch`) it restarted every time Discover rebuilt its section and
   re-parented the chat singleton — every shell repaint, every poll — so the
   whole chat sat translucent and blurred mid-fade (Nick, 2026-09-07 evening:
   "What the HELL happened here???"). No blur: a filter on the body also
   made it the containing block for the fixed modal scrim. */
/* (Nick: "a cool fade in animation just so it really
   'feels' like youre entering into something"): the room rises out of the
   dark over 900ms; the card and the rail arrive a beat later. */
.ch.is-thread.is-entering { animation: ch-enter 900ms cubic-bezier(.2,.7,.2,1) both; }
.ch.is-thread.is-entering .ch-body { animation: ch-enter-body 1100ms 120ms cubic-bezier(.2,.7,.2,1) both; }
.ch.is-thread.is-entering .ch-rail { animation: ch-enter-rail 700ms 420ms cubic-bezier(.2,.7,.2,1) both; }
@keyframes ch-enter { from { opacity: 0; } to { opacity: 1; } }
@keyframes ch-enter-body { from { opacity: 0; transform: translateY(22px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes ch-enter-rail { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .ch.is-thread.is-entering, .ch.is-thread.is-entering .ch-body, .ch.is-thread.is-entering .ch-rail { animation: none; } }

/* DISCOVER STARTS AT THE TOP OF THE WINDOW (chrome_JUbeO4qxGr, anonymous:
   the room ended 170px short — the generic fallback applied before the
   measurement landed). The fallback here is 0. */
.srch .ch.is-thread { height: calc(100dvh - var(--ch-top, 0px)); }

/* ===========================================================================
   THE PAIR CHIP (HANDOFF-12 lane 12.7, pelt-card Q1/Q3/Q14/Q27). Two halves:
   left the sender, right the recipient. A keep half is purple, deeper with
   --rel; a skip half is red-tinted AND hatched; a dot marks a keep, an ✕ a
   skip, nothing an empty half — readable with no colour. The label sits on
   top of both halves.
   =========================================================================== */
/* Two labelled rows of WHOLE chips (HANDOFF-12 lane 12.14, pelt-card entry 7;
   replaces the split halves). Per row a chip is lit purple by that person's
   keep strength (--rel), hatched red with an ✕ where they skip it, plain
   otherwise. Readable with no colour: the mark and the pattern carry it. */
.sc-pair { display: flex; flex-direction: column; gap: 8px; min-width: 0; max-width: 100%; }
.sc-pair-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.sc-pair-who { flex: none; display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-soft); min-width: 64px; }
.sc-pair-blank { width: 22px; height: 22px; border-radius: 50%; border: 1px dashed var(--line); display: inline-block; }
.sc-chips.is-pair { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.sc-tag.is-pair { position: relative; display: inline-flex; align-items: center; gap: 5px; padding: 4px 11px; border: 1px solid var(--line); background: var(--white); color: var(--ink); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; line-height: 1.3; }
.sc-tag.is-pair[data-state="keep"] { background: color-mix(in srgb, var(--violet) calc(var(--rel) * 42%), var(--white)); border-color: color-mix(in srgb, var(--violet) calc(var(--rel) * 60%), var(--line)); }
.sc-tag.is-pair[data-state="skip"] { background-color: color-mix(in srgb, #c8462f calc(var(--rel) * 26%), var(--white)); background-image: repeating-linear-gradient(135deg, transparent 0 4px, color-mix(in srgb, #c8462f calc(var(--rel) * 45%), transparent) 4px 6px); }
.sc-tag.is-pair[data-state="unknown"] { background: repeating-linear-gradient(90deg, transparent 0 3px, color-mix(in srgb, var(--line) 60%, transparent) 3px 4px); color: var(--ink-soft); }
.sc-tag.is-pair .sc-mark { font-style: normal; font-size: 9px; opacity: .85; }
.sc-tag.is-pair[data-state="skip"] .sc-mark { color: #8f2a19; }
.sc-tag.is-pair[data-state="keep"] .sc-mark { color: var(--spine); }
/* On a phone the chips are one horizontal scroll row with an edge fade (Q13). */
@media (max-width: 640px) {
  .sc-chips.is-pair { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent); mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent); }
  .sc-chips.is-pair::-webkit-scrollbar { display: none; }
  .sc-tag.is-pair { flex: none; }
}


/* ===========================================================================
   13.7 — THE THREAD AND THE DECK PANEL (suggestions/SPEC.md, answers 12, 13,
   16, 19). A plain scrolling thread over the blurred covers: the buddy speaks
   in the site serif with no bubble and no avatar, your words are a soft cream
   pill on the right, and a deck is a message in the thread like any other.
   The Choices icon pulls a fixed panel out on the left; on a phone the same
   panel is a bottom sheet over the thread, one card at a time.
   =========================================================================== */
.ch-thread { scroll-behavior: smooth; }
.ch-thread.is-empty { justify-content: center; }
/* 15.2 — SMS, BOTH WAYS (suggestions/SPEC.md entry 28). 13.7 gave the buddy
   serif with no bubble, which put its words straight onto the background — and
   the background is the thing Nick likes and the thing that made them hard to
   read: "it also is somewhat hard to read due to the unique background (which
   I do actually like the background though)". His reference for how it should
   feel is his OWN pill (`-chrome_gtthmrmBkX.png`). So the buddy gets the
   mirror of it: the same shape, the same softness, on the LEFT, with the
   corner turned the other way. The background stays exactly as it is; the
   words now sit on something. The serif stays — it is the buddy's voice. */
.ch-line.is-say .ch-card { background: none; border: 0; padding-left: 0; }
.ch-line.is-note .ch-note { background: none; border: 0; padding-left: 0; }
.ch-line .ch-mark { display: none; }
.ch-card-t, .ch-note { font-family: var(--serif, Georgia, "Times New Roman", serif); }
.ch-card-t {
  font-size: 17px; line-height: 1.55;
  max-width: min(72%, 560px); padding: 10px 16px;
  border-radius: 20px; border-bottom-left-radius: 6px;
  background: var(--white); color: var(--ink); box-shadow: var(--lift);
}
/* 15.2 — READABLE OVER A BACKGROUND NICK WANTS KEPT. "it also is somewhat
   hard to read due to the unique background (which I do actually like the
   background though)." So the bubble carries enough of the page's own paper to
   hold the words, and a blur so the covers behind it still read as depth
   rather than as noise under the text. The background itself is untouched. */
.srch .ch.is-thread .ch-card-t {
  background: color-mix(in srgb, var(--white) 78%, transparent);
  backdrop-filter: blur(7px);
}
.srch .ch.is-thread .ch-you {
  background: color-mix(in srgb, var(--you-bg, var(--cream, #f6efe3)) 82%, transparent);
  backdrop-filter: blur(7px);
}
/* An empty answer draws no empty bubble. */
.ch-card-t:empty { display: none; }
/* your words: a soft cream pill on the right */
/* 22.3 (suggestions/SPEC.md 36) — the bubble has its OWN tokens. `--cream`
   never had a dark value while `--ink` flips to near-white, so dark mode drew
   pale words on a cream pill. The light look is unchanged (the fallbacks). */
.ch-you { background: var(--you-bg, var(--cream, #f6efe3)); color: var(--you-ink, var(--ink)); box-shadow: var(--lift); }
/* the guider option rows and the autofill chips are untouched (answer 16) */

/* 15.2 — the deck's own covers on its row (suggestions/SPEC.md entry 28). */
.ch-deckrow-arts { display: inline-flex; align-items: center; gap: 3px; margin-left: 10px; flex: 0 0 auto; }
.ch-deckrow-art {
  width: 22px; height: 22px; border-radius: 3px; overflow: hidden; flex: 0 0 auto;
  display: grid; place-items: center; background: var(--paper-deep); color: var(--ink-faint);
}
.ch-deckrow-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ch-deckrow-more { font-size: 11px; font-weight: 700; color: var(--ink-soft); margin-left: 2px; }

.ch-deck-slot[hidden] { display: none; }
.ch-deck { position: absolute; z-index: 9; right: 12px; top: 12px; bottom: 12px; width: min(360px, 78%);
           display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: 4px;
           background: var(--white); color: var(--ink); box-shadow: var(--shadow); overflow: hidden;
           animation: ch-deck-in 420ms cubic-bezier(.2,.7,.2,1) both; }
@keyframes ch-deck-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }
/* 21.2 — the way out mirrors the way in (CHOICES_FADE_MS in chat.js). */
.ch-deck.is-closing { animation: ch-deck-out 180ms cubic-bezier(.4,0,.6,1) forwards; pointer-events: none; }
.ch-deck.is-sheet.is-closing { animation-name: ch-deck-sheet-out; }
@keyframes ch-deck-out { to { transform: translateX(24px); opacity: 0; } }
@keyframes ch-deck-sheet-out { to { transform: translateY(40px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ch-deck.is-closing { animation-duration: 1ms; } }
.ch-deck-top { display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.ch-deck-top b { flex: 1; font-size: 15px; }
.ch-deck-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.ch-deck-one { border-bottom: 1px solid var(--line-soft); }
.ch-deck-h { display: flex; flex-direction: column; align-items: stretch; gap: 3px; width: 100%; padding: 11px 14px; border: 0;
             background: none; font: inherit; font-size: 13px; font-weight: 600; color: var(--ink-soft);
             text-align: left; cursor: pointer; }
.ch-deck-hl { display: flex; align-items: baseline; gap: 10px; }
/* entry 26: the titles of that deck's cards, tiny, under the ask */
.ch-deck-meta { display: block; font-size: 11px; font-weight: 500; line-height: 1.35; color: var(--ink-faint);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-deck-h:hover { background: var(--paper); color: var(--ink); }
.ch-deck-one.is-open > .ch-deck-h { color: var(--ink); font-weight: 700; }
.ch-deck-ask { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ch-deck-n { flex: none; font-size: 11.5px; font-weight: 700; color: var(--ink-faint); }
.ch-deck-rows { display: flex; flex-direction: column; padding: 0 6px 10px; }
.ch-deck-rows .ch-drow { border: 0; border-top: 1px solid var(--line-soft); border-radius: 0; padding: 9px 8px; cursor: default; }
.ch-deck-rows .ch-drow:first-child { border-top: 0; }
.ch-deck-rows .ch-drow img, .ch-deck-rows .ch-drow .ch-drow-art { width: 42px; height: 42px; border-radius: 3px; }
.ch-drow-art { display: grid; place-items: center; color: var(--spine); background: var(--violet-wash); }
.ch-drow[data-v="none"] .ch-drow-v { color: var(--ink-faint); font-weight: 600; }
.ch-deck-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 14px; border-top: 1px solid var(--line); }
.ch-deck-note { flex: 1 1 100%; margin: 0; font-size: 12.5px; color: var(--ink-soft); }
/* the panel is cream in the dark room, like the cards */
.srch .ch-deck { --ink: #24142e; --ink-soft: #655c69; --ink-faint: #8b8290; --paper: #f5f0e8; --paper-deep: #ebe3d8; --white: #fffdfa;
  --line: rgba(36, 20, 46, 0.14); --line-soft: rgba(36, 20, 46, 0.08); --line-strong: rgba(36, 20, 46, 0.24);
  background: #fffdfa; color: #24142e; }

/* THE PHONE (answer 19): a bottom sheet over the thread, one card at a time */
@media (max-width: 640px) {
  .ch-deck.is-sheet { left: 0; right: 0; top: auto; bottom: 0; width: auto; max-height: 72dvh;
                      border-radius: 14px 14px 0 0; animation: ch-sheet-in 320ms cubic-bezier(.2,.7,.2,1) both; }
  @keyframes ch-sheet-in { from { transform: translateY(28px); opacity: 0; } to { transform: none; opacity: 1; } }
  /* 15.6: the grab bar is a real button (see `drawerNode`), so it is drawn
     here rather than as a `::before` nobody could press. Its hit area is the
     full width of the sheet's head and 18px tall — a thumb's worth — with the
     bar itself the visible 38×4 it always was. */
  .ch-deck.is-sheet .ch-deck-top { position: relative; padding-top: 18px; }
  .ch-deck.is-sheet .ch-sheet-grab {
    position: absolute; left: 0; right: 0; top: 0; height: 18px;
    border: 0; background: none; padding: 0; cursor: grab; display: block;
  }
  .ch-deck.is-sheet .ch-sheet-grab::before {
    content: ""; position: absolute; left: 50%; top: 6px; width: 38px; height: 4px;
    border-radius: 2px; background: var(--line-strong); transform: translateX(-50%);
    transition: width 160ms var(--ease), background 160ms var(--ease);
  }
  .ch-deck.is-sheet .ch-sheet-grab:active::before { width: 52px; background: var(--ink-soft); }
  /* Slid up: the sheet takes the screen, so a deck with more in it than one
     card's worth has the room to show it. */
  .ch-deck.is-sheet { transition: max-height 260ms cubic-bezier(.2,.7,.2,1), min-height 260ms cubic-bezier(.2,.7,.2,1); }
  .ch-deck.is-sheet.is-tall { max-height: 92dvh; min-height: 62dvh; }
  /* The other decks are what "all the choices" means; they are there only when
     the sheet has been slid up, and only when there ARE others. */
  .ch-sheet-more { display: none; border-top: 1px solid var(--line-soft); }
  .ch-deck.is-sheet.is-tall .ch-sheet-more { display: block; }
  @media (prefers-reduced-motion: reduce) { .ch-deck.is-sheet { transition: none; } }
  /* On a desktop the panel is already full height; the bar would be a control
     that changes nothing, so it is not drawn at all. */
}
.ch-deck.is-panel .ch-sheet-grab { display: none; }
.ch-sheet-deck { display: grid; grid-template-columns: 36px minmax(0, 1fr) 36px; align-items: center; gap: 8px;
                 padding: 12px; touch-action: pan-y; }
.ch-sheet-one { min-width: 0; }
.ch-sheet-n { grid-column: 1 / -1; text-align: center; font-size: 11.5px; font-weight: 700; color: var(--ink-faint); }
@media (prefers-reduced-motion: reduce) { .ch-deck, .ch-thread .ch-turn { animation: none; } .ch-thread { scroll-behavior: auto; } }

/* ===========================================================================
   13.7b (2026-09-08, suggestions/SPEC.md entry 22) — the deck carries through.
   The panel opens itself and stays; the thread keeps one compact row per deck;
   the Choices control is a HANDLE on the left edge, attached to the panel.
   =========================================================================== */
.ch.is-thread { position: relative; }
/* 14.3 (entry 23): "Should also be on the right side" — the panel and its
   handle live on the right edge now. */
.ch.is-thread .ch-rail.ch-handle {
  position: absolute; right: 0; left: auto; top: 50%; transform: translateY(-50%);
  order: 0; flex-direction: column; justify-content: center; padding: 0; margin: 0;
  z-index: 10; pointer-events: auto;
  transition: right 380ms cubic-bezier(.2,.7,.2,1);
}
.ch.is-thread.has-deck .ch-rail.ch-handle { right: calc(min(360px, 78%) + 14px); }
.ch-handle .ch-rail-b { width: 30px; height: 62px; border-radius: 14px 0 0 14px; border-right: 0; }
.ch-handle .ch-rail-b .ch-rail-n { top: -6px; left: auto; right: 20px; }
.ch-handle .ch-rail-b .ch-rail-l { left: auto; right: 38px; }
/* THE PHONE KEEPS THE HANDLE ON THE LEFT: the panel is a bottom sheet there,
   so the side is free, and on the right the handle sat on top of the send
   button and the allowance line. */
@media (max-width: 720px) {
  .ch.is-thread .ch-rail.ch-handle { position: absolute; left: 0; right: auto; top: auto; bottom: 96px; transform: none; order: 0; }
  .ch.is-thread.has-deck .ch-rail.ch-handle { left: 0; right: auto; opacity: 0; pointer-events: none; }
  .ch-handle .ch-rail-b { border-radius: 0 14px 14px 0; border-left: 0; border-right: 1px solid var(--line); }
  .ch-handle .ch-rail-b .ch-rail-n { right: auto; left: 20px; }
}

/* ===========================================================================
   14.3 (2026-09-09, suggestions/SPEC.md entry 25) — THE CARD BY ITSELF.
   A deck arrives centred and fixed at the top middle of the screen, over the
   thread, with the thread visible behind it. It is not a modal: nothing is
   dimmed and the thread keeps scrolling. Answered, it goes.
   =========================================================================== */
.ch-pop-slot[hidden] { display: none; }
/* 15.2 — THE OUTSIDE IS A THING YOU CAN PRESS. Without a scrim there was no
   "outside" to click: the card was a fixed box and everything around it
   belonged to the thread. The scrim is barely there — the thread must still
   read behind the card, which is entry 25's whole point — but it is what makes
   a press outside the card mean "close", and it is what fades. */
.ch-pop-scrim {
  /* 19.1 (`suggestion-card/SPEC.md` entry 9) — ABOVE THE BARS.
     At z-index 12 the scrim and the card sat UNDER `/search`'s sticky bar
     (z 25) and the phone top bar (z 35): the card's ✕ and the top of its
     artwork were behind the bar at every width, which is the "overflowing"
     Nick saw on a laptop — content drawn where nothing can reach it — and it
     is also why a press "outside" so often did nothing, because the top band
     of the scrim belonged to the bar. 38 clears both bars and still leaves
     the phone tab bar (z 40) and the app's dialogs (z 60) on top. */
  position: fixed; inset: 0; z-index: 38;
  background: color-mix(in srgb, var(--ink) 10%, transparent);
  animation: ch-scrim-in 200ms ease both;
}
.ch-pop-scrim.is-out { animation: ch-scrim-out 200ms ease both; }
.ch-pop-scrim.is-out .ch-pop { animation: ch-pop-out 200ms cubic-bezier(.4,0,1,1) both; }
@keyframes ch-scrim-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ch-scrim-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes ch-pop-out { from { transform: translateX(-50%); opacity: 1; }
                        to { transform: translate(-50%, -8px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .ch-pop-scrim, .ch-pop-scrim.is-out, .ch-pop-scrim.is-out .ch-pop, .ch-pop { animation: none; }
}
.ch-pop {
  /* 21.3 (suggestions/SPEC.md 35, navigation 7) — THE CARD NEVER COVERS THE
     SEARCH BAR. Nick, at 1320×743 with devtools docked: "MOSTLY at the top
     with the searchbar covered." It was `top: 14px` — ABOVE the bar's 69px
     bottom — and 19.1 had dropped the bar under it. Now the card lives in the
     band between the bar (chat.js sets --ch-pop-top from the bar's real
     bottom) and the composer, and `margin-block: auto` with both insets set
     centres it in that band. */
  position: fixed; z-index: 12;
  top: var(--ch-pop-top, 82px); bottom: 116px; height: fit-content; margin-block: auto;
  /* …and centred across the CONTENT column (the search bar's own span, set by
     chat.js), never across a viewport that includes the rail. */
  left: var(--ch-pop-cx, 50%); transform: translateX(-50%);
  /* it never reaches the composer: on a short window the card scrolls inside
     the popup rather than covering the box you type in */
  /* 19.1: 116px reserved 102px below the card and the composer band is 107px
     tall, so on a 620px-high laptop the card's last 5px sat on top of the box
     you type in. 130px leaves 14px of daylight — the same as the top gap. */
  width: min(900px, calc(var(--ch-pop-span, 100vw) - 32px)); max-height: calc(100dvh - var(--ch-pop-top, 82px) - 116px);
  overflow: auto; overscroll-behavior: contain;
  padding: 10px 12px 12px; border: 1px solid var(--line); border-radius: 16px;
  background: var(--white); color: var(--ink); box-shadow: var(--shadow);
  animation: ch-pop-in 320ms cubic-bezier(.2,.7,.2,1) both;
}
@keyframes ch-pop-in { from { transform: translate(-50%, -10px); opacity: 0; } to { transform: translateX(-50%); opacity: 1; } }
/* ===========================================================================
   18.8 (`suggestion-card/SPEC.md` entry 8) — ← → AS THIN FULL-HEIGHT STRIPS.

   Nick: "a big but thin vertical spanning (same height as the cards) but very
   thin, that you can click. There will be whitespace between the arrow and the
   card itself, but dont allow the whitespace to stop the move."

   So the BUTTON is the whole band from the viewport edge to the card's edge —
   `.ch-pop` is `width: min(900px, 100vw - 32px)` centred, so half of it is
   `min(450px, 50vw - 16px)` and that is exactly where each strip stops. The
   visible part is a hairline and an arrow near the outer edge; the gap between
   it and the card belongs to the same button, which is the whole point.

   Desktop only: a phone swipes, and a 390px screen has no room beside a card.
   =========================================================================== */
  position: fixed; z-index: 12; top: 14px; bottom: 102px;
  display: grid; place-items: center;
  padding: 0; border: 0; background: none; color: var(--ink-faint);
  font: inherit; font-size: 20px; line-height: 1; cursor: pointer;
  transition: color 140ms var(--ease);
}
  content: ""; position: absolute; top: 10%; bottom: 10%; width: 3px;
  border-radius: 3px; background: currentColor; opacity: .45;
  transition: opacity 140ms var(--ease);
}
             width: 34px; height: 34px; border-radius: 50%;
             background: var(--white); box-shadow: var(--shadow-sm); }

/* the caption that says the keyboard works — desktop only, faint, under the card */
.ch-keyhint { margin-left: 10px; font-size: 11px; font-weight: 600; letter-spacing: .04em;
              color: var(--ink-faint); opacity: .75; }
@media (max-width: 900px) { .ch-keyhint { display: none; } }

.ch-pop-x { position: absolute; top: 8px; right: 8px; z-index: 2; width: 28px; height: 28px; display: grid; place-items: center;
            border: 1px solid var(--line); border-radius: 999px; background: var(--white); color: var(--ink-soft); cursor: pointer; }
.ch-pop-x:hover { color: var(--ink); border-color: var(--line-strong); }
.ch-pop .ch-turn.is-picks { background: none; border: 0; box-shadow: none; padding: 0; }
.ch-pop .ch-picks { width: 100%; }
/* 15.3 — ONE SURFACE (suggestion-card/SPEC.md entry 6). Nick: "UI of the
   suggestion card popup is weird.. Its a box within a box." It was, literally:
   `.ch-pop` is a white panel with a border, a radius and a shadow, and
   `.sc-card` inside it is a second white panel with a border, a radius and a
   shadow — two frames with a gap between them. Inside the popup the card is
   not a card; the popup IS the card, so the inner frame comes off and the
   card's own padding goes with it. The card keeps its frame everywhere else
   (a deck drawn inline, the phone sheet).
   The stack behind it — the "there are more" shadows — is a lie about depth
   when the arrows and the dots already say how many there are, and it is what
   made the second frame visible at the edges. */
.ch-pop .sc-card {
  border: 0; border-radius: 0; background: none; box-shadow: none; padding: 0;
}
.ch-pop .cd-behind { display: none; }
.ch-pop .cd-stack { padding-bottom: 0; }
/* the card is cream in the dark room, like the panel */
.srch .ch-pop { --ink: #24142e; --ink-soft: #655c69; --ink-faint: #8b8290; --paper: #f5f0e8; --paper-deep: #ebe3d8; --white: #fffdfa;
  --line: rgba(36, 20, 46, 0.14); --line-soft: rgba(36, 20, 46, 0.08); --line-strong: rgba(36, 20, 46, 0.24);
  background: #fffdfa; color: #24142e; }
@media (max-width: 640px) {
  /* under the app's own header, and clear of the composer and the tab bar */
  /* 17.2: the budget was 210px of chrome and only ~90px of it exists — the
     tab bar. The card was giving away 60px of its own height to nothing, and
     that was the difference between showing the four actions and clipping
     them. It still stops short of the tab bar. */
  /* 21.3 — below the phone's top bar AND search bar, above composer + tab bar. */
  .ch-pop { top: var(--ch-pop-top, 124px); bottom: 152px; width: calc(100vw - 16px);
            max-height: calc(100dvh - var(--ch-pop-top, 124px) - 152px); padding: 8px; border-radius: 14px; }
}
@media (prefers-reduced-motion: reduce) { .ch-pop { animation: none; } }

/* the thread's row for a deck */
.ch-turn.is-deckrow { background: none; border: 0; box-shadow: none; padding: 0; }
.ch-deckrow {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 14px; border: 1px solid var(--line); border-radius: 14px;
  background: var(--white); color: var(--ink-soft); font: inherit; text-align: left; cursor: pointer;
  transition: border-color 180ms ease, transform 180ms var(--soft, ease);
}
.ch-deckrow:hover { border-color: var(--violet); color: var(--ink); transform: translateY(-1px); }
.ch-deckrow-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.ch-deckrow-n { flex: none; font-size: 11.5px; font-weight: 700; color: var(--ink-faint); }


/* ===========================================================================
   17.2 — A CARD NEVER SCROLLS (suggestion-card/SPEC.md entry 7).

   Nick: "optimize aspect ratio and never show the scroll sidebar. Only
   optimize." … "doesnt feel neatly concrete." The popup held one big ratio
   and grew a scrollbar the moment the window was short — on a 620px laptop
   the four actions were simply below the fold.

   So the popup's height is a budget and the card spends it. What gives, in
   order (17.2 point 4, the default Nick can overturn):

     1. the About prose clamps harder,
     2. fewer chips are shown,
     3. the artwork shrinks.

   The artwork is continuous rather than stepped — `clamp()` on dvh — so there
   is no height at which the card jumps; the other two need breakpoints,
   because a line count and a chip count are integers.
   =========================================================================== */
.ch-pop { overflow: hidden; display: flex; flex-direction: column; }
.ch-pop .ch-turn.is-picks { min-height: 0; flex: 1 1 auto; display: flex; }
.ch-pop .ch-picks { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.ch-pop .ch-dots { flex: none; }
.ch-pop .ch-viewer { min-height: 0; flex: 1 1 auto; display: flex; }
.ch-pop .cd-stack, .ch-pop .cd-swipe { min-height: 0; flex: 1 1 auto; display: flex; }
.ch-pop .sc-card { min-height: 0; flex: 1 1 auto; gap: 10px; }
.ch-pop .sc-work { gap: 8px; }
.ch-pop .sc-actions { padding-top: 8px; }
/* A grid row is auto-sized to its tallest item unless it is told otherwise —
   `minmax(0, 1fr)` is what lets the two columns actually give. */
.ch-pop .sc-split { min-height: 0; flex: 1 1 auto; align-items: stretch; grid-template-rows: minmax(0, 1fr); }
.ch-pop .sc-work, .ch-pop .sc-why { min-height: 0; overflow: hidden; }
.ch-pop .sc-actions { flex: none; }
/* 3. THE ARTWORK IS THE LAST TO GIVE, and it gives continuously. */
/* 19.1 — AND THE COVER GIVES WITH IT. `max-height: 100%` on a centred grid
   item never resolved against the hero here, so the cover stayed at its
   natural 250px while the hero shrank around it: the shrink CROPPED the
   artwork instead of scaling it, and a laptop card showed a cover with its
   bottom cut off. The hero's height is a variable now and the cover's cap is
   that same number less the hero's padding, which is a length and always
   resolves. */
.ch-pop .sc-work { --hero-h: calc(clamp(96px, 26dvh, 220px) * var(--sc-fit, 1)); }
.ch-pop .sc-work .cd-hero { flex: 0 1 auto; height: var(--hero-h); }
.ch-pop .sc-work .cd-art { max-height: calc(var(--hero-h) - 28px); }
/* 1. THE ABOUT PROSE CLAMPS FIRST. */
.ch-pop .sc-desc { min-height: 0; overflow: hidden; }
@media (max-height: 840px) { .ch-pop .sc-about-p { -webkit-line-clamp: 3; } }
@media (max-height: 720px) { .ch-pop .sc-about-p { -webkit-line-clamp: 2; } }
@media (max-height: 600px) { .ch-pop .sc-about-p { -webkit-line-clamp: 1; }
                             .ch-pop .sc-about-h { display: none; } }
/* 2. THEN FEWER CHIPS. They are last in the column now, so what is dropped is
   the tail of the quietest row — never a fact the card leads with. */
@media (max-height: 720px) { .ch-pop .sc-chips .sc-tag:nth-child(n+9) { display: none; } }
@media (max-height: 640px) { .ch-pop .sc-chips .sc-tag:nth-child(n+6) { display: none; } }
@media (max-height: 560px) { .ch-pop .sc-chips { display: none; } }
/* A PHONE'S BUDGET IS NOT ITS VIEWPORT. The popup there is `100dvh - 210px`,
   so a 844px phone is a 634px card — the height queries above never fire and
   the actions sat below the fold anyway. The same ladder, one column, shifted
   by the chrome the phone adds. */
@media (max-width: 640px) {
  /* One column means the artwork's height is spent straight out of the card's
     budget rather than beside the other column, so its share is smaller. */
  .ch-pop .sc-work { --hero-h: calc(clamp(84px, 13dvh, 150px) * var(--sc-fit, 1)); }
  .ch-pop .sc-about-p { -webkit-line-clamp: 2; }
  .ch-pop .sc-chips .sc-tag:nth-child(n+6) { display: none; }
}
@media (max-width: 640px) and (max-height: 780px) {
  .ch-pop .sc-about-p { -webkit-line-clamp: 1; }
  .ch-pop .sc-chips .sc-tag:nth-child(n+5) { display: none; }
}
@media (max-width: 640px) and (max-height: 680px) { .ch-pop .sc-chips { display: none; } }
/* 19.1 — AND THE SAMPLE PILL SHRINKS WITH THE COVER. On a phone the hero is
   ~100px tall and the pill's words are 163px wide, so it ran clear across the
   artwork it belongs to. Under 640px it keeps the glyph and the duration and
   drops the words, which the glyph already says. */
@media (max-width: 640px) {
  .ch-pop .cd-play { left: 8px; bottom: 8px; height: 28px; padding: 0 10px 0 6px; gap: 5px; }
  .ch-pop .cd-play .cd-play-l { display: none; }
}

/* 20.3 — WAKING: the chat is not drawn for an identity the server has not
   named yet, and the entrance fade waits for the stored thread rather than
   playing over an empty one (`src/ui/chat.js`, `hydrate`). */
.ch.is-thread.is-waking .ch-body { opacity: 0; animation: none; }
.ch.is-thread.is-waking .ch-rail { opacity: 0; animation: none; }

/* 26.8 — the round's refinement, under its ask */
.ch-deck-refine { display: block; margin-top: 2px; font-size: 12px; color: var(--ink-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* 2026-09-14 — the tip line: centred, quiet, a bulb, no box */
.ch-tip { display: flex; align-items: center; justify-content: center; gap: 6px; margin: 14px auto 0; max-width: 46ch;
  font-size: 13px; line-height: 1.45; color: var(--ink-faint); text-align: center; }
.ch-tip svg { flex: none; color: var(--ink-faint); }

/* 2026-09-14 — THE PHONE SEARCH PAGE (Nick's iPhone 7 round). The Choices
   handle sits middle-right; the hint chips are quiet; the page itself does
   not scroll — the thread does; the anonymous bar is one slim line; the
   suggestion popup and the deck scroll by touch. */
@media (max-width: 720px) {
  .ch.is-thread .ch-rail.ch-handle { left: auto; right: 0; top: 50%; bottom: auto; transform: translateY(-50%); }
  .ch-handle .ch-rail-b { border-radius: 14px 0 0 14px; border-right: 0; border-left: 1px solid var(--line); }
  .ch-handle .ch-rail-b .ch-rail-n { left: auto; right: 20px; }
  .ch-hints { padding: 8px 4px 4px; gap: 6px; }
  .ch-hint { height: 30px; padding: 0 10px; font-size: 12px; font-weight: 500; color: var(--ink-faint); box-shadow: none; background: transparent; }
  .ch-pop, .ch-deck-body, .ch-thread { touch-action: pan-y; -webkit-overflow-scrolling: touch; }
  .app[data-view="search"] .topbar-phone .topbar-title { display: none; }
  .app[data-view="search"] .main { overflow: hidden; overscroll-behavior: none; }
}

/* 2026-09-14 — PHONE CARDS: "shrink to fit and a bit of scroll … as little
   scroll as possible, but not hard to read". A short cover band, one size
   down on the words, tighter gaps; the popup still scrolls if it must. */
@media (max-width: 720px) {
  .ch-pop { padding: 8px; border-radius: 12px; max-height: calc(100dvh - var(--ch-pop-top, 82px) - 104px); }
  .ch-pop .sc-card { padding: 12px; gap: 8px; box-shadow: none; }
  .ch-pop .sc-split { gap: 10px; }
  .ch-pop .sc-work .cd-hero { height: 96px; padding: 6px; }
  .ch-pop .sc-work .cd-art { max-height: 84px; }
  .ch-pop .sc-title { font-size: 19px; line-height: 1.1; }
  .ch-pop .sc-creator { font-size: 13px; }
  .ch-pop .sc-desc p, .ch-pop .sc-why-p { font-size: 13px; line-height: 1.4; }
  .ch-pop .sc-desc p { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4; overflow: hidden; }
  .ch-pop .sc-why-box { padding: 8px 10px; }
  .ch-pop .sc-actions { padding-top: 6px; }
  .ch-thread .ch-turn .sc-card { padding: 12px; gap: 8px; }
  .ch-thread .ch-turn .sc-work .cd-hero { height: 120px; padding: 6px; }
  .ch-thread .ch-turn .sc-work .cd-art { max-height: 108px; }
  .tc-card { padding: 10px; gap: 8px; }
  .tc-yes, .tc-no { height: 40px; }
}
.ch-plusmenu { max-height: calc(100dvh - 140px); overflow: auto; overscroll-behavior: contain; }

/* 2026-09-15 — THE PHONE SUGGESTION CARD (Nick): "title / metadata, image +
   sample, why peltify picked it, interactables … everything above the
   interactables is a click away from the whole media popup." */
@media (max-width: 720px) {
  .ch-pop .sc-desc, .ch-pop .sc-open, .ch-pop .sc-chips, .ch-pop .sc-about, .ch-pop .sc-prov, .ch-pop .sc-returned { display: none; }
  .ch-pop .sc-split { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .ch-pop .sc-work { flex-direction: column-reverse; gap: 8px; }
  .ch-pop .sc-why-box { padding: 10px 12px; }
  .ch-pop .sc-card { cursor: pointer; }
}
