/* Lobby design system: see DESIGN.md before changing tokens. */
@font-face { font-family: "Atkinson Hyperlegible"; font-weight: 400; font-style: normal; font-display: swap; src: url(/fonts/AtkinsonHyperlegible-400.woff2) format("woff2"); }
@font-face { font-family: "Atkinson Hyperlegible"; font-weight: 400; font-style: italic; font-display: swap; src: url(/fonts/AtkinsonHyperlegible-400i.woff2) format("woff2"); }
@font-face { font-family: "Atkinson Hyperlegible"; font-weight: 700; font-style: normal; font-display: swap; src: url(/fonts/AtkinsonHyperlegible-700.woff2) format("woff2"); }
@font-face { font-family: "Comic Neue"; font-weight: 700; font-style: normal; font-display: swap; src: url(/fonts/ComicNeue-700.woff2) format("woff2"); }
@font-face { font-family: "Libre Franklin"; font-weight: 800; font-style: normal; font-display: swap; src: url(/fonts/LibreFranklin-800.woff2) format("woff2"); }
@font-face { font-family: "Barlow Condensed"; font-weight: 600; font-style: normal; font-display: swap; src: url(/fonts/BarlowCondensed-600.woff2) format("woff2"); }

:root {
  color-scheme: light;
  /* palette: red and white (member request) */
  --wall: #fbf4f4;       /* page background: white with a breath of red */
  --wall-2: #fffafa;     /* raised surfaces: sheets, inputs */
  --paper: #ffffff;      /* inputs, lists */
  --ink: #2e0b0f;        /* text: a red so deep it reads as black */
  --ink-2: #7d4a4f;      /* secondary text */
  --ink-3: #a98689;      /* tertiary text, hints */
  --rule: #ecd2d4;       /* separators, input borders */
  --felt: #b8111b;       /* the letterboard: red felt */
  --felt-groove: #9c0d16;
  --letter: #ffffff;     /* letterboard letters */
  --red: #d3121c;        /* your own bubbles, primary buttons */
  --red-ink: #ffffff;
  --bubble: #ffffff;     /* everyone else's bubbles, white in light and dark */
  --bubble-ink: #2e0b0f;
  --bubble-edge: #ebcfd1;
  --signal: #ffffff;     /* change requests + "in progress": white on the red board */
  --signal-ink: #b8111b;
  --focus: #d3121c;
  --note: #ffe6e6;       /* replies from 🅱️chat */
  --note-ink: #4a0d12;
  --danger: #b42318;
  --ok: #1f7a4d;
  /* type */
  --font: "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", sans-serif;
  --board: "Barlow Condensed", "Arial Narrow", sans-serif;
  --app-h: 100dvh;
  /* the home-bar inset. html.short-view zeroes it: an iPhone home-screen app installed back when the status bar
     was translucent gets a web view that stops a status bar short of the screen (iOS 26, WebKit bug 301108), so
     it never reaches the home bar and padding for it just leaves dead space above the unreachable strip */
  --sab: env(safe-area-inset-bottom);
}
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --wall: #1b0a0c;
    --wall-2: #250f12;
    --paper: #2f1418;
    --ink: #fbeeee;
    --ink-2: #cfa9ac;
    --ink-3: #9a777a;
    --rule: #4a252a;
    --felt: #9d0f17;
    --felt-groove: #830c13;
    --red: #e3222c;
    --bubble-edge: #ffffff;
    --focus: #ff5a62;
    --note: #3d1419;
    --note-ink: #ffdadc;
    --danger: #ff8a7a;
    --ok: #6fd3a0;
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html.short-view { --sab: 0px; }
/* a home-screen app whose web view starts under the status bar (see short-view, app.js) gets a black strip there,
   like the opaque status bar, so the clock and battery never sit on the red letterboard */
html.under-bar body::after { content: ""; position: fixed; z-index: 2147483647; top: 0; left: 0; right: 0; height: env(safe-area-inset-top); background: #000; pointer-events: none; }
html, body { margin: 0; height: 100%; touch-action: manipulation; background: var(--wall); color: var(--ink); font: 17px/1.45 var(--font); overscroll-behavior: none; }
/* status-bar area matches the board on iOS; the lower half matches the composer, so if iOS ever leaves a gap
   under the app it blends in instead of showing a red bar */
body.in-chat { background: #fff linear-gradient(var(--felt), var(--felt)) top / 100% 50% no-repeat; }
button { font: inherit; color: inherit; cursor: pointer; }
input, textarea { font: inherit; font-size: 17px; color: var(--ink); }
a { color: var(--red); }
[hidden] { display: none !important; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
.board :focus-visible { outline-color: var(--letter); }
.boot { height: var(--app-h); background: var(--felt); }

/* ---------- the letterboard ---------- */
.board {
  background-color: var(--felt);
  background-image: repeating-linear-gradient(to bottom, transparent 0 7px, var(--felt-groove) 7px 9px);
  color: var(--letter);
  font-family: var(--board);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .14em;
  text-shadow: 0 1px 0 #000a;
}
.board .row1 { font-size: 26px; line-height: 1; }
.board .row2 { font-size: 15px; line-height: 1.2; letter-spacing: .12em; opacity: .82; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---------- auth ---------- */
.auth { min-height: var(--app-h); display: flex; flex-direction: column; }
.auth .board { padding: calc(56px + env(safe-area-inset-top)) 24px 40px; }
.auth .board .row1 { font-size: 64px; letter-spacing: .18em; }
.auth .board .row2 { font-size: 20px; margin-top: 14px; white-space: normal; }
.auth-body { flex: 1; padding: 28px 24px calc(28px + var(--sab)); max-width: 420px; width: 100%; }
.auth-body p { margin: 0 0 20px; color: var(--ink-2); }
.auth form { display: grid; gap: 12px; }
.auth label { display: grid; gap: 5px; font-weight: 700; font-size: 15px; }
.field { width: 100%; padding: 12px 14px; background: var(--paper); border: 1.5px solid var(--rule); border-radius: 10px; outline: none; }
.field:focus { border-color: var(--red); }
.btn { border: 0; border-radius: 10px; padding: 13px 18px; font-weight: 700; background: var(--red); color: var(--red-ink); }
.btn:disabled { opacity: .5; }
.btn.quiet { background: transparent; border: 1.5px solid var(--rule); color: var(--ink); }
.btn.small { padding: 7px 12px; font-size: 15px; }
.btn.signal { background: var(--red); color: var(--red-ink); }
.err { color: var(--danger); min-height: 1.3em; font-size: 15px; }
.hint { color: var(--ink-2); font-size: 15px; margin-top: 22px; }

/* ---------- chat layout ---------- */
/* bug fix (Ethan: "make the background of the group chat solid white, the text still spills over certain graphic
   objects like the rectangle"): the chat is plain solid white under the letterboard, in dark mode too, with nothing
   drawn behind the messages (it replaced the liquid glass over the live Bii lobby). In dark mode the chat takes the
   light palette so its text stays dark on the white; the red letterboard is unchanged. */
.chat { position: fixed; top: var(--app-top, 0px); left: 0; right: 0; height: var(--app-h); display: flex; flex-direction: column; max-width: 760px; margin: 0 auto; background: #fff; --wall: #fff; }
@media (prefers-color-scheme: dark) {
  .chat, .sheet.docked {
    color-scheme: light; color: var(--ink);
    --wall-2: #fffafa; --paper: #ffffff; --ink: #2e0b0f; --ink-2: #7d4a4f; --ink-3: #a98689; --rule: #ecd2d4;
    --red: #d3121c; --bubble-edge: #ebcfd1; --focus: #d3121c; --note: #ffe6e6; --note-ink: #4a0d12; --danger: #b42318; --ok: #1f7a4d;
  }
  .chat .msg .who { color: var(--c, var(--ink-2)); }
  .chat .mface.cutout img { filter: none; }
  .chat .mpick:not(:first-child) { color: var(--c, var(--ink)); }
}
.chat > .top, .chat > .tabbar, .chat > .banner, .chat > .messages, .chat > .typing, .chat > .composer { position: relative; z-index: 1; }

.top { display: flex; align-items: center; gap: 12px; padding: calc(12px + env(safe-area-inset-top)) 14px 12px 18px; }
/* no letterboard (member request, Ethan): the chat's top is only the safe area in felt red; the section bar is the top
   of the screen */
.chat > .top { flex: none; display: block; padding: env(safe-area-inset-top) 0 0; background: var(--felt); }
.chat > .top + .tabbar { border-top: 0; }

/* Who's here (bug fix, Ethan: "display the active members' Bii faces along the top of the group chat tab"): a quiet
   row on the chat's white, you first, each Bii head over its member's name in their colour. It sits under the composer
   (member request, Ethan: "move the bar of active chat members under the keyboard"), where the keyboard covers it: it
   holds the bottom safe area while the keyboard is down and is gone while it's up, so the composer sits on the keys. */
.chat > .here { position: relative; z-index: 1; flex: none; display: flex; gap: 4px; padding: 6px 10px calc(4px + var(--sab)); overflow-x: auto;
  overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; border-top: 1px solid var(--rule); }
.chat > .here::-webkit-scrollbar { display: none; }
.chat > .composer:has(+ .here) { padding-bottom: 8px; }
body.ev-on .chat > .here, .kb-open .chat > .here { display: none; }
.here-p { flex: none; width: 64px; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 0 0 2px; border: 0;
  background: none; color: var(--c, var(--ink)); font: 700 12px/1.2 var(--font); cursor: pointer; -webkit-tap-highlight-color: transparent; }
.here-face { width: 50px; height: 50px; display: grid; place-items: center; }
.here-face .mface, .here-face img, .here-face svg { display: block; width: 100%; height: 100%; }
.here-face .mface.flat svg { padding: 4px; }
.here-n { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.here-p:active .here-face { scale: .92; }
/* the member whose change is being made (member request, Ethan): their head loops the loop with spiral eyes (app.js
   renderHere, plaza.js Lobby.dizzyFace) for as long as it runs */
.here-p.changing .here-face .mface.cutout { position: relative; }
/* On the Change tab (member request, Ethan) the bar stays out under it and shows the changes queue: one head per change
   in running order, each with its place in line on a felt tile (the board's red and white), the one being made first
   and spinning. app.js renderHere → queueRow; the docked sheet stops at its top (--here-h). */
body.ask-on .chat > .here { z-index: 3; background: #fff; }
html:not(.kb-open) body.ask-on .chat > .sheet.docked { bottom: var(--here-h, 0px); padding-bottom: 24px; }
.here.queue .here-face { position: relative; }
.here.queue .here-face img { padding: 6px; }
.q-n { position: absolute; top: 0; left: 2px; min-width: 18px; height: 20px; padding: 0 4px; display: grid; place-items: center; background: var(--felt);
  color: #fff; font: 600 15px/1 var(--board); border-radius: 2px; box-shadow: 0 0 0 2px #fff; }
.here.queue .now .q-n { background: #fff; color: var(--felt); box-shadow: inset 0 0 0 2px var(--felt); }
.here-note { margin: 0; min-height: 66px; display: flex; align-items: center; padding: 0 8px; color: var(--ink-3); font-size: 14px; }

/* the section bar (member request, Ethan: "a horizontal bar across the screen like the paintings, drawings, prints
   bar on ethanorchard.com"): Chat, Change and Lobby as equal cells edge to edge, labels in heavy tracked
   Libre Franklin caps. White and red (member request, Ethan): the cells are white with red labels, split by a faint red
   hairline, over a 2px red rule; the open one is solid red with a white label. The Lobby's screens open under it (--tabs-y). */
.tabbar { --c: #d3121c; --n: 3; --i: 0; flex: none; display: flex; background: #fff; border-top: 2px solid #fff; border-bottom: 2px solid var(--c);
  user-select: none; -webkit-user-select: none; }
.tabbar .tab { position: relative; flex: 1 1 0; min-width: 0; height: 44px; margin: 0; padding: 0 6px;
  display: flex; align-items: center; justify-content: center; gap: 6px; border: 0; background: #fff; color: var(--c);
  font: 800 15px/1 "Libre Franklin", "Helvetica Neue", Arial, sans-serif; letter-spacing: .1em; text-transform: uppercase;
  white-space: nowrap; overflow: hidden; cursor: pointer; -webkit-tap-highlight-color: transparent; text-rendering: geometricPrecision; }
/* no colour fade on a label (bug fix, Ethan: "sometimes in the Change tab the Chat button disappears"): on iPhone the
   label's inherited colour fade could stall and leave a label the colour of its cell. The real cells always stay white
   with red labels; the open look lives only in the lens below */
.tabbar .tab::before { content: ""; position: absolute; inset: 0; border: 6px solid #fff; pointer-events: none; }
.tabbar .tab + .tab::after { content: ""; position: absolute; left: 0; top: 12px; bottom: 12px; width: 1px;
  background: rgba(211, 18, 28, .35); pointer-events: none; }
.tabbar > .tab[aria-selected="true"]::after, .tabbar > .tab[aria-selected="true"] + .tab::after { display: none; }
.tabbar .tab > span { padding-left: .1em; overflow: hidden; text-overflow: clip; }
/* the sliding lens (member request, Ethan: "have the white slide along the top bar between the options smoothly"):
   app.js mountLens lays one red window (.tab-lens, a cell wide) over the bar and glides it to cell --i; inside, a
   copy of every cell in the open look slides the other way, so the labels hold still and turn white as the red
   crosses them. It runs up through the safe area to the top of the screen (member request, Ethan). */
.tabbar { position: relative; }
.tabbar > .tab-lens { position: absolute; left: 0; top: 0; bottom: 0; z-index: 1; width: calc(100% / var(--n)); overflow: hidden;
  pointer-events: none; transform: translate3d(calc(var(--i) * 100%), 0, 0); transition: transform .38s cubic-bezier(.3, .9, .2, 1); }
.chat > .top + .tabbar > .tab-lens { top: calc(-1 * env(safe-area-inset-top, 0px)); }
.tab-lens-row { display: flex; height: 100%; width: calc(100% * var(--n)); transform: translate3d(calc(var(--i) * -100% / var(--n)), 0, 0);
  transition: inherit; }
.tabbar > .tab-lens.still, .tabbar > .tab-lens.still .tab-lens-row { transition: none; }
@media (prefers-reduced-motion: reduce) { .tabbar > .tab-lens, .tab-lens-row { transition: none; } }
.tab-lens .tab { height: 100%; background: var(--c); color: #fff; }
.chat > .top + .tabbar .tab-lens .tab { padding-top: env(safe-area-inset-top, 0px); }
.tab-lens .tab::before, .tab-lens .tab::after { display: none; }
#tabbar .tab-lens .tab { outline-color: transparent !important; }
/* a press on the open cell (Chat again: the rooms bar) still dims under the red */
.tabbar:has(> .tab[aria-selected="true"]:active) > .tab-lens { filter: brightness(.94); }
.tabbar:has(> .tab[aria-selected="true"]:focus-visible) > .tab-lens::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 0 0 2px #fff; }
/* the open look wears the 🅱️ before its label (member request, Ethan) */
/* a big 🅱️ (member request, Ethan), well over the capitals: a fixed 1.45em glyph centred on the cap height; the label's
   clip (bug fix: it cropped the emoji) gets room above and below so no phone's emoji font loses an edge */
.tab-lens .tab > span { display: flex; align-items: center; padding-block: .3em; }
.tab-lens .tab > span::before { content: "🅱️"; flex: none; margin-right: .22em; letter-spacing: 0;
  font-size: 1.45em; line-height: 1; position: relative; top: -.03em; }
.tabbar .tab:active { filter: brightness(.94); }
/* the hard-button rim shows on the cells when pressed, but not as a standing ring on the open one */
#tabbar > .tab[aria-selected="true"]:not(:active, [data-hit], :focus-visible) { outline-color: transparent !important; }
.tabbar .tab-n { display: inline-grid; place-items: center; min-width: 19px; height: 19px; padding: 0 4px; background: var(--c); color: #fff;
  font: 800 11px/1 "Libre Franklin", Arial, sans-serif; letter-spacing: 0; font-style: normal; }
.tab-lens .tab .tab-n { background: #fff; color: var(--c); }
@media (max-width: 600px) {
  #askBtn, .tab-lens [data-id="askBtn"] { gap: 7px; }
  .tabbar .tab { font-size: 13px; letter-spacing: .07em; padding: 0 4px; gap: 4px; }
  .tabbar .tab::before { border-width: 5px; }
  .tab-lens .tab { letter-spacing: .04em; gap: 3px; }
  .tab-lens .tab > span::before { margin-right: .2em; }
}
/* the menu's pull tab (member request, henry; contract 13 keeps it visible): no red block, just a faint 3px grip
   sliver on the chat's left edge, halfway down, with a wide invisible hit area; tap it or swipe in from that edge */
.chat > .menu-pull { position: absolute; left: 0; top: 50%; z-index: 4; width: 3px; height: 44px; margin: -22px 0 0; padding: 0; border: 0;
  border-radius: 0; background: var(--ink-3); opacity: .45; touch-action: none; -webkit-tap-highlight-color: transparent; }
.chat > .menu-pull::before { content: ""; position: absolute; inset: -20px -22px -20px 0; }
.chat > .menu-pull[aria-expanded="true"] { visibility: hidden; }
@media (max-width: 350px) { .tabbar .tab { font-size: 11.5px; letter-spacing: .04em; } #askBtn, .tab-lens [data-id="askBtn"] { gap: 5px; padding: 0 2px; } }

/* Lobby and Arcade open under the bar, at the chat's width; sheets (the change and menu,
   opened from the rooms bar's Menu) still come up over them */
body.tabs-on .plaza, body.tabs-on .arc { top: calc(var(--app-top, 0px) + var(--tabs-y, 0px)); height: calc(var(--app-h) - var(--tabs-y, 0px)); max-width: 760px; margin: 0 auto; }
body.tabs-on .plaza .pz-top { padding-top: 10px; }
/* Landscape (member request, Ethan: "continues to the edge of the screen"): on a phone or tablet held sideways every page
   drops its 760px column and runs edge to edge; bars fill the width, and what people read or tap keeps clear of the
   notch and rounded corners (the side safe areas). */
@media (orientation: landscape) and (pointer: coarse) {
  .chat, body.tabs-on .plaza, body.tabs-on .arc, body.tabs-on .hsp, body.tabs-on .bk, body.tabs-on .evb, body.tabs-on .bath, .plaza { max-width: none; }
  .chat > .messages { padding-left: max(14px, env(safe-area-inset-left)); padding-right: max(14px, env(safe-area-inset-right)); }
  .chat > .composer { padding-left: max(12px, env(safe-area-inset-left)); padding-right: max(12px, env(safe-area-inset-right)); }
  .chat > .here { padding-left: max(10px, env(safe-area-inset-left)); padding-right: max(10px, env(safe-area-inset-right)); }
  .chat > .rooms { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }
  body .chat > .sheet.docked { padding-left: max(14px, env(safe-area-inset-left)); padding-right: max(14px, env(safe-area-inset-right)); }
}
body.tabs-on .arc-top { padding-top: 8px; }
body.tabs-on.in-plaza .scrim, body.tabs-on.in-arcade .scrim, body.tabs-on.in-books .scrim, body.tabs-on.in-board .scrim { z-index: 31; }
body.tabs-on.in-plaza .sheet, body.tabs-on.in-arcade .sheet, body.tabs-on.in-books .sheet, body.tabs-on.in-board .sheet { z-index: 32; }
/* Change is a section too (bug fix, Ethan: "it shouldn't be pulled up over the rest of the UI; format it the same as the
   arcade and the events"): the Change sheet docks under the bar on plain white paper, square, no scrim or slide, and
   no Close (the other cells and CHAT are the way out). app.js openSheet adds .docked + body.ask-on. */
/* Docked, it lives inside .chat (bug fix, Ethan: the Chat cell vanished while typing a change): it fills the column below
   the bar, and it sits a layer above it so nothing can ever slide over them. */
.chat > .sheet.docked { position: absolute; top: var(--tabs-y, 0px); bottom: 0; left: 0; right: 0; height: auto; max-height: none; max-width: none; margin: 0;
  z-index: 2; border-radius: 0; background: #fff; transform: none; transition: none; padding: 16px 14px calc(24px + var(--sab)); }
body.ask-on .chat > .top, body.ask-on .chat > .tabbar { z-index: 3; }
.kb-open .sheet.docked { border-radius: 0; padding-bottom: 16px; }
.sheet.docked .close { display: none; }
body.ask-on #jump { display: none; }

/* the chiron: a news-ticker strip (plaza.js "verse chiron", tap Jesus; plaza.css .pz-verse). A white slant-edged tab
   holds still on the left (Jesus's head in .dizzy-slot, then the reference in board letters); the words scroll past on deep ink. */
.chiron { flex: none; display: flex; height: 48px; background: #2e0b0f; color: #fff; overflow: hidden; }
.chiron > * { height: 48px; }
.chiron-tag { flex: none; display: flex; align-items: center; gap: 10px; padding: 0 26px 0 14px; background: #fff; color: var(--felt); font: 600 21px/1 var(--board); letter-spacing: .14em; text-transform: uppercase; clip-path: polygon(0 0, 100% 0, calc(100% - 13px) 100%, 0 100%); }
.chiron-tag .dizzy-slot { flex: none; display: block; width: 36px; height: 36px; pointer-events: none; }
.chiron-tag .dizzy-slot .mface { display: block; width: 100%; height: 100%; }
.chiron-tag .dizzy-slot .mface svg, .chiron-tag .dizzy-slot .mface img { display: block; width: 100%; height: 100%; }
.chiron-track { flex: 1; min-width: 0; overflow: hidden; display: flex; align-items: center; mask-image: linear-gradient(to right, transparent, #000 14px, #000 calc(100% - 14px), transparent); -webkit-mask-image: linear-gradient(to right, transparent, #000 14px, #000 calc(100% - 14px), transparent); }
.chiron-crawl { display: flex; flex: none; white-space: nowrap; animation: chiron-crawl var(--crawl-s, 20s) linear infinite; will-change: transform; }
.chiron-item { padding: 0 2.5em 0 1.2em; font: 700 18px/1 var(--font); }
.chiron-item::after { content: ""; display: inline-block; width: 8px; height: 8px; margin: 0 0 3px 2.5em; background: var(--felt); vertical-align: middle; }
@keyframes chiron-crawl { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* bug fix: the crawl keeps moving with Reduce Motion on, like the spinning 🅱️; a still chiron reads as broken */
@media (prefers-reduced-motion: reduce) {
  .chiron-crawl { animation-duration: var(--crawl-s, 20s) !important; animation-iteration-count: infinite !important; }
}

/* Events lives in the menu now (member request, Ethan): its row carries the count of events you haven't answered */
.sheet .item .ev-due { display: inline-grid; place-items: center; min-width: 19px; height: 19px; padding: 0 4px; margin-left: 6px; vertical-align: 1px;
  background: #d3121c; color: #fff; font: 800 11px/1 "Libre Franklin", Arial, sans-serif; }

.dz-loop { display: block; width: 100%; height: 100%; animation: dz-loop 1.5s linear infinite; }
/* the loop: the head travels round a small circle while turning over twice, like a coin rolling round a rim */
@keyframes dz-loop {
  from { transform: rotate(0turn) translateY(-3px) rotate(0turn); }
  to { transform: rotate(1turn) translateY(-3px) rotate(1turn); }
}
.dz-over { position: absolute; inset: 0; }
.dz-spin { transform-box: fill-box; transform-origin: center; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

.banner { display: flex; align-items: center; gap: 12px; padding: 10px 14px 10px 18px; background: var(--note); color: var(--note-ink); font-size: 15px; }
.banner .txt { flex: 1; }
.banner .x { background: none; border: 0; color: inherit; opacity: .6; font-size: 15px; padding: 4px 6px; text-decoration: underline; }

.messages { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 8px 14px 6px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.older { text-align: center; color: var(--ink-3); font-size: 14px; padding: 10px 0; min-height: 1px; }
.day { margin: 18px 4px 6px; font-size: 14px; font-weight: 700; color: var(--ink-2); }

.msg { display: flex; flex-direction: column; align-items: flex-start; margin-top: 3px; }
.msg.first { margin-top: 14px; }
.msg .who { font-size: 14px; font-weight: 700; margin: 0 0 3px 2px; color: var(--c, var(--ink-2)); }
@media (prefers-color-scheme: dark) { .msg .who { color: color-mix(in oklab, var(--c, var(--ink-2)) 55%, white); } }
.msg .bubble { max-width: min(80%, 540px); padding: 8px 13px 9px; border-radius: 16px; background: var(--bubble); color: var(--bubble-ink); white-space: pre-wrap; overflow-wrap: anywhere; box-shadow: 0 0 0 1px var(--bubble-edge); }
.msg:not(.own).last .bubble { border-bottom-left-radius: 5px; }
.msg .bubble a { color: inherit; }
.msg .time { font-size: 12.5px; color: var(--ink-3); margin: 3px 4px 0; }
.msg.own { align-items: flex-end; }
.msg.own .bubble { background: var(--red); color: var(--red-ink); box-shadow: none; }
.msg.own.last .bubble { border-bottom-right-radius: 5px; }
.msg.pending .bubble { opacity: .55; }
.msg.failed .bubble { outline: 2px solid var(--danger); }
.msg.failed .time { color: var(--danger); }
/* blood speak: the swapped-in 🅱️ emoji */
/* Mood faces: the sender's Mii head beside every message, pulling the face that fits what they said */
.msg .line { display: flex; align-items: flex-end; gap: 6px; width: 100%; }
.msg.own .line { flex-direction: row-reverse; }
.msg .mface { flex: none; width: 40px; height: 40px; margin-bottom: -4px; }
.msg .mface img, .msg .mface svg { display: block; width: 100%; height: 100%; }
.msg .mface.flat svg { padding: 3px; }
/* Whole-figure cutouts (Ethan's Henceforth Prior): the drawing's own face, ink only; light ink on dark */
@media (prefers-color-scheme: dark) { .mface.cutout img { filter: invert(1); } }
.msg:not(.own) .who { margin-left: 48px; }
.msg:not(.own) .time { margin-left: 50px; }
.msg.own .time { margin-right: 50px; }
.msg .bubble .blood { font-style: normal; font-weight: 400; margin-right: .03em; }
.msg.own .bubble .blood { filter: drop-shadow(0 0 1px #fff); } /* the red 🅱️ keeps its edge on a red bubble */
.msg .bubble .grawlix { font-family: "Comic Sans MS", "Comic Neue", "Chalkboard SE", "Comic Sans", cursive; font-weight: 700; font-style: normal; letter-spacing: .02em; white-space: nowrap; }
.msg .bubble.big, .msg.own .bubble.big { font-size: 44px; line-height: 1.1; background: none; color: var(--ink); box-shadow: none; padding: 0 2px; }

/* Simlish invisible ink (member request): henry's bubbles show gibberish under a shimmering speckle
   veil; rubbing or holding the bubble (public/app.js) reveals the real text for a few seconds, like
   iMessage's invisible ink. Real and fake text share one grid cell so the crossfade doesn't reflow. */
/* German rule (member request): translated bubbles flip to the original on tap; Charlie's non-German
   messages get a rubber "Abgelehnt" stamp slammed on them before the text goes back to the composer. */
.msg .bubble.tr { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.msg.rejected .bubble { position: relative; opacity: 1; animation: reject-fade .5s ease 1s forwards; }
.msg.rejected .bubble::after {
  content: attr(data-stamp); position: absolute; left: 50%; top: 50%; z-index: 1;
  padding: 1px 10px 2px; border: 3px solid var(--felt); border-radius: 4px; background: #fff; color: var(--felt);
  font: 600 22px/1.1 var(--board); letter-spacing: .08em; text-transform: uppercase; white-space: nowrap;
  transform: translate(-50%, -50%) rotate(-9deg); animation: stamp-slam .22s cubic-bezier(.2, 1.6, .5, 1) both;
}
.msg.rejected .time { color: var(--felt); font-weight: 700; }
@keyframes stamp-slam { from { opacity: 0; transform: translate(-50%, -50%) rotate(-16deg) scale(2.2); } }
@keyframes reject-fade { to { opacity: .25; transform: translateY(4px); } }
.msg .bubble.ink { display: inline-grid; touch-action: pan-y; -webkit-user-select: none; user-select: none; }
.msg .bubble.ink > span { grid-area: 1 / 1; }
.msg .bubble.ink .ink-fake, .msg .bubble.ink .ink-real { transition: opacity .35s ease; }
.msg .bubble.ink .ink-real { opacity: 0; }
.msg .bubble.ink.revealed .ink-fake { opacity: 0; }
.msg .bubble.ink.revealed .ink-real { opacity: 1; }
.msg .bubble.ink .ink-veil {
  border-radius: inherit; pointer-events: none; opacity: .5; transition: opacity .3s ease;
  background-image: radial-gradient(circle, color-mix(in oklab, var(--bubble-ink) 60%, transparent) 1px, transparent 1.5px);
  background-size: 7px 7px;
  animation: ink-shimmer 2.4s linear infinite;
}
.msg.own .bubble.ink .ink-veil { background-image: radial-gradient(circle, color-mix(in oklab, var(--red-ink) 65%, transparent) 1px, transparent 1.5px); }
.msg .bubble.ink.revealed .ink-veil { opacity: 0; }
@keyframes ink-shimmer { from { background-position: 0 0; } to { background-position: 14px -10px; } }

.sys { margin: 12px 4px; font-size: 14.5px; color: var(--ink-2); overflow-wrap: anywhere; }
.sys q { font-style: italic; color: var(--ink); quotes: "“" "”"; }
.gba-add { display: flex; align-items: center; gap: 12px; margin: 12px 4px; padding: 10px 12px; background: var(--paper); border: 1px solid var(--rule); border-radius: 10px; }
.gba-add .txt { flex: 1; min-width: 0; font-size: 15px; color: var(--ink-2); overflow-wrap: anywhere; }
.gba-add .txt b { color: var(--ink); }
.gba-add .btn { text-decoration: none; flex: none; }
.gba-mini { flex: none; position: relative; width: 46px; height: 29px; background: #4a4446; border-radius: 4px 4px 2px 2px; clip-path: polygon(0 12%, 8% 12%, 8% 0, 92% 0, 92% 12%, 100% 12%, 100% 100%, 0 100%); }
.gba-mini span { position: absolute; inset: 22% 9% 14%; background: #fff; border-radius: 2px; font: 600 10px/1 var(--board); letter-spacing: .08em; color: var(--felt); padding: 3px 4px; }

/* replies from Lobby: a note from the front desk */
.note { margin: 14px 0; max-width: min(88%, 560px); background: var(--note); color: var(--note-ink); border-radius: 4px; padding: 10px 14px 11px; box-shadow: 0 0 0 1px #d3121c26; }
.note .tag { display: flex; align-items: baseline; gap: 8px; margin-bottom: 3px; }
.note .tag b { font: 600 14px/1 var(--board); letter-spacing: .14em; text-transform: uppercase; background: var(--felt); color: var(--letter); padding: 3px 6px 2px; border-radius: 2px; }
.note .tag span { font-size: 13.5px; opacity: .75; }
.note .body { white-space: pre-wrap; overflow-wrap: anywhere; }
/* "Bugs wanted" poster (member request: advertise that bug fixes earn stars): a pale note under a strip of felt letter tiles. */
.bounty { margin: 16px 0; max-width: min(88%, 420px); background: var(--note); color: var(--note-ink); border-radius: 4px; padding: 0 0 12px; box-shadow: 0 0 0 1px #d3121c26; overflow: hidden; }
.bounty-board { display: flex; flex-wrap: wrap; gap: 3px; padding: 9px 12px; background: var(--felt); }
.bounty-board i { font: 600 17px/1 var(--board); font-style: normal; color: var(--letter); width: 15px; padding: 4px 0 3px; text-align: center; background: #ffffff14; border-radius: 2px; }
.bounty-board i.sp { background: none; width: 8px; }
.bounty-body { display: flex; gap: 12px; align-items: flex-start; padding: 12px 14px 10px; }
.bounty-body .t1 { font-weight: 700; }
.bounty-body .sub2 { font-size: 14.5px; opacity: .8; margin-top: 2px; }
.bounty-reward { position: relative; flex: none; width: 46px; height: 46px; transform: rotate(-8deg); }
.bounty-reward svg { width: 100%; height: 100%; fill: var(--red); stroke: var(--red); stroke-width: 1.5; stroke-linejoin: round; }
.bounty-reward b { position: absolute; inset: 0; display: grid; place-items: center; padding-top: 3px; font: 600 15px/1 var(--board); color: var(--red-ink); }
.bounty > .btn { margin-left: 72px; }
.bounty-paid { display: flex; align-items: center; gap: 8px; }
.bounty-paid .grow { flex: 1; min-width: 0; }
.bounty-paid b { color: var(--ink); }
.bp-star { flex: none; width: 18px; height: 18px; }
.bp-star svg { width: 100%; height: 100%; fill: var(--red); }
.bp-go { flex: none; border: 1.5px solid var(--rule); background: var(--paper); color: var(--red); font-weight: 700; font-size: 14px; border-radius: 10px; padding: 6px 10px; min-height: 36px; }
.bug-chip { display: inline-flex; align-items: center; gap: 7px; margin: 0 0 10px; border: 1.5px dashed var(--red); background: transparent; color: var(--red); font-weight: 700; font-size: 15px; border-radius: 10px; padding: 8px 12px; min-height: 40px; }
.bug-chip span { display: block; width: 18px; height: 18px; }
.bug-chip svg { width: 100%; height: 100%; fill: var(--red); }
.bug-chip[hidden] { display: none; }
.note.failed { background: transparent; border: 1.5px dashed var(--rule); color: var(--ink); box-shadow: none; }

.jump { position: absolute; right: 14px; bottom: calc(80px + var(--sab)); border: 0; background: var(--ink); color: var(--wall); border-radius: 99px; padding: 8px 14px; font-size: 14px; font-weight: 700; z-index: 4; }
.typing { height: 20px; padding: 0 18px; font-size: 13.5px; color: var(--ink-3); font-style: italic; }

.composer { display: flex; align-items: flex-end; gap: 8px; padding: 8px 12px calc(10px + var(--sab)); background: transparent; }
.composer textarea { flex: 1; resize: none; max-height: 150px; min-height: 44px; padding: 10px 15px; border-radius: 22px; border: 1.5px solid var(--rule); background: var(--paper); outline: none; line-height: 1.35; }
.composer textarea:focus { border-color: var(--red); }
.send { flex: none; width: 44px; height: 44px; border-radius: 50%; border: 0; background: var(--red); color: var(--red-ink); display: grid; place-items: center; }
.send svg { width: 20px; height: 20px; }

/* ---------- sheets ---------- */
.scrim { position: fixed; inset: 0; background: #2e0b0f80; z-index: 20; opacity: 0; transition: opacity .2s; }
.scrim.open { opacity: 1; }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 21; max-width: 760px; margin: 0 auto; max-height: calc(var(--app-h) - 28px - env(safe-area-inset-top)); overflow-y: auto; background: var(--wall-2); border-radius: 20px 20px 0 0; padding: 18px 18px calc(24px + var(--sab)); transform: translateY(100%); transition: transform .26s cubic-bezier(.2, .8, .2, 1); }
.sheet.open { transform: none; }
.sheet-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.sheet h2 { margin: 0; font-size: 22px; line-height: 1.2; }
.sheet h3 { margin: 26px 0 8px; font-size: 16px; }
.sheet .lede { color: var(--ink-2); margin: 6px 0 16px; max-width: 60ch; }
.close { flex: none; background: none; border: 0; color: var(--ink-2); font-size: 15px; font-weight: 700; padding: 6px 2px; text-decoration: underline; }
.muted { color: var(--ink-3); font-size: 15px; }

.ask textarea { width: 100%; min-height: 110px; resize: vertical; padding: 12px 14px; border-radius: 10px; border: 1.5px solid var(--rule); background: var(--paper); outline: none; }
.ask textarea:focus { border-color: var(--red); }
.ask .row { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 10px; }
.ask .row .muted { font-size: 14px; }

/* Stars (member request): a change costs one. Lit stars are the felt red, spent ones are just an outline. */
.stars-line { display: flex; align-items: center; gap: 14px; margin: 2px 0 14px; }
.stars-line .t1 { font-weight: 700; }
.stars-line .sub2 { color: var(--ink-3); font-size: 14px; }
.stars-row { display: flex; gap: 3px; flex: none; }
.stars-row i { display: block; width: 26px; height: 26px; }
.stars-row svg { width: 100%; height: 100%; fill: none; stroke: var(--rule); stroke-width: 2; stroke-linejoin: round; }
.stars-row i.on svg { fill: var(--red); stroke: var(--red); }
/* Star rules (member request, henry): the prices on a felt letterboard, one row per thing, word tiles on the left,
   a dotted leader, the amount in tiles with a star on the right. Earning rows show their amount on white tiles. */
.linkish { border: 0; background: none; padding: 0; color: var(--red); font: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.sr-board { display: grid; gap: 9px; padding: 14px 14px 15px; border-radius: 4px; background-color: var(--felt);
  background-image: repeating-linear-gradient(to bottom, transparent 0 7px, var(--felt-groove) 7px 9px); }
.sr-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.sr-what, .sr-k { display: flex; gap: 2px; flex-wrap: nowrap; }
.sr-what { min-width: 0; overflow: hidden; }
.sr-board i { font: 600 18px/1 var(--board); font-style: normal; color: var(--letter); width: 13px; padding: 5px 0 4px; text-align: center;
  background: #ffffff14; border-radius: 2px; text-shadow: 0 1px 0 #0008; }
.sr-board i.sp { background: none; width: 7px; }
.sr-dots { flex: 1; min-width: 12px; height: 3px; background: radial-gradient(circle, #ffffff70 1.2px, transparent 1.6px) 0 50% / 7px 3px repeat-x; }
.sr-k { flex: none; align-items: center; }
.sr-k.plus i { background: #fff; color: var(--felt); text-shadow: none; }
.sr-k b { display: block; width: 20px; height: 20px; margin-left: 4px; }
.sr-k b svg { width: 100%; height: 100%; fill: var(--letter); }
.sr-rules { margin: 18px 0 0; padding: 0; list-style: none; max-width: 60ch; }
.sr-rules li { position: relative; padding: 9px 0 9px 22px; border-top: 1px solid var(--rule); }
.sr-rules li:first-child { border-top: 0; }
.sr-rules li::before { content: ""; position: absolute; left: 3px; top: 17px; width: 7px; height: 7px; background: var(--felt); }
.sr-sub { margin: 0 0 12px; color: var(--ink-2); font-size: 15px; }
.sr-badges { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px 8px; margin: 0; padding: 0; list-style: none; }
.sr-badges li { display: flex; flex-direction: column; align-items: center; gap: 5px; font-size: 14px; color: var(--ink-3); }
.sr-medal { display: grid; place-items: center; width: 48px; height: 48px; clip-path: polygon(30% 0, 70% 0, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0 70%, 0 30%);
  background: var(--rule); color: var(--ink-3); font: 600 22px/1 var(--board); }
.sr-badges li.on { color: var(--ink); font-weight: 700; }
.sr-badges li.on .sr-medal { background: var(--felt); }
.sr-medal svg { width: 26px; height: 26px; fill: var(--letter); }
.sr-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.sr-go { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 22px; }
.sr-go .btn { text-decoration: none; }

.jobs { list-style: none; margin: 0; padding: 0; }
.job { padding: 12px 0; border-top: 1px solid var(--rule); }
.job:first-child { border-top: 0; }
.job .head { display: flex; align-items: baseline; gap: 8px; font-size: 15px; }
.job .head b { font-weight: 700; }
.job .head .status { color: var(--ink-2); }
.job .head .sp { flex: 1; }
.job .prompt { margin-top: 3px; white-space: pre-wrap; overflow-wrap: anywhere; }
.job .act { margin-top: 5px; font-size: 14.5px; color: var(--ink-2); font-style: italic; }
.job .reply { margin-top: 8px; padding: 7px 11px; background: var(--note); color: var(--note-ink); border-radius: 4px; font-size: 15.5px; }
.job.running .status { color: var(--ink); font-weight: 700; }
.job.running .status::before { content: ""; display: inline-block; width: 12px; height: 12px; margin-right: 6px; vertical-align: -1px; border-radius: 50%; border: 2px solid var(--rule); border-top-color: var(--ink); animation: spin .9s linear infinite; }
.job.failed .status { color: var(--danger); }

.working { display: flex; gap: 16px; align-items: center; padding: 18px 16px; margin-top: 4px; background: var(--felt); color: var(--letter); border-radius: 12px; }
.working .spinner { flex: none; width: 44px; height: 44px; border-radius: 50%; border: 4px solid #ffffff33; border-top-color: var(--signal); border-right-color: var(--signal); animation: spin 1s linear infinite; }
.working .t1 { font-weight: 700; font-size: 18px; }
.working .t2 { font-size: 15px; opacity: .8; margin-top: 2px; }
.working .t3 { font-size: 14.5px; opacity: .7; margin-top: 4px; font-style: italic; min-height: 1.3em; }
.working .actions { display: flex; gap: 14px; margin-top: 10px; }
.working .actions button { background: none; border: 0; color: var(--letter); padding: 0; font-size: 15px; text-decoration: underline; opacity: .85; }

.reply-card { margin-top: 4px; background: var(--note); color: var(--note-ink); border-radius: 4px; padding: 16px 18px 18px; }
.reply-card .t1 { font-weight: 700; font-size: 18px; margin-bottom: 6px; }
.reply-card .body { white-space: pre-wrap; margin-bottom: 14px; }
.reply-card.failed { background: var(--paper); color: var(--ink); border: 1.5px dashed var(--rule); }

.group { background: var(--paper); border-radius: 12px; overflow: hidden; }
.item { display: flex; align-items: center; gap: 12px; width: 100%; padding: 13px 16px; border: 0; border-top: 1px solid var(--rule); background: none; text-align: left; text-decoration: none; color: var(--ink); }
.item:first-child { border-top: 0; }
.item .sub2 { color: var(--ink-2); font-size: 14.5px; }
.item .grow { flex: 1; min-width: 0; }
.people { display: flex; flex-wrap: wrap; gap: 6px 16px; }
.person { display: flex; align-items: center; gap: 7px; font-weight: 700; color: var(--c, var(--ink)); }
@media (prefers-color-scheme: dark) { .person { color: color-mix(in oklab, var(--c, var(--ink)) 55%, white); } }
.person .d { width: 9px; height: 9px; border-radius: 50%; border: 1.5px solid var(--ink-3); }
.person .d.on { background: var(--ok); border-color: var(--ok); }
.invite-box { display: flex; gap: 8px; padding: 0 16px 14px; }
.invite-box input { flex: 1; min-width: 0; padding: 9px 11px; border-radius: 8px; border: 1.5px solid var(--rule); background: var(--wall-2); font-size: 15px; }

.toast { position: fixed; left: 50%; top: calc(14px + env(safe-area-inset-top)); transform: translate(-50%, -160%); background: var(--ink); color: var(--wall); padding: 10px 16px; border-radius: 10px; z-index: 40; transition: transform .3s; max-width: calc(100% - 32px); font-size: 15px; font-weight: 700; }
.toast.show { transform: translate(-50%, 0); }

/* the menu drawer (member request, henry: "restore the menu as a left side slideout"): full height from the left
   edge, square with a hard edge, the felt letterboard with 🅱️CHAT across its top (the board's safe area included) */
.sheet.drawer { top: var(--app-top, 0px); bottom: auto; right: auto; height: var(--app-h); max-height: none; width: min(88vw, 380px); max-width: none;
  margin: 0; border-radius: 0; padding: 0 16px calc(24px + var(--sab)); box-shadow: 1px 0 0 var(--rule); transform: translateX(-100%); }
.sheet.drawer.open { transform: none; }
.kb-open .sheet.drawer { border-radius: 0; }
.drawer-board { position: sticky; top: 0; z-index: 1; margin: 0 -16px 14px; padding: calc(10px + env(safe-area-inset-top)) 14px 10px 16px; background: var(--felt); color: var(--letter); }
.sheet .drawer-board h2 { font: 600 30px/1 var(--board); letter-spacing: .12em; text-transform: uppercase; }
.drawer-board .close { color: var(--letter); }
.drawer-board :focus-visible { outline-color: var(--letter); }
@media (prefers-reduced-motion: reduce) { .sheet.drawer { transition: none; } }

/* keyboard open (iOS): the sheet rides above the keyboard, and home-bar padding goes away */
.sheet { bottom: var(--kb, 0px); overscroll-behavior: contain; }
.kb-open .composer { padding-bottom: 8px; }
.kb-open .sheet { padding-bottom: 16px; border-radius: 20px; }

@media (min-width: 761px) { .chat { box-shadow: 0 0 0 1px var(--rule); } }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0s !important; animation-iteration-count: 1 !important; transition-duration: 0s !important; }
  .dz-loop { animation-duration: 1.6s !important; animation-iteration-count: infinite !important; }
  .dz-spin { animation-duration: .9s !important; animation-iteration-count: infinite !important; }
}
.confetti { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 50; }

/* @mentions (member request): a member's name in a bubble is bold in their name color; your own name (or @everyone)
   is a small red felt tile, and the whole bubble gets a red edge so you spot it scrolling past. */
.msg .bubble .mention { font-weight: 700; color: var(--mc, inherit); white-space: nowrap; }
.msg .bubble .mention.me { color: #fff; background: var(--felt); border-radius: 4px; padding: 0 .22em; margin: 0 -.05em; box-decoration-break: clone; -webkit-box-decoration-break: clone; }
.msg.own .bubble .mention { color: inherit; text-decoration: underline 2px; text-underline-offset: .18em; }
.msg.own .bubble .mention.me { color: var(--felt); background: #fff; text-decoration: none; }
.msg.at-me:not(.own) .bubble:not(.big) { box-shadow: 0 0 0 2px var(--red); }

/* The picker: members float over the bottom of the chat while you type "@". Best match first, Bii face and name. */
.mention-bar { position: absolute; left: 0; right: 0; bottom: 100%; display: flex; gap: 6px; padding: 0 12px 4px; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.mention-bar::-webkit-scrollbar { display: none; }
.mention-bar[hidden] { display: none; }
.mpick { flex: none; display: flex; align-items: center; gap: 7px; min-height: 44px; padding: 3px 14px 3px 4px; border: 0; background: var(--paper); color: var(--c, var(--ink)); font: inherit; font-size: 16px; font-weight: 700; -webkit-tap-highlight-color: transparent; }
.mpick:first-child { background: var(--felt); color: #fff; }
.mpick .mp-face { width: 36px; height: 36px; display: grid; place-items: center; }
.mpick .mp-face .mface, .mpick .mp-face img, .mpick .mp-face svg { display: block; width: 100%; height: 100%; }
.mpick.all .mp-face { font-size: 24px; line-height: 1; color: var(--red); }
.mpick.all:first-child .mp-face { color: #fff; }
.mpick:not(.here):not(.all) .mp-face { opacity: .6; } /* away: their Bii is asleep */
@media (prefers-color-scheme: dark) { .mpick:not(:first-child) { color: color-mix(in oklab, var(--c, var(--ink)) 55%, white); } }

/* ---------- replies (member request, Ethan: "swiping right on someone's message allows you to reply to it") ----------
   Swipe a message right: it follows the finger and a hard square reply tile slides in from the edge, pale while you
   pull, felt red once letting go would start the reply. A reply carries the quote as a pale slip in the original
   author's colour, tucked behind the top of its bubble: their Bii head, name and one line of their words (or a photo
   thumbnail). The composer shows the same slip above the field while you write, with an x to drop it. */
#list .msg[data-id] { touch-action: pan-y; }
.msg.swiping, .msg.swipe-back { position: relative; transform: translateX(var(--sx, 0px)); }
.msg.swipe-back { transition: transform .24s cubic-bezier(.25, 1.2, .4, 1); }
.swipe-arrow {
  position: absolute; left: -46px; top: 50%; width: 34px; height: 34px; margin-top: -17px; display: grid; place-items: center;
  background: var(--note); color: var(--red); opacity: var(--sk, 0); transform: scale(calc(.5 + .5 * var(--sk, 0)));
  transition: background-color .12s, color .12s; pointer-events: none;
}
.swipe-arrow svg { width: 19px; height: 19px; }
.swipe-arrow.armed { background: var(--felt); color: #fff; }
@media (prefers-reduced-motion: reduce) { .msg.swipe-back { transition: none; } }

.msg.has-rq { margin-top: 12px; }
.msg .line { position: relative; z-index: 1; }
.rq, .rb-q {
  position: relative; display: flex; align-items: center; gap: 7px; min-width: 0; border: 0; text-align: left; cursor: pointer;
  background: color-mix(in oklab, var(--rc, var(--ink-3)) 11%, #fff); color: var(--ink-2); font: 14px/1.3 var(--font);
  -webkit-tap-highlight-color: transparent;
}
.rq::before, .rb-q::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--rc, var(--ink-3)); }
.msg .rq { max-width: min(76%, 440px); margin: 0 0 -8px 46px; padding: 5px 10px 13px 9px; }
.msg .rq::before { bottom: 8px; }
.msg.own .rq { margin: 0 46px -8px 0; }
.msg.has-rq .who + .rq { margin-top: 1px; }
.rq-face { flex: none; width: 24px; height: 24px; }
.rq-face .mface { display: block; width: 100%; height: 100%; margin: 0; }
.rq-face .mface img, .rq-face .mface svg { display: block; width: 100%; height: 100%; }
.rq-txt { display: flex; flex-direction: column; min-width: 0; }
.rq-txt b { font-size: 13px; font-weight: 700; color: color-mix(in oklab, var(--rc, var(--ink-2)) 80%, #000); }
.rq-txt > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rq-ph { flex: none; width: 34px; height: 34px; object-fit: cover; margin-left: auto; }
.msg.rq-ring .bubble, .msg.rq-ring .ph-bub { animation: rq-ring 1.7s ease both; }
@keyframes rq-ring { 0%, 55% { outline: 3px solid var(--ink); outline-offset: 3px; } 100% { outline: 3px solid transparent; outline-offset: 3px; } }

.composer.replying { flex-wrap: wrap; row-gap: 6px; }
.reply-bar { order: -1; flex: 0 0 100%; display: flex; min-width: 0; }
.reply-bar[hidden] { display: none; }
.rb-q { flex: 1; padding: 6px 10px 6px 12px; min-height: 44px; }
.rb-q .rq-face { width: 30px; height: 30px; }
.rb-to { font-size: 13px; }
.rb-to b { color: color-mix(in oklab, var(--rc, var(--ink-2)) 80%, #000); }
.rb-x { flex: none; width: 44px; border: 0; display: grid; place-items: center; background: color-mix(in oklab, var(--rc, var(--ink-3)) 11%, #fff); color: var(--ink-2); cursor: pointer; }
.rb-x svg { width: 18px; height: 18px; }
