/* Holli Bot — dark, iOS-style UI modeled on Grok Bot. */

/* Holli Bot's font, code and the terminal included: Satoshi (Indian Type
   Foundry, from Fontshare), on a phone, a tablet and a computer alike (--font,
   below). It's served with the app, so it works offline and from Holli Bot
   Computer too (vendor/fonts, with its license): one variable file per style,
   every weight from 300 to 900. */
@font-face { font-family: 'Satoshi'; src: url('vendor/fonts/satoshi/Satoshi-Variable.woff2') format('woff2'); font-weight: 300 900; font-style: normal; font-display: block; }
@font-face { font-family: 'Satoshi'; src: url('vendor/fonts/satoshi/Satoshi-VariableItalic.woff2') format('woff2'); font-weight: 300 900; font-style: italic; font-display: block; }

:root {
  --bg: #121212;
  --surface: #1b1b1b;
  --surface-2: #232323;
  --surface-3: #2c2c2e;
  --elevated: #1e1e1e;
  --border: #2a2a2a;
  --border-strong: #3a3a3c;
  /* Words and icons come in three shades only, in every theme: --text;
     --muted, for what's secondary (hints, notes, times, labels, a row's
     second line, placeholders, quiet icons); and --dim, for chevrons and
     the like, never for words. --text-2 is --text (older rules name it).
     Surfaces that are dark in every theme (an agent's computer card, the
     terminal, a tooltip) use Dark's three: #F2F2F2, #A1A1A6, #5C5C60. */
  --text: #f2f2f2;
  --text-2: var(--text);
  --muted: #a1a1a6;
  --dim: #5c5c60;
  --blue: #0a84ff;
  --green: #30d158;
  --green-dot: #34c759;
  --orange: #f5a623;
  --red: #ff6b6b;
  --red-strong: #ff453a;
  --bot-bubble: #1e1e1e; /* the agents' bubbles: two shades under what they were (#262626), the owner's call */
  --user-bubble: #435a6b;
  --code-bg: #0f0f0f;
  --scrim: rgba(0, 0, 0, 0.55);
  /* A bottom sheet, and a round button on it (its close or back). */
  --sheet: #161616;
  --sheet-btn: #1f1f1f;
  /* What floats over the page: menus and dialogs. */
  --raised: #1c1c1e;
  --raised-line: #2c2c2e;
  /* A switch that's off. */
  --track: #4a4a4f;
  --radius-sheet: 34px;
  --radius-card: 22px;
  --tap: 44px;
  --font: "Satoshi", sans-serif;
  /* Words come in three sizes only, on every screen (phone, iPhone, iPad,
     the web, Mac and Windows), so the same kind of words is the same size
     everywhere; only the logo, and your name at the foot of the sidebar
     (14 px), are sizes of their own:
       --fs-sm  what's secondary: a row's second line, previews, times,
                hints, notes, labels, badges, chips, small buttons, code;
       --fs-md  everything else: messages, a row's name and value, buttons,
                menus, tabs, boxes, every top bar's title (Medium), and the
                headings within a page, card or article (Bold);
       --fs-lg  a page's big title, the name at the top of a profile, and
                big numbers and prices.
     And in three weights only: 450 Regular (reading, and what's secondary),
     500 Medium (names, labels, buttons, every top bar's title) and 700 Bold
     (headings, big numbers). */
  --fs-sm: 13px;
  --fs-md: 15px;
  --fs-lg: 24px;
  --safe-top: env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
  --safe-left: env(safe-area-inset-left, 0px);
  --safe-right: env(safe-area-inset-right, 0px);
  color-scheme: dark;
}

/* ---------- Holli Bot for Mac's own window (desktop/src/app-window.js) ---------- */
/* The window has no title bar of the Mac's: the app goes up to its top edge,
   in the app's own colors, and its top 32px, where the window's close,
   minimize and full screen buttons are, is room left as for a phone's status
   bar (--safe-top: every bar at the top, sheet, page and toast leaves it). The
   room drags the window, and a double-click there zooms it, as a title bar's
   would (.mac-titlebar, index.html). The bars at the top of the sidebar and
   a chat don't leave it: they go up into it, beside the window's buttons,
   their own buttons' tops level with theirs (10 px down: --desk-top), as in
   Grok. Their buttons (search, +, the chat's name, the computer button and ⋯)
   are cut out of the room (no-drag), so they take clicks, and the rest of it
   still drags. The room comes first in the page and is painted under it
   (z-index -1), so the cut-outs come after it whichever way Chromium lists
   the regions. Full screen, the buttons go, and so does the room. */
:root.mac-app { --safe-top: 32px; }
:root.mac-app.mac-fullscreen { --safe-top: 0px; }
:root.mac-app:not(.mac-fullscreen) { --desk-top: 10px; }
.mac-titlebar { display: none; }
.mac-app .mac-titlebar { display: block; position: fixed; top: 0; left: 0; right: 0; height: var(--safe-top); z-index: -1; -webkit-app-region: drag; app-region: drag; }
.mac-app :is(.chat-topbar, .side-top) button { -webkit-app-region: no-drag; app-region: no-drag; }

/* Dark is ChatGPT's dark: the page, the bot list and a chat #212121, the
   menu under the list #171717 (--drawer-backdrop, below), bots' bubbles
   #303030, and what sits on the page a step lighter, all plain grays. */
:root[data-theme="dark"] {
  --bg: #212121;
  --surface: #2a2a2a;
  --surface-2: #303030;
  --surface-3: #3c3c3c;
  --elevated: #2d2d2d;
  --border: #383838;
  --bot-bubble: #303030;
  --user-bubble: #435a6b;
  --sheet: #262626;
  --sheet-btn: #333333;
  --raised: #2d2d2d;
  --raised-line: #3a3a3a;
  --track: #555555;
}

/* Light is Deep Teal, the app's own look (everyone starts on it: src/core/
   app.js), on ChatGPT's white: a white page (#FFFFFF) and deep teal text
   (#14343A); teal (#087E8B) for what's tapped, on or new: buttons, links,
   switches, the send button, unread; and amber (#E9A23B), sparingly, for
   what's waiting on you and for a highlight or two. Its grays are the text's
   teal mixed into white, and its surfaces white, outlined where they sit on
   the page: a light gray bubble for bots (ChatGPT's #F4F4F4, so it shows on
   the white) and a deep teal one (with white text) for you. Amber as words
   is a darker amber (--amber-ink), so it reads on the page. */
:root[data-theme="light"] {
  --teal: #087e8b;
  --teal-deep: #075c66;
  --amber: #e9a23b;
  --amber-ink: #9a5e08;
  --bg: #ffffff;
  --surface: #ffffff;
  --surface-2: #e5ecec;
  --surface-3: #dae3e3;
  --elevated: #ffffff;
  --border: #d8e1e1;
  --border-strong: #c6d2d2;
  --text: #14343a;
  --muted: #465e62;
  --dim: #9aaaac;
  --blue: var(--teal);
  --green: var(--teal);
  --orange: var(--amber);
  /* Apple's red for a light page: it reads on white. */
  --red: #d70015;
  --bot-bubble: #f4f4f4;
  --user-bubble: #14343a;
  --code-bg: #eaf1f0;
  --scrim: rgba(20, 52, 58, 0.32);
  --sheet: #ecf3f2;
  --sheet-btn: #ffffff;
  --raised: #ffffff;
  --raised-line: #d8e1e1;
  --track: #b9c6c7;
  color-scheme: light;
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
/* One background from top to bottom: iOS paints overscroll with the body
   color, and so the status bar in Safari and on the Home Screen (iOS 26 on)
   while nothing fixed is at the top of the page; Android, older iOS and Holli
   Bot for iPhone go by the theme color. Both are the color of what's at the
   top now (--canvas and theme-color: followPage in src/ui/components.js), so
   nothing lighter shows under a phone's bot list or beside the menu as it
   slides. A double tap never zooms the app (manipulation: scrolling and
   pinching only); tapping into a text box doesn't either (index.html). */
html, body { margin: 0; height: 100%; background: var(--canvas, var(--bg)); color: var(--text); font-family: var(--font); font-size: var(--fs-md); font-weight: 450; line-height: 1.35; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; overscroll-behavior: none; touch-action: manipulation; }
/* The status bar while the menu is on screen, in Safari or on the Home Screen
   on an iPhone or iPad (statusBand in src/ui/components.js). There iOS fills
   the status bar itself, over the page, with the color of something fixed
   across the top of the page, and looks again only when something fixed comes
   or goes. This strip is that something: fixed across the top, over
   everything, a little over 10px tall (less, and iOS passes it by) and so
   faint (a tenth and a bit: less is taken for nothing) that it all but
   doesn't show on the page itself; its color is what's under the status bar
   frame by frame, and .status-nudge comes or goes each time it changes, so
   iOS looks again. */
.status-band { position: fixed; top: 0; left: 0; right: 0; height: 12px; z-index: 999; opacity: .11; pointer-events: none; }
.status-nudge { position: fixed; left: 0; bottom: 0; width: 1px; height: 1px; visibility: hidden; pointer-events: none; }
.status-nudge.off { position: absolute; }
body { overflow: hidden; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, textarea, select { font: inherit; color: inherit; }
/* What a box asks for, in every box: the second shade (Safari and Firefox would dim it more). */
::placeholder { color: var(--muted); opacity: 1; }
/* Code, commands and keys are in the app's font too, with numbers of one width so they line up. */
pre, code, kbd, samp { font-family: inherit; font-variant-numeric: tabular-nums; }
a { color: var(--blue); }
/* A link that opens somewhere else, with Lucide's external-link after it. */
.ext-link { display: inline-flex; align-items: center; gap: 5px; }
svg { display: block; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; border-radius: 10px; }

#app { height: 100%; }
.app { height: 100%; display: flex; background: var(--bg); position: relative; overflow: hidden; }
.pane-list { width: 100%; height: 100%; display: flex; flex-direction: column; background: var(--bg); position: relative; touch-action: pan-y; }
.pane-chat { flex: 1; min-width: 0; height: 100%; display: flex; flex-direction: column; background: var(--bg); position: relative; }
/* On a phone a chat slides in from the right over the bot list, and Back
   slides it out to the right (src/ui/app.js useChatSlide), 450 ms each way:
   in on a curve that sets off at once and eases gently into place
   (--chat-in: 7% of the way at 50 ms, half at 150 ms), out on one that sets
   off softly and glides away (--chat-out: 3% at 50 ms, 18% at 100 ms), so
   it neither waits nor shoots. The list holds still under it and darkens
   (black, 42% once it's covered), on the same curves. The chat's left edge
   curves, 55 px (about an iPhone's own corners, so in place they're the
   screen's: --app-curve): the corners outside the curve are filled in the
   list's color, darkened with it (::before and ::after over the chat's
   edge), with no shadow, which would fall on the list but not on the corners
   and show a seam beside them. The chat is a layer of its own all the time,
   and so is the darkening, so nothing is drawn afresh as it moves; only the
   corners are hidden once it's all the way over, and back, darkened as the
   list is, as it sets off again. */
:root { --chat-ms: .45s; --chat-in: cubic-bezier(.32, .08, .24, 1); --chat-out: cubic-bezier(.4, 0, .2, 1); --app-curve: 55px; }
.app.mobile .pane-chat { position: absolute; inset: 0; z-index: 6; transform: translate3d(100%, 0, 0); will-change: transform; backface-visibility: hidden;
  transition: transform var(--chat-ms) var(--chat-out); }
.app.mobile:is(.chat-opening, .chat-in) .pane-chat { transform: none; transition-timing-function: var(--chat-in); }
.app.mobile.chat-enter .pane-chat { transition: none; }
.app.mobile .pane-chat::before, .app.mobile .pane-chat::after { content: ""; position: absolute; top: 0; bottom: 0; left: 0; z-index: 60; width: var(--app-curve);
  border-radius: var(--app-curve) 0 0 var(--app-curve); clip-path: inset(0 0 0 -1px); pointer-events: none; }
.app.mobile .pane-chat::before { box-shadow: 0 0 0 calc(var(--app-curve) / 2) var(--drawer-bg); }
.app.mobile .pane-chat::after { box-shadow: 0 0 0 calc(var(--app-curve) / 2) #000; opacity: 0; will-change: opacity;
  transition: opacity var(--chat-ms) var(--chat-out); }
.app.mobile:is(.chat-opening, .chat-in) .pane-chat::after { opacity: .42; transition-timing-function: var(--chat-in); }
.app.mobile.chat-in .pane-chat::before, .app.mobile.chat-in .pane-chat::after { visibility: hidden; }
.app.mobile .pane-list::before { content: ""; position: absolute; inset: 0; z-index: 5; background: #000; opacity: 0; pointer-events: none; will-change: opacity;
  transition: opacity var(--chat-ms) var(--chat-out); }
.app.mobile:is(.chat-opening, .chat-in) .pane-list::before { opacity: .42; transition-timing-function: var(--chat-in); }
@media (prefers-reduced-motion: reduce) {
  .app.mobile .pane-chat, .app.mobile .pane-chat::after, .app.mobile .pane-list::before { transition: none !important; }
}
/* A chat's messages still on their way say so only after a moment: most
   come before, and a word that flashes up and goes is only a flicker. */
.notice.loading { animation: late-in .25s ease .8s both; }
@keyframes late-in { from { opacity: 0; } to { opacity: 1; } }
/* On a phone the bot list is the drawer's panel, the card that slides over to
   the right as the menu comes out: it's in the panel's color (--drawer-bg,
   with the menu's, below), and so is its fade; a chat stays on the page's. */
.app.mobile:not(.in-chat), .app.mobile .pane-list { --bg: var(--drawer-bg); }
/* In Light that's white, and what's on it is in the drawer's colors: its
   boxes white like its buttons (--drawer-button-bg), with a hairline of
   the navy (--drawer-line), and a gray box, or a row as it's pressed, in the
   buttons' pressed gray (--drawer-button-press), which shows on the white
   where white wouldn't. Its words are the app's own two shades, and Light's
   own teal, red and amber read on it. So the
   search box, the note about the computer and an empty list are in its
   colors too. The + menu, which opens over it, stays Light's. */
:root[data-theme="light"] .app.mobile .pane-list :is(.topbar, .home-scroll) {
  --surface: var(--drawer-button-bg); --surface-2: var(--drawer-button-press); --surface-3: var(--drawer-button-press); --border: var(--drawer-line);
  color: var(--text);
}
:root[data-theme="light"] .app.mobile .row-bot:active { background: var(--surface-2); }
.app.wide .pane-list { width: 280px; flex: none; border-right: 1px solid var(--border); }
.app.wide .pane-chat.empty { display: flex; align-items: center; justify-content: center; color: var(--muted); }

/* ---------- avatars ---------- */
.avatar { position: relative; display: inline-flex; flex: none; }
.avatar svg { overflow: visible; }
.avatar-eye { transition: transform 420ms cubic-bezier(.3, 1.4, .5, 1); }
/* A blink shuts and opens quickly; the eyes glide again after it. */
.avatar.blinking .avatar-eye { transition: transform 90ms ease-in-out; }
/* Thinking animations — one style per bot (see THINKING in src/ui/avatar.js) */
.avatar .av-body { transform-box: view-box; transform-origin: 50px 50px; }
.avatar.is-thinking.think-ponder .av-body { animation: av-ponder 2.4s ease-in-out infinite; transform-origin: 50px 92px; }
.avatar.is-thinking.think-hop .av-body { animation: av-hop .9s cubic-bezier(.3, .7, .4, 1) infinite; transform-origin: 50px 96px; }
.avatar.is-thinking.think-jelly .av-body { animation: av-jelly 1.2s ease-in-out infinite; transform-origin: 50px 92px; }
.avatar.is-thinking.think-orbit .av-body,
.avatar.is-thinking.think-scan .av-body { animation: av-breathe 2.4s ease-in-out infinite; }
.avatar.is-thinking.think-sparkle .av-body { animation: av-breathe 1.5s ease-in-out infinite; }
.avatar.is-thinking.think-float .av-body { animation: av-float 2s ease-in-out infinite; }
.avatar.is-thinking.think-nod .av-body { animation: av-nod .8s ease-in-out infinite; transform-origin: 50px 95px; }
.avatar.is-thinking.think-twirl .av-body { animation: av-twirl 2.2s cubic-bezier(.55, 0, .3, 1) infinite; }
/* Doing a task (every bot the same): a busy rock. */
.avatar.is-working .av-body { animation: av-work .9s ease-in-out infinite; transform-origin: 50px 95px; }
@keyframes av-work {
  0%, 100% { transform: rotate(-4deg) translateY(0); }
  25% { transform: rotate(0deg) translateY(-3px); }
  50% { transform: rotate(4deg) translateY(0); }
  75% { transform: rotate(0deg) translateY(-3px); }
}
/* Idle (a `live` avatar that isn't busy): a slow, gentle bob. */
.avatar.is-live .av-body { animation: av-idle 3.6s ease-in-out infinite; transform-origin: 50px 92px; }
@keyframes av-idle { 0%, 100% { transform: translateY(0) scale(1, 1); } 50% { transform: translateY(-3.5px) scale(1.015, 1.03); } }
.av-thought { transform-box: fill-box; transform-origin: center; opacity: 0; animation: av-thought 2.1s ease-in-out infinite; }
.av-thought.t2 { animation-delay: .35s; }
.av-thought.t3 { animation-delay: .7s; }
.av-orbit { transform-box: view-box; transform-origin: 50px 50px; animation: av-spin 2.4s linear infinite; }
.av-shine { animation: av-shine 1.6s ease-in-out infinite; }
.av-star { transform-box: fill-box; transform-origin: center; opacity: 0; animation: av-twinkle 1.5s ease-in-out infinite; }
.av-star.s2 { animation-delay: .5s; }
.av-star.s3 { animation-delay: 1s; }
@keyframes av-ponder { 0%, 100% { transform: rotate(-5deg); } 50% { transform: rotate(5deg); } }
@keyframes av-hop {
  0% { transform: translateY(0) scale(1.08, .92); }
  30% { transform: translateY(-14px) scale(.94, 1.07); }
  50% { transform: translateY(-16px) scale(1, 1); }
  75% { transform: translateY(0) scale(1.1, .9); }
  88% { transform: translateY(0) scale(.97, 1.03); }
  100% { transform: translateY(0) scale(1.08, .92); }
}
@keyframes av-jelly {
  0%, 100% { transform: scale(1, 1); }
  15% { transform: scale(1.12, .88); }
  30% { transform: scale(.9, 1.1); }
  45% { transform: scale(1.06, .94) rotate(-2deg); }
  60% { transform: scale(.97, 1.03) rotate(2deg); }
  75% { transform: scale(1.02, .98); }
}
@keyframes av-breathe { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.04); } }
@keyframes av-float { 0%, 100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-9px) rotate(2deg); } }
@keyframes av-nod { 0%, 100% { transform: translateY(0) scale(1, 1) rotate(0); } 45% { transform: translateY(3px) scale(1.03, .95) rotate(3deg); } }
@keyframes av-twirl { 0% { transform: rotate(0); } 36%, 100% { transform: rotate(360deg); } }
@keyframes av-thought { 0% { opacity: 0; transform: scale(.3); } 20%, 70% { opacity: 1; transform: scale(1); } 90%, 100% { opacity: 0; transform: scale(.8); } }
@keyframes av-spin { to { transform: rotate(360deg); } }
@keyframes av-shine { 0% { transform: translateX(-60px) skewX(-18deg); } 100% { transform: translateX(150px) skewX(-18deg); } }
@keyframes av-twinkle { 0% { opacity: 0; transform: scale(.2) rotate(0); } 40% { opacity: 1; transform: scale(1) rotate(45deg); } 80%, 100% { opacity: 0; transform: scale(.3) rotate(90deg); } }
@media (prefers-reduced-motion: reduce) {
  .avatar .av-body, .av-dot-keep, .av-thought, .av-orbit, .av-shine, .av-star { animation: none !important; }
  .av-thought, .av-star { opacity: 1; }
  /* Still plainly busy without moving about: a thinking or working bot fades gently. */
  .avatar.is-thinking .av-body, .avatar.is-working .av-body { animation: av-fade 1.6s ease-in-out infinite !important; }
}
@keyframes av-fade { 0%, 100% { opacity: 1; } 50% { opacity: .5; } }
@keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5%); } }
/* The status dot, on the shape's own bottom-right edge, in a layer that moves as the body does (src/ui/avatar.js),
   with a ring of the page's color around it. */
.av-dot-ring { fill: var(--bg); }
.av-dot-fill { fill: var(--green-dot); }
.av-dot.dot-working { animation: pulse 1.4s ease-in-out infinite; }
.av-dot.dot-waiting .av-dot-fill { fill: var(--orange); }
.av-dot.dot-error .av-dot-fill { fill: var(--red-strong); }
/* It moves with the body, and where the body squashes and stretches, its own group scales the other way around its
   center so it stays round: each of these is the inverse of the body's animation above (keep them in step). */
.av-dot-keep { transform-box: fill-box; transform-origin: center; }
.avatar.is-thinking.think-hop .av-dot-keep { animation: av-hop-keep .9s cubic-bezier(.3, .7, .4, 1) infinite; }
.avatar.is-thinking.think-jelly .av-dot-keep { animation: av-jelly-keep 1.2s ease-in-out infinite; }
.avatar.is-thinking.think-nod .av-dot-keep { animation: av-nod-keep .8s ease-in-out infinite; }
.avatar.is-thinking.think-orbit .av-dot-keep,
.avatar.is-thinking.think-scan .av-dot-keep { animation: av-breathe-keep 2.4s ease-in-out infinite; }
.avatar.is-thinking.think-sparkle .av-dot-keep { animation: av-breathe-keep 1.5s ease-in-out infinite; }
.avatar.is-live .av-dot-keep { animation: av-idle-keep 3.6s ease-in-out infinite; }
@keyframes av-hop-keep {
  0%, 100% { transform: scale(.926, 1.087); }
  30% { transform: scale(1.064, .935); }
  50% { transform: scale(1, 1); }
  75% { transform: scale(.909, 1.111); }
  88% { transform: scale(1.031, .971); }
}
@keyframes av-jelly-keep {
  0%, 100% { transform: scale(1, 1); }
  15% { transform: scale(.893, 1.136); }
  30% { transform: scale(1.111, .909); }
  45% { transform: scale(.943, 1.064); }
  60% { transform: scale(1.031, .971); }
  75% { transform: scale(.98, 1.02); }
}
@keyframes av-nod-keep { 0%, 100% { transform: scale(1, 1); } 45% { transform: scale(.971, 1.053); } }
@keyframes av-breathe-keep { 0%, 100% { transform: scale(1); } 50% { transform: scale(.962); } }
@keyframes av-idle-keep { 0%, 100% { transform: scale(1, 1); } 50% { transform: scale(.985, .971); } }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.avatar-stack { position: relative; display: inline-block; flex: none; }
.avatar-stack-item { position: absolute; }
.initials { width: var(--tap); height: var(--tap); border-radius: 50%; background: #2a2a2a; color: var(--muted); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-md); font-weight: 500; letter-spacing: .5px; border: 1px solid #333; flex: none; }
:root[data-theme="light"] .initials { background: #dde5e5; color: var(--muted); border-color: #d2dbdb; }

/* ---------- top bars ---------- */
/* The row at the top of the bot list, with the menu, search and +: its
   buttons 12 px below the status bar (--safe-top: in Holli Bot for iPhone the
   page goes up under the status bar, and the menu and the app beside it reach
   the top of the screen), the row as tall as ever (--head-h). */
.topbar { display: flex; align-items: center; gap: 10px; padding: calc(12px + var(--safe-top)) 16px 8px; min-height: calc(64px + var(--safe-top)); flex: none; background: var(--bg); }
.topbar .spacer { flex: 1; }
/* The round buttons (.circle-btn, .top-btn, the plugins' filter, the camera's
   and the gallery's, and a bot computer's grow) are 44 px (grow 34), a finger's width (--tap), on a phone and in
   Holli Bot for iPhone. On a computer (the website, Holli Bot for Windows and
   Mac: the wide layout, src/ui/app.js) they're all 30 px (--round-btn), the
   header's size, each icon scaled down with its button, and what's laid out
   around them follows: a sheet's header and Settings' bar are as much
   shorter, the side columns of Settings' bar as much narrower, the gallery's
   pictures start as much higher, and the plugins' search box and Installed
   are as tall, so the room around each button stays as it was. The menu
   that slides out on the left (--drawer-w) is narrower there too: 360 px. */
@media (min-width: 900px), (max-width: 899.98px) and (hover: hover) and (pointer: fine) {
  :root:not(.ios-app) { --round-btn: 30px; --drawer-w: min(calc(100vw - 85px), 360px); }
}
.circle-btn { width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); border-radius: 50%; background: var(--surface); border: 1px solid var(--border); display: inline-flex; align-items: center; justify-content: center; flex: none; color: var(--text); transition: background .15s, transform .1s; }
.circle-btn:active { transform: scale(.94); background: var(--surface-2); }
.circle-btn svg { width: calc(var(--round-btn, var(--tap)) / 2); height: calc(var(--round-btn, var(--tap)) / 2); }
/* Every close button is this one: a round button in the surface's color with
   a hairline around it and Lucide's x in the text's color, 44 px with a 22 px
   x (on a computer 30 px with a 15 px x: --round-btn, above); smaller in a
   card or a notice (sm: 32 px, on a computer 30 px as the rest), and on an
   attachment (xs). Back buttons are the same button with Lucide's
   chevron-left. */
.circle-btn.sm { width: var(--round-btn, 32px); height: var(--round-btn, 32px); }
.circle-btn.sm svg { width: 16px; height: 16px; }
.circle-btn.xs { width: 22px; height: 22px; }
.circle-btn.xs svg { width: 12px; height: 12px; }
/* The buttons at the top on a phone (the bot list's menu, search and new, and a chat's
   computer button and ⋯, with the notepad under it): a round bubble like
   .circle-btn's, 44 px, with a 24 px Lucide icon in the text's colour (not
   .icon-btn, the smaller gray buttons further down); the computer button's is
   its connection's (below). Its icon is always 6/11 of it, so on a computer,
   where it's 30 px (--round-btn, above), the icon is 16 px. */
.top-btn { width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); border-radius: 50%; background: var(--surface); border: 1px solid var(--border); padding: 0; display: inline-flex; align-items: center; justify-content: center; flex: none; color: var(--text); transition: background .15s, transform .1s; }
.top-btn:active { transform: scale(.94); background: var(--surface-2); }
.top-btn svg { width: calc(var(--round-btn, var(--tap)) * 6 / 11); height: calc(var(--round-btn, var(--tap)) * 6 / 11); }
/* On a phone's bot list in Light (the drawer's white): white bubbles
   (--drawer-button-bg) with no line or shadow, a touch grayer pressed, and the menu's, search's and +'s icons in
   --drawer-button-icon (the computer's stays its connection's). */
:root[data-theme="light"] .app.mobile .topbar .top-btn { background: var(--drawer-button-bg); border: 0;
  box-shadow: none; }
:root[data-theme="light"] .app.mobile .topbar .top-btn:active { background: var(--drawer-button-press); }
:root[data-theme="light"] .app.mobile .topbar .top-btn:not(.computer-btn) { color: var(--drawer-button-icon); }
/* The computer button, left of a chat's ⋯ (src/ui/computer-button.js):
   grayed out while there's no computer, the computer pulsing blue while it
   connects, solid green while connected. */
.computer-btn { transition: background .15s, transform .1s, color .3s, opacity .3s; }
.computer-btn.is-off { color: var(--dim); }
.computer-btn.is-connecting { color: var(--blue); }
.computer-btn.is-connecting svg { animation: computer-pulse 1.2s ease-in-out infinite; }
.computer-btn.is-connected { color: var(--green-dot); }
@keyframes computer-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.9); } }
@media (prefers-reduced-motion: reduce) { .computer-btn.is-connecting svg { animation: none; } }
/* ⋯ at the top right of a chat, right of the computer button
   (src/ui/computer-button.js MoreMenu). A tap drops its buttons down under it in a column, 12 px apart
   (the notepad, and what comes after it): each starts hidden behind ⋯, at 40% of its
   size, clear and blurred (4 px), and springs down into place, overshooting
   a little and settling (.56 s), sharpening and coming in as it goes, 55 ms
   after the one before and under it as they fan out; ⋯'s dots turn upright
   on the same spring. Closing tucks them back up into ⋯ the other way round,
   the last one first, quicker (.24 s) and speeding up, as the dots turn back.
   The spring is a real one where the browser can draw it (linear(): damping
   0.66, 6% over at 40% of the way); elsewhere a cubic that overshoots about
   as much. While it's open, a clear layer over the rest of the screen closes
   it on a tap there, rather than the tap going through. With reduced motion,
   they fade in and out in place. */
.more-menu { position: relative; display: flex; flex: none; width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); --spring: cubic-bezier(.34, 1.36, .64, 1); }
@supports (transition-timing-function: linear(0, 1)) {
  .more-menu { --spring: linear(0, 0.008 1.3%, 0.031 2.5%, 0.074 4%, 0.14 5.8%, 0.242 8%, 0.551 14.2%, 0.674 17%, 0.771 19.5%, 0.854 22%, 0.92 24.5%, 0.972 27%, 1.013 29.8%, 1.042 32.8%, 1.059 36.3%, 1.063 40.5%, 1.048 47.3%, 1.012 60.3%, 0.998 70.8%, 0.998 91%, 1); }
}
.more-btn { position: relative; z-index: 2; }
.more-btn svg { transition: transform .3s cubic-bezier(.4, 0, .2, 1); }
.more-menu.open .more-btn svg { transform: rotate(90deg); transition: transform .56s var(--spring); }
.more-scrim { position: fixed; inset: 0; z-index: 1; }
.more-items { position: absolute; top: calc(100% + 12px); left: 0; z-index: 1; display: flex; flex-direction: column; gap: 12px; pointer-events: none; }
.more-menu.open .more-items { pointer-events: auto; }
.more-item { --back: calc((var(--n) - 1 - var(--i)) * 40ms); display: flex; z-index: calc(var(--n) - var(--i));
  opacity: 0; visibility: hidden; filter: blur(4px); transform: translateY(calc((var(--i) + 1) * (var(--tap) + 12px) * -1)) scale(.4);
  transition: transform .24s cubic-bezier(.5, 0, .75, 0) var(--back), opacity .16s ease-in calc(var(--back) + .08s),
    filter .24s ease-in var(--back), visibility 0s linear calc(var(--back) + .24s); }
.more-menu.open .more-item { --delay: calc(var(--i) * 55ms);
  opacity: 1; visibility: visible; filter: none; transform: none;
  transition: transform .56s var(--spring) var(--delay), opacity .2s ease-out var(--delay), filter .32s ease-out var(--delay), visibility 0s; }
@media (prefers-reduced-motion: reduce) {
  .more-btn svg, .more-menu.open .more-btn svg { transition: none; }
  .more-item, .more-menu.open .more-item { filter: none; transform: none; }
  .more-item { transition: opacity .15s, visibility 0s linear .15s; }
  .more-menu.open .more-item { transition: opacity .15s, visibility 0s; }
}
/* On a computer (the website, Holli Bot for Windows and Mac: .chat-actions.desk),
   a chat's computer button and ⋯ are the phone's round buttons (.top-btn,
   the same icons), smaller: 30 px
   (--round-btn, above), with their icons scaled down with them (to 16 px),
   as are the sidebar's search
   and + (.side-top.desk, below); a touch darker under the pointer, and each
   named when the pointer rests on it. ⋯ drops down a small
   menu under it, right-aligned: a card in the message menu's colours (.mm)
   with each item's icon and name, highlighted under the pointer or the
   keyboard's focus. It closes as the phone's does: Escape, a click anywhere
   else, or an item. */
@media (hover: hover) { :is(.chat-actions, .side-top).desk .top-btn:hover:not(:active) { background: color-mix(in srgb, var(--surface) 94%, var(--text)); } }
.desk-menu { position: absolute; top: calc(100% + 10px); right: 0; z-index: 2; min-width: 184px; padding: 5px; border-radius: 12px;
  background: #2a2a2c; border: .5px solid rgba(255, 255, 255, .07); box-shadow: none;
  transform-origin: calc(100% - var(--round-btn, var(--tap)) / 2) 0; animation: mm-drop-in .18s cubic-bezier(.2, .9, .3, 1.08) both; }
:root[data-theme="dark"] .desk-menu { background: #363638; border-color: rgba(255, 255, 255, .08); }
:root[data-theme="light"] .desk-menu { background: #ffffff; border-color: rgba(20, 52, 58, .08); box-shadow: none; }
.desk-menu button { display: flex; align-items: center; gap: 10px; width: 100%; height: 34px; padding: 0 10px; border-radius: 8px;
  color: var(--text); font-size: var(--fs-md); font-weight: 500; text-align: left; white-space: nowrap; outline: none; transition: background .12s; }
.desk-menu button svg { width: 18px; height: 18px; color: var(--text-2); flex: none; }
.desk-menu button.danger, .desk-menu button.danger svg { color: var(--red); }
.desk-menu button:focus-visible, .desk-menu button:active { background: color-mix(in srgb, var(--text) 8%, transparent); }
@media (hover: hover) { .desk-menu button:hover { background: color-mix(in srgb, var(--text) 8%, transparent); } }
@media (prefers-reduced-motion: reduce) { .desk-menu { animation: none; } }
/* A chat's bar: Back (on a phone), the bot's name, and the computer button and
   ⋯ (.chat-actions). The two side columns are the same width, so the name is
   centred on the screen while it fits between the buttons; a longer one
   takes what room there is (.name-pill cuts it short with … past that). The
   name is set as the agent list's names are: the middle size, Medium. */
.chat-topbar { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; padding: calc(10px + var(--safe-top)) 16px 10px; background: var(--bg); flex: none; position: relative; z-index: 3; }
.chat-actions { display: flex; align-items: center; gap: 10px; justify-self: end; }
.name-pill { justify-self: center; display: inline-flex; align-items: center; gap: 10px; height: var(--tap); padding: 0 8px; border: 0; background: none; max-width: 100%; min-width: 0; }
.name-pill:active { opacity: .6; }
.name-pill .name { font-weight: 500; font-size: var(--fs-md); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* On a computer (the website, Holli Bot for Windows and Mac) the row is as tall
   as its 30 px buttons and sits near the top of the window, a small gap above
   it (--desk-top: 6 px; 10 px in Holli Bot for Mac, up beside the window's
   buttons, above), level with the sidebar's search and +, and 12 px under it:
   a bar 48 px tall (52 px in Holli Bot for Mac: --head-h, below). Not in Holli
   Bot for iPhone's wide window (an iPad), which has no buttons there. */
:root:not(.ios-app) .app.wide .chat-topbar { padding: var(--desk-top) 16px 12px; }
:root:not(.ios-app) .app.wide .name-pill { height: 30px; }
/* The top bars float over their lists, as in Claude: no background of their
   own, the bot list and the chat scroll up under them (their first rows start
   below, --head-h), and show through frosted glass (src/ui/components.js
   HeadGlass), fading away in it as they go up, as under a bar in iOS.
   The glass reaches 16 px below the bar, so what's at the top of a list at
   rest is clear. From there up to the buttons' top (--fade: 68 px on a phone,
   58 px on a computer's shorter bar), what's under it is tinted with the
   page's color along an S-curve: barely at first, so there's no line where it
   starts (2% an eighth of the way up), then more and more quickly (about a
   sixth at a phone's bar's bottom, near half 40% of the way), easing off
   into a long tail (nine tenths by the buttons' middle) until by their top
   it's the page's color alone, as is all above it (the status bar too, in
   Holli Bot for iPhone), so nothing meets the top of the page in a line.
   The blur trails the tint: none until what's under it is a fifth gone, then
   1 px, 2 px and 4 px in bands that fade into each other, so it's softened
   as it goes rather than smeared while it still shows, and a dark picture or
   bubble going up under the bar melts away rather than showing through
   behind the buttons as a gray box. Measured up from the glass's bottom, so
   every bar fades the same, however tall.
   With Reduce Transparency, a plain bar. */
:root { --head-h: calc(64px + var(--safe-top)); --desk-top: 6px; }
:root:not(.ios-app) :is(.app.wide, .app.wide ~ .toasts) { --head-h: calc(var(--desk-top) + 42px); }
.topbar, .chat-topbar { position: absolute; top: 0; left: 0; right: 0; z-index: 3; background: none; }
.topbar ~ .home-scroll { padding-top: var(--head-h); }
.chat-body > .chat-scroll { padding-top: calc(var(--head-h) + 12px); }
.chat-topbar + .empty-home { margin-top: var(--head-h); }
.head-glass { --fade: 68px; position: absolute; top: 0; left: 0; right: 0; bottom: -16px; z-index: -1; pointer-events: none;
  background: linear-gradient(to bottom, var(--bg) calc(100% - var(--fade) * .7), transparent);
  background: linear-gradient(to bottom, var(--bg) calc(100% - var(--fade)),
    color-mix(in srgb, var(--bg) 98.5%, transparent) calc(100% - var(--fade) * .88), color-mix(in srgb, var(--bg) 96%, transparent) calc(100% - var(--fade) * .8),
    color-mix(in srgb, var(--bg) 91%, transparent) calc(100% - var(--fade) * .72), color-mix(in srgb, var(--bg) 83%, transparent) calc(100% - var(--fade) * .64),
    color-mix(in srgb, var(--bg) 72%, transparent) calc(100% - var(--fade) * .56), color-mix(in srgb, var(--bg) 59%, transparent) calc(100% - var(--fade) * .48),
    color-mix(in srgb, var(--bg) 44%, transparent) calc(100% - var(--fade) * .4), color-mix(in srgb, var(--bg) 29%, transparent) calc(100% - var(--fade) * .32),
    color-mix(in srgb, var(--bg) 16%, transparent) calc(100% - var(--fade) * .24), color-mix(in srgb, var(--bg) 7%, transparent) calc(100% - var(--fade) * .16),
    color-mix(in srgb, var(--bg) 2%, transparent) calc(100% - var(--fade) * .08), transparent 100%); }
:root:not(.ios-app) .app.wide .head-glass { --fade: 58px; }
.head-glass > i { position: absolute; inset: 0; }
.head-glass > i:nth-child(1) { -webkit-backdrop-filter: blur(1px); backdrop-filter: blur(1px);
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(100% - var(--fade) * .58), #000 calc(100% - var(--fade) * .46), #000 calc(100% - var(--fade) * .32), transparent calc(100% - var(--fade) * .2));
  mask-image: linear-gradient(to bottom, transparent calc(100% - var(--fade) * .58), #000 calc(100% - var(--fade) * .46), #000 calc(100% - var(--fade) * .32), transparent calc(100% - var(--fade) * .2)); }
.head-glass > i:nth-child(2) { -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(100% - var(--fade) * .82), #000 calc(100% - var(--fade) * .7), #000 calc(100% - var(--fade) * .58), transparent calc(100% - var(--fade) * .46));
  mask-image: linear-gradient(to bottom, transparent calc(100% - var(--fade) * .82), #000 calc(100% - var(--fade) * .7), #000 calc(100% - var(--fade) * .58), transparent calc(100% - var(--fade) * .46)); }
.head-glass > i:nth-child(3) { -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--fade) * .82), transparent calc(100% - var(--fade) * .7));
  mask-image: linear-gradient(to bottom, #000 calc(100% - var(--fade) * .82), transparent calc(100% - var(--fade) * .7)); }
@media (prefers-reduced-transparency: reduce) {
  .head-glass { bottom: 0; background: var(--bg); }
  .head-glass > i { display: none; }
}

/* ---------- home list ---------- */
.home-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding-bottom: calc(24px + var(--safe-bottom)); touch-action: pan-y; }
/* A word about the account's computer, at the top of the bot list (src/ui/home.js). */
.list-notice { display: flex; align-items: center; gap: 10px; margin: 4px 16px 10px; padding: 8px 8px 8px 14px; border-radius: 14px; background: var(--surface); border: 1px solid var(--border);
  color: var(--text-2); font-size: var(--fs-sm); line-height: 18px; animation: fade .2s ease-out; }
.list-notice > svg { flex: none; color: var(--muted); }
.list-notice > span { flex: 1; min-width: 0; }
.list-notice-action { flex: none; padding: 6px 4px; color: var(--blue); font-weight: 500; }
.search-bar { margin: 4px 16px 8px; display: flex; align-items: center; gap: 8px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 0 12px; height: 42px; }
.search-bar input { flex: 1; background: none; border: 0; outline: none; font-size: var(--fs-md); }
.search-bar svg { width: 18px; height: 18px; color: var(--muted); }
/* Clears what's typed in the search bar. */
.field-clear { flex: none; width: 32px; height: 32px; margin-right: -6px; border-radius: 50%; display: grid; place-items: center; color: var(--muted); }
.field-clear:active { background: var(--surface-3); }
.search-bar .field-clear svg, .field-clear svg { width: 16px; height: 16px; }
/* The bot list in two parts (src/ui/home.js): Workspaces, then Agents (each
   bot's own chat, the Chief Coordinator's first), each under its name, in
   the middle size, Medium, in the text's own color, so it stands out from the
   previews (in the second shade), in line with the faces, and 20 px
   between the last row of one and the name of the next. In the rail, no
   names: a short line between the two, in the sidebar's line color. */
.list-label { margin: 0; padding: 8px 20px 4px; color: var(--text); font-size: var(--fs-md); line-height: 20px; font-weight: 500; }
.list-section + .list-section { margin-top: 20px; }
/* No workspace yet (src/ui/home.js StartWorkspace): under Workspaces, a
   card, all of it a button that opens New Workspace: Holli Bot's bots round
   a table watching Holli Bot on a laptop (src/ui/table-scene.js), then
   Start a workspace over the line on what one is, and Create at the right,
   a small teal pill. */
.ws-start { display: grid; grid-template-columns: 1fr auto; align-items: end; gap: 10px 8px; width: calc(100% - 32px); margin: 2px 16px 4px; padding: 10px;
  border-radius: 16px; background: var(--surface); color: var(--text); text-align: left; box-shadow: 0 0 0 1px var(--border); transition: transform .12s; }
:root[data-theme="light"] .ws-start { background: #fff; box-shadow: 0 0 0 1px rgba(20, 52, 58, .07); }
.ws-start:active { transform: scale(.985); }
.app.wide .ws-start { width: calc(100% - 24px); margin: 2px 12px 4px; }
.ws-start .table-scene { grid-column: 1 / -1; display: block; width: 100%; height: auto; aspect-ratio: 480 / 200; border-radius: 11px; }
.ws-start-text { min-width: 0; padding: 0 2px 2px; }
.ws-start-title { display: block; font-size: var(--fs-md); line-height: 20px; font-weight: 500; }
.ws-start-sub { display: block; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; }
.ws-start-go { display: inline-flex; align-items: center; height: 30px; padding: 0 14px; margin: 0 2px 2px 0; border-radius: 15px; background: var(--blue); color: #fff; font-size: var(--fs-sm); font-weight: 500; }
/* The scene, unhurried: each bot bobs very gently, slowly, at its own
   pace, and blinks now and then; its eyes drift (over a second, with no
   overshoot) between the screen and the bot it's talking to (set from
   src/ui/table-scene.js); the screen goes round every 12 s: the message
   sent at .7 s, the agent typing at 2.6 s, its answer at 4.9 s, and at
   10.6 s it clears; the laptop's glow breathes slowly. Still with Reduce
   Motion. */
.ts-move { animation: ts-bob 6.4s ease-in-out infinite; }
.ts-move.two { animation-duration: 7.3s; animation-delay: -2.4s; }
.ts-move.three { animation-duration: 5.8s; animation-delay: -4.1s; }
.ts-move.four { animation-duration: 6.9s; animation-delay: -1.2s; }
.ts-move.five { animation-duration: 6.1s; animation-delay: -5s; }
@keyframes ts-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.6px); } }
.ts-eye { transition: transform 1.1s cubic-bezier(.45, 0, .25, 1); }
.ts-blink { transform-box: fill-box; transform-origin: center; animation: ts-blink 4.6s infinite; }
@keyframes ts-blink { 0%, 93%, 100% { transform: scaleY(1); } 96% { transform: scaleY(.12); } }
.ts-sent, .ts-typing, .ts-answer { transform-box: fill-box; }
.ts-sent { transform-origin: 100% 100%; animation: ts-sent 12s ease-out infinite; }
@keyframes ts-sent {
  0%, 6% { opacity: 0; transform: translateY(4px) scale(.8); }
  11% { opacity: 1; transform: translateY(0) scale(1.04); }
  14%, 88% { opacity: 1; transform: translateY(0) scale(1); }
  94%, 100% { opacity: 0; transform: translateY(0) scale(1); } }
.ts-typing { opacity: 0; transform-origin: 0 100%; animation: ts-typing 12s ease-out infinite; }
@keyframes ts-typing {
  0%, 22% { opacity: 0; transform: scale(.8); }
  26%, 40% { opacity: 1; transform: scale(1); }
  42%, 100% { opacity: 0; transform: scale(1); } }
.ts-answer { transform-origin: 0 100%; animation: ts-answer 12s ease-out infinite; }
@keyframes ts-answer {
  0%, 41% { opacity: 0; transform: translateY(4px) scale(.8); }
  46% { opacity: 1; transform: translateY(0) scale(1.03); }
  49%, 88% { opacity: 1; transform: translateY(0) scale(1); }
  94%, 100% { opacity: 0; transform: translateY(0) scale(1); } }
.ts-dot { animation: ts-dot 1.5s ease-in-out infinite; }
.ts-dot.two { animation-delay: .25s; }
.ts-dot.three { animation-delay: .5s; }
@keyframes ts-dot { 0%, 60%, 100% { transform: translateY(0); opacity: .5; } 30% { transform: translateY(-2px); opacity: 1; } }
.ts-glow { animation: ts-glow 7s ease-in-out infinite; }
@keyframes ts-glow { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .table-scene *, .ws-start { animation: none !important; transition: none !important; }
}
/* The name folds its part away, or opens it (src/ui/home.js ListSection):
   all of it a button, with Lucide's plus at the right, in line with the rows'
   ends, in the name's gray: 19 px with 1 px strokes, at the top of the
   name's 20 px line rather than centered in it, so they sit on whole pixels,
   sharp on any screen (half a pixel lower they'd fall between pixels, and
   look soft). While the part is open, the plus's upright stroke lies down,
   so it's a minus; folded, it stands up again. Folded, the part is gone, only its name left (no fade, no strip).
   Opening or folding, it glides to its new height in .46 s, setting off
   quickly and easing gently into place, as a sheet does in iOS. A new
   visitor sees both parts open; the device remembers from then on. With
   Reduce Motion, it's there at once. */
.list-toggle { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; padding: 0; margin: 0; border: 0; background: none;
  color: inherit; font: inherit; text-align: left; cursor: pointer; }
.list-toggle svg { flex: none; align-self: flex-start; width: 19px; height: 19px; stroke-width: 1.263; }
.list-toggle svg path { transform-box: fill-box; transform-origin: center; transition: transform .46s cubic-bezier(.32, .72, 0, 1); }
.list-section:not(.folded) .list-toggle svg path:last-child { transform: rotate(90deg); }
.list-body { overflow: hidden; }
@media (prefers-reduced-motion: reduce) { .list-toggle svg path { transition: none; } }
.app.wide .list-label { padding: 4px 20px 6px; }
.app.rail .list-section + .list-section { margin-top: 0; }
.app.rail .list-section + .list-section::before { content: ''; display: block; width: 32px; height: 1px; margin: 5px auto 12px; background: var(--side-line); }
.row-bot { display: flex; align-items: center; gap: 14px; padding: 14px 20px; width: 100%; text-align: left; position: relative;
  background: var(--bg); touch-action: pan-y; }
/* Swipe a row left to open its chat (src/ui/home.js ThreadRow). */
.swipe-row { position: relative; overflow: hidden; }
.row-bot:active, .row-bot.active { background: var(--surface); }
.row-bot .meta { flex: 1; min-width: 0; }
.row-bot .line1 { display: flex; align-items: baseline; gap: 10px; }
/* A name or preview too long for its line fades out at its end, rather than ending in "…". */
.row-bot .title, .row-bot .preview { white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
.row-bot .title { flex: 1; font-size: 14px; font-weight: 500; }
/* When the chat was last active, on every row, in the preview's size, weight and color. */
.row-bot .time { color: var(--muted); font-size: var(--fs-sm); font-weight: 450; white-space: nowrap; flex: none; }
/* A bot got from the Agent Store (src/ui/store.js): the store's shopping bag
   after its name, small, in the second shade. */
.row-bot .store-mark { display: inline-flex; vertical-align: -1px; margin-left: 6px; color: var(--muted); }
.row-bot .store-mark svg { width: 14px; height: 14px; stroke-width: 2.2; }
.app.wide .row-bot .store-mark svg { width: 13px; height: 13px; }
.row-bot .line2 { display: flex; align-items: center; gap: 10px; margin-top: 3px; }
.row-bot .preview { flex: 1; color: var(--muted); font-size: var(--fs-sm); }
.row-bot .preview.waiting { color: var(--orange); }
/* In light, amber as words is the darker amber, which reads on the page. */
:root[data-theme="light"] :is(.row-bot .preview.waiting, .job-meta .full, .approval .head, .mem .imp, .mem .acts button.on, .sb-count.full) { color: var(--amber-ink); }
.row-bot .preview.error { color: var(--red); }
/* On a phone's bot list in Light: names, and last messages (and when, in their
   color), navy-leaning on the drawer's white. */
:root[data-theme="light"] .app.mobile .row-bot .title { color: var(--drawer-text-primary); }
:root[data-theme="light"] .app.mobile .row-bot :is(.preview:not(.waiting, .error), .time) { color: var(--drawer-text-secondary); }
/* Unread or waiting. Every row keeps room for it, so every preview fades out at the same place. */
.dot { width: 10px; height: 10px; border-radius: 50%; flex: none; }
.dot.unread { background: var(--blue); }
.dot.waiting { background: var(--orange); }
.row-bot .kind-badge { font-size: var(--fs-sm); color: var(--muted); border: 1px solid var(--border); border-radius: 6px; padding: 0 5px; margin-left: 6px; }
.empty-home { padding: 60px 28px; text-align: center; color: var(--muted); display: flex; flex-direction: column; align-items: center; gap: 16px; }
.empty-home h2 { color: var(--text); margin: 8px 0 0; font-size: var(--fs-lg); }
.empty-home p { margin: 0; max-width: 320px; line-height: 1.45; }
.section-label { color: var(--muted); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: .6px; padding: 18px 20px 6px; }

/* ---------- the bot list as a sidebar, on a computer or a tablet: after Grok's ---------- */
/* Only in the wide layout (src/ui/home.js, wide): a phone's list is as above.
   280 px wide. At the top right search and +: on a computer round buttons
   near the top of the window (.desk, below; in Holli Bot for Mac up beside
   the window's buttons), with the search box under them once search is
   clicked; in Holli Bot for iPhone on an iPad, below the status bar
   (--safe-top), only +, gray, with no circle, and a search box under it
   that's always there; then the chats, close together (58 px apart): 38 px faces,
   the name (16 px, as on a phone) over the last message (13 px), no time, a
   rounded highlight on the open chat and under the pointer, and at the right
   a dot (8 px) for unread (blue) or waiting (orange). At the bottom the Bot
   Store, and who's signed in (Settings) with the computer button. */
.app.wide .pane-list {
  --side-line: #2e2e2e; --side-field: #232323; --side-well: #181818;
  --side-hover: #1b1b1b; --side-active: #313131; --side-unread: #5d9df7; }
:root[data-theme="dark"] .app.wide .pane-list {
  --side-line: #3a3a3c; --side-field: #2c2c2e; --side-well: #222224;
  --side-hover: #2a2a2a; --side-active: #3a3a3c; background: #171717; }
:root[data-theme="light"] .app.wide .pane-list {
  --side-line: #d8e1e1; --side-field: #ffffff; --side-well: #eaf1f0;
  --side-hover: #eeefef; --side-active: #e3e9e9; --side-unread: var(--teal); background: #f9f9f9; }
.side-top { flex: none; height: calc(47px + var(--safe-top)); display: flex; justify-content: flex-end; align-items: flex-start; padding: calc(7px + var(--safe-top)) 15px 0; }
.side-plus { width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; color: var(--muted); transition: background .15s, color .15s; }
.side-plus svg { width: 18px; height: 18px; }
.side-search { flex: none; display: flex; align-items: center; gap: 4px; height: 32px; margin: 0 12px 8px; padding: 0 4px 0 6px; border: 1px solid var(--side-line); border-radius: 7px;
  background: var(--side-field); color: var(--muted); cursor: text; }
.side-search > svg { width: 16px; height: 16px; flex: none; }
.side-search input { flex: 1; min-width: 0; height: 100%; padding: 0; background: none; border: 0; outline: none; color: var(--text); font-size: var(--fs-md); }
.side-search .field-clear { width: 24px; height: 24px; margin-right: 0; }
.side-search .field-clear svg { width: 14px; height: 14px; }
/* On a computer (the website, Holli Bot for Windows and Mac: .side-top.desk),
   search and + are the round buttons at the top right of a chat (.top-btn:
   30 px, Lucide's search and plus at 16 px), 10 px apart and level with the chat's computer button and ⋯ (a
   small gap below the top of the window, --desk-top: in Holli Bot for Mac up
   beside the window's buttons), with 12 px under them, a touch darker under
   the pointer (and search while its box is out), each named when the
   pointer rests on it. A click on search drops the search box in under
   them, ready to type in; another, or Escape with nothing typed, puts it
   away. */
.side-top.desk { height: calc(var(--desk-top) + 42px); gap: 10px; padding: var(--desk-top) 16px 0; }
.side-top.desk .top-btn[aria-expanded="true"] { background: color-mix(in srgb, var(--surface) 94%, var(--text)); }
.side-top.desk ~ .side-search { animation: side-search-in .18s cubic-bezier(.2, .9, .3, 1) both; }
@keyframes side-search-in { from { opacity: 0; transform: translateY(-6px); } }
@media (prefers-reduced-motion: reduce) { .side-top.desk ~ .side-search { animation: none; } }
.app.wide .home-scroll { padding-bottom: 8px; }
/* On a computer, the list starts 48 px under search and + (12 px of their
   bar, these 32 and the first name's 4). */
.side-top.desk ~ .home-scroll { padding-top: 32px; }
/* In Holli Bot for iPhone's wide window (an iPad), 48 px under its search box
   (8 px under it, these 36 and the first name's 4); on a phone and in Holli
   Bot for iPhone, the first part starts 48 px under the top bar's buttons
   (8 px of the bar, these 32 and its name's 8), or under the search field
   while it's out. */
.side-top:not(.desk) ~ .home-scroll { padding-top: 36px; }
.app.mobile .list-section:first-of-type { margin-top: 32px; }
.app.wide .list-notice { margin: 0 12px 8px; }
.app.wide .swipe-row { margin: 0 12px 4px; border-radius: 10px; }
.app.wide .row-bot { height: 62px; padding: 0 8px; gap: 9px; border-radius: 10px; background: none; }
.app.wide .row-bot:active, .app.wide .row-bot.active { background: var(--side-active); }
.app.wide .row-bot .title, .app.wide .row-bot .preview { -webkit-mask-image: none; mask-image: none; text-overflow: ellipsis; }
.app.wide .row-bot .title { line-height: 20px; }
/* In Holli Bot for iPhone (.ios-app: src/main.js), on an iPhone and an iPad, the agent list's headings (Workspaces,
   Agents) and its names are 17 pt, iOS's own size for a list's text, the line under each name 14 pt, and what's typed
   in its search box 17 pt. */
.ios-app .list-label { font-size: 17px; line-height: 22px; }
.ios-app .row-bot .title { font-size: 17px; }
.ios-app .app.wide .row-bot .title { line-height: 22px; }
.ios-app .row-bot .preview, .ios-app .app.wide .row-bot .preview { font-size: 14px; }
.ios-app .app.wide .row-bot .preview { line-height: 18px; }
.ios-app :is(.search-bar, .side-search) input { font-size: 17px; }
.app.wide .row-bot .line2 { margin-top: 1px; }
.app.wide .row-bot .preview { font-size: var(--fs-sm); line-height: 17px; color: var(--muted); }
.app.wide .row-bot.active .preview { color: var(--muted); }
/* In Light, the preview in Grok's neutral grey, as black at 60% over the sidebar would be, and over the open chat's highlight. */
.app.wide .row-bot .preview.waiting { color: var(--orange); }
.app.wide .row-bot .preview.error { color: var(--red); }
.app.wide .row-bot.no-preview .line2 { display: none; }
.app.wide .row-bot.has-dot .meta { padding-right: 18px; }
.app.wide .row-bot > .dot { position: absolute; right: 8px; top: 50%; width: 8px; height: 8px; margin-top: -4px; }
.app.wide .row-bot > .dot.unread { background: var(--side-unread); }
.side-foot { flex: none; padding: 8px 0 calc(10px + var(--safe-bottom)); }
.side-link { display: flex; align-items: center; gap: 9px; height: 42px; margin: 0 12px; padding: 0 8px; border-radius: 10px; min-width: 0;
  color: var(--text); font-size: 14px; font-weight: 500; text-align: left; }
.side-link:active { background: var(--side-active); }
/* The account menu's button: a small chevron at its right, and held while the menu is out (src/ui/account-menu.js). */
.side-chev { flex: none; width: 15px; height: 15px; margin-right: 2px; color: var(--dim); stroke-width: 2.25; transition: transform .2s; }
.side-link[aria-expanded="true"] { background: var(--side-active); }
.side-link[aria-expanded="true"] .side-chev { transform: rotate(180deg); }
.rail-me[aria-expanded="true"] { filter: brightness(1.2); }
/* Who's signed in: their initials in a circle as big as the rail's (.rail-me),
   so two wide letters (MW) clear its edge. */
.side-ic { width: 35px; height: 35px; border-radius: 50%; flex: none; display: grid; place-items: center; background: var(--side-well); border: 1px solid var(--side-line); color: var(--text-2); }
.side-ic svg { width: 16px; height: 16px; }
.side-initials { background: var(--side-field); color: var(--muted); font-size: var(--fs-sm); font-weight: 500; letter-spacing: .3px; }
/* Your name (14 px), then your plan in 13 px and the second gray, after a
   dash: “Mr Wonderful – Starter”. A long name is cut short, never the plan. */
.side-label { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 4px; white-space: nowrap; }
.side-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.side-plan { flex: none; font-size: var(--fs-sm); font-weight: 450; color: var(--muted); }
@media (hover: hover) {
  .side-plus:hover { background: var(--side-hover); color: var(--text); }
  .app.wide .row-bot:hover:not(.active) { background: var(--side-hover); }
  .side-link:hover { background: var(--side-hover); }
}

/* ---------- the sidebar as black glass, in Holli Bot for Mac ---------- */
/* In Holli Bot for Mac (2.4.0 on: .mac-glass, from desktop/src/app-window.js by index.html), in Black and Dark,
   the agent list beside the chat (and the rail) is see-through black glass: the window under the page is the
   Mac's own frosted glass (its dark HUD material), and while the list is showing, the page and the app have no
   background of their own, so the Mac's desktop, and the windows behind this one, show through the list, softly
   blurred, as through a Mac's sidebar. Over it, black at 25%, so the names read on any wallpaper, and a hairline
   of white at its right edge where the glass ends. On the glass, everything that was a solid gray is white at a
   low strength: the open chat's highlight 12%, under the pointer 7%, the search box, the initials, the round
   buttons and a note at the top 8%, with hairlines of white at 12-14%, and the lines under the names white at
   62%. The chat, the panel beside it, the menu and every sheet keep their own backgrounds. Light, Cosmos (which has
   a sky of its own), the website, Holli Bot for Windows and an iPad are as they were. */
:root.mac-glass:not([data-theme="light"], .cosmos):has(.app.wide), :root.mac-glass:not([data-theme="light"], .cosmos):has(.app.wide) body,
:root.mac-glass:not([data-theme="light"], .cosmos) .app.wide { background: transparent; }
:root.mac-glass:not([data-theme="light"], .cosmos) .app.wide .pane-list {
  --muted: rgba(255, 255, 255, .62); --dim: rgba(255, 255, 255, .45);
  --side-line: rgba(255, 255, 255, .12); --side-field: rgba(255, 255, 255, .08); --side-well: rgba(255, 255, 255, .06);
  --side-hover: rgba(255, 255, 255, .07); --side-active: rgba(255, 255, 255, .12);
  border-right-color: rgba(255, 255, 255, .1); background: rgba(0, 0, 0, .25); box-shadow: inset -1px 0 0 rgba(255, 255, 255, .05); }
:root.mac-glass:not([data-theme="light"], .cosmos) .app.wide .pane-list :is(.top-btn, .list-notice) { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .14); }
:root.mac-glass:not([data-theme="light"], .cosmos) .app.wide .pane-list .top-btn:active { background: rgba(255, 255, 255, .14); }
:root.mac-glass:not([data-theme="light"], .cosmos) .app.wide .pane-list .side-top.desk .top-btn[aria-expanded="true"] { background: rgba(255, 255, 255, .16); }
@media (hover: hover) { :root.mac-glass:not([data-theme="light"], .cosmos) .app.wide .pane-list .side-top.desk .top-btn:hover:not(:active) { background: rgba(255, 255, 255, .13); } }

/* ---------- the sidebar as a rail, in a narrow window on a computer ---------- */
/* On a computer (a mouse or trackpad points), a window narrower than the wide
   layout's 900 px keeps the chat beside the bot list rather than turning into
   a phone's (src/ui/app.js rail, src/ui/home.js): the sidebar collapses to a
   rail 88 px wide inside its line, of the chats' faces alone, 38 px and
   spaced evenly: 25 px between one face and the next, as between a face and
   either side of the rail and the top (below the window's buttons in Holli
   Bot for Mac, which sit at the top of the rail). A rounded 56 px square, the
   face in its middle, is highlighted on the open chat and under the pointer;
   each chat's name shows on hover, and the dot for unread (blue) or waiting
   (orange) sits on the face's bottom right, ringed in what's behind it. No
   search box. At the bottom: + (18 px), gray with no circle, and who's signed
   in (their initials in a 35 px circle), which opens Settings. The chat beside it is the wide layout's. */
.app.rail .pane-list { width: 89px; }
.app.rail .home-scroll { padding: calc(16px + var(--safe-top)) 0 8px; scrollbar-width: none; }
.app.rail .home-scroll::-webkit-scrollbar { display: none; }
.app.rail .swipe-row { width: 56px; margin: 0 auto 7px; }
.app.rail .row-bot { height: 56px; padding: 0; justify-content: center; }
.app.rail .row-bot .meta { display: none; }
.app.rail .row-bot > .dot { top: auto; right: 7px; bottom: 7px; width: 10px; height: 10px; margin: 0; box-shadow: 0 0 0 2px var(--bg); }
.app.rail .row-bot:active > .dot, .app.rail .row-bot.active > .dot { box-shadow: 0 0 0 2px var(--side-active); }
.rail-foot { flex: none; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 8px 0 calc(12px + var(--safe-bottom)); }
.rail-btn { position: relative; width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center; color: var(--muted); transition: background .15s, color .15s; }
.rail-btn svg { width: 18px; height: 18px; }
.rail-btn:active { background: var(--side-active); }
/* While there's a note about the account's computer: the computer, with a blue
   dot, which shows the note beside the rail, as it is at the top of the
   sidebar, over the chat, until a click elsewhere. */
.rail-alert { color: var(--text-2); }
.rail-alert::after { content: ""; position: absolute; top: 6px; right: 6px; width: 7px; height: 7px; border-radius: 50%; background: var(--side-unread); box-shadow: 0 0 0 2px var(--bg); }
.app.rail .rail-notice { position: fixed; left: 97px; z-index: 41; width: min(340px, calc(100vw - 112px)); margin: 0; box-shadow: none;
  animation: pop .16s ease-out; transform-origin: bottom left; }
.rail-me { width: 35px; height: 35px; margin-top: 8px; border-radius: 50%; display: grid; place-items: center; background: var(--side-field); border: 1px solid var(--side-line);
  color: var(--muted); font-size: var(--fs-sm); font-weight: 500; letter-spacing: .3px; transition: filter .15s; }
.rail-me svg { width: 16px; height: 16px; }
@media (hover: hover) {
  .rail-btn:hover { background: var(--side-hover); color: var(--text); }
  .rail-me:hover { filter: brightness(1.15); }
  .app.rail .row-bot:hover:not(.active) > .dot { box-shadow: 0 0 0 2px var(--side-hover); }
}

/* ---------- popover menus ---------- */
.popover-scrim { position: fixed; inset: 0; z-index: 40; }
.popover { position: fixed; z-index: 41; background: var(--raised); border: 1px solid var(--raised-line); border-radius: 26px; box-shadow: none; padding: 12px 0; min-width: 240px; max-width: calc(100vw - 16px); animation: pop .16s ease-out; transform-origin: top right; }
.popover.from-bottom { transform-origin: bottom left; }
.popover button { display: flex; align-items: center; gap: 16px; width: 100%; padding: 15px 28px; font-size: var(--fs-md); text-align: left; }
.popover button:active { background: var(--surface-2); }
.popover button svg { width: 24px; height: 24px; color: var(--text-2); flex: none; }
.popover button span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* + (New Agent, New Workspace: src/ui/home.js) in the type of the list's
   Workspaces and Agents names: the middle size, Medium, with 19 px icons drawn in
   1 px lines, so they're sharp on any screen. Each row's
   highlight is rounded, inside the card. On a phone and in Holli Bot for
   iPhone, rows a finger's height (46 px), 16 px in from the highlight's
   edges, 12 px between icon and name, the card 6 px round them; on a
   computer, a menu's (36 px), 12 px in, 10 px between, the card 5 px round
   them, a row highlighted under the pointer. */
.popover.new-menu { padding: 6px; border-radius: 20px; min-width: 216px; }
.popover.new-menu button { height: 46px; padding: 0 16px; gap: 12px; border-radius: 14px; font-size: var(--fs-md); line-height: 20px; font-weight: 500; }
.popover.new-menu button svg { width: 19px; height: 19px; stroke-width: 1.263; }
:root:not(.ios-app) .app.wide .popover.new-menu { padding: 5px; border-radius: 14px; min-width: 196px; }
:root:not(.ios-app) .app.wide .popover.new-menu button { height: 36px; padding: 0 12px; gap: 10px; border-radius: 9px; }
@media (hover: hover) { :root:not(.ios-app) .app.wide .popover.new-menu button:hover:not(:active) { background: color-mix(in srgb, var(--text) 7%, transparent); } }
@keyframes pop { from { opacity: 0; transform: scale(.92); } to { opacity: 1; transform: scale(1); } }

/* ---------- the account menu (src/ui/account-menu.js) ----------
   Where the slide-out menu was: a small pop-up from the button that shows who's
   signed in (the menu button on a phone, who's signed in at the bottom of the
   sidebar, the round button at the bottom of the rail). Who is signed in at its
   top, a hairline, then rows in groups with hairlines between: an icon, the name,
   and at the right what it shows (the credits left; the computer's dot and
   state; a link out). Usage has its slim meter under its name. On a phone a
   row is a finger's height (44 px); on a computer a menu's (36 px), lit under
   the pointer. Its own color (--am-bg) stands apart from the sidebar and the
   agent list it opens over, in every look: a lighter gray in Black (#2C2C2E)
   and Dark (#3A3A3C), a pale teal gray in Light (#E3ECEA), a lavender in Pearl
   (#E2D5F4) and a violet slate in Cosmos (#2B2840; the glass rules for
   .popover give way to it there). The words, icons, hairlines and highlights
   are the app's text color at a strength, so they're right on each of them.
   In Holli Bot for iPhone, on a phone, it's a drawer from the left (below). */
.account-menu, .account-drawer { --am-ic: 20px; --am-gap: 12px; --am-accent: #9d9bff; --am-line: color-mix(in srgb, var(--text) 11%, transparent); --am-bg: #2c2c2e; }
.popover.account-menu { background: var(--am-bg);
  width: 288px; padding: 6px; border-radius: 20px; overflow-y: auto; overscroll-behavior: contain; outline: none; transform-origin: top left; }
:root[data-theme="dark"] :is(.account-menu, .account-drawer) { --am-bg: #3a3a3c; }
:root[data-theme="light"] :is(.account-menu, .account-drawer) { --am-bg: #e3ecea; }
:root.pearl[data-theme] :is(.account-menu, .account-drawer) { --am-bg: #e2d5f4; }
:root.cosmos[data-theme] :is(.account-menu, .account-drawer) { --am-bg: #2b2840; }
:root[data-theme="light"] :is(.account-menu, .account-drawer), :root.pearl[data-theme] :is(.account-menu, .account-drawer) { --am-accent: var(--teal); }
.popover.account-menu:is(.up, .side) { transform-origin: bottom left; }
.am-who { display: flex; align-items: center; gap: 12px; padding: 8px 10px 11px; }
.am-initials { flex: none; width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center; background: color-mix(in srgb, var(--text) 12%, transparent);
  color: var(--text); font-size: var(--fs-sm); font-weight: 600; letter-spacing: .3px; }
.am-names { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.am-name, .am-mail { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.am-name { color: var(--text); font-size: var(--fs-md); line-height: 20px; font-weight: 600; }
.am-mail { color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; }
.am-group { padding: 5px 0; }
.am-group:first-child { padding-top: 0; }
.am-who + .am-group, .am-group + .am-group { border-top: 1px solid var(--am-line); }
.am-group:last-child { padding-bottom: 0; }
.account-menu .am-item { display: flex; align-items: center; gap: var(--am-gap); width: 100%; min-height: 44px; padding: 0 12px; border-radius: 12px;
  color: var(--text); font-size: var(--fs-md); line-height: 20px; font-weight: 500; text-align: left; }
.account-menu .am-item svg { width: var(--am-ic); height: var(--am-ic); stroke-width: 1.75; color: var(--text-2); flex: none; }
.account-menu .am-item:active { background: color-mix(in srgb, var(--text) 12%, transparent); }
.account-menu .am-item:focus-visible { background: color-mix(in srgb, var(--text) 9%, transparent); outline: none; }
.am-label { flex: 1; min-width: 0; }
.account-menu .am-value { flex: none; max-width: 50%; display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--fs-sm); font-weight: 450; font-variant-numeric: tabular-nums; }
.account-menu .am-ext { width: 15px; height: 15px; margin-right: 1px; color: var(--dim); stroke-width: 2.1; }
.am-state { --tone: var(--muted); }
.am-state.ok { --tone: var(--green); }
.am-state.warn { --tone: var(--orange); }
.am-state.new { --tone: var(--blue); }
.am-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--tone); }
/* Usage: its slim meter under the name, from where the name starts. */
.account-menu .am-item.has-bar { flex-wrap: wrap; row-gap: 7px; padding-top: 10px; padding-bottom: 12px; }
.am-item.has-bar .credits-bar { flex: 0 0 calc(100% - var(--am-ic) - var(--am-gap)); height: 5px; margin: 0 0 0 calc(var(--am-ic) + var(--am-gap)); border-radius: 3px; background: color-mix(in srgb, var(--text) 14%, transparent); }
/* Upgrade Plan, on Free: in the app's accent. */
.account-menu .am-item.accent, .account-menu .am-item.accent svg { color: var(--am-accent); }
.am-version { padding: 6px 12px 4px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; text-align: right; font-variant-numeric: tabular-nums; letter-spacing: .2px; }
@media (hover: hover) { .account-menu .am-item:hover:not(:active) { background: color-mix(in srgb, var(--text) 8%, transparent); } }
/* On a computer: a menu's rows, 36 px, with a smaller card round them. */
:root:not(.ios-app) .app.wide .popover.account-menu { --am-ic: 18px; --am-gap: 10px; padding: 5px; border-radius: 14px; }
:root:not(.ios-app) .app.wide .account-menu .am-item { min-height: 36px; padding: 0 10px; border-radius: 9px; }
:root:not(.ios-app) .app.wide .account-menu .am-item.has-bar { padding-top: 8px; padding-bottom: 10px; }
:root:not(.ios-app) .app.wide .am-who { padding: 6px 8px 9px; }
@media (prefers-reduced-motion: reduce) { .popover.account-menu { animation: none; } }
/* In Holli Bot for iPhone, on a phone, the account menu is a drawer from the left instead (src/ui/account-menu.js
   AccountDrawer, useDrawer in src/ui/components.js), the screen's full height, after Claude's: the same rows, a
   finger's height, on the menu's own color (--am-bg, which the status bar's text goes by too), and as it comes out the
   agent list (or a chat) moves over to the right beside it, by its width, dimming, its left corners rounding off as the
   screen's are, all on the drawer's curve and under a finger, and back the same way. Settings and its pages push it
   out to the left as they come in from the right (usePushPage), the list going back under them. */
:root[data-theme] .sheet.drawer.account-drawer.main-page { --drawer-backdrop: var(--am-bg); --bg: var(--am-bg); background: var(--am-bg); border-right: 0; }
.account-menu.in-drawer { padding: 10px 0 0; }
:root.ios-app .app.mobile { transition: transform var(--drawer-ms, .45s) var(--menu-out); }
:root.ios-app.drawer-open .app.mobile { transform: translateX(calc(var(--drawer-w) + var(--drawer-drag, 0px))); transition-timing-function: var(--menu-in); }
:root.ios-app.drawer-mounted .app.mobile { border-radius: var(--app-curve) 0 0 var(--app-curve); }
:root.ios-app.drawer-dragging .app.mobile { transition: none; }
:root.ios-app.drawer-flung .app.mobile { transition-timing-function: var(--drawer-ease, var(--drawer-in)); }
@media (prefers-reduced-motion: reduce) { :root.ios-app .app.mobile { transition: none; } }

/* ---------- a message held down (src/ui/message-menu.js) ---------- */
/* As in Messages: the whole screen dims and blurs a little, and the message
   lifts out of it, a copy of its bubble, moved only as far as it takes to fit
   a row of reactions over it and a menu under it on the screen; one too tall
   shows its start, fading out. The reactions come up from the message one
   after another, the menu drops down from it, and it all goes back the way it
   came. Plain, solid surfaces with no shadow, the same shapes in every
   theme: a near-black gray one in Black, a gray one a step up in Dark, white
   in Light, where the dimming is the app's deep teal. */
.mm {
  --mm-glass: #2a2a2c; --mm-ink: var(--text); --mm-icon: var(--text-2); --mm-line: rgba(255, 255, 255, .07);
  --mm-press: rgba(255, 255, 255, .08); --mm-on: rgba(255, 255, 255, .17);
  --mm-scrim: rgba(0, 0, 0, .62);
  position: fixed; inset: 0; z-index: 45; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
:root[data-theme="dark"] .mm { --mm-glass: #363638; --mm-line: rgba(255, 255, 255, .08); --mm-scrim: rgba(0, 0, 0, .58); }
:root[data-theme="light"] .mm {
  --mm-glass: #ffffff; --mm-line: rgba(20, 52, 58, .08); --mm-press: rgba(20, 52, 58, .06); --mm-on: rgba(8, 126, 139, .14);
  --mm-scrim: rgba(10, 30, 34, .36);
}
.mm-scrim { position: absolute; inset: 0; touch-action: none; background: var(--mm-scrim); -webkit-backdrop-filter: blur(8px) saturate(115%); backdrop-filter: blur(8px) saturate(115%); animation: mm-fade .24s ease-out both; }
.mm-safe { position: fixed; top: var(--safe-top); bottom: var(--safe-bottom); left: 0; width: 0; visibility: hidden; pointer-events: none; }
/* The message, lifted. */
.mm-msg { position: absolute; margin: 0; pointer-events: none; animation: mm-lift .36s cubic-bezier(.2, .9, .3, 1) both; }
.mm-msg > .bubble { width: 100%; max-width: none !important; box-shadow: none; }
:root[data-theme="light"] .mm-msg > .bubble { box-shadow: none; }
.mm-msg.clipped { overflow: hidden; border-radius: 22px; -webkit-mask-image: linear-gradient(#000 calc(100% - 56px), transparent); mask-image: linear-gradient(#000 calc(100% - 56px), transparent); }
.mm-msg.clipped > .bubble { box-shadow: none; }
/* The reactions: eight, and + for more. */
.mm-bar { position: absolute; height: 52px; display: flex; align-items: center; justify-content: space-between; gap: 2px; padding: 0 6px;
  border-radius: 26px; background: var(--mm-glass); border: .5px solid var(--mm-line); box-shadow: none;
  transform-origin: 24px 100%; animation: mm-in .32s cubic-bezier(.2, .9, .3, 1.12) both; }
.mm.mine .mm-bar { transform-origin: calc(100% - 24px) 100%; }
.mm-bar button { flex: 0 1 40px; min-width: 0; height: 40px; border-radius: 20px; display: inline-flex; align-items: center; justify-content: center;
  font-size: var(--fs-lg); line-height: 1; transition: transform .12s ease, background .15s;
  animation: mm-pop .38s cubic-bezier(.2, .9, .3, 1.3) both; animation-delay: calc(50ms + var(--i, 0) * 18ms); }
.mm-bar button:active { transform: scale(.8); }
.mm-bar button.on { background: var(--mm-on); }
.mm-plus { color: var(--mm-icon); background: var(--mm-press); }
.mm-plus svg { width: 20px; height: 20px; }
/* The menu: Reply, Copy, Select, Share. */
.mm-menu { position: absolute; padding: 6px; border-radius: 20px; background: var(--mm-glass); border: .5px solid var(--mm-line); box-shadow: none;
  transform-origin: 24px 0; animation: mm-drop-in .3s cubic-bezier(.2, .9, .3, 1.08) 30ms both; }
.mm.mine .mm-menu { transform-origin: calc(100% - 24px) 0; }
.mm-menu button { display: flex; align-items: center; gap: 14px; width: 100%; height: 46px; padding: 0 14px; border-radius: 14px;
  color: var(--mm-ink); font-size: var(--fs-md); font-weight: 500; text-align: left; transition: background .12s; }
.mm-menu button:active { background: var(--mm-press); }
.mm-menu button svg { width: 20px; height: 20px; color: var(--mm-icon); flex: none; }
.mm-menu button span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* + : more reactions, where the menu was. */
.mm-more { position: absolute; padding: 8px; border-radius: 22px; background: var(--mm-glass); border: .5px solid var(--mm-line); box-shadow: none;
  display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); grid-auto-rows: 44px; align-content: start; gap: 2px;
  overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y; scrollbar-width: none; animation: mm-drop-in .26s cubic-bezier(.2, .9, .3, 1.08) both; }
.mm-more::-webkit-scrollbar { display: none; }
.mm-more button { border-radius: 14px; display: flex; align-items: center; justify-content: center; font-size: var(--fs-lg); line-height: 1; transition: transform .12s ease, background .15s; }
.mm-more button:active { transform: scale(.82); }
.mm-more button.on { background: var(--mm-on); }
@media (hover: hover) {
  :is(.mm-bar, .mm-more) button:hover:not(.on), .mm-menu button:hover { background: var(--mm-press); }
}
/* Going away: the message settles back where it was, the rest fades. */
.mm.closing .mm-scrim { animation: mm-fade-out .2s ease-in both; }
.mm.closing .mm-msg { animation: mm-settle .2s cubic-bezier(.4, 0, .6, 1) both; }
.mm.closing :is(.mm-bar, .mm-menu, .mm-more) { animation: mm-out .16s ease-in both; }
@keyframes mm-fade { from { opacity: 0; } }
@keyframes mm-fade-out { to { opacity: 0; } }
@keyframes mm-lift { 0% { transform: translateY(var(--from, 0px)); } 60% { transform: translateY(0) scale(1.02); } 100% { transform: none; } }
@keyframes mm-settle { from { transform: none; } to { transform: translateY(var(--from, 0px)); } }
@keyframes mm-in { from { opacity: 0; transform: translateY(10px) scale(.86); } }
@keyframes mm-drop-in { from { opacity: 0; transform: translateY(-10px) scale(.9); } }
@keyframes mm-pop { from { opacity: 0; transform: scale(.35); } }
@keyframes mm-out { to { opacity: 0; transform: scale(.94); } }
@media (prefers-reduced-motion: reduce) {
  .mm-msg, .mm-bar, .mm-bar button, .mm-menu, .mm-more, .mm.closing :is(.mm-msg, .mm-bar, .mm-menu, .mm-more), .reacts, .reply-bar { animation: none; }
  .mm-scrim { animation-duration: .01s; }
}
@media (prefers-reduced-transparency: reduce) {
  .mm-scrim { -webkit-backdrop-filter: none; backdrop-filter: none; }
}
/* The bubble held: its copy stands in for it until the menu has gone. */
.mm-held { visibility: hidden; }
/* On a phone, holding a bubble opens its menu, not the phone's text
   selection; Select, in the menu, selects its words (.selecting) until the
   selection leaves it. */
@media (hover: none) {
  .chat-inner .bubble { -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
}
.chat-inner .bubble.selecting { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; box-shadow: 0 0 0 2px color-mix(in srgb, var(--blue) 55%, transparent); }
/* Reactions on a message, at its bubble's bottom corner: the user's and the
   bots'. Ringed in the page's color, so they sit cut out of the bubble. */
.reacts { position: relative; z-index: 1; align-self: flex-start; display: inline-flex; align-items: center; gap: 2px; min-height: 26px; margin: -12px 0 0 14px; padding: 3px 7px;
  border-radius: 13px; background: #2c2c2e; box-shadow: 0 0 0 2px var(--bg); font-size: var(--fs-md); line-height: 20px; cursor: pointer;
  animation: mm-pop .36s cubic-bezier(.2, .9, .3, 1.3); }
:root[data-theme="dark"] .reacts { background: #3c3c3c; }
:root[data-theme="light"] .reacts { background: #fff; box-shadow: 0 0 0 2px var(--bg); }
.msg.user .reacts { align-self: flex-end; margin: -10px 14px 0 0; }
/* Above a message sent with Reply: what it answers, quietly; tapping it goes there. */
.reply-quote { display: flex; flex-direction: column; gap: 2px; max-width: min(78%, 520px); margin: 0 4px 4px; padding: 7px 12px 8px; border-radius: 16px;
  border: 1px solid var(--border); color: var(--muted); font-size: var(--fs-sm); line-height: 1.3; text-align: left; }
.reply-quote:active { background: var(--surface); }
.rq-who { display: inline-flex; align-items: center; gap: 5px; color: var(--text-2); font-size: var(--fs-sm); font-weight: 500; }
.rq-text { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.msg.flash .bubble { animation: msg-flash 1.4s ease-out; }
@keyframes msg-flash { 0%, 35% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--blue) 55%, transparent); } 100% { box-shadow: 0 0 0 3px transparent; } }
/* Over the prompt bar while replying: to what, and × to not. */
.reply-bar { display: flex; align-items: center; gap: 10px; margin: 0 2px 6px; padding: 7px 6px 7px 14px; border-radius: 18px;
  background: var(--surface); border: 1px solid var(--border); color: var(--muted); transform-origin: 50% 100%; animation: mm-in .26s cubic-bezier(.2, .9, .3, 1.08) both; }
:root[data-theme="light"] .reply-bar { background: #f4f8f8; }
.reply-bar > svg { flex: none; color: var(--blue); }
.rb-body { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: var(--fs-sm); line-height: 1.3; }
.rb-body b { color: var(--text); font-weight: 500; }
.rb-body span { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.rb-x { width: 32px; height: 32px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; color: var(--muted); flex: none; }
.rb-x:active { background: var(--surface-2); }
.composer.one-line .reply-bar { max-width: 960px; margin: 0 auto 8px; }

/* ---------- Take Photo: the camera, over everything (src/ui/camera.js), black in light and dark alike ---------- */
.cam { position: fixed; inset: 0; z-index: 70; display: flex; flex-direction: column; background: #000; color: #fff; animation: fade .2s ease-out;
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; touch-action: manipulation; }
.cam-view { position: relative; flex: 1; min-height: 0; overflow: hidden; }
.cam-view video, .cam-view img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: #000; border-radius: 0; }
.cam-view video { transition: opacity .2s; }
.cam-view video.mirror { transform: scaleX(-1); }
.cam-view video.off { opacity: 0; }
.cam-flash { position: absolute; inset: 0; background: #fff; pointer-events: none; animation: cam-flash .28s ease-out forwards; }
@keyframes cam-flash { from { opacity: .75; } to { opacity: 0; } }
.cam-msg { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.cam-msg .spinner { width: 28px; height: 28px; border-width: 3px; border-color: rgba(255, 255, 255, .25); border-top-color: #fff; }
.cam-top { position: absolute; top: 0; left: 0; right: 0; display: flex; padding: calc(var(--safe-top) + 10px) 16px 10px; pointer-events: none; }
.cam-btn { pointer-events: auto; width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; flex: none;
  background: rgba(0, 0, 0, .45); color: #fff; -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); }
.cam-btn:active { background: rgba(255, 255, 255, .2); }
.cam-btn svg { width: calc(var(--round-btn, var(--tap)) * 6 / 11); height: calc(var(--round-btn, var(--tap)) * 6 / 11); }
.cam-bar { flex: none; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; min-height: 112px; padding: 16px 24px calc(16px + var(--safe-bottom)); }
.cam-bar > :last-child { justify-self: end; }
.cam-bar .cam-btn { background: rgba(255, 255, 255, .14); }
.cam-shutter { width: 74px; height: 74px; border-radius: 50%; padding: 5px; border: 4px solid #fff; background: none; }
.cam-shutter::after { content: ""; display: block; width: 100%; height: 100%; border-radius: 50%; background: #fff; transition: transform .12s ease-out; }
.cam-shutter:active::after { transform: scale(.88); }
.cam-shutter:disabled { opacity: .4; }
.cam-shutter:focus-visible { outline: 2px solid #fff; outline-offset: 4px; }
.cam-word { justify-self: start; min-height: 44px; padding: 0 6px; color: #fff; font-size: var(--fs-md); }
.cam-word.use { justify-self: end; padding: 0 18px; border-radius: 999px; background: #fff; color: #000; font-weight: 500; }
.cam-word.use:active { background: rgba(255, 255, 255, .8); }
.cam-word:focus-visible, .cam-btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; border-radius: 999px; }
@media (prefers-reduced-motion: reduce) { .cam, .cam-flash { animation: none; } .cam-flash { display: none; } }

/* ---------- the gallery: pictures full screen (src/ui/gallery.js), dark in light and dark alike ---------- */
.att-img, .gen-image, .md img, .pending-att:not(.video) img { cursor: zoom-in; }
.gal { position: fixed; inset: 0; z-index: 80; color: #fff; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
  /* Where pictures are: between the top bar and the strip, and on a computer clear of the arrows at the sides. */
  --gal-top: calc(var(--safe-top) + var(--round-btn, var(--tap)) + 20px); --gal-bottom: calc(var(--safe-bottom) + 24px); --gal-side: 12px; }
.gal.multi { --gal-bottom: calc(var(--safe-bottom) + 76px); }
@media (hover: hover) and (pointer: fine) { .gal.multi { --gal-side: calc(var(--round-btn, var(--tap)) + 32px); } }
.gal-veil { position: absolute; inset: 0; background: rgba(0, 0, 0, .93); -webkit-backdrop-filter: blur(28px) saturate(1.3); backdrop-filter: blur(28px) saturate(1.3);
  transition: opacity .32s ease; }
.gal-probe { position: absolute; left: var(--gal-side); right: var(--gal-side); top: var(--gal-top); bottom: var(--gal-bottom); visibility: hidden; pointer-events: none; }
.gal-stage { position: absolute; inset: 0; overflow: hidden; touch-action: none; }
/* Each picture a screen wide, with a little room between them as they slide by. */
.gal-track { position: absolute; top: 0; bottom: 0; left: -12px; right: -12px; display: flex; transition: transform .34s cubic-bezier(.2, .9, .25, 1), opacity .2s ease; will-change: transform; }
.gal-slide { position: relative; flex: 0 0 100%; height: 100%; }
.gal-frame { position: absolute; top: var(--gal-top); bottom: var(--gal-bottom); left: calc(var(--gal-side) + 12px); right: calc(var(--gal-side) + 12px);
  display: flex; align-items: center; justify-content: center; }
.gal-img { display: block; flex: none; max-width: none; transform-origin: 50% 50%; will-change: transform; -webkit-user-drag: none;
  -webkit-clip-path: inset(0 round 0); clip-path: inset(0 round 0);
  transition: transform .32s cubic-bezier(.2, .9, .25, 1), clip-path .32s cubic-bezier(.2, .9, .25, 1), -webkit-clip-path .32s cubic-bezier(.2, .9, .25, 1), opacity .26s ease; }
.gal.moving .gal-img, .gal.moving .gal-track, .gal.moving .gal-veil, .gal.flying .gal-img, .gal.flying .gal-veil { transition: none; }
.gal.opening:not(.flying) .gal-track { opacity: 0; }
.gal-top { position: absolute; top: 0; left: 0; right: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: calc(var(--safe-top) + 10px) 16px 28px; background: linear-gradient(rgba(0, 0, 0, .5), transparent); transition: opacity .22s ease; }
.gal-count { font-size: var(--fs-sm); font-weight: 500; font-variant-numeric: tabular-nums; color: rgba(255, 255, 255, .92); }
.gal-btn { width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; flex: none; color: #fff;
  background: rgba(255, 255, 255, .13); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); transition: background .15s, opacity .22s ease; }
.gal-btn:active { background: rgba(255, 255, 255, .26); }
.gal-btn svg { width: calc(var(--round-btn, var(--tap)) * 6 / 11); height: calc(var(--round-btn, var(--tap)) * 6 / 11); }
.gal-btn:disabled { opacity: .25; cursor: default; }
.gal-btn:focus-visible, .gal-strip button:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.gal-side { position: absolute; top: 50%; margin-top: calc(var(--round-btn, var(--tap)) / -2); }
.gal-side.prev { left: 16px; }
.gal-side.next { right: 16px; }
.gal-side:disabled { opacity: 0; pointer-events: none; }
@media (hover: none) { .gal-side { display: none; } }
@media (hover: hover) { .gal-btn:hover:not(:disabled) { background: rgba(255, 255, 255, .22); } }
.gal-strip { position: absolute; left: 0; right: 0; bottom: 0; display: flex; gap: 6px; overflow-x: auto; scrollbar-width: none;
  padding: 26px 16px calc(var(--safe-bottom) + 14px); background: linear-gradient(transparent, rgba(0, 0, 0, .5)); transition: opacity .22s ease; }
.gal-strip::-webkit-scrollbar { display: none; }
.gal-strip button { flex: none; width: 44px; height: 44px; padding: 0; border-radius: 10px; overflow: hidden; opacity: .5; transition: opacity .2s ease, transform .2s ease, box-shadow .2s ease; }
.gal-strip button:first-child { margin-left: auto; }
.gal-strip button:last-child { margin-right: auto; }
.gal-strip button.on { opacity: 1; transform: scale(1.1); box-shadow: 0 0 0 2px #fff; }
.gal-strip img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gal.bare .gal-top, .gal.bare .gal-strip, .gal.bare .gal-side,
.gal.opening .gal-top, .gal.opening .gal-strip, .gal.opening .gal-side,
.gal.closing .gal-top, .gal.closing .gal-strip, .gal.closing .gal-side { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { .gal-img, .gal-track, .gal-veil { transition: none; } }

/* ---------- sheets ---------- */
.sheet-scrim { position: fixed; inset: 0; background: var(--scrim); z-index: 50; animation: fade .45s cubic-bezier(.2, .8, .2, 1); }
.sheet { position: fixed; left: 0; right: 0; bottom: 0; top: calc(var(--safe-top) + 12px); z-index: 51; background: var(--sheet); border-radius: var(--radius-sheet) var(--radius-sheet) 0 0; display: flex; flex-direction: column; animation: rise .45s cubic-bezier(.2, .8, .2, 1); box-shadow: none; overflow: hidden; margin: 0 auto; max-width: 720px; }
.sheet.inset { margin: 0 8px; }
.sheet-head { display: flex; align-items: center; gap: 16px; padding: 18px 18px 8px; flex: none; min-height: calc(var(--round-btn, var(--tap)) + 28px); }
.sheet-head h2 { margin: 0; font-size: var(--fs-md); font-weight: 500; flex: 1; }
.sheet-head .circle-btn { background: var(--sheet-btn); }
.sheet-body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 8px 18px calc(28px + var(--safe-bottom)); }
/* Pulled down from its top, what's on a sheet doesn't bounce: the finger is sliding the sheet down (useSheetSwipe). */
.sheet:not(.drawer) .sheet-body { overscroll-behavior-y: none; }
.sheet-foot { flex: none; padding: 12px 20px calc(16px + var(--safe-bottom)); }
@keyframes rise { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
/* Each sheet sits in a .sheet-layer (src/ui/app.js), which lays nothing out
   itself. A sheet slides up in 450 ms, easing off as it arrives, and closing
   slides back down in 450 ms, the way up played backwards (the easing
   mirrored, like the drawer's), its dimming fading with it; taps go past it
   meanwhile. A finger slides it down too, and back up: from its top, or from
   anywhere on it scrolled to its top (useSheetSwipe in src/ui/components.js,
   which moves it and its dimming itself). Settings, the drawer, slides
   itself. */
.sheet-layer { display: contents; }
.sheet-layer.closing { pointer-events: none; }
.sheet-layer.closing .sheet:not(.drawer), .sheet-layer.closing .hello.in-app { animation: sink .45s cubic-bezier(.8, 0, .8, .2) forwards; }
.sheet-layer.closing .sheet-scrim:not(.drawer-scrim) { animation: fade-out .45s cubic-bezier(.8, 0, .8, .2) forwards; }
@keyframes sink { from { transform: translateY(0); } to { transform: translateY(100%); } }
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .sheet:not(.drawer), .sheet-scrim, .hello.in-app { animation: none; }
}
@media (min-width: 900px) {
  .sheet { top: 5vh; bottom: 5vh; left: 50%; right: auto; width: 560px; transform: translateX(-50%); border-radius: 30px; animation: popc .22s ease-out; }
  @keyframes popc { from { opacity: 0; transform: translateX(-50%) scale(.96); } to { opacity: 1; transform: translateX(-50%) scale(1); } }
  .sheet-layer.closing .sheet:not(.drawer) { animation: popc-out .2s ease-in forwards; }
  @keyframes popc-out { from { opacity: 1; transform: translateX(-50%) scale(1); } to { opacity: 0; transform: translateX(-50%) scale(.96); } }
}
/* Settings (src/ui/settings.js, useDrawer in src/ui/components.js): a drawer
   from the left, full height, square-cornered and 85px short of the right
   edge, that slides out over the app, as Expensify's does: the agent list and
   the chat stay where they are, dimming under it (the drawer's scrim), and
   what's on the menu slides with it. It opens over 450ms and closes the same
   way, back out to the left, the way out being the way in played backwards
   (the easing mirrored), on every platform.
   The page's .drawer-open class says where it is, so a move always carries on
   from wherever it is (.drawer-mounted stays on until it's gone). While a
   finger swipes it out (right, on the chat list or in a chat) or back (left,
   on it or the dimmed strip beside it), --drawer-drag is how far it is from
   fully open (px, 0 or less) and --drawer-shown how much of it shows; let go,
   and it goes on to the end the swipe was carrying it to, at the finger's
   speed (.drawer-flung, below). It's at most 560 px wide on a phone or an
   iPad, 360 px on a computer (the web app and the Mac app). */
:root {
  --drawer-w: min(calc(100vw - 85px), 560px);
  --drawer-in: cubic-bezier(.2, .8, .2, 1);
  --drawer-out: cubic-bezier(.8, 0, .8, .2);
  /* The menu slides as a chat does (--chat-in and --chat-out, with the chat's
     slide): coming out, it sets off at once and eases into place (--menu-in),
     and going, it sets off gently and glides away (--menu-out). After a swipe
     it carries on at the finger's speed instead (settle). */
  --menu-in: var(--chat-in);
  --menu-out: var(--chat-out);
  /* The drawer's colors: --drawer-bg is the bot list's on a phone (a chat
     stays on the page's), and --drawer-backdrop the menu's background, set
     apart from the list, as Claude's is. The status bar takes the backdrop
     while the menu is out (pageColor in src/ui/components.js). The menu's own
     pages (Usage, Plugins and the rest) and Settings, the page of its own, are
     pages to read, on --drawer-page. */
  --drawer-bg: var(--bg);
  --drawer-backdrop: #000;
  --drawer-page: #000;
}
:root[data-theme="dark"] { --drawer-backdrop: #171717; --drawer-page: #171717; }
/* Light: ChatGPT's white and sidebar gray. The bot list is white, as a chat
   is (--drawer-bg), its bots' names (--drawer-text-primary) and last messages
   (--drawer-text-secondary) in the app's two shades on it, and its buttons
   at the top white (--drawer-button-bg) with icons in the text's color
   (--drawer-button-icon), with no line or shadow, a
   touch grayer pressed (--drawer-button-press); boxes on it have a hairline
   of the navy (--drawer-line). The menu beside it is ChatGPT's sidebar gray,
   #F9F9F9 (--drawer-backdrop), its headings the navy softened into the gray,
   so they read on it; its cards stay white, with a hairline so they show on
   the gray, and its pages are on the same gray. Holli Bot for iPhone's status
   bar text is dark over the gray, as over the list or a chat (it goes by the
   color's brightness: isDark in ios/HolliBot/Web/Views.swift). */
:root[data-theme="light"] {
  --drawer-bg: #ffffff; --drawer-backdrop: #f9f9f9; --drawer-page: #f9f9f9;
  --drawer-text-primary: var(--text); --drawer-text-secondary: var(--muted);
  --drawer-button-bg: #ffffff; --drawer-button-icon: var(--text);
  --drawer-button-press: color-mix(in srgb, var(--drawer-text-primary) 7%, var(--drawer-button-bg));
  --drawer-line: color-mix(in srgb, var(--drawer-text-primary) 12%, transparent);
}
.sheet.drawer { top: 0; bottom: 0; left: 0; right: auto; width: var(--drawer-w); max-width: none; margin: 0; padding: 0; border-radius: 0; overflow: visible;
  animation: none; transform: translateX(-100%); transition: transform var(--drawer-ms, .45s) var(--menu-out);
  will-change: transform; touch-action: pan-y; box-shadow: none; border-right: 1px solid var(--border); }
/* What's on the menu goes on its background, the backdrop, and anything on it
   drawn on the app's background (--bg) is on the menu's. Its own pages are
   pages to read, on --drawer-page; the menu itself is .main-page
   (src/ui/settings.js). Its right edge is a hairline, so it shows over the
   dimmed app in Black and Dark too. */
:root[data-theme] .sheet.drawer, .sheet.drawer { --bg: var(--drawer-backdrop); background: var(--bg); }
.sheet.drawer:not(.main-page) { --drawer-backdrop: var(--drawer-page); }
/* What's on it (.drawer-still, in .drawer-clip: Sheet in src/ui/components.js) slides with it. */
.drawer-clip { position: absolute; inset: 0; overflow: hidden; }
.drawer-still { display: flex; flex-direction: column; height: 100%; padding: var(--safe-top) 0 0 var(--safe-left); }
.drawer-open .sheet.drawer { transform: translateX(var(--drawer-drag, 0px)); transition-timing-function: var(--menu-in); }
/* The app holds still under it, a layer of its own all the time, so pulling
   the menu out doesn't change the layers under the finger (iOS cancels a
   touch when that happens). */
.app { will-change: transform; }
/* With the menu out, the app dims under it (the page's dimming, --scrim, as
   under a sheet), as much as the menu shows (--drawer-shown under a finger),
   and stays dimmed under Settings' page. The dimming is on the drawer's
   scrim, which closes the drawer when tapped and takes a swipe left on the
   strip beside it. */
.sheet-scrim.drawer-scrim { background: none; animation: none; touch-action: none; }
.drawer-scrim::before { content: ""; position: absolute; inset: 0; background: var(--scrim); opacity: 0; pointer-events: none; will-change: opacity;
  transition: opacity var(--drawer-ms, .45s) var(--menu-out); }
.drawer-open .drawer-scrim::before { opacity: var(--drawer-shown, 1); transition-timing-function: var(--menu-in); }
.page-mounted .drawer-scrim::before { opacity: 1; }
.drawer-dragging .sheet.drawer, .drawer-dragging .drawer-scrim::before { transition: none; }
/* Let go of mid-swipe: it carries on from where the finger left it, at the
   finger's speed, easing to a stop at the end it was thrown to, on a curve of
   its own (--drawer-ease and --drawer-ms: settle in src/ui/components.js),
   and the dimming with it. */
.drawer-flung .sheet.drawer, .drawer-flung .drawer-scrim::before { transition-timing-function: var(--drawer-ease, var(--drawer-in)); }
/* Settings, the page the gear on the drawer's account card opens
   (src/ui/settings.js SettingsPage, usePushPage in src/ui/components.js): a
   page of its own over the whole screen. It slides in from the right edge of
   the screen over 450ms, easing off as it arrives: it crosses the dimmed
   strip of the app beside the drawer, and once its edge reaches the drawer's
   (a couple of frames on a phone) it pushes the drawer out to the left, the
   two joined (usePushPage works out when they meet and the drawer's curve
   from there: --push-*). Back slides it all the way out to the right edge
   again, on the same curve, so it's moving from the first moment, and the
   drawer comes back in joined to its edge until it's in its place, where it
   stops, and the page goes on over the dimmed strip to the edge (--pull-*).
   .page-mounted is on while it's there, .page-open while it's in or coming
   in. */
.push-page { position: fixed; inset: 0; z-index: 51; display: flex; flex-direction: column; overflow: hidden; background: var(--bg);
  transform: translateX(100%); transition: transform var(--drawer-ms, .45s) var(--drawer-in); will-change: transform; }
.page-open .push-page { transform: translateX(0); }
.page-open .sheet.drawer { transition: transform var(--push-ms, .45s) var(--push-ease, cubic-bezier(.2, .8, .2, 1)) var(--push-delay, 0s); }
.page-mounted:not(.page-open) .sheet.drawer { transition: transform var(--pull-ms, .45s) var(--pull-ease, cubic-bezier(.2, .8, .2, 1)); }
/* A swipe right on the page slides it off under the finger (usePushPage):
   it leaves sideways moves to the page, as the drawer does, and follows the
   finger (--page-drag), the drawer coming back in joined to its edge until
   it's in its place (the drawer's --drawer-drag). Let go, and it carries on
   to the end it was going to on a swipe's curve (--page-ease), the drawer
   joined to it (--pull-*, --push-*). */
.push-page { touch-action: pan-y pinch-zoom; }
.page-dragging .push-page { transform: translateX(var(--page-drag, 0px)); }
.page-dragging :is(.push-page, .sheet.drawer) { transition: none; }
.page-flung .push-page { transition-timing-function: var(--page-ease, var(--drawer-in)); }
/* On a computer (the website, Holli Bot for Mac and Windows: the drawer's
   360 px), Settings stops at the menu's edge instead (.side, usePushPage in
   src/ui/components.js): it slides in from the right edge of the window as
   ever, and stops exactly at the menu's edge (--page-x: the menu's width),
   covering the dimmed app beside it, square-cornered as the menu is, the
   menu staying where it is. Back (or Escape) slides it back out to the
   right; the menu closing takes it along. */
.push-page.side { left: var(--page-x, 0px); }
:root:not(.drawer-open) .push-page.side { transform: translateX(100%); }
/* There, once it's in, Back closes it, sliding it out to the right: its
   arrow spins round to point that way (a half turn), and back again on one
   of its pages, where Back goes back (SettingsPage). With Reduce Motion it
   just points the way. */
.page-back svg { transition: transform .4s cubic-bezier(.2, .8, .2, 1); }
.page-back.closes svg { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .page-back svg { transition: none; } }
@media (prefers-reduced-motion: reduce) { .push-page { transition: none; } }

/* ---------- grouped settings rows (iOS style) ---------- */
.group { background: var(--elevated); border-radius: var(--radius-card); margin: 14px 0; overflow: hidden; }
.group-label { color: var(--muted); font-size: var(--fs-sm); margin: 22px 20px -6px; }
.group-note { color: var(--muted); font-size: var(--fs-sm); margin: -6px 20px 14px; line-height: 1.4; }
.row { display: flex; align-items: center; gap: 12px; min-height: 58px; padding: 12px 18px 12px 20px; width: 100%; text-align: left; position: relative; }
.row + .row::before, .row + .row-wrap::before, .row-wrap + .row::before { content: ""; position: absolute; top: 0; left: 20px; right: 0; height: 1px; background: var(--border); }
.row-wrap { position: relative; }
button.row:active { background: var(--surface-2); }
.row .label { flex: 1; min-width: 0; }
.row .label .t { font-size: var(--fs-md); }
.row .label .s { color: var(--muted); font-size: var(--fs-sm); margin-top: 3px; line-height: 1.35; }
.row .value { color: var(--muted); font-size: var(--fs-md); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 55%; }
.row .chev { width: 18px; height: 18px; color: var(--dim); flex: none; }
.row.danger .t { color: var(--red); }
.row.center { justify-content: center; }
.row.center .t { text-align: center; }
.row input[type="text"], .row input[type="password"], .row input[type="url"], .row input[type="email"], .row select { flex: 1; min-width: 0; background: none; border: 0; outline: none; text-align: right; color: var(--text-2); font-size: var(--fs-md); }
.row select { direction: rtl; }
.row select option { direction: ltr; }

/* toggles */
/* Every switch, in Settings and everywhere else, is the size and look of
   iOS's: 51 × 31, off in --track so it's plainly visible (Toggle's `small`
   is the same size). */
.toggle { width: 51px; height: 31px; border-radius: 16px; background: var(--track); position: relative; flex: none; transition: background .2s; }
.toggle.on { background: var(--green); }
.toggle::after { content: ""; position: absolute; top: 2px; left: 2px; width: 27px; height: 27px; border-radius: 50%; background: #fff; transition: transform .22s cubic-bezier(.3, 1.3, .5, 1); box-shadow: none; }
.toggle.on::after { transform: translateX(20px); }

/* ---------- inside the Settings drawer, and Settings, the page its gear opens (src/ui/settings.js) ----------
   A native-feeling settings list on a bright background: related settings
   share a light gray surface with soft corners and no border, with thin
   lines between rows set in to the text. Who is signed in and Usage open it,
   and small quiet headings name the sections below. Every row has the same
   padding and height, and what sits at its right (a value, chevron or
   switch) lines up on one edge. The pages inside the drawer, and the
   Settings page and its pages, are made of the same surfaces, rows,
   headings and notes. */
/* Colours picked to read clearly on a phone: surfaces that stand apart from
   the background, descriptions and headings in a mid gray rather than a pale
   one, and switches whose off state is plainly visible. In light, the
   surfaces are the same gray as the app's other gray boxes (--surface-2), and
   code on them is on white so it still shows. */
/* In Black, the cards are the agents' bubbles' color (#1e1e1e, two shades under the #252528 they were: the owner's
   call, any box the bubbles' old color takes their new one), pressed a step lighter. */
:is(.drawer, .settings-page) { --card: #1e1e1e; --card-press: #2a2a2c; --card-line: rgba(255, 255, 255, .11); --meter: rgba(255, 255, 255, .16); --radius-group: 16px; }
:root[data-theme="dark"] :is(.drawer, .settings-page) { --card: #2a2a2a; --card-press: #353535; }
:root[data-theme="light"] :is(.drawer, .settings-page) { --card: #ffffff; --card-press: #e8efee; --card-line: rgba(20, 52, 58, .1); --meter: rgba(20, 52, 58, .12); }
.drawer .sheet-head { padding: 16px 16px 4px; min-height: calc(var(--round-btn, var(--tap)) + 24px); gap: 14px; }
.drawer .sheet-body { padding: 4px 16px calc(28px + var(--safe-bottom)); touch-action: pan-y; }
.drawer.headless .sheet-body { padding-top: 14px; } /* Settings itself: no header above it */
.sheet.drawer.main-page .sheet-body { padding-top: 21px; } /* the menu: its cards a little lower, the same 16 px either side */
:is(.drawer, .settings-page) .group { background: var(--card); border: 0; border-radius: var(--radius-group); margin: 0; overflow: hidden; }
/* In Light the cards are white on the menu's pale gray: a hairline shows where they are. */
:root[data-theme="light"] :is(.drawer, .settings-page) .group { box-shadow: 0 0 0 1px var(--card-line); }
:is(.drawer, .settings-page) .group + .group { margin-top: 14px; }
:is(.drawer, .settings-page) .group-label { margin: 22px 16px 7px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 500; letter-spacing: 0; text-transform: none; }
:is(.drawer, .settings-page) .group-note { margin: 7px 16px 12px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; font-weight: 450; }
:is(.drawer, .settings-page) .row { width: 100%; margin: 0; min-height: 50px; padding: 9.5px 16px; gap: 12px; border-radius: 0; }
/* The line between rows, from the text's edge to the surface's. */
:is(.drawer, .settings-page) .row + .row::before, :is(.drawer, .settings-page) .row + .row-wrap::before, :is(.drawer, .settings-page) .row-wrap + .row::before, .drawer .drawer-account + .row::before {
  content: ""; position: absolute; top: 0; left: 16px; right: 0; display: block; height: 1px; background: var(--card-line); }
:is(.drawer, .settings-page) button.row:active { background: var(--card-press); }
@media (hover: hover) { :is(.drawer, .settings-page) button.row:hover { background: var(--card-press); } }
:is(.drawer, .settings-page) .row .label .t { color: var(--text); font-size: var(--fs-md); line-height: 21px; font-weight: 500; }
:is(.drawer, .settings-page) .row .label .s { margin-top: 2px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; }
/* A row's name, and the line under it, each keep to one line, so every row
   stays its height: what's too long fades out at its end, as a chat's name
   and preview do in the bot list, rather than running onto another line.
   What has to be read in full goes in a note under the surface. */
:is(.drawer, .settings-page) .row .label :is(.t, .s) { display: block; white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
:is(.drawer, .settings-page) .row .value { color: var(--muted); font-size: var(--fs-md); line-height: 21px; font-weight: 450; }
/* A chevron's stroke sits 5 px inside its box: pulled out to the edge a
   switch ends on, and in towards a value. The external-link icon fills its box. */
:is(.drawer, .settings-page) .row .chev { width: 16px; height: 16px; margin: 0 -5px 0 -9px; color: var(--dim); stroke-width: 2.5; }
:is(.drawer, .settings-page) .row .chev.ext { margin: 0 -1px 0 -4px; stroke-width: 2.25; }
:is(.drawer, .settings-page) .row > svg:first-child { flex: none; color: var(--text-2); }
:is(.drawer, .settings-page) .row.danger .label .t { color: var(--red); }
:is(.drawer, .settings-page) .bubble.plain-bot, :root[data-theme="light"] :is(.drawer, .settings-page) .bubble.plain-bot { padding: 0; border-radius: 0; background: none; }
:is(.drawer, .settings-page) .mem { margin: 8px 0; padding: 0; border-radius: 0; background: none; }

/* The top of Settings, one surface: who is signed in, then Usage, a row
   with what's left at its right and a slim meter under it. */
.drawer-account { display: flex; align-items: center; gap: 12px; padding: 12px 16px; }
.drawer-account .initials { width: 46px; height: 46px; font-size: var(--fs-md); font-weight: 500; letter-spacing: .3px; border: 0; background: #3c3c40; color: var(--text); }
:root[data-theme="light"] .drawer-account .initials { background: #dde5e5; color: var(--text-2); }
.drawer-account .who { flex: 1; min-width: 0; }
.drawer-account .name { font-size: var(--fs-md); line-height: 21px; font-weight: 500; white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
/* Lucide's gear, at the right, where it always was: a round button around it
   (44 px, for a finger) opens Settings, the page of its own (below). */
.drawer-account .gear { flex: none; width: 44px; height: 44px; margin-right: -11px; display: flex; align-items: center; justify-content: center;
  border-radius: 50%; color: var(--muted); transition: background .15s; }
.drawer-account .gear svg { width: 22px; height: 22px; }
.drawer-account .gear:active { background: var(--card-press); }
@media (hover: hover) { .drawer-account .gear:hover { background: var(--card-press); } }
/* The app's own version (Holli Bot for iPhone's, or for Mac or Windows) at
   the bottom right of the menu, under everything else (src/ui/settings.js).
   Quiet, in figures that line up. */
.sheet.drawer .sheet-foot { padding: 6px 20px calc(10px + var(--safe-bottom)); }
.sheet.drawer:has(.sheet-foot) .sheet-body { padding-bottom: 12px; }
.drawer-version { text-align: right; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-variant-numeric: tabular-nums; letter-spacing: .2px; }
.drawer .usage-row { flex-wrap: wrap; row-gap: 9px; padding-bottom: 13px; }
.drawer .usage-row .credits-bar { flex: 0 0 100%; height: 5px; margin: 0; border-radius: 3px; background: var(--meter); }

/* The menu itself (Settings' first page, .main-page: src/ui/settings.js MainPage) is plain, in the
   appearance's own colors (the page #171717 in Dark, black in Black, pale gray in Light: --drawer-backdrop),
   its cards solid; each row has its Lucide icon, with room for it. */
.sheet.drawer.main-page .row { gap: 13px; }

/* On Free, above it all: Upgrade Plan, which opens the plan page over the app. */
.upgrade-card { position: relative; width: 100%; display: flex; align-items: center; gap: 12px; margin: 0 0 14px; padding: 13px 14px 13px 13px;
  border-radius: var(--radius-group); overflow: hidden; color: #fff; text-align: left;
  background: linear-gradient(118deg, #0a84ff 0%, #5e5ce6 58%, #bf5af2 100%);
  box-shadow: none; transition: transform .15s, filter .2s; }
/* In light, teal into deep teal, with its sparkle in amber. */
:root[data-theme="light"] .upgrade-card { background: linear-gradient(118deg, #087e8b 0%, #075c66 58%, #14343a 100%); box-shadow: none; }
:root[data-theme="light"] .upgrade-icon { color: var(--amber); }
.upgrade-card::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .18), rgba(255, 255, 255, 0) 60%); }
.upgrade-card:active { transform: scale(.985); filter: brightness(.94); }
@media (hover: hover) { .upgrade-card:hover { filter: brightness(1.06); } }
.upgrade-icon { flex: none; width: 38px; height: 38px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: rgba(255, 255, 255, .2); }
.upgrade-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.upgrade-title { font-size: var(--fs-md); line-height: 21px; font-weight: 500; letter-spacing: -.1px; }
.upgrade-sub { margin-top: 1px; font-size: var(--fs-sm); line-height: 17px; font-weight: 500; opacity: .88; white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
.upgrade-chev { flex: none; width: 16px; height: 16px; margin-right: -2px; opacity: .85; stroke-width: 2.5; }

/* ---------- Settings → Computer (src/ui/computers.js), under Routines ----------
   The computer the bots work on, as a card of its own at the top: its icon
   in a rounded tile, its name, and whether it's on (a dot and the words, in
   green when it's online, amber when it's on but out of reach, gray when
   it's off), then what it is (system, processor, cores) on one quiet line,
   and three rings, Memory, CPU and Disk, each filled as far as it's used,
   blue (teal in Light), amber from three quarters, red from nine tenths,
   with the percentage in the middle and what it means under it, read again
   every few seconds. Free up memory is a soft button across the card's foot.
   Under it, its actions in a list with tinted icon tiles, as iOS's Settings
   has them; the account's computers, each with its tile and state; and
   Upgrade Bot Computer. Everything sits on the menu's own cards (--card),
   so it's the same on a phone, in Holli Bot for iPhone, on the web and in
   Holli Bot for Mac and Windows, Black, Dark and Light; in a wide menu the
   rings grow. */
.pc-hero { position: relative; padding: 16px 16px 14px; border-radius: var(--radius-group); overflow: hidden;
  background: radial-gradient(130% 80% at 0% 0%, color-mix(in srgb, var(--pc-glow, transparent) 13%, transparent), transparent 62%), var(--card); }
:root[data-theme="light"] .pc-hero { box-shadow: 0 0 0 1px var(--card-line); }
.pc-hero:has(.pc-tile.ok) { --pc-glow: var(--green); }
.pc-hero:has(.pc-tile.warn) { --pc-glow: var(--orange); }
:is(.pc-tile, .pc-state) { --pc-tone: var(--muted); }
:is(.pc-tile, .pc-state).ok { --pc-tone: var(--green); }
:is(.pc-tile, .pc-state).warn { --pc-tone: var(--orange); }
:root[data-theme="light"] .pc-state.warn { --pc-ink: var(--amber-ink); }
.pc-state.off { --pc-ink: var(--muted); }
.pc-hero-top { display: flex; align-items: center; gap: 14px; }
.pc-tile { flex: none; width: 52px; height: 52px; border-radius: 15px; display: flex; align-items: center; justify-content: center;
  color: var(--pc-tone); background: color-mix(in srgb, var(--pc-tone) 16%, var(--card)); }
.pc-tile.sm { width: 34px; height: 34px; border-radius: 10px; }
.pc-hero-id { flex: 1; min-width: 0; }
.pc-name { margin: 0; color: var(--text); font-size: var(--fs-md); line-height: 23px; font-weight: 500; letter-spacing: -.2px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pc-state { display: flex; align-items: center; gap: 6px; min-width: 0; margin-top: 2px; color: var(--pc-ink, var(--pc-tone));
  font-size: var(--fs-sm); line-height: 20px; font-weight: 500; }
.pc-state > span:not(.pc-dot, .pc-tag) { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pc-dot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--pc-tone); }
.pc-state.ok .pc-dot { box-shadow: 0 0 0 3px color-mix(in srgb, var(--pc-tone) 24%, transparent); animation: pc-pulse 2.4s ease-in-out infinite; }
@keyframes pc-pulse { 50% { box-shadow: 0 0 0 5px color-mix(in srgb, var(--pc-tone) 4%, transparent); } }
.pc-tag { flex: none; display: inline-flex; align-items: center; height: 20px; padding: 0 7px; margin-left: 2px; border-radius: 6px;
  color: var(--blue); background: color-mix(in srgb, var(--blue) 15%, transparent); font-size: var(--fs-sm); line-height: 1; font-weight: 700; letter-spacing: .2px; }
.pc-tag.quiet { color: var(--muted); background: var(--meter); }
.pc-specs { margin: 12px 0 0; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
.pc-specs.wrap { white-space: normal; line-height: 1.45; -webkit-mask-image: none; mask-image: none; }
.pc-hero-note { margin: 12px 0 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; font-weight: 450; }

/* The rings: three across, with a hairline above them. */
.pc-gauges { container-type: inline-size; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;
  margin: 14px 0 0; padding: 16px 0 0; border-top: 1px solid var(--card-line); }
.pc-gauge { display: flex; flex-direction: column; align-items: center; min-width: 0; text-align: center; }
.pc-ring { --ring: var(--blue); position: relative; width: min(76px, 100%); aspect-ratio: 1; }
.pc-ring.mid { --ring: var(--orange); }
.pc-ring.high { --ring: var(--red-strong); }
:root[data-theme="light"] .pc-ring.high { --ring: var(--red); }
.pc-ring svg { display: block; width: 100%; height: 100%; transform: rotate(-90deg); }
.pc-ring circle { fill: none; stroke-width: 7; }
.pc-ring-track { stroke: var(--meter); }
.pc-ring-fill { stroke: var(--ring); stroke-linecap: round; stroke-dasharray: 100; stroke-dashoffset: 100;
  transition: stroke-dashoffset .7s cubic-bezier(.2, .8, .2, 1), stroke .4s ease; }
.pc-ring.unknown .pc-ring-fill { opacity: 0; }
.pc-ring-pct { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: var(--text);
  font-size: var(--fs-md); line-height: 1; font-weight: 700; letter-spacing: -.5px; font-variant-numeric: tabular-nums; }
.pc-ring-pct small { margin: 3px 0 0 1px; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; letter-spacing: 0; }
.pc-ring.unknown .pc-ring-pct { color: var(--muted); }
.pc-gauge-label { margin-top: 9px; color: var(--text); font-size: var(--fs-sm); line-height: 18px; font-weight: 500; }
.pc-gauge-detail { margin-top: 1px; color: var(--muted); font-size: var(--fs-sm); line-height: 17px; font-weight: 450; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
@container (min-width: 380px) {
  .pc-ring { width: 92px; }
  .pc-ring circle { stroke-width: 6.5; }
  .pc-ring-pct { font-size: var(--fs-lg); }
}

/* Free up memory, across the card's foot. */
.pc-free { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; height: 44px; margin-top: 16px; border-radius: 22px;
  background: var(--meter); color: var(--text); font-size: var(--fs-md); font-weight: 500; transition: background .15s, color .25s, transform .15s, opacity .2s; }
.pc-free:active:not(:disabled) { transform: scale(.985); }
@media (hover: hover) { .pc-free:hover:not(:disabled) { background: color-mix(in srgb, var(--text) 15%, var(--card)); } }
.pc-free:disabled { opacity: .45; cursor: default; }
.pc-free.done { color: var(--green); }
.pc-free .spinner { width: 17px; height: 17px; }

/* Its actions, and the account's computers, on the menu's cards. */
.pc-hero + .group, .pc-hero + .pc-use, .pc-use + .group { margin-top: 14px; }
.pc-hero + .group-label { margin-top: 22px; }
.pc-row-icon { --pc-icon: var(--muted); flex: none; width: 30px; height: 30px; border-radius: 9px; display: flex; align-items: center; justify-content: center;
  color: var(--pc-icon); background: color-mix(in srgb, var(--pc-icon) 16%, var(--card)); }
.pc-row-icon.star { --pc-icon: var(--orange); }
.pc-row-icon.screen { --pc-icon: var(--blue); }
.pc-row-icon.danger { --pc-icon: var(--red); }
:is(.drawer, .settings-page) .pc-actions .row + .row::before, :is(.drawer, .settings-page) .pc-list .row + .row::before { left: 58px; }
.pc-list .row { min-height: 60px; }
.pc-list .label .t .pc-tag { height: 18px; margin-left: 8px; vertical-align: 2px; }
.pc-list .label .s .pc-state { display: inline-flex; margin: 0; line-height: 18px; }
.pc-list .row .value { font-size: var(--fs-md); }
.pc-use { margin-top: 14px; }
.pc-loading { display: flex; justify-content: center; padding: 48px 0; }

/* Upgrade Bot Computer: a card with a bright tile, under the list. */
.pc-upgrade { width: 100%; display: flex; align-items: center; gap: 12px; margin: 22px 0 0; padding: 14px; border-radius: var(--radius-group);
  background: var(--card); color: var(--text); text-align: left; transition: background .15s, transform .15s; }
:root[data-theme="light"] .pc-upgrade { box-shadow: 0 0 0 1px var(--card-line); }
.pc-upgrade:active { transform: scale(.985); background: var(--card-press); }
@media (hover: hover) { .pc-upgrade:hover { background: var(--card-press); } }
.pc-upgrade-icon { flex: none; width: 40px; height: 40px; border-radius: 12px; display: flex; align-items: center; justify-content: center; color: #fff;
  background: linear-gradient(135deg, #0a84ff 0%, #5e5ce6 100%); box-shadow: none; }
:root[data-theme="light"] .pc-upgrade-icon { background: linear-gradient(135deg, #087e8b 0%, #14343a 100%); color: var(--amber); box-shadow: none; }
.pc-upgrade-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.pc-upgrade-title { font-size: var(--fs-md); line-height: 21px; font-weight: 500; }
.pc-upgrade-sub { margin-top: 1px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; white-space: nowrap; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent); }
.pc-upgrade-chev { flex: none; width: 16px; height: 16px; margin-right: -2px; color: var(--dim); stroke-width: 2.5; }
.pc-hero.empty .pc-setup { margin-top: 16px; }
@media (prefers-reduced-motion: reduce) {
  .pc-state.ok .pc-dot { animation: none; }
  .pc-ring-fill, .pc-free, .pc-upgrade { transition: none; }
}

/* ---------- Settings, the page the gear opens (src/ui/settings.js SettingsPage) ----------
   The drawer's surfaces and rows (above), on the menu's pages' background
   (--drawer-page), white in light. At the top, on the page's own background
   with no glass: Back, round like a chat's, and the page's name in the
   middle. The page scrolls below them, never under them: what scrolls up is
   cut off where the first surface rests. Its own list has an icon at the
   start of each row (Lucide's, 22 px), and the lines between rows run from
   the text to where the chevrons and switches end. At the bottom, Sign Out,
   a button across the page, and under it Holli Bot's AI and who is signed
   in. */
:root[data-theme="light"] .settings-page { --bg: var(--drawer-page); }
/* What's on the page sits in a panel: on a phone it is the page (it fills it); on a computer it's the window (below). */
.settings-page .page-panel { flex: 1; min-height: 0; width: 100%; display: flex; flex-direction: column; }
.page-head { flex: none; display: grid; grid-template-columns: var(--round-btn, var(--tap)) 1fr var(--round-btn, var(--tap)); align-items: center; gap: 10px;
  padding: calc(10px + var(--safe-top)) calc(16px + var(--safe-right)) 18px calc(16px + var(--safe-left)); }
.page-head h2 { margin: 0; text-align: center; font-size: var(--fs-md); line-height: 22px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.page-scroll { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; touch-action: pan-y;
  padding: 0 calc(16px + var(--safe-right)) calc(40px + var(--safe-bottom)) calc(16px + var(--safe-left)); }
.page-body { max-width: 600px; margin: 0 auto; }
/* Beside the side bar a computer's Settings has (below), or on its own: the header and the page under it. */
.set-main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
.set-nav, .set-close { display: none; }
.settings-page .group.with-icons .row + .row::before { left: 50px; right: 16px; }
.settings-signout { display: block; width: 100%; height: 46px; margin: 28px 0 0; border-radius: 23px; background: var(--card); color: var(--text);
  font-size: var(--fs-md); font-weight: 500; text-align: center; transition: background .15s; }
.settings-signout:active { background: var(--card-press); }
@media (hover: hover) { .settings-signout:hover { background: var(--card-press); } }
.settings-foot { margin: 16px 16px 0; color: var(--muted); font-size: var(--fs-sm); line-height: 20px; font-weight: 450; text-align: center; overflow-wrap: anywhere; }
/* General's foot on a computer: the version of Holli Bot running, at the right. */
.settings-foot.set-version { text-align: right; font-variant-numeric: tabular-nums; }
/* Account and About: who, or what, the page is about, big and centered at its top. */
.page-hero { display: flex; flex-direction: column; align-items: center; gap: 2px; margin: 6px 16px 24px; text-align: center; }
.page-hero .initials { width: 76px; height: 76px; margin-bottom: 12px; font-size: var(--fs-lg); font-weight: 500; letter-spacing: .5px; border: 0; background: #3c3c40; color: var(--text); }
:root[data-theme="light"] .page-hero .initials { background: #dde5e5; color: var(--text-2); }
.page-hero .avatar { margin-bottom: 12px; }
.page-hero .t { font-size: var(--fs-lg); line-height: 30px; font-weight: 700; overflow-wrap: anywhere; }
.page-hero .s { color: var(--muted); font-size: var(--fs-sm); line-height: 20px; font-weight: 450; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }

/* ---------- Settings → Plugins (src/ui/settings.js PluginsPage) ----------
   Laid out like a plugin store. Its header (Back, the title, and a rounded
   count of what's installed with their logos) and the search box and filter
   under it stay at the top, on frosted glass (HeadGlass), while the list
   scrolls under them. The plugins are in sections: a quiet heading, with
   View all at its right when there are more, over rows with no surface of
   their own. Each is a logo on a rounded tile (a picture, icons/plugins; a
   skill's is a puzzle piece on gray), the name and what it does in up to two
   lines, and a rounded Add at the right, or Added, grayed. */
.drawer.own-head .sheet-body { padding-top: 0; }
/* Plugins draws its own header, so the page has none (SettingsSheet in src/ui/settings.js): it starts under the status bar. */
.settings-page.own-head { padding-top: var(--safe-top); }
.settings-page.own-head .page-scroll { padding-top: 0; }
.plugins { --pill: #333336; --tile: #333336; --tile-ring: rgba(255, 255, 255, .07); }
:root[data-theme="dark"] .plugins { --pill: #3a3a3d; --tile: #3a3a3d; }
:root[data-theme="light"] .plugins { --pill: #ffffff; --tile: #ffffff; --tile-ring: rgba(20, 52, 58, .1); }
.plug-top { position: sticky; top: 0; z-index: 3; margin: 0 -16px; padding: 16px 16px 12px; }
.plug-head { display: flex; align-items: center; gap: 14px; min-height: 48px; }
.plug-head h2 { flex: 1; min-width: 0; margin: 0; font-size: var(--fs-md); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.plug-head .circle-btn { background: var(--sheet-btn); }
.plug-installed { flex: none; display: inline-flex; align-items: center; gap: 10px; height: var(--round-btn, var(--tap)); padding: 0 16px 0 10px; border-radius: calc(var(--round-btn, var(--tap)) / 2);
  background: var(--sheet-btn); border: 1px solid var(--border); font-size: var(--fs-md); font-weight: 500; white-space: nowrap; transition: background .15s, color .15s; }
.plug-installed.bare { padding-left: 16px; }
.plug-installed.on { background: var(--text); border-color: var(--text); color: var(--bg); }
.plug-stack { display: inline-flex; }
.plug-stack .plug-logo { width: calc(var(--round-btn, var(--tap)) * 6 / 11); height: calc(var(--round-btn, var(--tap)) * 6 / 11); border-radius: 50%; box-shadow: 0 0 0 2px var(--sheet-btn); }
.plug-stack .plug-logo + .plug-logo { margin-left: -7px; }
.plug-installed.on .plug-stack .plug-logo { box-shadow: 0 0 0 2px var(--text); }
.plug-search { position: relative; display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.plug-search-box { flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px; height: var(--round-btn, var(--tap)); padding: 0 6px 0 15px; border-radius: calc(var(--round-btn, var(--tap)) / 2);
  background: var(--sheet-btn); border: 1px solid var(--border); cursor: text; }
.plug-search-box:focus-within { border-color: var(--border-strong); }
.plug-search-box > svg { flex: none; width: 20px; height: 20px; color: var(--muted); }
.plug-search-box input { flex: 1; min-width: 0; height: 100%; padding: 0; background: none; border: 0; outline: none; font-size: var(--fs-md); color: var(--text); }
.plug-filter { flex: none; width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); border-radius: 50%; display: grid; place-items: center; background: var(--sheet-btn); border: 1px solid var(--border); color: var(--text); transition: background .15s, color .15s; }
.plug-filter svg { width: calc(var(--round-btn, var(--tap)) / 2); height: calc(var(--round-btn, var(--tap)) / 2); }
.plug-filter.on { background: var(--text); border-color: var(--text); color: var(--bg); }
/* Its menu hangs from it, not from the screen: the drawer it's in moves. */
.popover.plug-menu { position: absolute; top: calc(100% + 8px); right: 0; min-width: 230px; }
.plug-menu button { justify-content: space-between; min-height: 54px; padding-top: 0; padding-bottom: 0; }
.plug-group { margin-top: 14px; }
.plug-group-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 32px; padding: 0 4px; }
.plug-group-head h3 { margin: 0; font-size: var(--fs-sm); line-height: 19px; font-weight: 500; color: var(--muted); }
.plug-more { flex: none; margin-right: -4px; padding: 6px 4px; font-size: var(--fs-sm); line-height: 19px; font-weight: 500; color: var(--muted); }
.plug-more:active { opacity: .6; }
.plug-row { display: flex; align-items: center; gap: 12px; padding: 0 4px; }
.plug-main { flex: 1; min-width: 0; display: flex; align-items: center; gap: 14px; padding: 12px 0; text-align: left; }
button.plug-main:active { opacity: .6; }
.plug-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.plug-name { font-size: var(--fs-md); line-height: 21px; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.plug-sub { font-size: var(--fs-sm); line-height: 19px; color: var(--muted); display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; overflow-wrap: anywhere; }
/* A plugin's own row under it (Connect Gmail again), lined up with its name. */
.plug-extra .plug-main { padding: 0 0 12px 52px; }
.plug-extra .plug-name { white-space: normal; font-size: var(--fs-md); line-height: 19px; }
.plug-logo { position: relative; flex: none; width: 38px; height: 38px; border-radius: 9px; overflow: hidden; display: grid; place-items: center; background: #fff; }
.plug-logo img { width: 100%; height: 100%; display: block; -webkit-user-select: none; user-select: none; -webkit-touch-callout: none; }
.plug-logo::after { content: ""; position: absolute; inset: 0; border-radius: inherit; box-shadow: inset 0 0 0 1px var(--tile-ring); pointer-events: none; }
.plug-logo.icon { background: var(--tile); color: var(--text); }
.plug-logo.icon svg { width: 20px; height: 20px; }
.plug-stack .plug-logo.icon svg { width: 13px; height: 13px; }
.plug-pill { flex: none; min-width: 58px; height: 32px; padding: 0 13px; border-radius: 16px; display: inline-flex; align-items: center; justify-content: center;
  background: var(--pill); color: var(--text); font-size: var(--fs-md); font-weight: 500; white-space: nowrap; transition: transform .1s, opacity .15s; }
.plug-pill.added { color: var(--muted); }
.plug-pill:active { transform: scale(.96); }
.plug-pill:disabled { opacity: .45; }
.plug-pill[aria-busy="true"] { opacity: 1; }
.plugins .group-note { margin: 2px 4px 4px; }
.plugins .group-note.plug-foot { margin-top: 22px; }
.plug-empty { margin: 40px 16px; text-align: center; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }

/* ---------- forms ---------- */
.field { display: flex; flex-direction: column; gap: 6px; margin: 14px 0; }
.field label { color: var(--muted); font-size: var(--fs-sm); padding-left: 4px; }
.input, .textarea, .select { width: 100%; background: var(--surface-2); border: 1px solid transparent; border-radius: 14px; padding: 13px 14px; font-size: var(--fs-md); outline: none; color: var(--text); }
.input:focus, .textarea:focus, .select:focus { border-color: var(--border-strong); }
/* In light, a box is white with a hairline, like the round buttons, so it
   shows on a sheet's gray as well as on white. */
:root[data-theme="light"] :is(.input, .textarea, .select) { background: var(--surface); border-color: var(--border); }
:root[data-theme="light"] :is(.input, .textarea, .select):focus { border-color: var(--border-strong); }
.textarea { resize: vertical; min-height: 110px; line-height: 1.4; }
.textarea.grow { resize: none; overflow: hidden; }
.textarea.mono, .input.mono { font-variant-numeric: tabular-nums; font-size: var(--fs-md); }
/* A bot's profile → Secrets (src/ui/secrets.js): Add secret's +, and the form's note and the secret it replaces. */
:is(.secret-add, .member-add) { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; flex: none; border-radius: 50%; background: var(--surface-2); color: var(--blue); }
/* Add members, under a workspace's members: as big as their faces. */
.member-add { width: 32px; height: 32px; }
.secret-form .secret-note { margin-top: 2px; }
.secret-form .secret-shown { color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.secret-form .secret-why { margin: 2px 0 4px; color: var(--text); }
/* A bot asking for a key, in the chat (request_secret): its card's buttons. */
.card.secret-request .btn-row { margin-top: 12px; }
/* Settings → Plugins → Add SSH server: the address, with its port beside it. */
.ssh-where { display: flex; gap: 10px; }
.ssh-where .field:first-child { flex: 1; min-width: 0; }
.ssh-where .field:last-child { flex: none; width: 92px; }
.hint { color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; padding: 0 4px; }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 46px; padding: 0 20px; border-radius: 23px; background: var(--surface-2); color: var(--text); font-weight: 500; font-size: var(--fs-md); text-decoration: none; }
.btn:active { transform: scale(.98); }
.btn.primary { background: #fff; color: #000; }
/* In light, a plain button is white with a hairline too (it would all but
   vanish into a sheet's gray); the others keep their fill. */
:root[data-theme="light"] .btn { background: var(--surface); box-shadow: inset 0 0 0 1px var(--border); }
:root[data-theme="light"] .btn.primary { background: var(--teal); color: #fff; box-shadow: none; }
.btn.danger { background: rgba(255, 69, 58, .14); color: var(--red); }
:root[data-theme="light"] .btn.danger { background: rgba(255, 69, 58, .14); box-shadow: none; }
.btn.block { width: 100%; }
.btn.big { height: 52px; border-radius: 26px; font-size: var(--fs-md); }
.btn:disabled, .btn.disabled { background: var(--surface-3); color: var(--muted); pointer-events: none; }
:root[data-theme="light"] .btn:disabled { background: #d8e1e1; box-shadow: none; }
.btn.small { height: 34px; padding: 0 14px; font-size: var(--fs-sm); border-radius: 17px; }
.btn-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.segmented { display: flex; background: var(--surface-2); border-radius: 12px; padding: 3px; gap: 2px; }
.segmented button { flex: 1; padding: 8px 10px; border-radius: 10px; font-size: var(--fs-sm); color: var(--muted); font-weight: 500; }
.segmented button.on { background: var(--surface-3); color: var(--text); }
/* In light, the one that's on is white on a gray track, like Monthly | Yearly on the plan page. */
:root[data-theme="light"] .segmented { background: var(--surface-3); }
:root[data-theme="light"] .segmented button.on { background: var(--surface); box-shadow: none; }
.chips { display: flex; gap: 8px; flex-wrap: wrap; }
.chip { padding: 6px 12px; border-radius: 16px; background: var(--surface-2); color: var(--muted); font-size: var(--fs-sm); }
.chip.on { background: var(--text); color: var(--bg); }
:root[data-theme="light"] .chip.on { background: var(--teal); color: #fff; }
/* In light, chips and tabs that aren't on are white with a hairline, like the buttons. */
:root[data-theme="light"] :is(.chip, .tab):not(.on) { background: var(--surface); box-shadow: inset 0 0 0 1px var(--border); }

/* ---------- create bot ---------- */
.create-preview { display: flex; justify-content: center; padding: 34px 0 30px; }
.name-input { width: 100%; height: 64px; border-radius: 20px; background: var(--surface-2); border: 0; outline: none; text-align: center; font-size: var(--fs-lg); font-weight: 500; color: var(--text); }
/* Its title, under its name: a line of its own, a step smaller (src/ui/create-bot.js). */
.name-input.title-input { height: 50px; margin-top: 10px; border-radius: 16px; font-size: var(--fs-md); font-weight: 450; }
/* its job and rules, in the user's words (src/ui/create-bot.js JobBox): folded to a few lines
   (a long job, to its summary), or open to all of it, to read or edit, in a box that grows to fit it */
.job-box { display: block; width: 100%; background: var(--surface-2); border: 1px solid transparent; border-radius: 14px; text-align: left; color: var(--text); }
.job-box.open:focus-within { border-color: var(--border-strong); }
.job-box.big { margin-top: 12px; border: 0; border-radius: 20px; }
/* In light, on a sheet, white with a hairline like the other boxes: the
   name, Bot Memory and rules, its briefing and a Bot Store bot's memory. */
:root[data-theme="light"] .sheet :is(.name-input, .job-box, .brief, .memory-locked) { background: var(--surface); border: 1px solid var(--border); }
:root[data-theme="light"] .sheet .name-input:focus, :root[data-theme="light"] .sheet .job-box.open:focus-within { border-color: var(--border-strong); }
.job-area { display: block; width: 100%; min-height: 96px; padding: 13px 14px 8px; background: none; border: 0; outline: none; resize: none; overflow: hidden; font-size: var(--fs-md); line-height: 1.4; color: var(--text); }
.job-box.big .job-area { padding: 14px 18px 8px; }
/* a rules box is shorter, but in Create New Bot (big) as tall as the job's: its placeholder takes three lines on a phone */
.job-box.rules:not(.big) .job-area { min-height: 72px; }
.job-text { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; padding: 13px 14px 0; font-size: var(--fs-md); line-height: 1.4; white-space: pre-line; overflow-wrap: anywhere; }
.job-box.big .job-text { padding: 14px 18px 0; }
.job-meta { display: flex; align-items: center; gap: 8px; min-height: 40px; padding: 0 6px 4px 14px; color: var(--muted); font-size: var(--fs-sm); }
.job-box.big .job-meta { padding-left: 18px; }
.job-meta .full { color: var(--orange); }
.job-toggle { display: grid; place-items: center; flex: none; width: 36px; height: 36px; margin-left: auto; border-radius: 10px; color: var(--dim); }
.job-box.folded:active { opacity: .85; }
.job-hint { text-align: center; margin-top: 8px; }
.brief-note { color: var(--muted); font-size: var(--fs-sm); margin: -6px 4px 14px; }
.brief { margin: -4px 0 14px; background: var(--surface-2); border-radius: 14px; }
.brief summary { display: flex; align-items: center; justify-content: space-between; padding: 12px 14px; font-size: var(--fs-sm); color: var(--text-2); cursor: pointer; list-style: none; }
.brief summary::-webkit-details-marker { display: none; }
.brief summary svg { color: var(--dim); transition: transform .2s; }
.brief[open] summary svg { transform: rotate(180deg); }
.brief-text { padding: 0 14px 14px; font-size: var(--fs-md); line-height: 1.45; color: var(--text-2); white-space: pre-wrap; overflow-wrap: anywhere; }
.picker { display: grid; justify-content: center; gap: 14px 30px; margin: 34px auto 0; }
.picker.shapes { grid-template-columns: repeat(4, 52px); }
.picker.colors { grid-template-columns: repeat(6, 38px); gap: 16px 24px; margin-top: 30px; }
.picker.colors .row2 { grid-column: 1 / -1; display: flex; justify-content: center; gap: 24px; }
.picker-label { text-align: center; margin: 34px 0 0; font-size: var(--fs-sm); font-weight: 500; letter-spacing: .02em; text-transform: uppercase; color: var(--muted); }
.picker.thinking { grid-template-columns: repeat(3, 84px); gap: 10px 14px; margin-top: 14px; }
.think-pick { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 14px 4px 9px; border-radius: 16px; border: 0; background: transparent; color: var(--muted); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.think-pick.on { background: var(--surface-2, rgba(127,127,127,.14)); color: var(--text); box-shadow: inset 0 0 0 2px var(--text); }
.think-pick .avatar { margin-top: 4px; }
.pick { width: 52px; height: 52px; border-radius: 16px; display: flex; align-items: center; justify-content: center; border: 2px solid transparent; }
.pick.on { border-color: var(--text); }
.swatch { width: 38px; height: 38px; border-radius: 50%; padding: 3px; border: 2px solid transparent; }
.swatch span { display: block; width: 100%; height: 100%; border-radius: 50%; }
.swatch.on { border-color: var(--text); }

/* ---------- chat ---------- */
/* Up and down is the chat's scrolling and pinching zooms it, but a swipe to
   the side is the page's: to the right, it pulls the menu out, as on the chat
   list (useDrawerPull in src/ui/components.js). A wide code block or table
   still scrolls sideways under a finger. */
.pane-chat, .chat-scroll { touch-action: pan-y pinch-zoom; }
.chat-scroll { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 12px 16px 8px; display: flex; flex-direction: column; overscroll-behavior: contain; }
/* On the web and the Mac the chat has no scroll bar (with a mouse, the Mac
   draws one with a gray track down the chat's right edge): it still scrolls
   with a wheel, a trackpad or the keys. The iPhone app keeps its own
   indicator, which shows only while scrolling. */
:root:not(.ios-app) .chat-scroll { scrollbar-width: none; }
:root:not(.ios-app) .chat-scroll::-webkit-scrollbar { display: none; }
.chat-inner { margin-top: auto; display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 760px; align-self: center; }
/* In a wide window (a computer, or a tablet sideways) the chat is a column up
   to 960 px wide, as wide as the prompt bar under it: the bot's replies down
   its left and yours down its right, each at most 560 px, or 80% of a narrower
   column. In a big window they're far apart, and they come together as the
   window narrows. Everything a bot sends (its bubbles and cards) fills its
   side. On a phone, the bubbles stay nearly as wide as the screen (below). */
.app.wide .chat-inner { max-width: 960px; }
.app.wide :is(.steps, .msg.user .bubble, .att-row, .msg.bot > .agent-card) { max-width: min(80%, 560px); }
.app.wide .steps > :is(.bubble, .agent-card, .error-card) { max-width: 100%; }
.app.wide .steps > .card { width: 100%; }
/* The chat, and over its bottom edge while you're scrolled up, a down arrow
   that glides back to the newest message (src/ui/chat.js): the app's round
   button in frosted glass, with no rim: the text behind it shows through
   blurred under a 50% tint of --glass, the same all over (no fading), with
   no shadow. White glass in
   Light; in Black and Dark, a gray one shade lighter than the app's
   (--surface-3). With Reduce Transparency, solid. */
.chat-body { flex: 1; min-height: 0; position: relative; display: flex; flex-direction: column; }
.scroll-down { --glass: #3a3a3c; position: absolute; left: 50%; bottom: 12px; z-index: 2; margin-left: calc(var(--round-btn, var(--tap)) / -2); border: 0;
  background: var(--glass);
  background: color-mix(in srgb, var(--glass) 50%, transparent);
  -webkit-backdrop-filter: blur(6px) saturate(160%); backdrop-filter: blur(6px) saturate(160%);
  box-shadow: none;
  opacity: 0; transform: translateY(8px) scale(.9); pointer-events: none; transition: opacity .2s, transform .2s, background .15s; }
:root[data-theme="dark"] .scroll-down { --glass: #48484a; }
:root[data-theme="light"] .scroll-down { --glass: #fff; box-shadow: none; }
.scroll-down.shown { opacity: 1; transform: none; pointer-events: auto; }
.scroll-down.shown:active { transform: scale(.94); background: color-mix(in srgb, var(--glass) 85%, transparent); }
:root[data-theme="light"] .scroll-down.shown:active { background: color-mix(in srgb, var(--surface-2) 85%, transparent); }
@media (prefers-reduced-transparency: reduce) {
  .scroll-down { background: var(--glass); -webkit-backdrop-filter: none; backdrop-filter: none; }
}
.day-sep { text-align: center; color: var(--muted); font-size: var(--fs-sm); margin: 18px 0 8px; }
.msg { display: flex; flex-direction: column; max-width: 100%; }
.msg.user { align-items: flex-end; }
.msg.bot { align-items: flex-start; }
.msg .author { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); margin: 4px 0 4px 4px; }
.bubble { border-radius: 22px; padding: 11px 16px; max-width: min(88%, 620px); font-size: var(--fs-md); line-height: 1.42; word-wrap: break-word; overflow-wrap: anywhere; }
.msg.bot .bubble { background: var(--bot-bubble); }
.msg.user .bubble { background: var(--user-bubble); }
.bubble.plain-bot { background: var(--bot-bubble); }
:root[data-theme="light"] .msg.user .bubble { color: #fff; }
/* Said out loud with the Screen's mic on (src/ui/message.js UserMessage). */
.said-mic { display: inline-flex; vertical-align: -2px; margin-right: 6px; opacity: .6; }
/* Help, the legal pages and the file viewer stay white boxes on their gray sheet. */
:root[data-theme="light"] .bubble.plain-bot { background: #fff; }
.bubble p { margin: 0 0 .6em; }
.bubble p:last-child, .bubble ul:last-child, .bubble ol:last-child, .bubble pre:last-child { margin-bottom: 0; }
.bubble ul, .bubble ol { margin: .2em 0 .6em; padding-left: 1.3em; }
.bubble li { margin: .15em 0; }
.bubble h1, .bubble h2, .bubble h3, .bubble h4 { font-size: 1em; margin: .8em 0 .4em; }
.bubble h1:first-child, .bubble h2:first-child, .bubble h3:first-child { margin-top: 0; }
.bubble a { color: #6cb4ff; text-decoration: none; }
:root[data-theme="light"] .bubble a { color: var(--blue); }
.bubble code { font-variant-numeric: tabular-nums; font-size: var(--fs-sm); background: rgba(255, 255, 255, .08); padding: .1em .35em; border-radius: 6px; }
:root[data-theme="light"] .bubble :not(pre) > code { background: rgba(20, 52, 58, .07); }
.bubble pre { background: var(--code-bg); border-radius: 12px; padding: 12px; overflow-x: auto; margin: .5em 0; font-size: var(--fs-sm); line-height: 1.45; }
.bubble pre code { background: none; padding: 0; font-size: inherit; }
.bubble table { border-collapse: collapse; margin: .5em 0; font-size: var(--fs-sm); display: block; overflow-x: auto; }
.bubble th, .bubble td { border: 1px solid var(--border); padding: 5px 9px; text-align: left; }
.bubble blockquote { margin: .4em 0; padding-left: 12px; border-left: 3px solid var(--border-strong); color: var(--text-2); }
.bubble img { max-width: 100%; border-radius: 12px; }
.bubble hr { border: 0; border-top: 1px solid var(--border); margin: .8em 0; }
/* Chat bubbles, yours and the bots', nearly as wide as the screen, with close
   lines. Bots may use light structure (src/core/prompts.js), drawn quietly:
   headings at the text's size, bold as medium, muted bullets and numbers, a
   faint quote bar and rule. Help and the legal pages use bubbles outside .msg
   and keep their own look. */
.msg .bubble { max-width: min(94%, 680px); padding: 10px 15px; line-height: 1.3; }
.msg .bubble p { margin: 0 0 .55em; }
.msg .bubble :is(strong, b), .agent-card .a :is(strong, b) { font-weight: 500; }
.msg .bubble :is(h1, h2, h3, h4, h5, h6), .agent-card .a :is(h1, h2, h3, h4, h5, h6) { font-size: 1em; font-weight: 500; margin: .8em 0 .3em; }
.msg .bubble :is(ul, ol), .agent-card .a :is(ul, ol) { padding-left: 1.2em; margin: .15em 0 .55em; }
.msg .bubble li, .agent-card .a li { margin: 0 0 .25em; }
.msg .bubble li > p { margin: 0; }
.msg.bot .bubble li::marker, .agent-card .a li::marker { color: var(--muted); }
.msg .bubble blockquote, .agent-card .a blockquote { border-left: 2px solid var(--border-strong); padding-left: 10px; margin: 0 0 .55em; color: var(--muted); }
.msg .bubble hr, .agent-card .a hr { border: 0; border-top: 1px solid var(--border); margin: .7em 0; }
.msg .bubble :is(h1, h2, h3, h4, h5, h6):first-child { margin-top: 0; }
.msg .bubble :is(p, ul, ol, li, blockquote, h1, h2, h3, h4, h5, h6):last-child { margin-bottom: 0; }
/* Code blocks (src/ui/markdown.js): a header row with the language and Copy,
   then the code, which scrolls sideways under it. White in light mode so the
   block stands out from the gray bot bubble. */
.code-box { margin: .5em 0; background: var(--code-bg); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
.code-box:last-child { margin-bottom: 0; }
:root[data-theme="light"] .code-box { background: #fff; }
.code-head { display: flex; align-items: center; gap: 8px; min-height: 36px; padding: 0 4px 0 12px; border-bottom: 1px solid var(--border); font-family: var(--font); font-size: var(--fs-sm); line-height: 1; color: var(--muted); }
.code-lang { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.copy-code { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 9px; margin-left: auto; border-radius: 8px; flex: none; font-size: var(--fs-sm); font-weight: 500; color: var(--text-2); }
.copy-code:active { background: var(--surface-3); }
.code-box pre { margin: 0; padding: 10px 12px 12px; background: none; border-radius: 0; overflow-x: auto; font-variant-numeric: tabular-nums; font-size: var(--fs-sm); line-height: 1.45; }
.cursor::after { content: ""; display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--text); margin-left: 4px; vertical-align: middle; animation: pulse 1s infinite; }
.msg-meta { color: var(--muted); font-size: var(--fs-sm); margin: 4px 8px 0; display: flex; gap: 10px; align-items: center; }
.msg-actions { display: flex; gap: 4px; margin: 2px 4px 0; }
.msg-actions button { padding: 6px; border-radius: 8px; color: var(--muted); }
.msg-actions button svg { width: 17px; height: 17px; }
.msg-actions button:active { background: var(--surface); }
/* The notepad, once the reply is in Notes (src/ui/message.js). */
.msg-actions button.on { color: var(--text); }
/* Under a bot's reply, one line exactly as wide as its bubble (the same
   max-width, both being items of .steps): Copy and Read aloud at its right end,
   whether or not the bot remembered anything, the last reply's Regenerate
   before them, and the note about what it remembered ("Remembered 2 things") at
   the left when there is one (src/ui/message.js BotMessage). The last button's
   glyph ends exactly on the bubble's edge: its box reaches past the edge by its
   padding (6px) and by the play glyph's own margin inside its icon (2 of 24
   units, at 17px). A note too long for the room lets the buttons drop to the
   right end of the next line. */
.msg-foot { display: flex; align-items: center; flex-wrap: wrap; column-gap: 8px; max-width: min(94%, 680px); margin-top: -4px; }
.app.wide .steps > .msg-foot { max-width: 100%; }
.msg-foot .memory-note { min-width: 0; }
.msg-foot .msg-actions { flex: none; margin: 0 calc(-6px - 17px / 12) 0 auto; }
/* On a computer (a wide window: the website, Holli Bot for Mac and Windows), the buttons are 16 px, the last glyph
   still ending exactly on the reply bubble's right edge (the play glyph's margin is 2 of 24 units at 16 px). */
.app.wide .msg-actions button svg { width: 16px; height: 16px; }
.app.wide .msg-foot .msg-actions { margin-right: calc(-6px - 16px / 12); }
.notice { align-self: center; color: var(--muted); font-size: var(--fs-sm); text-align: center; max-width: 86%; margin: 6px 0; line-height: 1.4; }
.notice.routine { display: flex; align-items: center; gap: 6px; color: var(--text-2); background: var(--surface); padding: 8px 14px; border-radius: 14px; }
.notice.routine svg { flex: none; }
:root[data-theme="light"] .notice.routine { background: var(--surface-2); }
.att-row { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; margin-bottom: 6px; max-width: 88%; }
.att-img { width: 150px; height: 150px; object-fit: cover; border-radius: 16px; background: var(--surface); }
/* A video you sent (src/ui/message.js VideoTile): a picture from it, a play
   mark in the middle, and how long it is. Tap to play it from the bot's drive. */
.att-video { position: relative; width: 150px; height: 150px; padding: 0; border: 0; border-radius: 16px; overflow: hidden; background: #000; flex: none; color: #fff; }
.att-video img { width: 100%; height: 100%; object-fit: cover; display: block; }
.att-video .play { position: absolute; left: 50%; top: 50%; width: 44px; height: 44px; margin: -22px 0 0 -22px; border-radius: 50%; background: rgba(0, 0, 0, .55);
  display: flex; align-items: center; justify-content: center; }
.att-video .play svg { width: 18px; height: 18px; margin-left: 2px; }
.att-video .dur { position: absolute; right: 8px; bottom: 8px; padding: 1px 6px; border-radius: 8px; background: rgba(0, 0, 0, .6); font-size: var(--fs-sm); line-height: 16px; font-variant-numeric: tabular-nums; }

/* tool activity inside bot messages */
.steps { display: flex; flex-direction: column; gap: 6px; max-width: min(92%, 640px); width: 100%; }
.activity { display: flex; align-items: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); padding: 4px 6px; border-radius: 10px; max-width: 100%; text-align: left; }
.activity .ic { width: 18px; height: 18px; flex: none; display: inline-flex; align-items: center; justify-content: center; }
.activity .ic svg { width: 16px; height: 16px; }
.activity .lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.activity.running .lbl { background: linear-gradient(90deg, var(--muted) 0%, var(--text) 45%, var(--muted) 90%); background-size: 200% 100%; -webkit-background-clip: text; background-clip: text; color: transparent; animation: shimmer 1.6s linear infinite; }
.activity.error { color: var(--red); }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
.activity-detail { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 10px 12px; font-size: var(--fs-sm); color: var(--text-2); margin: 0 0 4px 26px; max-height: 320px; overflow: auto; }
.activity-detail pre { margin: 0; white-space: pre-wrap; word-break: break-word; font-variant-numeric: tabular-nums; font-size: var(--fs-sm); }
.activity-detail .k { color: var(--muted); font-size: var(--fs-sm); text-transform: uppercase; letter-spacing: .5px; margin: 8px 0 4px; }
.activity-detail .k:first-child { margin-top: 0; }
.thinking { color: var(--muted); font-size: var(--fs-sm); }
.sources { display: flex; gap: 6px; flex-wrap: wrap; margin: 2px 0 0 2px; }
.source { display: inline-flex; align-items: center; gap: 6px; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 4px 10px 4px 6px; font-size: var(--fs-sm); color: var(--text-2); text-decoration: none; max-width: 220px; }
.source img { width: 14px; height: 14px; border-radius: 3px; flex: none; }
.source span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* cards (question, approval, agent chat, files) */
.card { background: var(--elevated); border-radius: 26px; padding: 18px 18px 16px; width: min(92%, 640px); position: relative; border: 1px solid transparent; }
:root[data-theme="light"] .card { border-color: var(--border); }
.card-title { font-size: var(--fs-md); font-weight: 500; margin: 2px 40px 6px 2px; line-height: 1.3; }
.card-sub { color: var(--muted); font-size: var(--fs-md); margin: 0 2px 14px; line-height: 1.35; }
/* Its × (32 px) centered on the title's first line (26 px, 20 px down). */
.card-x { position: absolute; top: 17px; right: 14px; }
.options { background: #121212; border: 1px solid var(--border); border-radius: 18px; overflow: hidden; }
:root[data-theme="dark"] .options { background: var(--bg); }
:root[data-theme="light"] .options { background: #ecf3f2; }
.option { display: flex; align-items: center; gap: 14px; width: 100%; padding: 13px 14px; text-align: left; font-size: var(--fs-md); position: relative; }
.option + .option::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px; background: var(--border); }
.option:active { background: var(--surface-2); }
.option .letter { width: 30px; height: 30px; border-radius: 9px; background: #1d1d1d; color: var(--muted); display: inline-flex; align-items: center; justify-content: center; font-size: var(--fs-sm); flex: none; }
:root[data-theme="dark"] .option .letter, :root[data-theme="light"] .option .letter { background: var(--surface-2); }
.option .ok { margin-left: auto; color: var(--green); width: 22px; height: 22px; }
.option.picked { font-weight: 500; }
.option.multi-on .letter { background: var(--text); color: var(--bg); }
.card-foot { color: var(--muted); font-size: var(--fs-sm); margin: 14px 2px 0; }
/* A bot on Free offering what a paid plan's computer would let it do
   (src/ui/message.js UpgradeOffer): a card like a question's, marked with
   Upgrade Plan's gradient, then No thanks and See plans. */
.upsell-head { display: flex; align-items: center; gap: 12px; margin: 0 0 8px; }
.upsell-head .card-title { margin: 0; }
.upsell-icon { flex: none; width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff;
  background: linear-gradient(118deg, #0a84ff 0%, #5e5ce6 58%, #bf5af2 100%); }
:root[data-theme="light"] .upsell-icon { background: linear-gradient(118deg, #087e8b 0%, #075c66 58%, #14343a 100%); color: var(--amber); }
.approval { border: 1px solid rgba(245, 166, 35, .35); }
/* A sign-in Holli Bot's browser was turned away from, waiting for the user in
   a normal Chrome window (src/ui/message.js SignInCard): a card like a
   question's, with a shield, then Not now, See the screen and I'm signed in,
   which takes the room left; answered, one quiet line. */
.signin-head { display: flex; align-items: center; gap: 12px; margin: 0 0 8px; }
.signin-head .card-title { margin: 0; }
.signin-icon { flex: none; width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--surface-3); color: var(--green); }
:root[data-theme="light"] .signin-icon { background: rgba(8, 126, 139, .12); color: var(--teal); }
.signin .btn-row .signin-done { flex: 1 1 auto; }
.card.signin.is-done { align-self: flex-start; padding: 12px 16px; width: auto; max-width: min(92%, 640px); }
.signin.is-done .signin-head { margin: 0; color: var(--muted); font-size: var(--fs-sm); gap: 8px; }
/* Don't ask me again, on a sign-in with one of the user's accounts (src/ui/message.js SignInAskCard). */
.signin-always { display: flex; align-items: center; gap: 10px; margin: 12px 0 2px; font-size: var(--fs-sm); color: var(--text-2); cursor: pointer; }
.signin-always input { width: 18px; height: 18px; margin: 0; accent-color: var(--teal); flex: none; }
.signin.is-done .ok { width: 18px; height: 18px; color: var(--green); flex: none; }
/* Permission required (src/ui/message.js ApprovalCard): what the bot wants to
   do, where and what to know, in plain words; what to read first in a box;
   and, under the buttons, what Always allow would let it do. */
.approval .ask-what { margin: 0 2px 8px; color: var(--text); font-size: var(--fs-md); line-height: 1.4; overflow-wrap: anywhere; }
.approval .ask-notes { margin: 0 2px 14px; padding: 0; list-style: none; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }
.approval .ask-notes li + li { margin-top: 3px; }
.approval .ask-detail { margin-bottom: 14px; padding: 10px 12px; border-radius: 12px; background: var(--surface-2); color: var(--text-2); font-size: var(--fs-sm); line-height: 1.4;
  white-space: pre-wrap; overflow-wrap: anywhere; max-height: 200px; overflow: auto; }
.approval .ask-always { margin: 12px 2px 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }
.approval .head { display: flex; align-items: center; gap: 8px; color: var(--orange); font-weight: 500; font-size: var(--fs-sm); margin-bottom: 8px; }
.approval .head svg { width: 18px; height: 18px; }
.approval.done { border-color: transparent; opacity: .85; }
.agent-card { background: var(--surface); border: 1px solid var(--border); border-radius: 18px; padding: 10px 12px; margin: 2px 0; max-width: min(92%, 620px); }
.agent-card .who { display: flex; align-items: center; gap: 8px; font-size: var(--fs-sm); font-weight: 500; margin-bottom: 6px; }
.agent-card .who .arrow { color: var(--muted); font-weight: 450; }
.agent-card .q { color: var(--muted); font-size: var(--fs-md); margin-bottom: 6px; }
.agent-card .a { font-size: var(--fs-md); }
.agent-card .a .md p { margin: 0 0 .4em; }
.file-card { display: flex; align-items: center; gap: 12px; background: var(--surface); border: 1px solid var(--border); border-radius: 18px; padding: 12px 14px; width: min(88%, 420px); text-align: left; }
.file-card .fi { width: 40px; height: 40px; border-radius: 10px; background: var(--surface-3); display: flex; align-items: center; justify-content: center; color: var(--text-2); flex: none; }
.file-card .fi svg { width: 22px; height: 22px; }
.file-card .fn { font-size: var(--fs-md); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.file-card .fs { color: var(--muted); font-size: var(--fs-sm); }
.gen-image { max-width: min(88%, 420px); border-radius: 18px; display: block; }
.error-card { background: rgba(255, 69, 58, .1); border: 1px solid rgba(255, 69, 58, .3); color: var(--text); border-radius: 18px; padding: 12px 14px; font-size: var(--fs-md); max-width: min(92%, 620px); }
.error-card .btn-row { margin-top: 10px; }
.memory-note { color: var(--muted); font-size: var(--fs-sm); display: inline-flex; align-items: center; gap: 6px; margin: 2px 6px; }
.memory-note svg { width: 14px; height: 14px; }
/* A place in the app a bot showed the user (src/ui/message.js OpenPlace): a small button, the way there on it. */
.open-place { align-self: flex-start; max-width: 100%; margin: 2px 4px 0; gap: 6px; }
.open-place span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.open-place svg { flex: none; width: 16px; height: 16px; }
/* A stopped reply's Continue (src/ui/message.js Stopped), a small button like Retry's. */
.stopped-row { align-self: flex-start; display: flex; align-items: center; gap: 10px; margin: 4px 4px 0; color: var(--muted); font-size: var(--fs-sm); }
.terminal { background: #0a0a0a; color: #f2f2f2; font-variant-numeric: tabular-nums; font-size: var(--fs-sm); border-radius: 12px; padding: 10px 12px; white-space: pre-wrap; word-break: break-word; max-height: 300px; overflow: auto; }
.terminal .cmd { color: #7ee787; }
.terminal .err { color: #ff8b8b; }
.chart-img { max-width: 100%; border-radius: 12px; background: #fff; margin-top: 6px; }
/* A working bot's line ("Thinking · 0:43"), 15 px: a step above a subhead's. */
.typing { display: flex; align-items: center; gap: 10px; color: var(--muted); font-size: var(--fs-sm); padding: 6px 0 2px 2px; }
/* Beside its face: what the bot is doing, and for how long (src/ui/chat.js RunStatus). */
.run-status { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ---------- composer: the prompt bar ---------- */
/* One line, like Claude's, on a phone as on a computer or a tablet: +, the
   message, dictation and the round button (send or stop) (.one-line). It
   grows upward as the message gets longer, its buttons staying on the bottom
   line. In the wide layout it's as wide as the chat's messages; on a phone it
   keeps to the screen's edges, a pill until it grows. src/ui/composer.js */
.composer { flex: none; padding: 6px 10px calc(10px + var(--safe-bottom)); background: var(--bg); position: relative; z-index: 3; }
.pbar {
  --pb-btn: var(--surface); --pb-icon: var(--muted);
  --pb-accent: #5f9ea8; --pb-accent-ring: rgba(150, 205, 213, .4); --pb-accent-ink: #0b2226;
  position: relative; display: flex; flex-direction: column; gap: 10px; padding: 12px 10px 10px;
  border: 1px solid var(--border); border-radius: 26px; background: var(--bg); transition: border-color .15s, box-shadow .15s;
}
:root[data-theme="light"] .pbar {
  --pb-btn: #e9f0ef; --pb-icon: var(--muted);
  --pb-accent: var(--teal); --pb-accent-ring: rgba(8, 126, 139, .2); --pb-accent-ink: #ffffff;
  background: #fff; border-color: var(--border); box-shadow: none;
}
.pbar-input { width: 100%; min-height: 30px; max-height: 160px; padding: 4px 6px 2px; resize: none; border: 0; outline: none; background: none; color: var(--text); font-size: var(--fs-md); line-height: 1.35; }
/* Gone while typing, so the keyboard's gray predictions never sit on top of it. */
.pbar-input:focus::placeholder { color: transparent; }
.pbar-btn { width: 36px; height: 36px; border-radius: 50%; background: var(--pb-btn); color: var(--text); display: inline-flex; align-items: center; justify-content: center; flex: none; }
.pbar-btn svg { width: 20px; height: 20px; }
.pbar-btn.recording { color: var(--red-strong); animation: pulse 1.2s infinite; }
.pbar-go { width: 38px; height: 38px; border-radius: 50%; background: var(--pb-accent); color: var(--pb-accent-ink); box-shadow: 0 0 0 2px var(--pb-accent-ring); display: inline-flex; align-items: center; justify-content: center; flex: none; }
.pbar-go svg { width: 20px; height: 20px; }
.pbar-go.stop svg { width: 16px; height: 16px; }
.pbar-go:disabled .spinner { border-color: color-mix(in srgb, var(--pb-accent-ink) 30%, transparent); border-top-color: var(--pb-accent-ink); }
.composer.one-line { padding: 4px 16px calc(16px + var(--safe-bottom)); }
.composer.one-line .pbar { flex-direction: row; align-items: flex-end; gap: 8px; width: 100%; max-width: 960px; margin: 0 auto; padding: 8px; }
.composer.one-line .pbar-input { flex: 1; min-width: 0; width: auto; min-height: 36px; padding: 7px 2px 6px; }
.composer.one-line .mention-list { left: max(16px, calc(50% - 480px)); right: max(16px, calc(50% - 480px)); }
.app.mobile .composer.one-line { padding: 6px 10px calc(10px + var(--safe-bottom)); }
.app.mobile .composer.one-line .pbar { gap: 6px; padding: 6px; }
.app.mobile .composer.one-line .pbar-input { padding: 7px 4px 6px; }
/* Files dragged over the chat: the chat dims, and the prompt bar lights up in
   its accent and says to drop them (src/ui/composer.js). */
.drop-veil { position: absolute; inset: 0; z-index: 2; background: color-mix(in srgb, var(--bg) 55%, transparent); pointer-events: none; }
.pbar.drop, :root[data-theme="light"] .pbar.drop { border-color: var(--pb-accent); box-shadow: 0 0 0 3px var(--pb-accent-ring); }
.pbar-drop { position: absolute; inset: 0; z-index: 1; border-radius: inherit; background: inherit; display: flex; align-items: center; justify-content: center; gap: 8px;
  color: var(--text); font-size: var(--fs-sm); font-weight: 500; pointer-events: none; }
.pbar-drop svg { width: 18px; height: 18px; color: var(--pb-accent); flex: none; }
.icon-btn { width: 40px; height: 40px; border-radius: 20px; display: inline-flex; align-items: center; justify-content: center; color: var(--muted); flex: none; }
.icon-btn svg { width: 22px; height: 22px; }
.pending-atts { display: flex; gap: 8px; padding: 0 16px 6px; overflow-x: auto; flex: none; }
.pending-atts.one-line { width: 100%; max-width: 992px; align-self: center; padding-top: 8px; }
.pending-att { position: relative; flex: none; }
.pending-att img { width: 64px; height: 64px; object-fit: cover; border-radius: 12px; display: block; }
.pending-att .file { height: 64px; min-width: 120px; max-width: 180px; border-radius: 12px; background: var(--surface); border: 1px solid var(--border); display: flex; align-items: center; gap: 8px; padding: 0 10px; font-size: var(--fs-sm); }
.pending-att .file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pending-att .rm { position: absolute; top: -6px; right: -6px; }
/* A video waiting to go: its first picture, with a play mark and how long it is. */
.pending-att .vid-badge { position: absolute; left: 4px; bottom: 4px; display: inline-flex; align-items: center; gap: 3px; padding: 1px 5px; border-radius: 8px;
  background: rgba(0, 0, 0, .6); color: #fff; font-size: var(--fs-sm); line-height: 15px; font-variant-numeric: tabular-nums; }
.pending-att .vid-badge svg { width: 9px; height: 9px; }
.mention-list { position: absolute; bottom: calc(100% + 4px); left: 16px; right: 16px; background: var(--raised); border: 1px solid var(--raised-line); border-radius: 16px; overflow: hidden; box-shadow: none; }
.mention-list button { display: flex; align-items: center; gap: 10px; padding: 10px 14px; width: 100%; text-align: left; }
.mention-list button:active { background: var(--surface-2); }

/* ---------- panels: memory, files, computer ---------- */
.tabs { display: flex; gap: 6px; padding: 0 18px 8px; overflow-x: auto; flex: none; }
.tab { padding: 8px 14px; border-radius: 18px; background: var(--surface); color: var(--muted); font-size: var(--fs-md); white-space: nowrap; }
.tab.on { background: var(--text); color: var(--bg); }
.mem { background: var(--elevated); border-radius: 18px; padding: 12px 14px; margin: 10px 0; }
.mem .txt { font-size: var(--fs-md); line-height: 1.4; }
.mem .meta { display: flex; gap: 8px; align-items: center; color: var(--muted); font-size: var(--fs-sm); margin-top: 8px; flex-wrap: wrap; }
.mem .badge { border: 1px solid var(--border-strong); border-radius: 8px; padding: 1px 7px; }
.mem .imp { letter-spacing: 1px; color: var(--orange); }
.mem .acts { margin-left: auto; display: flex; gap: 2px; }
.mem .acts button { padding: 4px 6px; border-radius: 8px; color: var(--muted); }
.mem .acts button svg { width: 16px; height: 16px; }
.mem .acts button.on { color: var(--orange); }
.mem textarea { width: 100%; }
.routine-row { display: flex; align-items: center; gap: 10px; }
.routine-head { flex: 1; min-width: 0; text-align: left; }
.routine-title { font-size: var(--fs-md); font-weight: 500; }
/* Whose routine it is, first in the line under its title. */
.routine-bot { color: var(--text-2); font-weight: 500; }
.mem .routine-prompt { margin-top: 8px; color: var(--text-2); font-size: var(--fs-sm); }
/* Notes (src/ui/notes.js): a card like a memory's, with the reply's own
   formatting inside (the chat's bubble styles, without the bubble). A long
   one shows its first eight lines until it's tapped. Under it, in gray: the
   chat it came from, when, and Share, Edit and Delete at the right. Each bot's
   notes come under its face and name. */
.notes-group { display: flex; align-items: center; gap: 8px; margin: 22px 4px 4px; color: var(--muted); font-size: var(--fs-sm); }
.notes-group .n { color: var(--text); font-weight: 500; font-size: var(--fs-md); }
.notes-group .gone { border: 1px solid var(--border-strong); border-radius: 8px; padding: 1px 7px; }
.notes-group .c { margin-left: auto; }
.note { background: var(--elevated); border-radius: 18px; padding: 12px 14px; margin: 10px 0; }
.note .txt { cursor: default; }
.note .txt.clamped { display: -webkit-box; -webkit-line-clamp: 8; -webkit-box-orient: vertical; overflow: hidden; cursor: pointer; }
.note .bubble { padding: 0; border-radius: 0; background: none; max-width: none; font-size: var(--fs-md); line-height: 1.45; }
:root[data-theme="light"] .note .bubble.plain-bot { background: none; }
.note .meta { display: flex; gap: 10px; align-items: center; color: var(--muted); font-size: var(--fs-sm); margin-top: 8px; flex-wrap: wrap; }
.note .meta .chat { display: inline-flex; align-items: center; gap: 4px; color: var(--muted); min-width: 0; max-width: 60%; }
.note .meta .chat svg { flex: none; }
.note .meta button.chat:active, .note .meta .more:active { opacity: .6; }
.note .meta .more { color: var(--blue); }
.note .acts { margin-left: auto; display: flex; gap: 2px; }
.note .acts button { padding: 4px 6px; border-radius: 8px; color: var(--muted); }
.note .acts button svg { width: 16px; height: 16px; }
.note .acts button:active { background: var(--surface-2); }
.note textarea { width: 100%; }
.file-row { display: flex; align-items: center; gap: 12px; padding: 12px 4px; border-bottom: 1px solid var(--border); width: 100%; text-align: left; }
.file-row .fi { width: 36px; height: 36px; border-radius: 9px; background: var(--surface-2); display: flex; align-items: center; justify-content: center; flex: none; color: var(--text-2); }
.file-row .fi svg { width: 20px; height: 20px; }
.file-row .chev { flex: none; color: var(--dim); }
.file-row .n { flex: 1; min-width: 0; }
.file-row .n div:first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: var(--fs-md); }
.file-row .n div:last-child { color: var(--muted); font-size: var(--fs-sm); }
.preview-frame { width: 100%; height: 60vh; border: 0; border-radius: 14px; background: #fff; }
.preview-video { display: block; width: 100%; max-height: 70vh; border-radius: 14px; background: #000; }
.preview-text { background: var(--code-bg); border-radius: 14px; padding: 12px; font-variant-numeric: tabular-nums; font-size: var(--fs-sm); white-space: pre-wrap; word-break: break-word; max-height: 60vh; overflow: auto; }
.act-item { display: flex; gap: 10px; padding: 10px 2px; border-bottom: 1px solid var(--border); font-size: var(--fs-sm); }
.act-item .when { color: var(--muted); font-size: var(--fs-sm); flex: none; width: 54px; }
.act-item .what { flex: 1; min-width: 0; }
.act-item .what .d { color: var(--muted); font-size: var(--fs-sm); margin-top: 3px; white-space: pre-wrap; word-break: break-word; max-height: 90px; overflow: hidden; }
/* The computer's Screen tab (src/ui/computer.js Screen) is the whole screen, in black in every look: a bar at
   the top (Back, the Wi-Fi link, ⋯), the picture as wide as the screen in the middle, and at the bottom the mic and the
   keyboard button. With the phone's keyboard up it ends at the keyboard: --vv-top and --vv-bottom are what the
   phone shows of the page. Behind the keyboard it's black too, its shadow reaching down there, so nothing of the
   chat under it shows around the keyboard, its bar or its rounded corners, or through them. Nothing on it pans
   or zooms the page. */
.sheet.screen-sheet { top: var(--vv-top, 0px); bottom: var(--vv-bottom, 0px); left: 0; right: 0; width: auto; max-width: none; margin: 0;
  transform: none; border-radius: 0; box-shadow: 0 var(--vv-bottom, 0px) 0 0 #000; background: #000; color: var(--text); color-scheme: dark; touch-action: none;
  --bg: #000; --surface: #1c1c1e; --surface-2: #2c2c2e; --surface-3: #3a3a3c; --border: #1c1c1e; --border-strong: #3a3a3c;
  --text: #f2f2f2; --text-2: var(--text); --muted: #a1a1a6; --raised: #1c1c1e; --raised-line: #2c2c2e; --red: #ff6b6b; }
@media (min-width: 900px) and (prefers-reduced-motion: no-preference) {
  .sheet.screen-sheet { animation: fade .25s ease-out; }
  .sheet-layer.closing .sheet.screen-sheet { animation: fade-out .2s ease-in forwards; }
}
.screen-sheet .sheet-body { display: flex; flex-direction: column; padding: 0; overflow: hidden; }
.screen-page { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; padding: 0 var(--safe-right) 0 var(--safe-left); }
.screen-head { flex: none; display: flex; align-items: center; gap: 12px; padding: calc(10px + var(--safe-top)) 16px 10px; }
.screen-gap { flex: 1; }
.screen-stage { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; container-type: size; }
/* The Wi-Fi link's button, left of ⋯: green while the Screen goes straight to the computer over the Wi-Fi, gray
   and crossed out while it goes through the internet, pulsing while it looks for the computer on the Wi-Fi. */
.screen-wifi { color: var(--muted); transition: background .15s, transform .1s, color .3s; }
.screen-wifi.on { color: var(--green-dot); }
.screen-wifi.trying svg { animation: computer-pulse 1.2s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .screen-wifi.trying svg { animation: none; } }
/* A phone held sideways: the picture alone, as big as the screen allows, with no bar over it or buttons under it.
   Turned back upright, they're back. */
.screen-page.sideways .screen-head, .screen-page.sideways .screen-keys, .screen-page.sideways .screen-ask { display: none; }
.screen-keys { flex: none; display: flex; justify-content: space-between; padding: 12px 16px calc(12px + var(--safe-bottom));
  padding-bottom: max(12px, calc(12px + var(--safe-bottom) - var(--vv-bottom, 0px))); }
.screen-keys .circle-btn.on { background: var(--surface-3); }
/* The mic (src/ui/screen-voice.js), bottom left, Lucide's mic: tapped, it stays open, white, with a ring that
   breathes while it listens, and the wave while the bot's reply is read out; tapped again, it's off. Between it and
   the keyboard button, on two lines at most: what it hears as it's said, what went to the bot, and the bot's reply. */
.screen-mic { transition: background .15s, color .15s, border-color .15s, transform .1s; }
.screen-keys .screen-mic.on { background: #fff; border-color: #fff; color: #000; }
.screen-mic.listening { animation: mic-breathe 1.8s ease-in-out infinite; }
@keyframes mic-breathe { 0%, 100% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .26); } 60% { box-shadow: 0 0 0 10px rgba(255, 255, 255, 0); } }
@media (prefers-reduced-motion: reduce) { .screen-mic.listening { animation: none; box-shadow: 0 0 0 4px rgba(255, 255, 255, .2); } }
.screen-voice { flex: 1; min-width: 0; margin: 0; padding: 0 14px; align-self: center; text-align: center; color: var(--text);
  font-size: var(--fs-sm); line-height: 1.35; overflow-wrap: anywhere; display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden; }
.screen-voice.dim { color: var(--muted); }
/* What the Screen's bot waits on, over the mic and the keyboard button: a permission (Deny, Always allow,
   Approve) or a question and its options, to tap, or with the mic on, answer out loud. Dark in every look. */
.screen-ask { flex: none; margin: 6px 16px 0; padding: 12px 14px 14px; border-radius: 20px; background: var(--surface); animation: fade .2s ease-out; }
.screen-ask p { display: flex; gap: 8px; margin: 0 2px 11px; font-size: var(--fs-sm); line-height: 1.4; overflow-wrap: anywhere; }
.screen-ask p svg { flex: none; width: 17px; height: 17px; margin-top: 1px; color: var(--muted); }
.screen-ask .btn-row { gap: 8px; }
.sheet.screen-sheet .screen-ask .btn { background: var(--surface-3); color: var(--text); box-shadow: none; }
.sheet.screen-sheet .screen-ask .btn.primary { flex: 1; background: #fff; color: #000; }
/* The computer the app is on (src/ui/computer.js Screen's self): its screen is only shown, and this says so in
   place of the mic and the keyboard button. */
.screen-self { flex: 1; margin: 0; padding: 9px 4px; text-align: center; color: var(--muted); font-size: var(--fs-sm); line-height: 1.35; }
/* The box whose keyboard it is: out of sight, at the top, where the phone has no reason to scroll to it, and at
   16px, which a phone doesn't zoom in on. Its stand-in holds the keyboard up from the tap that opens the Screen
   until the Screen is there (raiseKeyboard). */
.screen-typing { position: absolute; top: 0; left: 0; width: 1px; height: 1px; margin: 0; padding: 0; border: 0; opacity: 0;
  font-size: var(--fs-md); color: transparent; caret-color: transparent; background: none; pointer-events: none; }
.screen-typing.stand-in { position: fixed; }
/* The picture's frame: as wide as the screen, or as the height leaves room for (--ratio: the picture's width over
   its height). A pinch zooms the picture inside this frame, not the page; one finger moves it once it's zoomed in.
   The picture is laid out at its zoomed size (width, height, left, top), so it's drawn from the full image and
   stays sharp. Where the mouse works the computer as its own screen (.mouse), or the computer is the one the app
   is on and its screen is only shown (.self), the pointer over it is the usual arrow. */
.screen-view { position: relative; flex: none; width: 100%; width: min(100cqw, 100cqh * var(--ratio, 1.6)); overflow: hidden; background: #000;
  cursor: crosshair; touch-action: pan-y; -webkit-user-select: none; user-select: none; }
.screen-view.mouse, .screen-view.self { cursor: default; }
.screen-view.zoomed { touch-action: none; }
.screen-view.empty { width: 100%; background: none; cursor: auto; touch-action: auto; }
.screen-notice { margin: 0 auto; padding: 60px 0; }
/* A sign-in window open on the computer, over the desktop's picture (src/ui/computer.js): what to do, and I'm signed in. */
.screen-signin { flex: none; display: flex; align-items: center; gap: 10px; margin: 0 12px 8px; padding: 8px 8px 8px 14px; border-radius: 16px;
  background: #2c2c2e; color: #f2f2f2; font-size: var(--fs-sm); line-height: 1.35; }
.screen-signin span { flex: 1; min-width: 0; }
.screen-signin .btn { flex: none; }
/* The bot browser closed for a sign-in in a normal window (src/ui/computer.js): what to do, and the way there. */
.screen-handoff { max-width: 340px; padding: 36px 16px; text-align: center; line-height: 1.4; }
.screen-handoff p { margin: 0 0 14px; }
.screen-handoff .btn-row { justify-content: center; }
.screen-img { position: absolute; left: 0; top: 0; width: 100%; height: 100%; max-width: none; display: block; -webkit-user-drag: none; -webkit-touch-callout: none; }
/* The screen as live video (src/ui/screen-stream.js): the video, or the canvas it's drawn on as it's decoded, filling the frame,
   zoomed and moved with it; the frame takes the mouse and taps. */
.screen-video { background: #000; overflow: hidden; }
.screen-media { position: absolute; inset: 0; width: 100%; height: 100%; display: block; object-fit: fill; pointer-events: none; -webkit-user-select: none; user-select: none; }
.screen-zoom { position: absolute; top: 8px; left: 8px; padding: 5px 10px; border-radius: 999px; background: rgba(0, 0, 0, .62); color: #fff;
  font-size: var(--fs-sm); font-weight: 500; font-variant-numeric: tabular-nums; cursor: pointer; }
/* On a computer (src/ui/computer.js Screen's framed: a mouse, not a phone or a tablet), laid out as Grok's
   computer is: dark over the whole window, the app faint behind it; the picture in the middle, as big as fits
   between the top row and the pill under it, in the computer screen's own shape (all of it, never cut off) with
   rounded corners; across the top, a black bezel 32 px tall (Holli Bot for Mac's own room for its window's
   buttons, which sit at its left, their middle its middle), with the shrink arrows, which close it, at its right,
   centred in it too; and under the picture, in the middle, a pill of black liquid glass: the mic and the
   keyboard, a thin line, the Wi-Fi link and ⋯. What the mic hears and the agent's questions are just above the pill.
   The glass is dark and see-through, what's behind it blurred and its color deepened, lit along its top edge
   with a soft sheen falling down it and a faint bright rim, its icons white; pressed (the keyboard open) a lighter
   pane of it, and the mic, listening, white with a black icon. */
.sheet.screen-sheet:has(.screen-page.framed) { background: rgba(20, 20, 20, .88); box-shadow: none; }
.screen-top { position: absolute; z-index: 3; top: 0; left: 0; right: 0; height: 32px; display: flex; align-items: center; justify-content: flex-end; padding: 0 10px; background: #000; }
.screen-shrink { width: 28px; height: 28px; border-radius: 8px; display: grid; place-items: center; color: #d1d1d6; transition: background .15s, color .15s; }
.screen-shrink svg { width: 16px; height: 16px; }
@media (hover: hover) { .screen-shrink:hover { background: rgba(255, 255, 255, .1); color: #fff; } }
.screen-page.framed .screen-stage { position: absolute; top: 44px; left: 24px; right: 24px; bottom: calc(66px + var(--safe-bottom)); }
.screen-page.framed .screen-view:not(.empty) { border-radius: 10px; box-shadow: none; }
/* Actual size (the pill's 1:1): with room for it, the picture is one of its pixels to each of the screen's
   (--actual: its width over the device's pixels to a point), in the middle; without, as big as fits, and zoomed
   in to that (src/ui/computer.js Screen's actual). */
.screen-page.framed .screen-view.actual:not(.empty) { width: min(100cqw, 100cqh * var(--ratio, 1.6), var(--actual, 100cqw)); }
.screen-dock .screen-actual { font-size: 12px; font-weight: 700; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.screen-dock { position: absolute; z-index: 3; left: 50%; bottom: calc(14px + var(--safe-bottom)); transform: translateX(-50%); display: flex; align-items: center; gap: 2px;
  padding: 4px; border-radius: 999px;
  background: linear-gradient(180deg, rgba(255, 255, 255, .14) 0%, rgba(255, 255, 255, .04) 45%, rgba(255, 255, 255, 0) 55%, rgba(255, 255, 255, .03) 100%), rgba(16, 16, 18, .52);
  -webkit-backdrop-filter: blur(22px) saturate(180%); backdrop-filter: blur(22px) saturate(180%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .22), inset 0 -1px 0 rgba(255, 255, 255, .06), inset 0 0 0 1px rgba(255, 255, 255, .09); }
.screen-dock .circle-btn { width: 32px; height: 32px; background: none; border: 0; box-shadow: none; color: #f2f2f7; }
.screen-dock .circle-btn svg { width: 16px; height: 16px; }
.screen-dock .circle-btn.on { background: rgba(255, 255, 255, .16); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .14); }
.screen-dock .screen-mic.on { background: #fff; color: #000; }
.screen-dock .screen-wifi { color: #8e8e93; }
.screen-dock .screen-wifi.on { color: #30d158; }
.screen-dock-sep { width: 1px; height: 18px; margin: 0 4px; background: rgba(255, 255, 255, .16); }
@media (hover: hover) { .screen-dock .circle-btn:hover:not(.on) { background: rgba(255, 255, 255, .1); } }
.screen-float { position: absolute; z-index: 3; left: 50%; bottom: calc(66px + var(--safe-bottom)); width: min(560px, calc(100% - 32px));
  transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 8px; pointer-events: none; }
.screen-float > * { pointer-events: auto; }
.screen-float .screen-ask { align-self: stretch; margin: 0; box-shadow: none; }
.screen-float .screen-voice, .screen-float .screen-self { flex: none; max-width: 100%; padding: 8px 14px; border-radius: 16px;
  background: rgba(28, 28, 30, .86); -webkit-backdrop-filter: blur(16px); backdrop-filter: blur(16px); pointer-events: none; }
.screen-page.framed .screen-signin { position: absolute; z-index: 3; top: 52px; left: 50%; width: min(560px, calc(100% - 32px)); margin: 0;
  transform: translateX(-50%); box-shadow: none; }
.mac-app .screen-page.framed :is(.screen-shrink, .screen-view, .screen-dock, .screen-float, .screen-signin) { -webkit-app-region: no-drag; app-region: no-drag; }
/* The computer's RAM in use, at the top of the computer's own tab (src/ui/computer.js) */
.ram-meter { margin: 14px 0; padding: 14px 16px 15px; border-radius: var(--radius-card); background: var(--elevated); }
.ram-meter-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: var(--fs-sm); }
.ram-meter-head b { font-weight: 500; }
.ram-meter-head span { color: var(--muted); font-variant-numeric: tabular-nums; }
.ram-meter-bar { height: 8px; margin-top: 9px; border-radius: 4px; background: var(--surface-3); overflow: hidden; }
.ram-meter-bar span { display: block; height: 100%; border-radius: inherit; background: var(--green); transition: width .4s ease, background-color .4s ease; }
.ram-meter-bar span.mid { background: var(--orange); }
.ram-meter-bar span.high { background: var(--red-strong); }
.ram-meter-note { margin-top: 7px; font-size: var(--fs-sm); color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .ram-meter-bar span { transition: none; } }
/* ---------- Settings → Usage (src/ui/usage.js) ----------
   The account's AI credits, on the drawer's surfaces (--card), easy to take
   in from the top: Use credits from, a switch of three (Together, Monthly,
   Purchased) with a line on what it means; one card with what the bots can
   use, big, a bar of it (monthly credits green, purchased amber), and each
   kind on a line of its own, a dot of its colour, its name and what's left
   (one the bots don't use greyed, saying so), and when the monthly credits
   refill; then Add credits, across; then How credits work, folded. Every
   state says itself in words, not colour alone. */
.usage { container: usage / inline-size; display: flex; flex-direction: column; gap: 14px; }
.usage-label { color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 500; }
.usage-draw { display: flex; flex-direction: column; gap: 8px; }
.usage-draw > .usage-label, .usage-draw-about { margin: 0 4px; }
.usage-switch { display: flex; gap: 2px; padding: 3px; border-radius: 14px; background: var(--meter, var(--surface-3)); }
.usage-switch button { flex: 1 1 0; min-width: 0; height: 38px; padding: 0 8px; border-radius: 11px; color: var(--muted);
  font-size: var(--fs-sm); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background .2s, color .2s, box-shadow .2s; }
.usage-switch button.on { background: var(--card, var(--elevated)); color: var(--text); box-shadow: none; }
:root[data-theme="light"] .usage-switch { background: rgba(20, 52, 58, .07); }
:root[data-theme="light"] .usage-switch button.on { background: #fff; box-shadow: none; }
.usage-switch.is-saving button.on { animation: pulse 1.2s ease-in-out infinite; }
.usage-draw-about { color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }
.usage-total { border-radius: var(--radius-group, 16px); background: var(--card, var(--elevated)); padding: 18px; }
.usage-total-n { margin-top: 2px; color: var(--muted); font-size: var(--fs-md); line-height: 1.15; font-weight: 500; overflow-wrap: anywhere; }
.usage-total-n b { color: var(--text); font-size: var(--fs-lg); line-height: 1.1; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.usage-keys { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.usage-key { display: flex; align-items: center; gap: 10px; min-height: 20px; color: var(--text); font-size: var(--fs-sm); line-height: 20px; }
.usage-dot { flex: none; width: 10px; height: 10px; border-radius: 50%; }
.usage-dot.monthly, .credits-bar span.monthly { background: var(--green); }
.usage-dot.purchased, .credits-bar span.purchased { background: var(--orange); }
.usage-key-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.usage-key-n { flex: none; font-weight: 500; font-variant-numeric: tabular-nums; }
.usage-key.off { color: var(--muted); }
.usage-key.off .usage-dot { background: none; box-shadow: inset 0 0 0 1.5px var(--dim); }
.usage-key-tag { flex: none; padding: 1px 8px; border-radius: 999px; background: var(--meter, var(--surface-3)); color: var(--muted); font-size: var(--fs-sm); line-height: 16px; font-weight: 500; }
:root[data-theme="light"] .usage-key-tag { background: rgba(20, 52, 58, .07); }
.usage-total .usage-card-sub { display: flex; align-items: center; gap: 6px; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line, var(--border)); color: var(--muted); font-size: var(--fs-sm); line-height: 18px; }
.usage-card-sub svg { flex: none; }
/* Bots running on purchased credits: a quiet chip, in words. */
.usage-chip { display: inline-flex; align-items: center; gap: 6px; margin-top: 12px; padding: 5px 11px 5px 9px; border-radius: 999px;
  background: rgba(48, 209, 88, .13); color: var(--green); font-size: var(--fs-sm); line-height: 18px; font-weight: 500; }
:root[data-theme="light"] .usage-chip { background: rgba(8, 126, 139, .12); color: var(--teal-deep); }
.usage-out { display: flex; align-items: flex-start; gap: 10px; margin-top: 14px; padding: 12px 14px; border-radius: 12px; background: rgba(255, 69, 58, .12); }
.usage-out > svg { flex: none; margin-top: 1px; color: var(--red-strong); }
:root[data-theme="light"] .usage-out > svg { color: var(--red); }
.usage-out-title { color: var(--text); font-size: var(--fs-md); line-height: 21px; font-weight: 500; }
.usage-out-sub { margin-top: 2px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; }
.usage-actions { display: flex; flex-direction: column; gap: 10px; }
.usage-actions .btn { width: 100%; height: 46px; border-radius: 23px; font-size: var(--fs-md); }
.usage-add { background: var(--card, var(--elevated)); color: var(--text); box-shadow: none; }
:root[data-theme="light"] .usage-add { background: #fff; box-shadow: none; }
.usage-add svg, .usage-add-main svg { flex: none; margin-left: -3px; }
.usage-note { margin: 0 16px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }
.usage-actions .usage-note { margin: 0 4px; text-align: center; }
.usage-how { border-radius: var(--radius-group, 16px); background: var(--card, var(--elevated)); }
.usage-how summary { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 50px; padding: 12px 16px 12px 18px;
  font-size: var(--fs-md); line-height: 21px; font-weight: 500; list-style: none; cursor: pointer; border-radius: inherit; }
.usage-how summary::-webkit-details-marker { display: none; }
.usage-how summary svg { flex: none; color: var(--dim); transition: transform .2s; }
.usage-how[open] summary svg { transform: rotate(180deg); }
.usage-how summary:active { background: var(--card-press, var(--surface-2)); }
.usage-how-body { padding: 0 18px 16px; color: var(--muted); font-size: var(--fs-md); line-height: 1.5; }
.usage-how-body p { margin: 0 0 10px; }
.usage-how-body p:last-child { margin-bottom: 0; }
.usage-upgrade { margin-top: 6px; }
/* Before the credits are read: the page's shape, with no numbers in it. */
.usage-skel { position: relative; overflow: hidden; border-radius: var(--radius-group, 16px); background: var(--card, var(--elevated)); }
.usage-skel.is-switch { height: 44px; border-radius: 14px; }
.usage-skel.is-total { height: 200px; }
.usage-skel.is-add { height: 46px; border-radius: 23px; }
.usage-skel::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .05), transparent); animation: usage-shimmer 1.4s ease-in-out infinite; }
:root[data-theme="light"] .usage-skel::after { background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .7), transparent); }
@keyframes usage-shimmer { to { transform: translateX(100%); } }
.usage-error { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 28px 20px 24px; border-radius: var(--radius-group, 16px); background: var(--card, var(--elevated)); text-align: center; }
.usage-error > svg { color: var(--orange); }
.usage-error-title { margin-top: 4px; font-size: var(--fs-md); font-weight: 500; }
.usage-error-sub { color: var(--muted); font-size: var(--fs-sm); }
.usage-error .btn { margin-top: 10px; background: var(--meter, var(--surface-3)); box-shadow: none; }
:root[data-theme="light"] .usage-error .btn { background: rgba(20, 52, 58, .08); }
/* The credits' bar, on the Usage page and under Usage at the top of Settings
   (CreditsBar): what's left of the kinds the bots use, side by side, the
   monthly credits green and the purchased ones amber. */
.credits-bar { display: flex; gap: 2px; height: 12px; margin-top: 12px; border-radius: 6px; background: var(--surface-3); overflow: hidden; }
.credits-bar span { display: block; flex: none; height: 100%; border-radius: inherit; background: var(--green); transition: width .5s ease; }
.usage .usage-bar { height: 10px; margin-top: 14px; border-radius: 5px; background: var(--meter, var(--surface-3)); }
@media (prefers-reduced-motion: reduce) { .credits-bar span { transition: none; } .usage-skel::after { animation: none; } .usage-how summary svg { transition: none; } }
.status-pill { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-sm); padding: 4px 10px; border-radius: 12px; background: var(--surface-2); color: var(--muted); }
.status-pill.ok { color: var(--green); }
.status-pill.bad { color: var(--red); }
.status-pill .d { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.kbd { font-variant-numeric: tabular-nums; font-size: var(--fs-sm); background: var(--code-bg); border-radius: 8px; padding: 2px 6px; }
.code-block { font-variant-numeric: tabular-nums; font-size: var(--fs-sm); background: var(--code-bg); border-radius: 12px; padding: 10px 12px; white-space: pre-wrap; word-break: break-all; }
.code-block.copyable { display: block; width: 100%; text-align: left; border: 0; color: var(--text); cursor: copy; position: relative; padding-right: 34px; }
.code-block.copyable::after { content: ''; position: absolute; right: 12px; top: 10px; width: 15px; height: 15px; background: var(--muted);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/svg%3E") center / contain no-repeat; mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='14' height='14' x='8' y='8' rx='2' ry='2'/%3E%3Cpath d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2'/%3E%3C/svg%3E") center / contain no-repeat; } /* Lucide's copy, as Icon.copy draws it */
.code-block.copyable:active { opacity: .7; }

/* ---------- voice mode ---------- */
.voice { position: fixed; inset: 0; z-index: 80; background: radial-gradient(circle at 50% 38%, #1d1d1f 0%, #050505 70%); display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: calc(24px + var(--safe-top)) 24px calc(34px + var(--safe-bottom)); animation: fade .25s; }
:root[data-theme="light"] .voice { background: radial-gradient(circle at 50% 38%, #fff 0%, #e2eeec 70%); }
.voice .top { width: 100%; display: flex; justify-content: space-between; align-items: center; }
.voice .center { display: flex; flex-direction: column; align-items: center; gap: 26px; text-align: center; }
.voice .status { color: var(--muted); font-size: var(--fs-md); }
.voice .said { font-size: var(--fs-lg); line-height: 1.35; max-width: 560px; min-height: 60px; }
.voice .reply { font-size: var(--fs-md); color: var(--text-2); max-width: 560px; max-height: 30vh; overflow: auto; line-height: 1.45; }
.voice .controls { display: flex; gap: 28px; align-items: center; }
.voice .big { width: 76px; height: 76px; border-radius: 50%; background: #fff; color: #000; display: flex; align-items: center; justify-content: center; }
.voice .big.listening { box-shadow: 0 0 0 10px rgba(255, 255, 255, .12), 0 0 0 22px rgba(255, 255, 255, .05); animation: breathe 1.6s ease-in-out infinite; }
.voice .big svg { width: 32px; height: 32px; }
.voice .end { width: 60px; height: 60px; border-radius: 50%; background: var(--red-strong); color: #fff; display: flex; align-items: center; justify-content: center; }
.voice .end svg { width: 26px; height: 26px; }
@keyframes breathe { 0%, 100% { box-shadow: 0 0 0 8px rgba(255, 255, 255, .12), 0 0 0 18px rgba(255, 255, 255, .05); } 50% { box-shadow: 0 0 0 14px rgba(255, 255, 255, .16), 0 0 0 30px rgba(255, 255, 255, .06); } }
.voice .level { display: flex; gap: 5px; align-items: center; height: 40px; }
.voice .level span { width: 6px; border-radius: 3px; background: var(--text); transition: height .08s; }

/* ---------- toasts / dialogs ---------- */
/* Toasts drop in below the top bars, never over them: under the bot list's
   and a chat's (--head-h: their buttons end 10 px above it, 12 px on a
   computer), with room to
   spare, so they're clear of Settings' and its pages' bars, the gallery's
   and the camera's too; in Holli Bot for Mac, below the window's buttons as
   well (--safe-top, which a computer's bars reach past). While a sheet is open, below its header (a phone's
   sheet starts 12 px down and its header is 72 px tall; in a wide window it
   starts 5vh down). On the web and in Holli Bot for Mac and Windows they're
   centered over the chat, beside the sidebar (280 px) or the rail (89 px),
   so they cover neither its + nor its search box. */
.toasts { position: fixed; left: 50%; top: calc(var(--head-h) + 8px); transform: translateX(-50%); z-index: 100; display: flex; flex-direction: column; gap: 8px; width: min(92vw, 440px); pointer-events: none; transition: top .3s cubic-bezier(.2, .8, .2, 1); }
.app.wide ~ .toasts { left: calc(140px + 50vw); width: min(440px, calc(100vw - 312px)); }
.app.rail ~ .toasts { left: calc(44.5px + 50vw); width: min(440px, calc(100vw - 121px)); }
.sheet-layer:not(.closing):has(.sheet:not(.drawer)) ~ .toasts { top: calc(var(--safe-top) + 92px); left: 50%; width: min(92vw, 440px); }
@media (min-width: 900px) {
  .sheet-layer:not(.closing):has(.sheet:not(.drawer)) ~ .toasts { top: calc(5vh + 80px); }
}
/* Liquid Glass, black and frosted (the owner's recipe; .glass on anything
   makes it this glass, in its own shape): what's under it shows through,
   blurred (10 px) and a little richer in color (145%), under a black tint
   that's darkest at the top; a specular sheen arcs across its top curve
   (::before), and a thin rim of light, brightest along the top, lines its
   edge (::after, over the rim in its inset shadows, for a brighter
   "lensing" line); no shadow under it. Without backdrop-filter, or with Reduce Transparency, solid black. */
.glass { position: relative; color: #fff; overflow: hidden; isolation: isolate;
  background: radial-gradient(ellipse 130% 90% at 50% 0%, rgba(0, 0, 0, .63) 0%, rgba(0, 0, 0, .55) 45%, rgba(0, 0, 0, .5) 100%);
  -webkit-backdrop-filter: blur(10px) saturate(145%); backdrop-filter: blur(10px) saturate(145%);
  box-shadow: inset 0 0 8px rgba(255, 255, 255, .04), inset 0 4px 8px -4px rgba(0, 0, 0, .22), inset 0 -1px 1px rgba(0, 0, 0, .28), inset 0 1px 1px rgba(255, 255, 255, .096), inset 0 0 0 1px rgba(255, 255, 255, .063); }
.glass::before { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: inherit; pointer-events: none;
  background: radial-gradient(ellipse 110% 65% at 50% -15%, rgba(255, 255, 255, .121) 0%, rgba(255, 255, 255, 0) 70%);
  opacity: .55; mix-blend-mode: screen; }
.glass::after { content: ""; position: absolute; inset: 0; z-index: 1; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 0 0 .5px rgba(255, 255, 255, .07), inset 0 1px 0 rgba(255, 255, 255, .096), inset 0 -1px 0 rgba(255, 255, 255, .06); }
.glass > * { position: relative; z-index: 2; }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass { background: #000; }
}
@media (prefers-reduced-transparency: reduce) {
  .glass { background: #000; -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* A toast is that glass (src/ui/components.js Toasts), rounded. Errors look
   the same. Tapped, it fades up and away (src/ui/app.js dismissToast). */
.toast { display: flex; align-items: center; gap: 12px; padding: 13px 18px; border-radius: 22px;
  font-size: var(--fs-md); line-height: 1.35; cursor: pointer; pointer-events: auto;
  animation: drop .25s ease-out; transition: opacity .2s, transform .2s; }
.toast:active { transform: scale(.985); }
.toast.out { opacity: 0; transform: translateY(-10px) scale(.98); pointer-events: none; }
.toast button { margin-left: auto; color: #8ec5ff; font-weight: 500; font-size: var(--fs-md); }
@keyframes drop { from { opacity: 0; transform: translateY(-12px); } to { opacity: 1; transform: none; } }
.dialog-scrim { position: fixed; inset: 0; background: var(--scrim); z-index: 90; display: flex; align-items: center; justify-content: center; padding: 24px; animation: fade .15s; }
.dialog { background: var(--raised); border-radius: 22px; width: min(100%, 360px); padding: 22px 20px 16px; text-align: center; box-shadow: none; animation: pop .18s ease-out; }
.dialog h3 { margin: 0 0 8px; font-size: var(--fs-md); font-weight: 500; }
.dialog p { margin: 0 0 18px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }
.dialog .btn-row { justify-content: center; }
.dialog .btn { flex: 1; }

.spinner { width: 16px; height: 16px; border: 2px solid var(--border-strong); border-top-color: var(--text); border-radius: 50%; animation: spin .7s linear infinite; display: inline-block; flex: none; }
@keyframes spin { to { transform: rotate(360deg); } }
.welcome { padding: 8px 4px 20px; }
.welcome h1 { font-size: var(--fs-lg); margin: 10px 0 8px; }
.welcome p { color: var(--muted); line-height: 1.5; margin: 0 0 12px; }

/* ---------- the computer sheet on Free (src/ui/computer.js ComputerChoice) ---------- */
/* Where the bots can work, in the plan page's cards: a hosted virtual machine
   (a paid plan's) on top and boldest, ringed like a chosen plan, then a
   computer of your own, plainer. */
.pc-choice { display: flex; flex-direction: column; gap: 14px; padding: 2px 0 12px; }
.pc-eyebrow { margin: 2px 2px 0; color: var(--muted); font-size: var(--fs-sm); line-height: 16px; font-weight: 700; letter-spacing: 1.8px; text-transform: uppercase; }
.pc-title { margin: 0 2px; font-size: var(--fs-lg); line-height: 36px; font-weight: 700; letter-spacing: -.8px; }
.pc-lead { margin: -4px 2px 6px; color: var(--muted); font-size: var(--fs-md); line-height: 23px; }
.pc-card { display: flex; flex-direction: column; gap: 14px; padding: 20px 18px 16px; border-radius: 24px; background: var(--surface);
  box-shadow: inset 0 0 0 1px var(--border); animation: sub-rise .5s cubic-bezier(.2, .9, .3, 1) backwards; }
.pc-card.hosted { gap: 16px; box-shadow: inset 0 0 0 2px var(--text); animation-delay: 60ms; }
.pc-card.own { animation-delay: 140ms; }
.pc-card-head { display: flex; align-items: center; gap: 14px; }
.pc-card-head h2 { margin: 0; font-size: var(--fs-md); line-height: 26px; font-weight: 700; letter-spacing: -.3px; }
.pc-badge { display: inline-block; margin-top: 6px; padding: 3px 9px; border-radius: 999px; background: var(--surface-2); color: var(--muted);
  font-size: var(--fs-sm); line-height: 15px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase; }
.pc-badge.paid { background: var(--text); color: var(--bg); }
.pc-device { flex: none; width: 54px; height: 54px; border-radius: 16px; display: flex; align-items: center; justify-content: center; background: var(--surface-2); color: var(--text); }
.pc-text { margin: 0; color: var(--text-2); font-size: var(--fs-md); line-height: 22px; }
.pc-points { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 14px; }
.pc-points li { display: flex; align-items: center; gap: 12px; }
.pc-icon { flex: none; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; background: var(--surface-2); color: var(--text); }
.pc-icon svg { width: 19px; height: 19px; }
.pc-points b { display: block; font-size: var(--fs-md); line-height: 21px; font-weight: 700; }
.pc-points li div span { display: block; margin-top: 1px; color: var(--muted); font-size: var(--fs-sm); line-height: 19px; }
.pc-card .btn { margin-top: 2px; }
.pc-note { margin: -4px 0 0; display: flex; align-items: center; justify-content: center; gap: 6px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; text-align: center; }
.pc-error { margin: 0 4px; color: var(--red); font-size: var(--fs-sm); line-height: 20px; }
.pc-foot { margin: 4px 10px 0; display: flex; align-items: flex-start; justify-content: center; gap: 8px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; }
.pc-foot svg { flex: none; margin-top: 1px; }
@media (prefers-reduced-motion: reduce) { .pc-card { animation: none; } }
.provider-card { display: flex; align-items: center; gap: 12px; }
.provider-logo { width: 34px; height: 34px; border-radius: 10px; background: var(--surface-3); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: var(--fs-sm); flex: none; }
.ok-check { color: var(--green); display: inline-flex; }
/* A model Free doesn't run (the model picker): it comes with a paid plan. */
.plan-pill { flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 3px 9px; border-radius: 999px; background: var(--surface-3);
  color: var(--text-2); font-size: var(--fs-sm); line-height: 16px; font-weight: 500; white-space: nowrap; }
.ok-check svg { width: 20px; height: 20px; }
.usage-table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.usage-table td, .usage-table th { padding: 8px 6px; border-bottom: 1px solid var(--border); text-align: right; }
.usage-table td:first-child, .usage-table th:first-child { text-align: left; }
.usage-table th { color: var(--muted); font-weight: 500; font-size: var(--fs-sm); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---------- remote mode ---------- */
.computer-cta { display: flex; align-items: center; gap: 12px; margin: 26px auto 0; max-width: 420px; width: 100%; padding: 14px 16px; border: 0; border-radius: 18px;
  background: var(--surface-2); color: var(--muted); text-align: left; font: inherit; font-size: var(--fs-sm); line-height: 1.4; cursor: pointer; }
.computer-cta b { color: var(--text); font-size: var(--fs-md); }
.computer-cta svg:first-child { flex: none; color: var(--text); }
.computer-cta svg:last-child { flex: none; color: var(--dim); }

/* ---------- the splash, while the app starts (index.html #boot) ---------- */
/* Holli's cloud hops while the app loads, like a bot thinking with Hop
   (src/ui/avatar.js): it crouches, springs up stretched, lands squashed and
   bounces back, 1.2 s a hop, with no shadow under it. Its eyes glide between the bots' own expressions, four hops a round:
   up as it jumps and a happy squint as it lands, then a glance up and to the
   right, then up again, then a look to the left; and it blinks after the
   second and the fourth. With Reduce Motion it holds still and fades gently,
   and still blinks.
   It's in the middle of the screen below the status bar: in Holli Bot for
   iPhone the page goes up under the status bar, and there the cloud stays
   where the app's own loading screen had it (ios/, SplashView). */
#boot { padding-top: env(safe-area-inset-top, 0px); }
#boot .boot-bot { overflow: visible; }
#boot .boot-body, #boot .boot-eye, #boot .boot-eye line { transform-box: view-box; }
#boot .boot-body { transform-origin: 50px 88px; animation: boot-hop 1.2s infinite; }
#boot .boot-eye.l { animation: boot-eye-l 4.8s ease-in-out infinite; }
#boot .boot-eye.r { animation: boot-eye-r 4.8s ease-in-out infinite; }
#boot .boot-eye line { transform-origin: 0 0; animation: boot-blink 4.8s infinite; }
@keyframes boot-hop {
  0% { transform: translateY(0) scale(1.1, .9); animation-timing-function: cubic-bezier(.25, .8, .4, 1); }
  45% { transform: translateY(-17px) scale(.97, 1.04); animation-timing-function: cubic-bezier(.6, 0, .85, .5); }
  72% { transform: translateY(0) scale(1.12, .88); animation-timing-function: ease-out; }
  86% { transform: translateY(0) scale(.97, 1.03); animation-timing-function: ease-in-out; }
  100% { transform: translateY(0) scale(1.1, .9); }
}
/* The eyes, at the cloud's face (SHAPES.cloud: face 51, 56; up and down 0.75):
   happy, up, lookUpRight, neutral and left from EXPRESSIONS. */
@keyframes boot-eye-l {
  0%, 18.5%, 27%, 68.5%, 77%, 100% { transform: translate(40px, 54.5px) rotate(38deg) scaleY(.8); }
  5%, 15%, 56%, 65% { transform: translate(41px, 48.5px) rotate(0deg) scaleY(.95); }
  31%, 40% { transform: translate(49px, 50px) rotate(-18deg) scaleY(1); }
  43.5%, 52%, 93.5%, 97% { transform: translate(41px, 56px) rotate(0deg) scaleY(1); }
  81%, 90% { transform: translate(31px, 57.5px) rotate(-8deg) scaleY(1); }
}
@keyframes boot-eye-r {
  0%, 18.5%, 27%, 68.5%, 77%, 100% { transform: translate(62px, 54.5px) rotate(-38deg) scaleY(.8); }
  5%, 15%, 56%, 65% { transform: translate(61px, 48.5px) rotate(0deg) scaleY(.95); }
  31%, 40% { transform: translate(73px, 46.25px) rotate(-18deg) scaleY(1); }
  43.5%, 52%, 93.5%, 97% { transform: translate(61px, 56px) rotate(0deg) scaleY(1); }
  81%, 90% { transform: translate(51px, 57.5px) rotate(-8deg) scaleY(1); }
}
@keyframes boot-blink {
  0%, 44%, 47%, 94%, 97%, 100% { transform: scaleY(1); }
  45.5%, 95.5% { transform: scaleY(.12); }
}
@keyframes boot-fade { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) {
  #boot .boot-body, #boot .boot-eye.l, #boot .boot-eye.r { animation: none; }
  #boot .boot-bot { animation: boot-fade 1.6s ease-in-out infinite; }
}

/* ---------- signed out: welcome, sign in, create account ---------- */
/* Macronaut's welcome and sign-in layout on one plain page in Deep Teal (a
   mint page, as Light's was before it took ChatGPT's white, deep teal text,
   teal for the way on and amber for a saving), whatever the app's appearance
   setting. Redefining the
   palette here draws shared pieces (spinner, icons, focus rings) in its
   colors too. */
html.signed-out, html.signed-out body { background: #f3f9f8; color-scheme: light; }
html.signed-out #boot { background: #f3f9f8 !important; }
.hello {
  --teal: #087e8b; --teal-deep: #075c66; --amber: #e9a23b; --amber-ink: #9a5e08;
  --blue: var(--teal); --green: var(--teal); --orange: var(--amber);
  --bg: #f3f9f8; --surface: #fff; --surface-2: #e5ecec; --border: #d8e1e1; --border-strong: #c6d2d2;
  --text: #14343a; --text-2: var(--text); --muted: #465e62; --dim: #9aaaac; --red: #d70015;
  position: fixed; inset: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  background: var(--bg); color: var(--text); color-scheme: light;
}
.hello-canvas { width: 100%; max-width: 600px; min-height: 100%; margin: 0 auto; display: flex; flex-direction: column; padding-top: var(--safe-top); }
.hello-hero { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 20px; padding: 32px 24px; }
.hello-wordmark { margin: 0; display: flex; flex-direction: column; align-items: center; }
/* Negative right margins cancel the tracking after the last letter, so each line is truly centered. */
/* The logo, the one size outside the text sizes (--fs-*). */
.hello-wordmark .w-holly { font-size: 46px; line-height: 50px; font-weight: 700; letter-spacing: 6px; margin-right: -6px; }
.hello-wordmark .w-bot { font-size: var(--fs-lg); line-height: 32px; font-weight: 500; letter-spacing: 1.5px; margin: 2px -1.5px 0 0; }
.hello-dock { flex: none; display: flex; flex-direction: column; gap: 16px; padding: 0 24px calc(max(var(--safe-bottom), 12px) + 8px); }
.hello-ctas { display: flex; flex-direction: column; gap: 12px; }
.hello-cta, .auth-btn { width: 100%; height: 50px; border-radius: 12px; display: flex; align-items: center; justify-content: center; gap: 10px; font-size: var(--fs-md); line-height: 22px; font-weight: 500; transition: opacity .15s; }
.hello-cta { background: var(--teal); color: #fff; }
.hello-cta:active, .auth-btn:active { opacity: .85; }
.hello-cta:disabled { opacity: .5; }
.hello-cta .spinner { border-color: rgba(255, 255, 255, .35); border-top-color: #fff; }
.hello-cta.secondary { background: var(--bg); color: var(--text); border: 1px solid var(--border-strong); }
.hello-cta.secondary .spinner { border-color: var(--border); border-top-color: var(--text); }
.hello-cta.secondary.danger { color: var(--red); border-color: var(--red); }
.hello-more { align-self: center; min-height: 44px; padding: 0 12px; font-size: var(--fs-sm); line-height: 20px; }
.hello-more:disabled { opacity: .5; }
.hello-note { max-width: 320px; margin: 0; text-align: center; color: var(--muted); font-size: var(--fs-sm); line-height: 21px; }
.hello-hero.device { gap: 14px; text-align: center; }
.device-title { margin: 6px 0 0; font-size: var(--fs-lg); line-height: 30px; font-weight: 700; }
.device-text { max-width: 400px; margin: 0; color: var(--text-2); font-size: var(--fs-md); line-height: 23px; }
.hello-warning { max-width: 400px; margin: 4px 0 0; padding: 12px 14px; border-radius: 12px; background: var(--surface-2); color: var(--text-2);
  font-size: var(--fs-sm); line-height: 20px; text-align: left; }
.hello-warning b { color: var(--text); }
.hello-warning code { font-size: var(--fs-sm); }

.hello-canvas.auth { animation: hello-push .3s cubic-bezier(.22, 1, .36, 1); }
@keyframes hello-push { from { opacity: 0; transform: translateX(28px); } to { opacity: 1; transform: none; } }
.auth-head { display: flex; align-items: center; min-height: 48px; padding: 4px 16px 0; }
.auth-head h1 { flex: 1; margin: 0; text-align: center; font-size: var(--fs-md); line-height: 22px; font-weight: 500; }
.auth-back { width: var(--round-btn, var(--tap)); height: var(--round-btn, var(--tap)); flex: none; }
.auth-form { display: flex; flex-direction: column; gap: 20px; padding: 28px 24px 32px; }
.auth-lead { margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 21px; }
.auth-providers { display: flex; flex-direction: column; gap: 12px; }
.auth-btn.apple { background: #000; color: #fff; }
.auth-btn.google { background: #fff; border: 1px solid var(--border-strong); }
.auth-btn:disabled { opacity: .4; }
.auth-btn.apple .spinner { border-color: rgba(255, 255, 255, .35); border-top-color: #fff; }
.auth-note { margin: -8px 0 0; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; text-align: center; }
.auth-error { margin: 0; color: var(--red); font-size: var(--fs-sm); line-height: 20px; font-weight: 500; }
.auth-unavailable { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 14px 16px 4px; border-radius: 12px; background: var(--surface-2); }
.auth-unavailable p { margin: 0; color: var(--text-2); font-size: var(--fs-sm); line-height: 21px; }
.auth-skip { min-height: 44px; font-size: var(--fs-sm); font-weight: 500; text-decoration: underline; }
.auth-switch { min-height: 44px; padding: 12px 0; text-align: center; font-size: var(--fs-sm); line-height: 20px; color: var(--muted); }
.auth-switch u { color: var(--text); font-weight: 500; }
.auth-legal { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; text-align: center; }
.auth-legal a { color: var(--text); text-decoration: underline; }
/* The email and password under Apple and Google, and the screens for the
   code emailed to verify an address or reset a password. Boxes are in the
   middle size (--fs-md); the page's maximum scale (index.html) keeps iPhone
   from zooming in as one is tapped. */
.auth-or { display: flex; align-items: center; gap: 12px; margin: -4px 0; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; }
.auth-or::before, .auth-or::after { content: ""; flex: 1; height: 1px; background: var(--border-strong); }
.auth-email { display: flex; flex-direction: column; gap: 12px; }
.auth-input { width: 100%; height: 50px; padding: 0 14px; border: 1px solid var(--border-strong); border-radius: 12px; background: var(--surface); color: var(--text);
  font: inherit; font-size: var(--fs-md); outline: none; transition: border-color .15s, box-shadow .15s; }
.auth-input:focus { border-color: var(--teal); box-shadow: 0 0 0 3px rgba(8, 126, 139, .16); }
.auth-input:disabled { opacity: .6; }
.auth-pass { position: relative; }
.auth-pass .auth-input { padding-right: 72px; }
.auth-show { position: absolute; top: 3px; right: 4px; height: 44px; padding: 0 12px; color: var(--teal-deep); font-size: var(--fs-sm); font-weight: 500; }
.auth-code { text-align: center; letter-spacing: 8px; font-size: var(--fs-lg); font-weight: 500; font-variant-numeric: tabular-nums; }
.auth-code::placeholder { letter-spacing: 8px; }
.auth-hint { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; }
.auth-lead b { color: var(--text); font-weight: 500; overflow-wrap: anywhere; }
.auth-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 0 8px; margin-top: -8px; }
.auth-link { align-self: center; min-height: 44px; padding: 0 10px; color: var(--text); font-size: var(--fs-sm); font-weight: 500; text-decoration: underline; }
.auth-link:disabled { opacity: .5; text-decoration: none; }

/* More options (src/ui/welcome.js): a sheet up from the bottom, as the app's
   are: up in 450 ms, easing off as it arrives, and back down the same way,
   its dimming with it; a finger slides it down and back up (useSheetSwipe). */
.hello-layer { display: contents; }
.hello-layer.closing { pointer-events: none; }
.hello-scrim { position: fixed; inset: 0; z-index: 50; background: rgba(20, 52, 58, .35); animation: fade .45s cubic-bezier(.2, .8, .2, 1); }
.hello-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 51; max-width: 600px; margin: 0 auto; display: flex; flex-direction: column; gap: 16px;
  padding: 10px 24px calc(max(var(--safe-bottom), 12px) + 8px); background: var(--bg); border-radius: 24px 24px 0 0; box-shadow: none;
  animation: rise .45s cubic-bezier(.2, .8, .2, 1); }
.hello-layer.closing .hello-sheet { animation: sink .45s cubic-bezier(.8, 0, .8, .2) forwards; }
.hello-layer.closing .hello-scrim { animation: fade-out .45s cubic-bezier(.8, 0, .8, .2) forwards; }
@media (prefers-reduced-motion: reduce) { .hello-sheet, .hello-scrim { animation: none; } }
.hello-grabber { align-self: center; width: 36px; height: 5px; border-radius: 3px; background: var(--border-strong); }
.hello-sheet h2 { margin: 2px 0 4px; text-align: center; font-size: var(--fs-md); line-height: 22px; font-weight: 500; }
.hello-sheet-cancel { align-self: center; min-height: 44px; padding: 0 16px; font-size: var(--fs-md); font-weight: 500; }

/* ---------- the subscription page (src/ui/subscribe.js) ---------- */
/* On the white page, right after Create Account: the plans as cards to pick
   from, Monthly or Yearly, and Subscribe docked at the bottom. */
.sub-canvas { max-width: 560px; }
.sub-head { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 28px 24px 0; text-align: center;
  animation: sub-rise .5s cubic-bezier(.2, .9, .3, 1) backwards; }
.sub-title { margin: 8px 0 0; font-size: var(--fs-lg); line-height: 36px; font-weight: 700; letter-spacing: -.5px; }
.sub-lead { max-width: 340px; margin: 0; color: var(--muted); font-size: var(--fs-md); line-height: 22px; }
.sub-notice { margin: 18px 24px 0; padding: 12px 14px; border-radius: 12px; background: var(--surface-2); color: var(--text-2); font-size: var(--fs-sm); line-height: 20px; text-align: center; }

/* Monthly | Yearly, with a thumb that slides between them. */
.sub-cycle { position: relative; display: grid; grid-template-columns: 1fr 1fr; width: min(100% - 48px, 400px); margin: 22px auto 0; padding: 4px;
  border-radius: 14px; background: var(--surface-2); }
.sub-cycle::before { content: ""; position: absolute; top: 4px; bottom: 4px; left: 4px; width: calc(50% - 4px); border-radius: 10px; background: #fff;
  box-shadow: none; transition: transform .32s cubic-bezier(.3, .9, .3, 1); }
.sub-cycle.year::before { transform: translateX(100%); }
.sub-cycle label { position: relative; z-index: 1; min-height: 40px; display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 2px 6px;
  padding: 2px 4px; border-radius: 10px; color: var(--muted); font-size: var(--fs-sm); line-height: 20px; font-weight: 500; cursor: pointer; transition: color .2s; }
.sub-cycle label.on { color: var(--text); }
.sub-cycle input, .sub-plan input { position: absolute; top: 0; left: 0; opacity: 0; pointer-events: none; }
.sub-cycle label:has(input:focus-visible), .sub-plan:has(input:focus-visible) { outline: 2px solid var(--blue); outline-offset: 2px; }
.sub-free, .sub-save { padding: 2px 7px; border-radius: 999px; background: #faead4; color: #8f5806; font-size: var(--fs-sm); line-height: 16px; font-weight: 700;
  letter-spacing: .2px; white-space: nowrap; }

/* A plan: the radio, its name and price on top, its server underneath. */
.sub-plans { display: flex; flex-direction: column; gap: 12px; padding: 20px 24px 0; }
.sub-plan { position: relative; display: grid; grid-template-columns: auto 1fr auto; grid-template-areas: "radio name price" "radio note price" ". specs specs";
  column-gap: 12px; align-items: start; padding: 16px 16px 16px 14px; border-radius: 18px; background: #fff; cursor: pointer; box-shadow: inset 0 0 0 1px var(--border);
  transition: box-shadow .25s, transform .15s; animation: sub-rise .5s cubic-bezier(.2, .9, .3, 1) backwards; animation-delay: calc(var(--i, 0) * 70ms + 60ms); }
.sub-plan.on { box-shadow: inset 0 0 0 2px var(--teal); }
.sub-plan:active { transform: scale(.985); }
.sub-radio { grid-area: radio; width: 24px; height: 24px; margin-top: 1px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff;
  box-shadow: inset 0 0 0 2px var(--border-strong); transition: background .2s, box-shadow .2s; }
.sub-radio svg { opacity: 0; transform: scale(.4); transition: opacity .15s, transform .3s cubic-bezier(.3, 1.6, .5, 1); }
.sub-plan.on .sub-radio { background: var(--teal); box-shadow: none; }
.sub-plan.on .sub-radio svg { opacity: 1; transform: none; }
.sub-name { grid-area: name; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: var(--fs-md); line-height: 26px; font-weight: 700; letter-spacing: -.2px; }
.sub-note { grid-area: note; color: var(--muted); font-size: var(--fs-sm); line-height: 20px; }
.sub-price { grid-area: price; display: flex; flex-direction: column; align-items: flex-end; animation: fade .25s ease-out; }
.sub-price b { font-size: var(--fs-lg); line-height: 30px; font-weight: 700; letter-spacing: -.6px; font-variant-numeric: tabular-nums; }
.sub-price span { color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 500; }
.sub-specs { grid-area: specs; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.sub-specs > span { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px 6px 8px; border-radius: 9px; background: var(--surface-2); color: var(--text-2);
  font-size: var(--fs-sm); line-height: 16px; font-weight: 500; font-variant-numeric: tabular-nums; }
.sub-specs svg { color: var(--muted); }

.sub-includes { margin: 22px 24px 0; padding: 18px 18px 4px; border-radius: 18px; background: var(--surface-2); }
.sub-includes h2 { margin: 0 0 14px; color: var(--muted); font-size: var(--fs-sm); line-height: 16px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase; }
.sub-includes ul { margin: 0; padding: 0; list-style: none; }
.sub-includes li { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 14px; color: var(--text-2); font-size: var(--fs-md); line-height: 21px; }
.sub-includes li svg { flex: none; margin-top: 1px; color: var(--text); }

/* Have a tester code? (src/ui/subscribe.js): a link under what every plan includes, opening a field for the code,
   in capitals, and Use Code under it, as wide as the plans at most. */
.sub-tester { display: flex; flex-direction: column; align-items: center; margin: 18px 24px 0; }
.sub-tester-form { display: flex; flex-direction: column; gap: 10px; width: 100%; max-width: 420px; }
.sub-tester-input { text-align: center; text-transform: uppercase; letter-spacing: 1px; font-variant-numeric: tabular-nums; }
.sub-tester-input::placeholder { text-transform: none; letter-spacing: 0; }
.sub-account { display: flex; flex-direction: column; align-items: center; margin: 22px 24px 0; text-align: center; color: var(--muted); font-size: var(--fs-sm); line-height: 20px; }
.sub-account p { margin: 0; overflow-wrap: anywhere; }
.sub-account b { color: var(--text-2); font-weight: 500; }
.sub-account-actions { display: flex; align-items: center; gap: 2px; }
.sub-account-actions button { min-height: 44px; padding: 0 10px; color: var(--text); font-size: var(--fs-sm); font-weight: 500; }
.sub-account-actions button:disabled { opacity: .5; }
.hello-dock .sub-account { margin: -6px 0 0; }
.sub-legal { margin: 0 24px; }

/* Subscribe stays in reach while the plans scroll under it. */
.sub-dock { position: sticky; bottom: 0; z-index: 2; display: flex; flex-direction: column; gap: 10px; margin-top: auto;
  padding: 28px 24px calc(max(var(--safe-bottom), 12px) + 8px); background: linear-gradient(to bottom, rgba(243, 249, 248, 0), var(--bg) 28px); }
.sub-dock .auth-error { text-align: center; }
.sub-fine { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; text-align: center; }
@keyframes sub-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }

/* The plan page over the app (PlansPage): above Settings, sliding up, with a way back. */
.hello.in-app { z-index: 60; animation: rise .45s cubic-bezier(.2, .8, .2, 1); overscroll-behavior-y: none; }
/* A sheet, this page or More options once a finger has had it (useSheetSwipe
   in src/ui/components.js): done sliding in, so it doesn't slide in again,
   and closed by the finger, on its way out from where it was let go rather
   than sliding down again from the top; closed any other way later, it
   slides down as ever. */
:is(.sheet, .sheet-scrim, .hello, .hello-sheet, .hello-scrim)[data-held] { animation: none; }
:is(.sheet-layer, .hello-layer).closing :is(.sheet, .sheet-scrim, .hello, .hello-sheet, .hello-scrim)[data-slid] { animation: none; }
.sub-close { position: fixed; top: calc(var(--safe-top) + 10px); left: 16px; z-index: 4; }
.sub-close:disabled { opacity: .4; }
/* On a paid plan the cards show the plans, and Stripe changes them. */
.sub-plan.fixed { cursor: default; }
.sub-plan.fixed:active { transform: none; }
.sub-now { padding: 2px 8px; border-radius: 999px; background: var(--text); color: #fff; font-size: var(--fs-sm); line-height: 16px; font-weight: 700;
  letter-spacing: .2px; white-space: nowrap; }
/* A plan that isn't offered yet (convex/lib/plans.ts soon): greyed out, with
   Coming soon, and it can't be picked. */
.sub-plan.soon { opacity: .45; filter: grayscale(1); cursor: default; }
.sub-plan.soon:active { transform: none; }
.sub-cycle label.soon { opacity: .45; cursor: default; }
.sub-soon { padding: 2px 8px; border-radius: 999px; background: var(--text); color: #fff; font-size: var(--fs-sm); line-height: 16px; font-weight: 700;
  letter-spacing: .2px; white-space: nowrap; }

/* The Chief Coordinator's page (src/ui/chief.js): what it does, its name and look. */
.chief-points { max-width: 400px; margin: 2px 0 6px; padding: 16px 18px; list-style: none; display: flex; flex-direction: column; gap: 12px;
  border-radius: 18px; background: var(--surface-2); text-align: left; }
.chief-points li { display: flex; align-items: flex-start; gap: 10px; color: var(--text-2); font-size: var(--fs-md); line-height: 21px; }
.chief-points svg { flex: none; margin-top: 2px; color: var(--text); }
.chief-page .name-input { max-width: 400px; height: 56px; background: var(--surface-2); color: var(--text); font-size: var(--fs-lg); }
.chief-page .picker { margin-top: 10px; }
@media (min-width: 700px) {
  .sub-head { padding-top: 56px; }
}

/* Settings → Privacy Policy and Terms of Service: the body of privacy.html and terms.html. */
.legal-body h1 { display: none; }
.legal-body h2 { margin: 18px 0 4px; font-size: var(--fs-md); line-height: 22px; }
.legal-body p { margin: 0 0 10px; }
.legal-body .updated { color: var(--muted); font-size: var(--fs-sm); }
.legal-body a { color: inherit; text-decoration: underline; }

/* Notices at the top of the app (src/main.js): another device changed the
   account (it reloads when it's safe), or a payment didn't go through. They
   sit under the top bar, so they never cover its buttons (Back, Settings…). */
.banners { position: fixed; top: calc(var(--safe-top) + 74px); left: 50%; transform: translateX(-50%); z-index: 90; width: max-content; max-width: calc(100vw - 32px);
  display: flex; flex-direction: column; align-items: center; gap: 8px; }
.stale-banner { max-width: 100%; min-height: 36px; padding: 8px 16px; border-radius: 18px; background: var(--surface-2); color: var(--text); border: 1px solid var(--border);
  box-shadow: none; font-size: var(--fs-sm); line-height: 20px; font-weight: 500; animation: fade .2s ease-out; }
.stale-banner.warn { background: #b3261e; color: #fff; border-color: transparent; }
.stale-banner:disabled { opacity: .7; }
/* A new version of Holli Bot is ready (src/ui/update.js): out of the chats'
   way, in the blue of an unread dot. A small pill beside the menu button in
   the agent list's top bar on a phone; a row as wide as who's signed in, just
   above them, at the bottom of the sidebar on a computer; a dot on the
   initials in the rail. */
.update-pill { --update: var(--side-unread, var(--blue)); flex: none; display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px 0 9px;
  border-radius: 15px; background: color-mix(in srgb, var(--update) 15%, transparent); color: var(--update); font-size: var(--fs-sm); font-weight: 600; line-height: 1;
  white-space: nowrap; animation: fade .2s ease-out; }
.update-pill svg { width: 17px; height: 17px; stroke-width: 2.25; }
.update-pill:disabled { opacity: .7; }
.update-pill.wide { display: flex; width: calc(100% - 24px); height: 36px; margin: 0 12px 6px; padding: 0 10px; border-radius: 10px; gap: 9px; }
@media (hover: hover) { .update-pill:hover:not(:disabled) { background: color-mix(in srgb, var(--update) 22%, transparent); } }
.rail-me.has-update { position: relative; }
.rail-me.has-update::after { content: ""; position: absolute; top: -1px; right: -1px; width: 9px; height: 9px; border-radius: 50%; background: var(--side-unread, var(--blue)); box-shadow: 0 0 0 2px var(--bg); }

/* ---------- Settings → Support (src/ui/support.js) ----------
   Live Chat with Holli Bot Support, and the owner's answers to it: the
   chats' own bubbles (yours at the right, in your color), each with who and
   when over it, and under them a box to write in with a round Send. */
.sup-chat { display: flex; flex-direction: column; }
.sup-lead { color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; margin: 4px 6px 16px; }
.sup-log { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.sup-msg .bubble { white-space: pre-wrap; max-width: min(85%, 520px); }
.sup-who { color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; margin: 0 8px 4px; }
.sup-wait { color: var(--muted); font-size: var(--fs-sm); text-align: right; margin: 8px 8px 0; }
.sup-compose { display: flex; align-items: flex-end; gap: 8px; padding: 16px 0 calc(8px + var(--safe-bottom)); }
.sup-compose .textarea.grow { min-height: 46px; max-height: 200px; overflow-y: auto; border-radius: 23px; padding: 12px 16px; line-height: 22px; }
.sup-send { flex: none; width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; background: var(--user-bubble); color: #fff; transition: opacity .15s; }
.sup-send svg { width: 20px; height: 20px; stroke-width: 2.25; }
.sup-send:disabled { opacity: .35; }
.sup-mail .btn-row { margin: 4px 0 6px; }
.sup-state { font-size: var(--fs-sm); font-weight: 500; margin-right: 8px; color: var(--muted); }
.sup-state.waiting { color: var(--orange); }
.sup-time { color: var(--muted); font-size: var(--fs-sm); }
.sup-done { margin-left: 6px; color: var(--blue); font-weight: 500; font-size: var(--fs-sm); }

/* ---------- workspace: what a chat's bot works on (src/ui/workspace.js) ---------- */
.ws-sheet { --ws-accent: #4f959f; }
:root[data-theme="light"] .ws-sheet { --ws-accent: var(--teal); }
.ws-lead { color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; margin: 2px 4px 14px; }
.ws-lead.small { font-size: var(--fs-sm); margin: 10px 4px 8px; }
.ws-note { color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; margin: 12px 4px; }
.ws-link { color: var(--blue); font-weight: 500; }
.ws-head { display: flex; align-items: center; gap: 7px; margin: 16px 4px 8px; color: var(--muted); font-size: var(--fs-sm); }
.ws-count { margin-left: auto; color: var(--text-2); font-weight: 500; }
.ws-search { margin: 0 0 10px; }
.ws-list { background: var(--surface); border-radius: var(--radius-card); overflow: hidden; margin-top: 8px; }
:root[data-theme="light"] .ws-list { background: #fff; border: 1px solid var(--border); }
.ws-row { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 58px; padding: 10px 14px; text-align: left; position: relative; }
.ws-row + .ws-row::before { content: ""; position: absolute; top: 0; left: 50px; right: 0; border-top: 1px solid var(--border); }
.ws-row:active { background: var(--surface-2); }
.ws-row:disabled { opacity: .6; }
.ws-check, .ws-radio { width: 22px; height: 22px; flex: none; border: 1.5px solid var(--border-strong); display: inline-flex; align-items: center; justify-content: center; }
.ws-check { border-radius: 7px; }
.ws-radio { border-radius: 50%; }
.ws-row.on .ws-check { background: var(--ws-accent); border-color: transparent; color: #fff; }
.ws-row.on .ws-radio { border-color: var(--ws-accent); }
.ws-radio span { width: 11px; height: 11px; border-radius: 50%; background: var(--ws-accent); }
.ws-icon { flex: none; color: var(--muted); display: inline-flex; }
.ws-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.ws-title { font-size: var(--fs-md); font-weight: 500; display: flex; align-items: center; gap: 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.ws-lock { color: var(--muted); flex: none; }
.ws-kind { font-size: var(--fs-sm); font-weight: 500; color: var(--muted); border: 1px solid var(--border); border-radius: 6px; padding: 0 5px; line-height: 16px; }
.ws-sub { color: var(--muted); font-size: var(--fs-sm); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.ws-when { flex: none; color: var(--muted); font-size: var(--fs-sm); }
.ws-apps-label { margin: 22px 4px 0; }
.ws-empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px; padding: 30px 16px; color: var(--muted); font-size: var(--fs-sm); }
.ws-empty b { color: var(--text); font-size: var(--fs-md); }
.ws-empty p { margin: 0 0 8px; max-width: 300px; line-height: 1.4; }
.ws-empty-icon { width: 56px; height: 56px; border-radius: 18px; background: var(--surface-2); color: var(--text); display: inline-flex; align-items: center; justify-content: center; }

/* ---------- a bot's computer and workspace, in its profile (src/ui/bot-computer.js) ---------- */
/* Computer: just the live picture of the bot's computer's screen, a rectangle as wide as the column at 16:10 (the
   whole screen shows, letterboxed), a shade lighter than what it's on; under it, "<name>'s screen" in the second
   shade. With the pointer on the picture (or the keyboard's focus, or a finger pressing it), a dark shade fades in
   over all of it (.bc-over, 55% black) with Lucide's expand dead in its middle, white at 75%, 42 px, on nothing but the
   shade; a click anywhere
   on it opens the computer's view. Off, the bot is asleep in it (src/ui/sleep-scene.js, .sleep below); the
   computer itself is on the Work tab. */
.bc-card { margin: 14px 0; }
.bc-screen { position: relative; display: block; width: 100%; aspect-ratio: 16 / 10; padding: 0; border: 1px solid var(--border); border-radius: 16px; overflow: hidden;
  background: color-mix(in srgb, var(--text) 5%, transparent); color: var(--text); text-align: left; transition: transform .12s; }
.bc-screen:active { transform: scale(.99); }
.bc-screen img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; -webkit-user-select: none; user-select: none; }
/* The shade fades in by its color and the icon by its own opacity. */
.bc-over { position: absolute; inset: 0; z-index: 1; display: grid; place-items: center; background: rgba(0, 0, 0, 0); pointer-events: none;
  transition: background-color .18s ease-out; }
.bc-grow { display: grid; color: #fff; opacity: 0; transition: opacity .18s ease-out; }
.bc-grow svg { width: 42px; height: 42px; }
.bc-screen:is(:focus-visible, :active) .bc-over { background-color: rgba(0, 0, 0, .55); }
.bc-screen:is(:focus-visible, :active) .bc-grow { opacity: .75; }
.bc-empty { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; padding: 16px 20px 24px;
  text-align: center; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }
.bc-empty svg { width: 30px; height: 30px; margin-bottom: 4px; color: var(--muted); }
.bc-empty b { color: var(--text); font-size: var(--fs-md); font-weight: 500; }
.bc-setup { position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%); white-space: nowrap; padding: 7px 14px; border-radius: 999px;
  background: var(--text); color: var(--bg); font-size: var(--fs-sm); font-weight: 500; }
.bc-caption { margin: 10px 0 0; overflow: hidden; color: var(--muted); font-size: var(--fs-md); text-align: center; white-space: nowrap; text-overflow: ellipsis; }
@media (hover: hover) and (pointer: fine) {
  .bc-screen:hover .bc-over { background-color: rgba(0, 0, 0, .55); }
  .bc-screen:hover .bc-grow { opacity: .75; }
  .bc-screen:active { transform: none; }
}
/* The bot asleep (src/ui/sleep-scene.js): it breathes, swelling a touch and settling every 4 s, and its z's come up
   one after another over 3 s, each fading in as it rises and out again; with Reduce Motion it's still, the z's all
   there. */
.bc-empty .sleep { width: 72px; height: 60px; margin: 0 0 2px; color: var(--dim); }
.sleep-bot { transform-box: fill-box; transform-origin: 50% 100%; animation: sleep-breathe 4s ease-in-out infinite; }
.sleep-z { transform-box: fill-box; transform-origin: 0 100%; animation: sleep-z 3s ease-out infinite both; }
.sleep-z.z2 { animation-delay: 1s; }
.sleep-z.z3 { animation-delay: 2s; }
@keyframes sleep-breathe { 50% { transform: scale(1.04, 1.02); } }
@keyframes sleep-z { 0% { opacity: 0; transform: translate(-3px, 4px) scale(.7); } 25%, 60% { opacity: 1; } 100% { opacity: 0; transform: translate(3px, -4px); } }
@media (prefers-reduced-motion: reduce) { .sleep-bot, .sleep-z { animation: none; } }
/* The picker: the computers as rows, each with its tile, name (Default beside it) and what it's doing; the bot's ticked.
   The menu's Computer page's tiles and words (.pc-tile, .pc-state, .pc-tag), in a sheet: its colours here too. */
.bc-pick { --card: var(--elevated); --meter: color-mix(in srgb, var(--text) 14%, transparent); }
.bc-pick .row { gap: 12px; }
.bc-pick .row:disabled { opacity: .5; }
.bc-pick .row .label .t { display: flex; align-items: center; min-width: 0; }
.bc-pick .row .label .t .pc-tag { height: 18px; margin-left: 8px; }
.bc-pick .row .label .s .pc-state { display: inline-flex; margin: 0; line-height: 18px; }
.bc-pick .row + .row::before { left: 66px; }
@media (prefers-reduced-motion: reduce) { .bc-screen:active { transform: none; } .bc-over, .bc-grow { transition: none; } }
/* Workspace: one line until it's opened (its icon, GitHub repositories, what's picked, and a chevron that turns
   up), then the repositories under it, as in a chat's Workspace sheet (.ws-*), and Clear Workspace. */
.bot-ws { --ws-accent: #4f959f; margin: 14px 0; border-radius: var(--radius-card); background: var(--elevated); overflow: hidden; }
:root[data-theme="light"] .bot-ws { --ws-accent: var(--teal); }
.bw-head { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 58px; padding: 12px 16px 12px 18px; text-align: left; }
.bw-icon { flex: none; display: inline-flex; color: var(--text-2); }
.bw-title { flex: none; font-size: var(--fs-md); }
.bw-value { flex: 1; min-width: 0; text-align: right; color: var(--muted); font-size: var(--fs-md); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bw-value.set { color: var(--text-2); }
.bw-chev { flex: none; width: 18px; height: 18px; color: var(--muted); transition: transform .2s; }
.bot-ws.open .bw-chev { transform: rotate(180deg); }
.bw-head:active { background: var(--surface-2); }
@media (hover: hover) { .bw-head:hover { background: color-mix(in srgb, var(--text) 4%, transparent); } }
.bw-body { padding: 0 10px 10px; border-top: 1px solid var(--border); animation: fade .18s ease-out; }
.bw-body .ws-list { margin-top: 8px; }
.bw-body .ws-head { margin: 14px 6px 4px; }
.bw-body .ws-note { margin: 12px 6px; }
.bw-clear { display: block; margin: 12px auto 2px; padding: 6px 12px; border-radius: 10px; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
.bw-clear:active { background: var(--surface-2); }
@media (hover: hover) { .bw-clear:hover { color: var(--text); background: var(--surface-2); } }
@media (prefers-reduced-motion: reduce) { .bw-chev { transition: none; } .bw-body { animation: none; } }
/* A computer the app isn't on, over the whole screen (LiveScreenSheet): its name at the top, its live picture as
   big as fits, and at the bottom that it's for watching, with Open Chat. */
.bc-view-title { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bc-view-title b { font-size: var(--fs-md); font-weight: 500; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bc-view-title span { color: var(--muted); font-size: var(--fs-sm); }
.bc-view .screen-stage { position: relative; padding: 0 12px; }
.bc-view-pic { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 10px; -webkit-user-select: none; user-select: none; }
.bc-view-foot { flex: none; display: flex; align-items: center; gap: 12px; padding: 12px 16px calc(12px + var(--safe-bottom)); }
.bc-view-foot p { flex: 1; margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }

/* ---------- a bot's profile (src/ui/bot-profile.js) ---------- */
/* On a phone, one column on the sheet: who it is (its face, which changes its look, and its name, edited where it
   is), its computer, a card of how many memories, notes, routines and talks with agents it has, then the tabs,
   held at the top as it scrolls, with ⋯ at their right (Export agent, Delete agent), and what's on the one
   picked. From 760 px wide, two columns: at the left, on the card colour, who it is down to the counts,
   scrolling by itself, with its own X at the top left (the sheet's bar goes); at the right, the tabs over
   what's on the one picked, which scrolls. */
.bp-sheet .sheet-body { padding: 0 0 calc(28px + var(--safe-bottom)); }
.bp-side { position: relative; padding: 4px 18px 6px; text-align: center; }
.bp-close { display: none; position: absolute; top: 18px; left: 18px; z-index: 1; }
.bp-sheet .bp-close { background: var(--sheet-btn); }
.bp-look { display: flex; flex-direction: column; align-items: center; gap: 4px; padding-top: 8px; }
.bp-face { border-radius: 30%; }
button.bp-face:active { transform: scale(.97); }
.bp-picker { padding-bottom: 6px; }
.bp-picker .picker { margin-top: 22px; }
.bp-picker .picker-label { margin-top: 24px; }
.bp-name { display: block; width: 100%; padding: 3px 10px; border: 1px solid transparent; border-radius: 10px; background: none; outline: none;
  text-align: center; text-overflow: ellipsis; transition: background .15s, border-color .15s; }
.bp-name { margin-top: 12px; color: var(--text); font-size: var(--fs-lg); line-height: 30px; font-weight: 700; }
.bp-name:focus { border-color: var(--border-strong); background: var(--surface); }
@media (hover: hover) and (pointer: fine) { .bp-name:hover:not(:focus) { background: color-mix(in srgb, var(--text) 5%, transparent); } }
.bp-side .bc-card { margin: 18px 0 14px; }
/* Each count's name at the left and its number at the right, the same room from either edge. */
.bp-stats { padding: 0 20px; border-radius: var(--radius-card); background: var(--elevated); }
.bp-stat { display: flex; align-items: center; justify-content: space-between; gap: 16px; width: 100%; min-height: 52px; font-size: var(--fs-md); text-align: left; }
.bp-stat + .bp-stat { border-top: 1px solid var(--border); }
.bp-stat span { flex: 1; min-width: 0; text-align: left; color: var(--text-2); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bp-stat b { flex: none; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.bp-stat:disabled { cursor: default; }
.bp-stat:not(:disabled):active span { color: var(--muted); }
@media (hover: hover) and (pointer: fine) { .bp-stat:not(:disabled):hover span { color: var(--text); } }
/* The tabs, and ⋯ at their right (its menu: Export agent, Delete agent; src/ui/computer-button.js DropMenu). */
.bp-tabbar { position: sticky; top: 0; z-index: 3; display: flex; align-items: center; gap: 12px; margin-top: 8px; padding: 0 14px 0 18px; background: var(--sheet); border-bottom: 1px solid var(--border); }
.bp-tabs { flex: 1; min-width: 0; display: flex; gap: 22px; overflow-x: auto; scrollbar-width: none; }
.bp-tabs::-webkit-scrollbar { display: none; }
.bp-tabs button { position: relative; flex: none; padding: 16px 0 14px; color: var(--muted); font-size: var(--fs-md); font-weight: 500; white-space: nowrap; }
.bp-tabs button.on { color: var(--text); }
.bp-tabs button.on::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; border-radius: 2px; background: var(--blue); }
@media (hover: hover) and (pointer: fine) { .bp-tabs button:not(.on):hover { color: var(--text-2); } }
.bp-pane { padding: 16px 18px 0; }
.bp-pane > .group:first-child { margin-top: 0; }
.bp-pane > .group-label:first-child { margin-top: 4px; }
.bp-locked { margin-bottom: 12px; }
/* Instructions: a card for each, its name at the left and what's in it at the right ("None yet", or its words);
   tapped, it opens to the box it's written in, with what it's for under it. */
.bp-card { margin-bottom: 12px; border-radius: var(--radius-card); background: var(--elevated); }
.bp-card-head { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 60px; padding: 14px 18px 14px 20px; text-align: left; border-radius: var(--radius-card); }
.bp-card-title { flex: none; font-size: var(--fs-md); font-weight: 500; }
.bp-card-value { flex: 1; min-width: 0; text-align: right; color: var(--muted); font-size: var(--fs-sm); overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bp-card-chev { flex: none; width: 18px; height: 18px; color: var(--muted); transition: transform .2s; }
.bp-card.open .bp-card-chev { transform: rotate(180deg); }
.bp-card-head:active { background: color-mix(in srgb, var(--text) 4%, transparent); }
.bp-card.open .bp-card-head:active { background: none; }
.bp-card-body { padding: 0 20px 20px; animation: fade .18s ease-out; }
.bp-card-body > .hint { margin-top: 10px; }
.bp-card-body .brief { margin: 12px 0 0; }
.bp-card-body .brief-note { margin: 10px 4px 0; }
.bp-card :is(.job-box, .textarea) { background: var(--surface-2); }
:root[data-theme="light"] .bp-card :is(.job-box, .textarea, .brief) { background: var(--sheet); border: 1px solid transparent; }
.bp-card .textarea { min-height: 96px; }
.bp-talks { scroll-margin-top: 64px; }
@media (min-width: 760px) {
  .sheet.bp-sheet { max-width: 980px; }
  .bp-sheet .sheet-head { display: none; }
  .bp-sheet .sheet-body { display: flex; padding: 0; overflow: hidden; }
  .bp { flex: 1; min-width: 0; min-height: 0; display: grid; grid-template-columns: 320px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr);
    grid-template-areas: "side main"; }
  .bp-side { grid-area: side; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 22px 22px calc(22px + var(--safe-bottom)); background: var(--elevated); border-right: 1px solid var(--border); }
  .bp-close { display: inline-flex; top: 22px; left: 22px; }
  .bp-look { padding-top: 52px; }
  .bp-stats { padding: 0; border-radius: 0; background: none; }
  .bp-stat { min-height: 46px; }
  .bp-main { grid-area: main; min-width: 0; min-height: 0; display: flex; flex-direction: column; }
  .bp-tabbar { position: static; flex: none; margin: 0; padding: 0 18px 0 24px; }
  .bp-tabs { gap: 26px; }
  .bp-tabs button { padding: 22px 0 20px; }
  .bp-pane { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: 20px 24px calc(28px + var(--safe-bottom)); }
  /* The ten looks, two rows of five, a little smaller, to fit the column. */
  .bp-side .picker.looks { gap: 12px 6px; }
  .bp-side .look-face { width: min(100%, 52px); }
}
@media (min-width: 900px) {
  .sheet.bp-sheet { width: min(980px, calc(100vw - 64px)); max-width: none; }
}
/* A bot's panel beside its own chat on a computer, in a window 1,100 px wide or more (src/ui/bot-profile.js
   BotPanel), as Meta's Muse has it: its profile's left column (.bp-side, above), at the window's right, a line
   between it and the chat, scrolling by itself; X at its top left puts it away. Under the counts, the bot's
   settings, with an arrow, open its whole profile. In Holli Bot for Mac its X takes clicks in the title bar. */
.bot-panel { flex: none; width: 340px; height: 100%; padding-top: calc(var(--desk-top) + 16px); border-right: 0; border-left: 1px solid var(--border); }
.bot-panel .bp-close { top: calc(var(--desk-top) + 16px); }
.bp-more svg { flex: none; width: 18px; height: 18px; color: var(--muted); }
.mac-app .bot-panel button { -webkit-app-region: no-drag; app-region: no-drag; }
/* The panel's face (src/ui/bot-profile.js PanelFace). With the pointer on it, or the keyboard's focus, a soft dark veil
   fades in over its loop, which plays on under it, and Lucide's pencil, white, settles into its exact middle. Clicked,
   the ten looks drop down from it in a card with a small point toward the face: its title, two rows of five, and a
   line under them; the looks its other agents have are grayed out, with their names (.look-pick.taken, below). */
.bp-face-edit { --pop: #2a2a2c; position: relative; display: flex; flex-direction: column; align-items: center; }
:root[data-theme="dark"] .bp-face-edit { --pop: #363638; }
:root[data-theme="light"] .bp-face-edit { --pop: #ffffff; }
.bp-face-edit .bp-face { position: relative; display: flex; padding: 0; border-radius: 50%; outline: none; cursor: pointer; transition: transform .2s cubic-bezier(.2, .9, .3, 1); }
.bp-face-veil { position: absolute; inset: 0; z-index: 1; display: grid; place-items: center; border-radius: 50%; background: rgba(0, 0, 0, .42);
  color: #ffffff; opacity: 0; pointer-events: none; transition: opacity .2s ease; }
.bp-face-veil svg { display: block; width: 32px; height: 32px; transform: scale(.8); transition: transform .24s cubic-bezier(.2, .9, .3, 1.25); }
@media (hover: hover) {
  .bp-face-edit .bp-face:hover .bp-face-veil { opacity: 1; }
  .bp-face-edit .bp-face:hover .bp-face-veil svg { transform: none; }
}
.bp-face-edit .bp-face:focus-visible .bp-face-veil, .bp-face-edit.open .bp-face-veil { opacity: 1; }
.bp-face-edit .bp-face:focus-visible .bp-face-veil svg, .bp-face-edit.open .bp-face-veil svg { transform: none; }
.bp-face-edit .bp-face:focus-visible { box-shadow: 0 0 0 3px var(--elevated), 0 0 0 5px var(--blue); }
.look-pop { position: absolute; top: calc(100% + 14px); left: 50%; z-index: 4; width: 308px; max-width: calc(100vw - 24px); box-sizing: border-box;
  padding: 16px 14px 14px; border-radius: var(--radius-card); background: var(--pop); border: .5px solid rgba(255, 255, 255, .08);
  box-shadow: none; text-align: center;
  transform: translateX(-50%); transform-origin: 50% 0; animation: look-pop-in .24s cubic-bezier(.2, .9, .3, 1.08) both; }
:root[data-theme="light"] .look-pop { border-color: rgba(20, 52, 58, .08); box-shadow: none; }
.look-pop::before { content: ""; position: absolute; top: -6px; left: 50%; width: 12px; height: 12px; margin-left: -6px; border-radius: 3px 0 0 0;
  background: var(--pop); border: inherit; border-right: 0; border-bottom: 0; transform: rotate(45deg); }
@keyframes look-pop-in { from { opacity: 0; transform: translateX(-50%) translateY(-8px) scale(.94); } }
.look-pop-title { color: var(--text); font-size: var(--fs-md); font-weight: 700; }
.look-pop .picker.looks { gap: 14px 6px; margin: 16px auto 0; }
.look-pop .look-face { width: min(100%, 52px); }
.look-pop .look-pick.on .look-face { box-shadow: 0 0 0 2.5px var(--pop), 0 0 0 4.5px var(--text); }
.look-pop .look-pick:focus-visible .look-face { box-shadow: 0 0 0 2.5px var(--pop), 0 0 0 4.5px var(--blue); }
.look-pop-note { margin-top: 14px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.35; }
@media (prefers-reduced-motion: reduce) {
  .bp-face-edit .bp-face, .bp-face-veil, .bp-face-veil svg { transition: none; }
  .bp-face-veil svg { transform: none; }
  .look-pop { animation: none; }
}
@media (prefers-reduced-motion: reduce) { .bp-card-chev { transition: none; } .bp-card-body { animation: none; } }
/* Deleting an agent: DELETE typed in the dialog's box (ui.confirmTyped), in the middle, in capitals. */
.dialog .input.match { text-align: center; font-weight: 500; letter-spacing: .08em; }

/* ---------- Moving an agent to another account (src/ui/transfer.js) ----------
   Export and Accept alike: the agent's face and name at the top, a line on
   what it means, and for Export, what goes with it, what leaves and what
   stays, as rows each with a tinted icon; then its link in a box, Share link
   and Copy link, and End link under them, in red. */
.xa-hero { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 0 6px; text-align: center; }
.xa-hero b { font-size: var(--fs-lg); font-weight: 700; }
.xa-hero span { color: var(--muted); font-size: var(--fs-md); }
.xa-lead { margin: 8px 4px 14px; color: var(--text-2); font-size: var(--fs-md); line-height: 1.45; text-align: center; text-wrap: pretty; }
.xa-what .row { align-items: flex-start; }
.xa-what .row + .row::before { left: 66px; }
.xa-icon { flex: none; width: 34px; height: 34px; margin-top: 1px; border-radius: 10px; display: grid; place-items: center; }
.xa-icon.go { color: var(--blue); background: color-mix(in srgb, var(--blue) 14%, transparent); }
.xa-icon.leave { color: var(--red); background: color-mix(in srgb, var(--red) 12%, transparent); }
.xa-icon.stay { color: var(--text-2); background: var(--surface-2); }
.xa-link { display: flex; align-items: center; gap: 10px; margin: 16px 0 12px; padding: 12px 14px; border-radius: 14px; background: var(--surface-2); color: var(--text-2); font-size: var(--fs-sm); }
.xa-link span { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.xa-link svg { flex: none; color: var(--muted); }
:root[data-theme="light"] .xa-link { background: var(--surface); border: 1px solid var(--border); }
.xa-buttons { display: flex; flex-direction: column; gap: 10px; }
.xa-hint { margin: 12px 4px 0; text-align: center; }
.xa-end { display: block; margin: 14px auto 0; padding: 8px 12px; border-radius: 10px; color: var(--red); font-size: var(--fs-md); font-weight: 500; }
.xa-end:active { background: color-mix(in srgb, var(--red) 10%, transparent); }

/* ---------- a bot's Bot Memory (src/ui/create-bot.js, src/ui/bot-profile.js) ---------- */
.job-label { margin: 22px 6px -4px; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
/* A Bot Store bot's pre-trained memory: private, so a card that says so in place of a box. */
.memory-locked { display: flex; align-items: flex-start; gap: 12px; padding: 14px; border-radius: 14px; background: var(--surface-2); }
.memory-locked .what { flex: 1; min-width: 0; }
.memory-locked .t { font-size: var(--fs-md); font-weight: 500; }
.memory-locked .s { margin-top: 3px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.4; }
.memory-locked .lock { display: grid; place-items: center; flex: none; width: 34px; height: 34px; border-radius: 10px; color: #fff;
  background: linear-gradient(145deg, #8b88ff 0%, #5e5ce6 55%, #4643c9 100%); }
:root[data-theme="light"] .memory-locked .lock { background: linear-gradient(145deg, #0a95a3 0%, #087e8b 55%, #075c66 100%); }

/* ---------- the Bot Store (src/ui/store.js) ----------
   Apple's App Store in Holli Bot's colors: the date over a large title, the
   featured bots on big cards in their own color, category chips, every bot
   as an app icon (a white bot on its color) with the App Store's round price
   button, and a page for each bot: its icon, a strip of facts, what it can
   do, its story (an article in the bot's own color), and the fine print. */
.sheet.store { --sb-bg: #0c0c0e; --sb-card: #1c1c1e; --sb-pill: #2c2c2e; --sb-line: rgba(255, 255, 255, .1); --sb-blue: #0a84ff; background: var(--sb-bg); }
:root[data-theme="dark"] .sheet.store { --sb-bg: #1c1c1e; --sb-card: #2c2c2e; --sb-pill: #3a3a3c; }
:root[data-theme="light"] .sheet.store { --sb-bg: #f3f9f8; --sb-card: #ffffff; --sb-pill: #e5ecec; --sb-line: rgba(20, 52, 58, .14); --sb-blue: var(--teal); }
@media (min-width: 900px) { .sheet.store { width: 640px; } }
.sheet.store .sheet-head { min-height: calc(var(--round-btn, var(--tap)) + 20px); padding-bottom: 2px; }
.sheet.store .sheet-body { padding-top: 0; }
.sb-head-link { padding: 8px 4px; color: var(--sb-blue); font-size: var(--fs-md); font-weight: 500; }

.sb-top { padding: 0 2px 2px; }
.sb-date { color: var(--muted); font-size: var(--fs-sm); line-height: 16px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.sb-title { margin: 2px 0 0; font-size: var(--fs-lg); line-height: 36px; font-weight: 700; letter-spacing: -.5px; }
.sb-lead { margin: 6px 0 0; color: var(--muted); font-size: var(--fs-md); line-height: 1.4; }

/* The featured bots: big cards that snap into place as they're swiped. */
.sb-cards { display: flex; gap: 12px; margin: 18px -18px 0; padding: 0 18px 6px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 0 18px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.sb-cards::-webkit-scrollbar { display: none; }
.sb-card { position: relative; flex: 0 0 min(86%, 440px); min-height: 250px; display: flex; flex-direction: column; padding: 18px 18px 14px; border-radius: 22px; color: #fff; overflow: hidden;
  scroll-snap-align: start; cursor: pointer; box-shadow: none; transition: transform .2s; }
:root[data-theme="light"] .sb-card { box-shadow: none; }
.sb-cards .sb-card:only-child { flex-basis: 100%; }
.sb-card:active { transform: scale(.98); }
.sb-card-text { position: relative; z-index: 1; max-width: 58%; }
.sb-eyebrow { color: rgba(255, 255, 255, .8); font-size: var(--fs-sm); line-height: 16px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.sb-card-name { margin-top: 4px; font-size: var(--fs-lg); line-height: 31px; font-weight: 700; letter-spacing: -.01em; overflow-wrap: anywhere; }
.sb-card-tag { margin-top: 6px; color: rgba(255, 255, 255, .8); font-size: var(--fs-md); line-height: 1.35; }
.sb-card-art { position: absolute; right: -6px; top: 50%; transform: translateY(-60%); pointer-events: none; }
.sb-card-foot { position: relative; z-index: 1; display: flex; align-items: center; gap: 12px; margin-top: auto; padding-top: 16px; }
.sb-card-foot > span { flex: 1; min-width: 0; color: rgba(255, 255, 255, .8); font-size: var(--fs-sm); line-height: 17px; }

.sb-chips { display: flex; gap: 8px; margin: 22px -18px 0; padding: 0 18px; overflow-x: auto; scrollbar-width: none; }
.sb-chips::-webkit-scrollbar { display: none; }
.sb-chip { flex: none; height: 34px; padding: 0 15px; border-radius: 17px; background: var(--sb-pill); color: var(--text); font-size: var(--fs-sm); font-weight: 500; white-space: nowrap; transition: background .2s, color .2s; }
.sb-chip.on { background: var(--text); color: var(--sb-bg); }

.sb-h2 { margin: 26px 0 4px; font-size: var(--fs-md); line-height: 26px; font-weight: 700; letter-spacing: -.01em; }

/* Bots in a list, as in the App Store: icon, name, what it does, and its button. */
.sb-list { margin-top: 2px; }
.sb-row { position: relative; display: flex; align-items: center; gap: 14px; padding: 11px 0; cursor: pointer; }
.sb-row + .sb-row::before { content: ""; position: absolute; top: 0; left: 78px; right: 0; height: 1px; background: var(--sb-line); }
.sb-list.small .sb-row + .sb-row::before { left: 62px; }
.sb-row:active { opacity: .7; }
.sb-row .meta { flex: 1; min-width: 0; }
.sb-row .n { font-size: var(--fs-md); line-height: 22px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-row .d { margin-top: 1px; color: var(--muted); font-size: var(--fs-sm); line-height: 18px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.sb-row .c { margin-top: 3px; color: var(--muted); font-size: var(--fs-sm); line-height: 15px; }
.sb-row .chev { width: 18px; height: 18px; flex: none; color: var(--dim); }

/* A bot as an app icon: a white bot on its color, with a soft sheen. */
.sb-tile { position: relative; display: inline-flex; align-items: center; justify-content: center; flex: none; overflow: hidden;
  box-shadow: inset 0 0 0 .5px rgba(255, 255, 255, .14); }
.sb-tile::after { content: ""; position: absolute; inset: 0; border-radius: inherit; background: linear-gradient(180deg, rgba(255, 255, 255, .18), rgba(255, 255, 255, 0) 48%); pointer-events: none; }
:root[data-theme="light"] .sb-tile { box-shadow: inset 0 0 0 .5px rgba(0, 0, 0, .08); }

/* The App Store's button: Get, then Open. */
.sb-get { flex: none; display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-width: 76px; height: 30px; padding: 0 14px; border-radius: 15px;
  background: var(--sb-pill); color: var(--sb-blue); font-size: var(--fs-sm); font-weight: 700; letter-spacing: .01em; font-variant-numeric: tabular-nums;
  transition: background .2s, color .2s, min-width .25s ease, transform .1s; }
.sb-get:active { transform: scale(.96); }
.sb-get:disabled { opacity: .5; }
.sb-get.big { min-width: 86px; height: 34px; padding: 0 18px; border-radius: 17px; font-size: var(--fs-md); }
.sb-get.on-card { background: rgba(255, 255, 255, .24); color: #fff; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.sb-get.on-card .spinner { border-color: rgba(255, 255, 255, .4); border-top-color: #fff; }

/* A bot's page. */
.sb-hero { display: flex; align-items: flex-start; gap: 16px; margin: 4px 0 18px; }
.sb-hero-text { flex: 1; min-width: 0; padding-top: 2px; }
.sb-hero h1 { margin: 0; font-size: var(--fs-lg); line-height: 29px; font-weight: 700; letter-spacing: -.01em; overflow-wrap: anywhere; }
.sb-hero p { margin: 3px 0 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.35; }
.sb-hero-buy { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 14px; }
.sb-facts { display: flex; margin: 0 -18px; padding: 10px 6px; overflow-x: auto; border-top: 1px solid var(--sb-line); border-bottom: 1px solid var(--sb-line); scrollbar-width: none; }
.sb-facts::-webkit-scrollbar { display: none; }
.sb-fact { position: relative; flex: 1 0 25%; min-width: 92px; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 4px 8px; text-align: center; }
.sb-fact + .sb-fact::before { content: ""; position: absolute; left: 0; top: 10px; bottom: 10px; width: 1px; background: var(--sb-line); }
.sb-fact .k { color: var(--muted); font-size: var(--fs-sm); line-height: 14px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; white-space: nowrap; }
.sb-fact .v { display: flex; align-items: center; height: 28px; color: var(--muted); font-size: var(--fs-md); font-weight: 700; font-variant-numeric: tabular-nums; }
.sb-fact .s { max-width: 100%; color: var(--muted); font-size: var(--fs-sm); line-height: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sb-checks { margin: 4px 0 0; padding: 0; list-style: none; }
.sb-checks li { display: flex; align-items: flex-start; gap: 10px; padding: 7px 0; font-size: var(--fs-md); line-height: 1.4; }
.sb-checks li svg { flex: none; margin-top: 2px; color: var(--green); }
:root[data-theme="light"] .sb-checks li svg { color: var(--teal); }
/* A bot's story: the owner's Markdown as an article, like Apple's editorial
   pages. The first paragraph leads, sections under bold headings, dots and
   numbered steps in the bot's color (--bot, and --bot-deep behind white), and
   the things to say to it (> lines) as the messages you'd send. */
.sb-story { margin-top: 28px; padding-top: 26px; border-top: 1px solid var(--sb-line); }
.sb-article { font-size: var(--fs-md); line-height: 1.55; overflow-wrap: anywhere; }
.sb-article > :first-child { margin-top: 0; }
.sb-article > :last-child { margin-bottom: 0; }
.sb-article p { margin: 0 0 14px; }
.sb-article > p:first-child { margin-bottom: 20px; font-size: var(--fs-md); line-height: 1.42; font-weight: 500; letter-spacing: -.012em; }
.sb-article h1, .sb-article h2 { margin: 36px 0 10px; font-size: var(--fs-md); line-height: 26px; font-weight: 700; letter-spacing: -.01em; }
.sb-article h3, .sb-article h4 { margin: 26px 0 6px; font-size: var(--fs-md); line-height: 23px; font-weight: 700; }
.sb-article strong { font-weight: 700; }
.sb-article a { color: var(--sb-blue); text-decoration: none; }
.sb-article ul, .sb-article ol { margin: 0 0 16px; padding: 0; list-style: none; }
.sb-article li { position: relative; }
.sb-article li p { margin: 0; }
.sb-article li ul, .sb-article li ol { margin: 4px 0 0; }
.sb-article ul > li { padding: 5px 0 5px 24px; }
.sb-article ul > li::before { content: ""; position: absolute; left: 5px; top: calc(5px + .775em - 3.5px); width: 7px; height: 7px; border-radius: 50%; background: var(--bot); }
.sb-article ol { counter-reset: step; }
.sb-article ol > li { counter-increment: step; min-height: 32px; margin: 12px 0; padding: 3px 0 3px 46px; }
.sb-article ol > li::before { content: counter(step); position: absolute; left: 0; top: 0; width: 32px; height: 32px; border-radius: 50%; background: var(--bot-deep);
  color: #fff; font-size: var(--fs-sm); line-height: 32px; font-weight: 700; text-align: center; font-variant-numeric: tabular-nums; box-shadow: none; }
.sb-article blockquote { width: -webkit-fit-content; width: fit-content; max-width: 84%; margin: 0 0 8px auto; padding: 9px 15px; border-radius: 20px 20px 6px 20px;
  background: var(--bot-deep); color: #fff; font-size: var(--fs-md); line-height: 1.4; font-weight: 500; box-shadow: none; }
.sb-article blockquote p { margin: 0; }
.sb-article blockquote + :not(blockquote) { margin-top: 20px; }
.sb-article hr { height: 1px; margin: 28px 0; border: 0; background: var(--sb-line); }
.sb-article code { padding: 1px 5px; border-radius: 6px; background: var(--sb-pill); font-size: var(--fs-sm); }
.sb-article img { display: block; max-width: 100%; height: auto; margin: 18px 0; border-radius: 16px; }
.sb-info { margin: 4px 0 0; }
.sb-info div { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 12px 0; border-bottom: 1px solid var(--sb-line); font-size: var(--fs-sm); line-height: 1.35; }
.sb-info dt { flex: none; color: var(--muted); }
.sb-info dd { margin: 0; text-align: right; }
.sb-foot { margin: 22px 2px 4px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }
.sb-note { margin: 12px 2px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; }
.sb-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; min-height: 50vh; padding: 0 20px; color: var(--muted); font-size: var(--fs-md); text-align: center; }
.sb-empty p { margin: 0; max-width: 320px; line-height: 1.45; }

/* The owner's pages: their bots, and a bot to add or change. */
.sb-edit-preview { display: flex; justify-content: center; margin: 6px 0 18px; }
.sheet.store .field label { color: var(--muted); }
.sheet.store .input, .sheet.store .textarea, .sheet.store .select { background: var(--sb-card); }
.sheet.store .textarea.sb-long { min-height: 220px; }
.sheet.store .group { background: var(--sb-card); }
.sb-hint-row { display: flex; justify-content: space-between; gap: 12px; }
.sb-count { flex: none; font-variant-numeric: tabular-nums; }
.sb-count.full { color: var(--orange); }
@media (prefers-reduced-motion: reduce) { .sb-card, .sb-get, .sb-chip { transition: none; } }

/* ---------- New Workspace, one step at a time (src/ui/new-workspace.js) ----------
   After Slack's: × (or Back) at the top left, the round button every sheet
   has (.circle-btn, where every sheet has it), a thin bar under it that
   fills as the steps go, and each step's content in the middle of the
   sheet, at most 420 px wide, sliding in from the right (from the left
   going back). The line under a heading is in the second shade; its
   buttons are full width: the outlined ways in, then the filled Next. With
   reduced motion, the steps don't slide. Each step's heading is in the top
   bar, in the middle of the sheet and level with × (or Back), as every
   sheet's title is, but 16 px Medium, on two lines if it must; joining from
   an invite's too. Skip, on the
   steps that have it, is at the bottom right of the sheet, under the step
   (which scrolls above it), coming in with the step. */
.sheet.wsf .sheet-body { padding: 0; display: flex; flex-direction: column; overflow: hidden; }
.wsf-head { flex: none; display: grid; grid-template-columns: minmax(var(--round-btn, var(--tap)), 1fr) auto minmax(var(--round-btn, var(--tap)), 1fr); align-items: center; gap: 12px;
  min-height: calc(var(--round-btn, var(--tap)) + 28px); padding: 18px 18px 8px; }
.wsf-head .circle-btn { background: var(--sheet-btn); }
.wsf-head > :nth-child(3) { justify-self: end; }
.wsf-title { margin: 0; font-size: 16px; line-height: 22px; font-weight: 500; text-wrap: balance; text-align: center; animation: fade .3s ease-out; }
.wsf-skip { height: 44px; padding: 0 12px; border-radius: 12px; color: var(--blue); font-weight: 500; font-size: var(--fs-md); transition: background .15s; }
.wsf-skip:active { background: var(--surface-2); }
@media (hover: hover) { .wsf-skip:hover { background: color-mix(in srgb, var(--text) 5%, transparent); } }
.wsf-foot { flex: none; display: flex; justify-content: flex-end; padding: 0 14px calc(12px + var(--safe-bottom)); animation: fade .3s ease-out; }
.wsf-body.with-foot { padding-bottom: 8px; }
.wsf-progress { flex: none; height: 3px; background: color-mix(in srgb, var(--text) 8%, transparent); }
.wsf-progress > i { display: block; height: 100%; background: var(--blue); border-radius: 0 3px 3px 0; transition: width .55s cubic-bezier(.2, .8, .2, 1); }
.wsf-body { flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; display: flex; flex-direction: column; padding: 20px 24px calc(24px + var(--safe-bottom));
  animation: wsf-in .42s cubic-bezier(.2, .8, .2, 1) both; }
.wsf-body.back { animation-name: wsf-back; }
@keyframes wsf-in { from { opacity: 0; transform: translateX(32px); } }
@keyframes wsf-back { from { opacity: 0; transform: translateX(-32px); } }
.wsf-center { margin: auto 0; width: 100%; max-width: 420px; align-self: center; display: flex; flex-direction: column; gap: 14px; text-align: center; }
.wsf-lead { margin: 0 0 6px; color: var(--muted); font-size: var(--fs-md); line-height: 1.45; text-wrap: pretty; }
.wsf-input { width: 100%; height: 54px; padding: 0 16px; border-radius: 14px; border: 1px solid var(--border-strong); background: var(--surface); color: var(--text); font-size: var(--fs-md); outline: none;
  transition: border-color .15s, box-shadow .15s; }
.wsf-input:focus { border-color: var(--blue); box-shadow: 0 0 0 3px color-mix(in srgb, var(--blue) 18%, transparent); }
.wsf-legal { margin: 2px 0 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.5; text-align: left; }
.wsf-legal a { color: var(--blue); text-decoration: none; font-weight: 500; }
.wsf-options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 4px; }
.btn.wsf-option { height: 52px; border-radius: 16px; gap: 10px; background: transparent; box-shadow: inset 0 0 0 1px var(--border-strong); font-weight: 500; transition: background .15s, opacity .15s; }
.btn.wsf-option svg { width: 20px; height: 20px; flex: none; }
.btn.wsf-option[aria-busy="true"] { opacity: .55; }
.btn.wsf-option:disabled { background: transparent; color: var(--dim); }
:root[data-theme="light"] .btn.wsf-option, :root[data-theme="light"] .btn.wsf-option:disabled { background: var(--surface); color: var(--text); box-shadow: inset 0 0 0 1px var(--border-strong); }
:root[data-theme="light"] .btn.wsf-option:disabled { color: var(--dim); }
@media (hover: hover) { .btn.wsf-option:hover { background: color-mix(in srgb, var(--text) 5%, transparent); } }
.wsf-team { display: flex; align-items: center; justify-content: center; gap: 10px; color: var(--muted); font-size: var(--fs-sm); animation: fade .3s ease-out; }
/* Under the team step: a teal note, a bot's face in a white circle, and amber sparkles round it. */
/* Keep tabs on what matters, Tada! and joining: a picture of Holli Bot's
   bots in the invite link's style (icons/, made with Nano Banana Pro), as
   wide as the step, with rounded corners; still. */
.wsf-picture { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 670; margin: 4px 0; border-radius: 22px; background: #eef6f5; object-fit: cover; }
/* Name your Company / Team: Holli Bot's bots gathering round a
   chat, moving forever (src/ui/gather-scene.js). Each bot moves in its own
   100×100 box, so its turns and squashes pivot on points in it. The chat
   and the bots' eyes on it go round every 7 s: a message at .4 s,
   someone typing at 1.2 s, the answer at 2.6 s, two lines at 3.4 and 3.7 s;
   at 6.2 s it all fades, and comes again. Everything else has its own
   beat, so the whole never quite repeats. With Reduce Motion, still: the
   bots at their middle tilt, the chat full. Only the chat and the bots, on
   the page itself (no frame), cropped close round them (670 × 510 of the
   picture's 1200 × 670), so they're as big as the step is wide. */
.wsf-picture.gather { aspect-ratio: 670 / 510; border-radius: 0; background: none; overflow: visible; isolation: isolate; }
.gather svg { display: block; width: 100%; height: 100%; overflow: visible; }
.gather-float { animation: gather-float 5.6s ease-in-out infinite; }
@keyframes gather-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
.gather-move { transform-origin: 50px 50px; }
.gather-peek { animation: gather-peek 5.2s ease-in-out infinite; }
.gather-peek.late { animation-duration: 6.1s; animation-delay: -2.4s; }
@keyframes gather-peek { 0%, 100% { transform: translateY(3px); } 40%, 62% { transform: translateY(-6px); } }
.gather-sway { transform: rotate(-18deg); animation: gather-sway 3.8s ease-in-out infinite; }
@keyframes gather-sway { 0%, 100% { transform: rotate(-22deg) translateY(0); } 50% { transform: rotate(-14deg) translateY(-4px); } }
.gather-wave { transform-origin: 72px 45px; animation: gather-wave 3.2s ease-in-out infinite; }
@keyframes gather-wave { 0%, 44%, 100% { transform: rotate(0); } 8%, 24% { transform: rotate(-16deg); } 16%, 32% { transform: rotate(18deg); } }
.gather-bob { transform: rotate(-5deg); animation: gather-bob 3.4s ease-in-out infinite; }
@keyframes gather-bob { 0%, 100% { transform: rotate(-6deg) translateY(0); } 50% { transform: rotate(-3deg) translateY(-7px); } }
.gather-reach { transform-origin: 84px 86px; animation: gather-reach 2.6s ease-in-out infinite; }
@keyframes gather-reach { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(-16deg); } }
.gather-wobble { transform: rotate(-5deg); animation: gather-wobble 4.4s ease-in-out infinite; }
@keyframes gather-wobble { 0%, 100% { transform: rotate(-9deg) translateY(0); } 50% { transform: rotate(-1deg) translateY(-5px); } }
.gather-hop { transform-origin: 50px 96px; animation: gather-hop 3s ease-in-out -.6s infinite; }
@keyframes gather-hop {
  0%, 46%, 100% { transform: translateY(0) scale(1, 1); }
  54% { transform: translateY(0) scale(1.07, .93); }
  66% { transform: translateY(-15px) scale(.97, 1.03); }
  78% { transform: translateY(0) scale(1.05, .95); }
  86% { transform: translateY(0) scale(1, 1); } }
.gather-blink { transform-box: fill-box; transform-origin: center; animation: gather-blink 4.6s infinite; }
@keyframes gather-blink { 0%, 93%, 100% { transform: scaleY(1); } 96% { transform: scaleY(.12); } }
/* The eyes glide to what they look at (src/ui/gather-scene.js) as the
   app's bots' do (.avatar-eye): quickly, overshooting a little. */
.gather-eye { transition: transform 420ms cubic-bezier(.3, 1.4, .5, 1); }
.gather-msg, .gather-typing, .gather-line { transform-box: fill-box; }
.gather-msg.one { transform-origin: 0 100%; animation: gather-msg-one 7s ease-out infinite; }
.gather-msg.two { transform-origin: 100% 100%; animation: gather-msg-two 7s ease-out infinite; }
@keyframes gather-msg-one {
  0%, 5% { opacity: 0; transform: translateY(6px) scale(.7); }
  10% { opacity: 1; transform: translateY(0) scale(1.04); }
  13%, 88% { opacity: 1; transform: translateY(0) scale(1); }
  94%, 100% { opacity: 0; transform: translateY(0) scale(1); } }
@keyframes gather-msg-two {
  0%, 37% { opacity: 0; transform: translateY(6px) scale(.7); }
  42% { opacity: 1; transform: translateY(0) scale(1.04); }
  45%, 88% { opacity: 1; transform: translateY(0) scale(1); }
  94%, 100% { opacity: 0; transform: translateY(0) scale(1); } }
.gather-typing { opacity: 0; transform-origin: 100% 100%; animation: gather-typing 7s ease-out infinite; }
@keyframes gather-typing {
  0%, 16% { opacity: 0; transform: scale(.7); }
  20%, 35% { opacity: 1; transform: scale(1); }
  38%, 100% { opacity: 0; transform: scale(.9); } }
.gather-dot { animation: gather-dot 1s ease-in-out infinite; }
.gather-dot.two { animation-delay: .15s; }
.gather-dot.three { animation-delay: .3s; }
@keyframes gather-dot { 0%, 60%, 100% { transform: translateY(0); opacity: .55; } 30% { transform: translateY(-5px); opacity: 1; } }
.gather-line { transform-origin: 0 50%; animation: gather-line 7s ease-out infinite; }
.gather-line.two { animation-name: gather-line-two; }
@keyframes gather-line {
  0%, 48% { opacity: 0; transform: scaleX(0); }
  56%, 88% { opacity: 1; transform: scaleX(1); }
  94%, 100% { opacity: 0; transform: scaleX(1); } }
@keyframes gather-line-two {
  0%, 52% { opacity: 0; transform: scaleX(0); }
  60%, 88% { opacity: 1; transform: scaleX(1); }
  94%, 100% { opacity: 0; transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) {
  .gather * { animation: none !important; transition: none !important; }
}
/* Who else is on the team: three bots flying off in a paper airplane,
   forever (src/ui/plane-scene.js), on nothing, 2:1, its sides fading so the
   clouds come and go softly. The plane drifts round over 9 s, and bobs and
   banks every 3.6 s; its trail's dots stream out behind; the clouds cross
   right to left, each at its own speed, in the words' color, faint. The
   riders bounce, each at its own pace, squashing a little as they land;
   the middle one waves; the pilot's hair and scarf flutter in the wind, the
   hair's strands each a moment apart. With Reduce Motion, still. */
.wsf-picture.fly { aspect-ratio: 760 / 380; border-radius: 0; background: none; overflow: hidden;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent); mask-image: linear-gradient(to right, transparent, #000 7%, #000 93%, transparent); }
.fly > svg { display: block; width: 100%; height: 100%; }
.fly-cloud { color: var(--text); animation: fly-cloud 17s linear infinite; }
.fly-cloud path { fill: currentColor; fill-opacity: .08; }
@keyframes fly-cloud { from { transform: translateX(0); } to { transform: translateX(-960px); } }
.fly-drift { animation: fly-drift 9s ease-in-out infinite; }
@keyframes fly-drift { 0%, 100% { transform: translate(0, 0); } 25% { transform: translate(12px, -8px); } 50% { transform: translate(0, -14px); } 75% { transform: translate(-12px, -6px); } }
.fly-bank { transform-origin: 430px 250px; animation: fly-bank 3.6s ease-in-out infinite; }
@keyframes fly-bank { 0%, 100% { transform: translateY(0) rotate(-3deg); } 50% { transform: translateY(-8px) rotate(2deg); } }
.fly-trail { animation: fly-trail .9s linear infinite; }
@keyframes fly-trail { to { stroke-dashoffset: -30; } }
.fly-bounce { transform-origin: 50px 90px; animation: fly-bounce 1.5s ease-in-out infinite; }
.fly-bounce.two { animation-duration: 1.9s; animation-delay: -.6s; }
.fly-bounce.three { animation-duration: 1.7s; animation-delay: -1.1s; }
@keyframes fly-bounce { 0%, 100% { transform: translateY(0) scale(1, 1); } 45% { transform: translateY(-5px) scale(.98, 1.02); } 72% { transform: translateY(0) scale(1.03, .97); } }
.fly-wave { transform-origin: 88px 46px; animation: fly-wave 1.4s ease-in-out infinite; }
@keyframes fly-wave { 0%, 100% { transform: rotate(-14deg); } 50% { transform: rotate(20deg); } }
.fly-scarf path { transform-origin: 20px 64px; animation: fly-flutter .5s ease-in-out infinite alternate; }
.fly-scarf .late { animation-delay: -.25s; }
.fly-hair path { transform-origin: 64px 20px; animation: fly-flutter .42s ease-in-out infinite alternate; }
.fly-hair .late { animation-delay: -.14s; }
.fly-hair .later { animation-delay: -.28s; }
@keyframes fly-flutter { from { transform: rotate(-7deg) scaleX(1); } to { transform: rotate(6deg) scaleX(.9); } }
.fly-blink { transform-box: fill-box; transform-origin: center; animation: gather-blink 4.6s infinite; }
@media (prefers-reduced-motion: reduce) {
  .fly * { animation: none !important; }
}
/* Joining from an invite link: its picture; the workspace's name; who sent it; one button. */
.join-meta { margin: -8px 0 2px; color: var(--muted); font-size: var(--fs-sm); }
.join-agents { text-align: left; margin: 0; }
.join-agents-head { padding: 12px 16px 2px; color: var(--muted); font-size: var(--fs-sm); font-weight: 500; }
.link-btn { align-self: center; padding: 6px 8px; color: var(--blue); font-weight: 500; font-size: var(--fs-sm); }
.invite-sheet { margin: 10px auto 0; }
@media (prefers-reduced-motion: reduce) {
  .wsf-body, .wsf-title, .wsf-foot, .wsf-progress > i { animation: none !important; transition: none !important; }
}

/* Add by email (src/ui/new-workspace.js AddByEmailSheet), after Slack's:
   Cancel, the title in the middle and Send in the header; the addresses typed
   or pasted on one line that grows; under it every address found, each with
   a send mark on a tinted tile, Invite in bold and a box to tick. */
.sheet-text-btn { height: 40px; padding: 0 6px; color: var(--blue); font-size: var(--fs-md); font-weight: 500; }
.sheet-text-btn.strong { font-weight: 700; }
.sheet-text-btn:disabled { color: var(--dim); }
.sheet.add-email .sheet-head h2 { text-align: center; }
.invite-input { display: block; width: 100%; min-height: 52px; resize: none; border: 0; border-bottom: 1px solid var(--border); background: transparent; color: var(--text);
  font: inherit; font-size: var(--fs-md); line-height: 24px; padding: 14px 4px; outline: none; field-sizing: content; }
.invite-input:focus { border-bottom-color: var(--blue); }
.invite-hint { margin: 10px 0 14px; }
.invite-row .t b { font-weight: 700; }
.invite-ic { width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center; flex: none; background: color-mix(in srgb, var(--blue) 12%, transparent); color: var(--blue); }
.invite-box { width: 24px; height: 24px; border-radius: 7px; border: 1.5px solid var(--border-strong); display: grid; place-items: center; flex: none; color: #fff; transition: background .15s, border-color .15s; }
.invite-box.on { background: var(--blue); border-color: var(--blue); }

/* Someone else's message in a workspace shared with other people, or their
   agent's (src/ui/message.js MemberMessage): on the left as a bot's, a
   person's bubble a touch tinted with the user's color; a person's face is
   their initials in a gray circle. */
.msg.member { align-items: flex-start; }
.msg.member .bubble { background: var(--bot-bubble); }
.msg.member.person .bubble { background: color-mix(in srgb, var(--user-bubble) 16%, var(--bot-bubble)); }
.msg.member .reacts { align-self: flex-start; }
.person-face { width: 20px; height: 20px; border-radius: 50%; display: inline-grid; place-items: center; flex: none; background: var(--surface-2); color: var(--text-2); font-size: var(--fs-sm); font-weight: 500; letter-spacing: .2px; }
.person-face.big { width: 32px; height: 32px; font-size: var(--fs-sm); }
.msg .author .author-owner { color: var(--muted); }
.msg .author .author-owner::before { content: '·'; margin-right: 8px; }
.row.dim { opacity: .55; pointer-events: none; }
.link-btn.danger { color: var(--red); font-weight: 500; }

/* The plush looks (src/ui/avatar.js LOOKS): the portrait in a circle, as Meta's
   Muse is shown, with its own colorful background inside it; in the profile its
   loop plays there. The status dot sits on the circle's bottom-right edge in a
   ring of the page's color. Thinking, it bobs gently with a soft glow; working,
   it rocks a little. With Reduce Motion, no video and no moving about: a busy one
   fades gently. */
.avatar.plush { position: relative; display: inline-flex; flex: none; border-radius: 50%; }
.plush-face { position: absolute; inset: 0; border-radius: 50%; overflow: hidden; background: var(--surface-2); transform: translateZ(0); }
.plush-media { display: block; width: 100%; height: 100%; object-fit: cover; pointer-events: none; -webkit-user-drag: none; }
.plush-dot { position: absolute; right: 2%; bottom: 2%; border-radius: 50%; background: var(--green-dot); box-shadow: 0 0 0 2.5px var(--bg); }
.plush-dot.dot-waiting { background: var(--orange); }
.plush-dot.dot-error { background: var(--red-strong); }
.plush-dot.dot-working { animation: pulse 1.4s ease-in-out infinite; }
/* Thinking or working, the picture moves inside its circle, which holds still with its glow: the bob lifts the
   picture a little and grows it a touch more, so its edge never shows at the circle's bottom; a square turned a few
   degrees still covers the circle inside it, so the rock needs no growing. */
.avatar.plush.is-thinking .plush-face, .avatar.plush.is-working .plush-face { box-shadow: 0 0 0 2px color-mix(in srgb, var(--blue) 55%, transparent); }
.avatar.plush.is-thinking .plush-media { animation: plush-bob 1.6s ease-in-out infinite; }
.avatar.plush.is-working .plush-media { animation: plush-rock 1.2s ease-in-out infinite; }
@keyframes plush-bob { 0%, 100% { transform: translateY(0) scale(1); } 50% { transform: translateY(-3%) scale(1.07); } }
@keyframes plush-rock { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-4deg); } 75% { transform: rotate(4deg); } }
/* In the panel beside a chat on a computer (src/ui/bot-profile.js BotPanel), and in Settings → Agents, where the face
   is big and its loop plays, a thinking agent's picture doesn't bob and grow (the owner's call): it holds still in its
   circle, the glow round the circle saying it's busy. */
:is(.bot-panel, .ag-hero) .avatar.plush.is-thinking .plush-media { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .avatar.plush .plush-face, .avatar.plush .plush-media, .plush-dot { animation: none !important; }
  .avatar.plush.is-thinking .plush-face, .avatar.plush.is-working .plush-face { animation: av-fade 1.6s ease-in-out infinite !important; }
}
.bp-face:has(.plush) { border-radius: 50%; }

/* The look picker: the ten looks (eight plush animals, a robot and Holli), two rows of five, each in its circle with its name. */
.picker.looks { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px 8px; max-width: 360px; margin-left: auto; margin-right: auto; }
.look-pick { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 0; padding: 0; border: 0; background: none; color: var(--muted); font: inherit; font-size: var(--fs-sm); cursor: pointer; }
.look-face { display: block; width: min(100%, 64px); aspect-ratio: 1; border-radius: 50%; overflow: hidden; box-shadow: 0 0 0 2px transparent; transition: box-shadow .15s ease; }
.look-face img { display: block; width: 100%; height: 100%; object-fit: cover; }
.look-pick.on { color: var(--text); }
.look-pick.on .look-face { box-shadow: 0 0 0 2.5px var(--bg), 0 0 0 4.5px var(--text); }
.look-pick:focus-visible { outline: none; }
.look-pick:focus-visible .look-face { box-shadow: 0 0 0 2.5px var(--bg), 0 0 0 4.5px var(--blue); }
/* Its name, or the agent's who has it, cut short with … past its column. Any look one can pick grows a little under
   the pointer; one another of the user's agents has is gray and faded, can't be picked, and has that agent's name. */
.look-name { max-width: 100%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.look-face img { transition: transform .18s cubic-bezier(.2, .9, .3, 1.2); }
@media (hover: hover) { .look-pick:not(.taken):not(.on):hover .look-face img { transform: scale(1.07); } .look-pick:not(.taken):hover { color: var(--text); } }
.look-pick.taken { cursor: default; color: var(--dim); }
.look-pick.taken .look-face { filter: grayscale(1); opacity: .35; }
@media (prefers-reduced-motion: reduce) { .look-face img { transition: none; } }
@media (max-width: 360px) { .picker.looks { gap: 12px 4px; } }

/* ---------- The glass looks: Pearl and Cosmos ---------- */
/* Two Appearances are a sky with frosted glass over it (src/ui/app.js puts :root.pearl or :root.cosmos on the page,
   and index.html does for the splash): Pearl, Light in a lavender sky's colors, the account's (src/ui/hooks.js
   adoptDevicePearl); and Cosmos, Dark under a night sky's, the account's too, like
   Black, Dark and Light. Only the colors change: fonts, sizes and the layout are Light's or Dark's. Each look's own
   block (below) sets the sky (--sky) and the glass's colors; these rules lay them on, the same for both:
   - The sky sits behind everything, still: the app and a phone's chat are see-through over it, so as a chat slides
     in over the list only what's on it moves. The menu slides out over the app as a pane of frosted glass
     (--sheet), and Settings' page has the sky on it, held still as the page slides, so the page slides in over
     the app and the menu as if uncovering the sky there. Under the sky is its color at the top (--bg), the color Safari's status bar and the status band
     take (src/ui/components.js pageColor, bandColor), and Holli Bot for iPhone's theme-color, so its status bar's
     text suits it.
   - The agent list beside the chat, the agent's panel and a profile's column are panes of glass (--pane, with
     light falling across their top, --pane-sheen, and a rim at their inner edge, --glass-rim).
   - What sits on the page or a sheet (the round buttons, the prompt bar, the bots' bubbles, cards and boxes,
     search boxes and fields, buttons, chips and tabs, and the menu's and Settings' cards) is frosted glass: mostly
     clear (--frost), with light catching its top left corner and fading out across it (--frost-sheen), so the sky
     shows through toward its bottom right (Cosmos's is solid, so its glows don't show through: its block, below);
     a faint glow just inside it (--frost-edge), and no edge (No borders, at the end), and no shadow under it (nor
     under anything: the owner's call, at the end too). What's on, or
     a button with its menu out, is a touch brighter (--frost-on); pressed, clearer (--frost-press); under the
     pointer, brighter (--frost-hover). Rows in the menu's and Settings' cards tint as they're pressed.
   - What floats over the app (menus, the + menu and a message's among them, @-mentions, reactions and toasts) is
     clearer glass (--raised) over what's behind it, blurred and richer (--glass-blur-strong), with the same light
     and edge, and no shadow. The look picker's card is more solid (--pop), so its point doesn't show over it.
   - Sheets and dialogs are clear glass (--sheet, --raised) over the app, which their dimming blurs; a sheet blurs
     what's behind it itself too, on a layer under what's on it (::before, so what's on it is placed as ever), since
     a sheet opened from the menu, the Agent Store's, is over the menu, which its dimming is under. A profile's
     tabs, held at the top of its sheet on a phone as it scrolls, blur what scrolls under them the same way.
   - The bars at the top are frosted, what scrolls up under them blurred away under the sky's tint (--head-tint).
   - On a phone, the agent list's rows are see-through; a chat slides in over the list see-through too, the list fading away under it (rather than
     darkening, which would darken the sky through the chat), and back as the chat goes.
   The blurs stop at 12 px: some browsers draw a bigger one as no blur at all. None of it reaches the computer's
   black Screen. With Reduce Transparency the glass is solid (--solid, --solid-sheet, --solid-raised). */
/* The sky is a layer of its own, fixed behind everything (body::before), drawn once; the page under it is the sky's
   color at its top (--bg), plain. The sky on the page itself would be drawn afresh, all of it, each time anything
   about the page changes (the menu coming or going changes the page's classes), which made the menu lag; and the
   page's color as it follows the menu (--canvas) would too. The app is see-through over it. */
:root:is(.pearl, .cosmos)[data-theme]:not(.signed-out) { background: var(--bg); }
:root:is(.pearl, .cosmos)[data-theme]:not(.signed-out) body { background: none; }
:root:is(.pearl, .cosmos)[data-theme]:not(.signed-out) body::before { content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: var(--sky); background-color: var(--bg); will-change: transform; }
:root:is(.pearl, .cosmos)[data-theme]:not(.signed-out) #boot { background: var(--sky) !important; }
:root:is(.pearl, .cosmos)[data-theme] .app { background: none; }
/* The menu, sliding out over the app as it dims, is a pane of frosted glass: clear glass (--sheet) with light
   catching its top left (--frost-sheen) and a rim at its right edge (--glass-rim), blurring what's behind it on a
   layer under what's on it, as a sheet does (below). */
:root:is(.pearl, .cosmos)[data-theme] .sheet.drawer { background-color: var(--sheet); background-image: var(--frost-sheen); border-right-color: var(--glass-rim); }
/* Settings' page, over the app and the menu, has the sky on it (::before), which goes the other way as the page
   slides, just as far, so it stays still on the screen: the page's edge uncovers it. */
:root:is(.pearl, .cosmos)[data-theme] .push-page { background: none; }
:root:is(.pearl, .cosmos)[data-theme] .push-page::before { content: ""; position: absolute; top: 0; bottom: 0; left: calc(-1 * var(--page-x, 0px)); z-index: -1; width: 100vw;
  pointer-events: none; background: var(--sky); background-color: var(--bg);
  transform: translateX(calc(var(--page-x, 0px) - 100vw)); transition: transform var(--drawer-ms, .45s) var(--drawer-in); will-change: transform; }
:root:is(.pearl, .cosmos)[data-theme].page-open .push-page::before { transform: none; }
:root:is(.pearl, .cosmos)[data-theme]:not(.drawer-open) .push-page.side::before { transform: translateX(calc(var(--page-x, 0px) - 100vw)); }
:root:is(.pearl, .cosmos)[data-theme].page-dragging .push-page::before { transform: translateX(calc(-1 * var(--page-drag, 0px))); transition: none; }
:root:is(.pearl, .cosmos)[data-theme].page-flung .push-page::before { transition-timing-function: var(--page-ease, var(--drawer-in)); }
:root:is(.pearl, .cosmos)[data-theme] .pane-chat, :root:is(.pearl, .cosmos)[data-theme] .composer { background: none; }
:root:is(.pearl, .cosmos)[data-theme] .app.wide .pane-list {
  --side-line: var(--g-side-line); --side-field: var(--frost); --side-well: var(--frost);
  --side-hover: var(--frost-hover); --side-active: var(--frost-on); --side-unread: var(--g-unread);
  background-color: var(--pane); background-image: var(--pane-sheen); border-right-color: var(--glass-rim); }
:root:is(.pearl, .cosmos)[data-theme] .bot-panel { background-color: var(--pane); background-image: var(--pane-sheen); border-left-color: var(--glass-rim); }
@media (min-width: 760px) {
  :root:is(.pearl, .cosmos)[data-theme] .bp-side:not(.bot-panel) { background-color: var(--pane); background-image: var(--pane-sheen); border-right-color: var(--glass-rim); }
}
:root:is(.pearl, .cosmos)[data-theme] .app.mobile .pane-list, :root:is(.pearl, .cosmos)[data-theme] .app.mobile .row-bot:not(:active, .active) { background: none; }
:root:is(.pearl, .cosmos)[data-theme] .app.mobile .pane-chat::before, :root:is(.pearl, .cosmos)[data-theme] .app.mobile .pane-chat::after,
:root:is(.pearl, .cosmos)[data-theme] .app.mobile .pane-list::before { display: none; }
:root:is(.pearl, .cosmos)[data-theme] .app.mobile .pane-list { transition: opacity var(--chat-ms) var(--chat-out); }
:root:is(.pearl, .cosmos)[data-theme] .app.mobile:is(.chat-opening, .chat-in) .pane-list { opacity: 0; transition-timing-function: var(--chat-in); }
@media (prefers-reduced-motion: reduce) {
  :root:is(.pearl, .cosmos)[data-theme] .app.mobile .pane-list, :root:is(.pearl, .cosmos)[data-theme] .push-page::before { transition: none; }
}
:root:is(.pearl, .cosmos)[data-theme] .head-glass { background: var(--head-tint);
  -webkit-backdrop-filter: blur(12px) saturate(150%); backdrop-filter: blur(12px) saturate(150%);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--fade) * .55), transparent); mask-image: linear-gradient(to bottom, #000 calc(100% - var(--fade) * .55), transparent); }
:root:is(.pearl, .cosmos)[data-theme] .head-glass > i { display: none; }
/* The round buttons (at the top, on sheets, and the prompt bar's + and mic), with no shadow under them. */
:root:is(.pearl, .cosmos)[data-theme] :is(.top-btn, .circle-btn, .pbar-btn):not(.screen-sheet *), :root:is(.pearl, .cosmos)[data-theme] .app.mobile .topbar .top-btn {
  border-color: var(--frost-line); background-color: var(--frost); background-image: var(--frost-sheen);
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); box-shadow: var(--frost-edge); }
:root:is(.pearl, .cosmos)[data-theme] .top-btn[aria-expanded="true"] { background-color: var(--frost-on); }
@media (hover: hover) { :root:is(.pearl, .cosmos)[data-theme] :is(.top-btn, .circle-btn, .pbar-btn):not(.screen-sheet *):hover:not(:active) { background-color: var(--frost-hover); } }
:root:is(.pearl, .cosmos)[data-theme] :is(.top-btn, .circle-btn, .pbar-btn):not(.screen-sheet *):active, :root:is(.pearl, .cosmos)[data-theme] .app.mobile .topbar .top-btn:active { background-color: var(--frost-press); background-image: none; }
/* What sits on the page or a sheet. */
:root:is(.pearl, .cosmos)[data-theme] .pbar { --pb-btn: var(--frost);
  background-color: var(--frost); background-image: var(--frost-sheen); border-color: var(--frost-line); box-shadow: var(--frost-edge); }
:root:is(.pearl, .cosmos)[data-theme] .pbar.drop { border-color: var(--pb-accent); box-shadow: 0 0 0 3px var(--pb-accent-ring); }
:root:is(.pearl, .cosmos)[data-theme] :is(.msg.bot, .msg.member:not(.person)) .bubble,
:root:is(.pearl, .cosmos)[data-theme] :is(.card, .group, .mem, .note, .bp-card, .bc-screen, .ram-meter, .bot-ws, .ws-start, .ws-list, .list-notice, .search-bar, .side-search,
  .agent-card, .file-card, .pc-card, .activity-detail, .notice.routine, .reply-bar, .code-box, .options, .usage-add, .xa-link, .pending-att .file, .plug-search-box):not(.screen-sheet *),
:root:is(.pearl, .cosmos)[data-theme] .btn:not(.primary, .danger, :disabled, .disabled, .screen-sheet *) {
  background-color: var(--frost); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root:is(.pearl, .cosmos)[data-theme] :is(.card, .list-notice, .search-bar, .side-search, .ws-list, .agent-card, .file-card, .activity-detail, .pending-att .file, .xa-link, .options, .bc-screen) { border-color: var(--frost-line); }
:root:is(.pearl, .cosmos)[data-theme] :is(.input, .textarea, .select, .wsf-input, .sheet .name-input, .sheet .job-box, .sheet .brief, .sheet .memory-locked, .bp-card .job-box, .bp-card .textarea, .bp-card .brief):not(.screen-sheet *) {
  background-color: var(--frost); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root:is(.pearl, .cosmos)[data-theme] :is(.chip, .tab, .plug-installed, .plug-filter):not(.on, .screen-sheet *), :root:is(.pearl, .cosmos)[data-theme] :is(.source, .initials, .side-ic, .plug-pill, .plug-logo.icon, .kbd):not(.screen-sheet *) {
  background-color: var(--frost); background-image: var(--frost-sheen); border-color: var(--frost-line); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root:is(.pearl, .cosmos)[data-theme] :is(.segmented, .usage-switch) button.on:not(.screen-sheet *) { background-color: var(--frost-on); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root:is(.pearl, .cosmos)[data-theme] .scroll-down { background-image: var(--frost-sheen); box-shadow: var(--frost-edge); }
:root:is(.pearl, .cosmos)[data-theme] .app.wide .row-bot.active { background-color: var(--frost-on); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root:is(.pearl, .cosmos)[data-theme] :is(.drawer, .settings-page) { --card: var(--g-card); --card-press: var(--g-card-press); --card-line: var(--g-card-line); --meter: var(--g-meter); }
:root:is(.pearl, .cosmos)[data-theme] :is(.drawer, .settings-page) .group { background-color: var(--card); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root:is(.pearl, .cosmos)[data-theme] .plugins { --pill: var(--frost); --tile: var(--frost); --tile-ring: var(--frost-line); }
@media (max-width: 759.98px) {
  :root:is(.pearl, .cosmos)[data-theme] .bp-stats { background-color: var(--frost); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
}
/* What floats over the app. */
:root:is(.pearl, .cosmos)[data-theme] :is(.popover, .desk-menu, .mention-list, .mm-menu, .mm-more, .mm-bar, .reacts, .toast.glass):not(.screen-sheet *) {
  background-color: var(--raised); background-image: var(--frost-sheen); border-color: var(--raised-line); color: var(--text);
  -webkit-backdrop-filter: var(--glass-blur-strong); backdrop-filter: var(--glass-blur-strong); box-shadow: var(--frost-edge); }
:root:is(.pearl, .cosmos)[data-theme] .popover.account-menu { background-color: var(--am-bg); background-image: none; }
:root:is(.pearl, .cosmos)[data-theme] .bp-face-edit { --pop: var(--g-pop); }
:root:is(.pearl, .cosmos)[data-theme] .look-pop { background-image: var(--frost-sheen); border-color: var(--raised-line);
  -webkit-backdrop-filter: var(--glass-blur-strong); backdrop-filter: var(--glass-blur-strong); box-shadow: var(--frost-edge); }
/* Sheets and dialogs. */
:root:is(.pearl, .cosmos)[data-theme] :is(.sheet-scrim:not(.drawer-scrim), .dialog-scrim) { -webkit-backdrop-filter: blur(12px) saturate(160%); backdrop-filter: blur(12px) saturate(160%); }
:root:is(.pearl, .cosmos)[data-theme] .sheet:not(.drawer, .screen-sheet), :root:is(.pearl, .cosmos)[data-theme] .dialog { background-image: var(--frost-sheen); box-shadow: var(--frost-edge); }
:root:is(.pearl, .cosmos)[data-theme] .sheet:not(.screen-sheet)::before { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; pointer-events: none;
  -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur); }
:root:is(.pearl, .cosmos)[data-theme] .bp-tabbar { background: none; }
@media (max-width: 759.98px) {
  :root:is(.pearl, .cosmos)[data-theme] .bp-tabbar::before { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background-color: var(--sheet); background-image: var(--frost-sheen); -webkit-backdrop-filter: var(--glass-blur-strong); backdrop-filter: var(--glass-blur-strong); }
}
:root:is(.pearl, .cosmos)[data-theme] .sheet.store { --sb-bg: var(--sheet); --sb-card: var(--frost); --sb-line: var(--frost-line); }
@media (prefers-reduced-transparency: reduce) {
  :root:is(.pearl, .cosmos)[data-theme] { --surface: var(--solid); --elevated: var(--solid); --bot-bubble: var(--solid); --sheet: var(--solid-sheet); --raised: var(--solid-raised);
    --frost: var(--solid); --frost-sheen: none; --g-card: var(--solid); }
  :root:is(.pearl, .cosmos)[data-theme] .mm { --mm-glass: var(--solid-raised); }
  :root:is(.pearl, .cosmos)[data-theme] .head-glass { background: var(--bg); -webkit-mask-image: none; mask-image: none; }
  :root:is(.pearl, .cosmos)[data-theme] :is(.head-glass, .top-btn, .circle-btn, .pbar-btn, .popover, .desk-menu, .mention-list, .mm-menu, .mm-more, .mm-bar, .reacts, .look-pop, .sheet-scrim, .dialog-scrim, .toast) { -webkit-backdrop-filter: none; backdrop-filter: none; }
  :root:is(.pearl, .cosmos)[data-theme] .bp-tabbar::before { background-color: var(--solid-sheet); -webkit-backdrop-filter: none; backdrop-filter: none; }
  :root:is(.pearl, .cosmos)[data-theme] .sheet:not(.screen-sheet)::before { -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ---------- Pearl ---------- */
/* Light in a lavender sky's colors, after Aldra's: a soft wash of lavender with a peach blush at the top left,
   a light violet at the top right (half what it was: the owner's call), pink down the right and a pale sky blue at the bottom left; words in a deep violet ink,
   the second shade a gray violet; violet (#8B52E0) where Light has teal (what's tapped, on or new); and frosted
   liquid glass, after Apple's (the owner's call), clean: every box and the agents' bubbles a white frost (--frost,
   42%, so it reads clearly on the pale pearl sky) over a heavy blur of what's behind them, a touch richer and
   brighter (--box-blur, 24 px), smooth (no grain: it read as harsh); light pooling along its top and, faintly, its
   bottom, as on a curved pane (--frost-sheen); a crisp line of light just inside its top edge, a softer one inside
   its bottom, the edge itself brightening all round as glass does where it bends the light, and a milky glow inside
   (--frost-edge, drawn inside: no shadow, and no tint: a violet depth at the bottom muddied it); and over it all its
   specular rim, the mark of liquid glass: a hairline of light round its edge, white and brightest at its top left
   and bottom right corners where the light catches it, and between them a pearl's colors, blush pink and peach, then
   pale blue and lavender, softer in the middle (--glass-spec, ::after, below). Your bubbles are the same glass in the
   violet (below). Inside a sheet
   or dialog, which blur what's behind them themselves, boxes don't blur it again; with Reduce Transparency it's all
   solid. */
:root.pearl[data-theme] {
  --sky:
    linear-gradient(0deg, rgba(255, 255, 255, .34) 0%, rgba(255, 255, 255, .1) 7%, rgba(255, 255, 255, 0) 16%),
    linear-gradient(118deg, rgba(255, 255, 255, .52) 0%, rgba(255, 255, 255, .22) 12%, rgba(255, 255, 255, .06) 27%, rgba(255, 255, 255, 0) 39%),
    radial-gradient(44% 40% at 3% 0%, rgba(245, 214, 206, .95), rgba(245, 214, 206, 0)),
    radial-gradient(50% 48% at 100% 0%, rgba(203, 172, 240, .48), rgba(203, 172, 240, 0)),
    radial-gradient(40% 46% at 100% 60%, rgba(237, 196, 232, .9), rgba(237, 196, 232, 0)),
    radial-gradient(52% 46% at 0% 84%, rgba(194, 226, 241, .95), rgba(194, 226, 241, 0)),
    radial-gradient(46% 40% at 64% 104%, rgba(231, 200, 237, .85), rgba(231, 200, 237, 0)),
    linear-gradient(165deg, #ece2f2 0%, #e3d9f0 48%, #e2dbf2 100%);
  --glass-rim: rgba(255, 255, 255, .7);
  --glass-blur: blur(12px) saturate(170%);
  --glass-blur-strong: blur(12px) saturate(180%);
  --frost: rgba(255, 255, 255, .42);
  --box-blur: blur(24px) saturate(150%) brightness(1.04);
  --frost-sheen: linear-gradient(180deg, rgba(255, 255, 255, .5) 0%, rgba(255, 255, 255, .16) 30%, rgba(255, 255, 255, 0) 58%, rgba(255, 255, 255, .1) 100%);
  --frost-edge: inset 0 1px 1px rgba(255, 255, 255, .9), inset 0 -1px 1px rgba(255, 255, 255, .36), inset 0 0 6px rgba(255, 255, 255, .36),
    inset 0 0 20px rgba(255, 255, 255, .2);
  --glass-spec: linear-gradient(to bottom right, rgba(255, 255, 255, .95) 0%, rgba(255, 216, 238, .72) 16%, rgba(255, 228, 220, .4) 32%,
    rgba(214, 232, 255, .3) 48%, rgba(214, 226, 255, .36) 62%, rgba(230, 214, 255, .6) 80%, rgba(255, 255, 255, .9) 100%);
  --frost-line: rgba(255, 255, 255, .6);
  --frost-on: rgba(255, 255, 255, .58);
  --frost-hover: rgba(255, 255, 255, .48);
  --frost-press: rgba(255, 255, 255, .22);
  --pane: rgba(255, 255, 255, .34);
  --pane-sheen: linear-gradient(160deg, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0) 45%);
  --head-tint: linear-gradient(to bottom, rgba(255, 255, 255, .3), rgba(255, 255, 255, 0));
  --g-side-line: rgba(110, 84, 168, .14);
  --g-unread: var(--teal);
  --g-card: rgba(255, 255, 255, .46);
  --g-card-press: rgba(110, 84, 168, .08);
  --g-card-line: rgba(110, 84, 168, .12);
  --g-meter: rgba(110, 84, 168, .14);
  --g-pop: rgba(248, 244, 253, .92);
  --solid: #f7f3fc;
  --solid-sheet: #efe8f8;
  --solid-raised: #faf7fe;
  --teal: #8b52e0;
  --teal-deep: #6c3bc4;
  --bg: #e8e0f2;
  --surface: rgba(255, 255, 255, .5);
  --surface-2: rgba(122, 92, 184, .1);
  --surface-3: rgba(122, 92, 184, .16);
  --elevated: rgba(255, 255, 255, .55);
  --border: rgba(110, 84, 168, .16);
  --border-strong: rgba(110, 84, 168, .26);
  --text: #1f1a35;
  --muted: #5c5672;
  --dim: #a29bb9;
  --bot-bubble: rgba(255, 255, 255, .58);
  --user-bubble: #8b52e0;
  --code-bg: rgba(255, 255, 255, .5);
  --scrim: rgba(52, 34, 92, .22);
  --sheet: rgba(245, 240, 252, .6);
  --sheet-btn: rgba(255, 255, 255, .72);
  --raised: rgba(250, 247, 255, .52);
  --raised-line: rgba(255, 255, 255, .72);
  --track: rgba(110, 84, 168, .24);
  --drawer-bg: var(--bg);
  --drawer-backdrop: #e6ddf2;
  --drawer-page: #e6ddf2;
  --drawer-button-bg: rgba(255, 255, 255, .6);
}
/* The sky is a pane of liquid glass (the owner's call): on top of the lavender wash, light catching the pane's top left
   and its bottom edge (the first two layers of --sky, still), and under the glass, soft glows, pink, violet, peach and
   sky blue, drifting slowly, a thin streak of light slipping across with them (body::after: a layer twice the window's
   area on its own, moved by transform alone, so it costs the page nothing to draw: 46 s each way). The boxes' glass
   (--box-blur) blurs and brightens whatever passes under them, so the colors flow through the cards, bubbles and fields.
   It drifts only on a computer (a pointer that hovers); on a phone or a tablet, and with Reduce Motion, it holds
   still. */
:root.pearl[data-theme]:not(.signed-out) body::after { content: ""; position: fixed; inset: -22%; z-index: -1; pointer-events: none; will-change: transform;
  background:
    linear-gradient(118deg, rgba(255, 255, 255, 0) 41%, rgba(255, 255, 255, .24) 42.2%, rgba(255, 255, 255, .07) 44.6%, rgba(255, 255, 255, 0) 47.5%),
    radial-gradient(30vmax 26vmax at 52% 50%, rgba(255, 255, 255, .34), rgba(255, 255, 255, 0)),
    radial-gradient(34vmax 30vmax at 26% 32%, rgba(255, 186, 214, .5), rgba(255, 186, 214, 0)),
    radial-gradient(38vmax 32vmax at 76% 24%, rgba(148, 116, 240, .21), rgba(148, 116, 240, 0)),
    radial-gradient(34vmax 30vmax at 72% 76%, rgba(255, 204, 176, .46), rgba(255, 204, 176, 0)),
    radial-gradient(40vmax 34vmax at 24% 80%, rgba(136, 198, 245, .5), rgba(136, 198, 245, 0)); }
@keyframes pearl-liquid {
  0% { transform: translate3d(-3%, -2%, 0) rotate(-3deg) scale(1); }
  50% { transform: translate3d(2.5%, 1.5%, 0) rotate(2deg) scale(1.07); }
  100% { transform: translate3d(-1%, 3%, 0) rotate(5deg) scale(1.03); }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  :root.pearl[data-theme]:not(.signed-out) body::after { animation: pearl-liquid 46s ease-in-out infinite alternate; }
}
:root.pearl[data-theme] .pbar { --pb-accent: var(--teal); --pb-accent-ring: rgba(139, 82, 224, .22); }
/* The prompt bar is the same frosted glass, blurring the chat behind it as a box does. */
:root.pearl[data-theme] .pbar { -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
/* Your bubbles: the agents' bubbles' frosted glass (below, the owner's call), in the violet: violet at 62% at their
   top, so the colors behind them bloom softly through there, deepening into almost solid violet (91%) toward their
   bottom, over the same blur of what's behind them (--bubble-blur), with no edge, rim, line or shadow. The white
   words read clearly on it. A bubble being selected keeps its ring. */
:root.pearl[data-theme] .msg.user .bubble:not(.selecting) { background-color: rgba(139, 82, 224, .62);
  background-image: linear-gradient(180deg, rgba(139, 82, 224, 0) 0%, rgba(139, 82, 224, .3) 22%, rgba(139, 82, 224, .58) 46%, rgba(139, 82, 224, .76) 100%);
  box-shadow: none; -webkit-backdrop-filter: var(--bubble-blur); backdrop-filter: var(--bubble-blur); }
:root.pearl[data-theme] .msg.user .bubble { color: #fff; }
:root.pearl[data-theme] :is(.sheet, .dialog, .mm):not(.screen-sheet) { --box-blur: none; }
/* The specular rim: a 1 px ring cut out of --glass-spec with a mask, on a layer over the box (::after), so it follows
   the box's rounded corners. The boxes are placed relative to themselves for it unless they're placed already
   (:where, which wins over nothing). */
:where(:root.pearl[data-theme] :is(.msg.bot .bubble, .msg.member:not(.person) .bubble, .msg.user .bubble, .card, .drawer .group, .settings-page .group,
  .bp-card, .agent-card, .file-card, .list-notice, .search-bar, .reply-bar, .ws-start, .ws-list, .options, .pbar)) { position: relative; }
:root.pearl[data-theme] :is(.msg.bot .bubble, .msg.member:not(.person) .bubble, .msg.user .bubble, .card, .drawer .group, .settings-page .group,
  .bp-card, .agent-card, .file-card, .list-notice, .search-bar, .reply-bar, .ws-start, .ws-list, .options, .pbar):not(.screen-sheet *)::after {
  content: ""; position: absolute; inset: 0; padding: 1px; border-radius: inherit; pointer-events: none; background: var(--glass-spec);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask-composite: exclude; }
@media (prefers-reduced-transparency: reduce) {
  :root.pearl[data-theme] { --box-blur: none; }
  :root.pearl[data-theme] .msg.user .bubble:not(.selecting) { background-color: var(--user-bubble); background-image: none; }
}
/* Holding a message: the app blurs behind it under a violet dimming, and its menus are lavender glass. */
:root.pearl[data-theme] .mm {
  --mm-glass: var(--raised); --mm-line: rgba(255, 255, 255, .8); --mm-press: rgba(110, 84, 168, .08); --mm-on: rgba(139, 82, 224, .14);
  --mm-scrim: rgba(52, 34, 92, .3); }
:root.pearl[data-theme] .toast button { color: var(--teal-deep); }
/* Light's own grays and teals, in the lavender's and violet. */
:root.pearl[data-theme] .btn:disabled { background: rgba(110, 84, 168, .14); }
:root.pearl[data-theme] .sheet.store { --sb-pill: rgba(122, 92, 184, .1); }
:root.pearl[data-theme] :is(.signin-icon, .usage-chip) { background: rgba(139, 82, 224, .12); }
:root.pearl[data-theme] :is(.upgrade-card, .upsell-icon) { background: linear-gradient(118deg, #9a5ef0 0%, #6c3bc4 58%, #3d2470 100%); }
:root.pearl[data-theme] .upgrade-card { box-shadow: none; }
:root.pearl[data-theme] .pc-upgrade-icon { background: linear-gradient(135deg, #8b52e0 0%, #3d2470 100%); box-shadow: none; }
:root.pearl[data-theme] .memory-locked .lock { background: linear-gradient(145deg, #a06cf2 0%, #8b52e0 55%, #6c3bc4 100%); }
:root.pearl[data-theme] .voice { background: radial-gradient(circle at 50% 38%, #fff 0%, #e6dcf3 70%); }
/* For now, Pearl's sky is a picture, still, covering the window, after the owner's "My Music" picture: soft fields of a
   pearl's colors, clear enough for the frosted glass over them to show them but soft (aqua at the left, a white glow
   at the top, violet and lavender across the top, lilac in the middle, pink and peach down the right, periwinkle along
   the bottom), set in from the edges and fading into a pearl white toward them, so no edge of the window is a strong
   color (the owner's call), melting into one another with no edge anywhere (scripts/build-pearl.mjs draws them): a wide one on the
   website and in Holli Bot for Mac and Windows (icons/pearl-wide.webp, 16:9), a tall one in Holli Bot for iPhone and
   iPad (.ios-app: icons/pearl-tall.webp). Nothing over it, so its colors come through the glass as they are; no glows
   drifting under its glass. Take this out to bring the drawn sky and its glows back. The panes beside a chat on a
   computer (the agent list, the panel) are white frosted glass over it, after the owner's "Auld Lang Syne" picture:
   at their top half white (--pane, 50%), so the sky's colors bloom softly through there, deepening into almost solid
   white (88%) from about halfway down (--pane-sheen, white laid over --pane). Nothing marks where they meet the chat (no line, shade or glow at their edges: together
   they made a band there, the owner's call), only the step from their white to the chat's. The agent picked in the
   list is a brighter white, so it still stands out. */
:root.pearl[data-theme] { --sky: url("icons/pearl-wide.webp") center / cover no-repeat #ede6f4;
  --pane: rgba(255, 255, 255, .5);
  --pane-sheen: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, .3) 22%, rgba(255, 255, 255, .58) 46%, rgba(255, 255, 255, .76) 100%); }
:root.pearl[data-theme] .app.wide .pane-list { --frost-on: rgba(255, 255, 255, .92); --frost-hover: rgba(255, 255, 255, .8); }
/* In Holli Bot for iPhone and iPad, where no pane of glass sits over the sky, the sky has over it the same white liquid
   glass as the chat on a computer (the owner's call): white at 45%, light pooling along the top and, fainter, the
   bottom, and a pearl's glow at the top left, so its colors are a soft pearly tint. Its bubbles bring them up less
   (--bubble-blur, below). Its menus (the account menu sliding out from the left, a pop-up menu like + or ⋯, and a held
   message's) are the same white frosted glass as the side bars on a computer (--menu-bloom): half white at their top,
   so the colors behind bloom softly through there, deepening into almost solid white from about halfway down. */
:root.pearl.ios-app[data-theme] {
  --sky: linear-gradient(180deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .06) 30%, rgba(255, 255, 255, 0) 55%, rgba(255, 255, 255, .12) 100%),
    radial-gradient(90% 70% at 0% 0%, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0) 70%),
    linear-gradient(rgba(255, 255, 255, .45), rgba(255, 255, 255, .45)), url("icons/pearl-tall.webp") center / cover no-repeat #ede6f4;
  --menu-bloom: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, .3) 22%, rgba(255, 255, 255, .58) 46%, rgba(255, 255, 255, .76) 100%); }
:root.pearl.ios-app[data-theme] .sheet.drawer.account-drawer.main-page { background: var(--menu-bloom) rgba(255, 255, 255, .5); }
:root.pearl.ios-app[data-theme] :is(.popover, .mm-menu, .mm-more):not(.screen-sheet *),
:root.pearl.ios-app[data-theme] .popover.account-menu { background-color: rgba(255, 255, 255, .5); background-image: var(--menu-bloom); }
@media (prefers-reduced-transparency: reduce) {
  :root.pearl.ios-app[data-theme] .sheet.drawer.account-drawer.main-page { background: var(--solid-sheet); }
  :root.pearl.ios-app[data-theme] :is(.popover, .mm-menu, .mm-more):not(.screen-sheet *),
  :root.pearl.ios-app[data-theme] .popover.account-menu { background-color: var(--solid-raised); background-image: none; }
}
/* On a computer (the website, Holli Bot for Mac and Windows: a wide window, not the iPhone and iPad app), the chat in
   the middle is a pane of white liquid glass filling all the room between the panes beside it, with no gap (the
   owner's call), like theirs but more see-through: white at 45% (theirs are 60%) over a heavy blur of the sky (40 px)
   that brings its colors up only a touch (115%), so the sky is a soft pearly tint through it; light pooling along its
   top and, fainter, its bottom, as on a curved pane, and a pearl's luster (a soft white glow at its top left, fading
   across it); no edge, glow at its edges or shadow, so it meets the panes beside it with no band. It's a layer of its own under the chat (::before,
   painted before the rest of it) and holds or clips nothing, so the chat's menus are as ever. With Reduce
   Transparency it's solid. */
:root.pearl[data-theme]:not(.ios-app) .app.wide .pane-chat::before { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .06) 30%, rgba(255, 255, 255, 0) 55%, rgba(255, 255, 255, .12) 100%),
    radial-gradient(90% 70% at 0% 0%, rgba(255, 255, 255, .2), rgba(255, 255, 255, 0) 70%) rgba(255, 255, 255, .45);
  -webkit-backdrop-filter: blur(40px) saturate(115%); backdrop-filter: blur(40px) saturate(115%); }
:root.pearl[data-theme]:not(.ios-app) .app.wide .pane-chat.empty > * { position: relative; }
/* The agents' bubbles, on a computer and on a phone (Holli Bot for iPhone too), are the same white frosted glass as
   the side bars on a computer (the owner's call): half white at their top (50%), so the colors behind them bloom softly
   through there, deepening into almost solid white (88%) toward their bottom, over a blur of what's behind them (20 px;
   --bubble-blur; inside a sheet, a dialog or a held message's menu, which blur what's behind them themselves, none),
   with no edge, rim, line or shadow, like the side bars. Their words read clearly on the white. Yours are the same
   glass in Pearl's violet (above). A bubble being selected keeps its ring. */
:root.pearl[data-theme] { --bubble-blur: blur(20px) saturate(140%); }
:root.pearl.ios-app[data-theme] { --bubble-blur: blur(20px) saturate(120%); }
:root.pearl[data-theme] :is(.sheet, .dialog, .mm):not(.screen-sheet) { --bubble-blur: none; }
:root.pearl[data-theme] :is(.msg.bot, .msg.member:not(.person)) .bubble:not(.selecting) {
  background-color: rgba(255, 255, 255, .5);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, .3) 22%, rgba(255, 255, 255, .58) 46%, rgba(255, 255, 255, .76) 100%);
  box-shadow: none; -webkit-backdrop-filter: var(--bubble-blur); backdrop-filter: var(--bubble-blur); }
:root.pearl[data-theme] :is(.msg.bot, .msg.member:not(.person), .msg.user) .bubble::after { content: none; }
@media (prefers-reduced-transparency: reduce) {
  :root.pearl[data-theme]:not(.ios-app) .app.wide .pane-chat::before { background-color: var(--solid); -webkit-backdrop-filter: none; backdrop-filter: none; }
  :root.pearl[data-theme], :root.pearl.ios-app[data-theme] { --bubble-blur: none; }
  :root.pearl[data-theme] :is(.msg.bot, .msg.member:not(.person)) .bubble:not(.selecting) { background-color: var(--solid); }
}
:root.pearl[data-theme]:not(.signed-out) body::after { content: none; animation: none; }

/* ---------- Cosmos ---------- */
/* Dark under a night sky, after the owner's "peak productivity" picture: near black, lit by soft glows (steel teal
   across the top, magenta and plum down the right, indigo at the left, a warm olive at the bottom left, plum at the
   bottom right) and a faint scatter of crisp stars, all under a pane of black glass, near black at 25% (the owner's
   call), the same behind everything (the --sky, so Settings' copy of the sky is the same); the glows and the sky's
   slight fall from top to bottom, and your bubbles' tint (below), are its only gradients (the owner's call: no light across the
   glass, no tint fading down the top bars, which are clear, blurring what scrolls up under them); on the page, black liquid glass (the owner's
   call): a smoky see-through dark, a few percent of cool white over the near black, under a blur of whatever is
   behind it (--box-blur: the stars, or the words and bubbles under a menu or sheet, never crisp), with a thin lit
   edge along its top and left and a fainter one along its bottom and right (--frost-edge, drawn inside it: no
   shadow, and no light across it): the bubbles (yours too, the owner's call), buttons, cards, fields, chips, menus,
   sheets and dialogs, while the
   agent list's and the panel's panes stay see-through, and the prompt bar is liquid glass, clear, with no light
   across it (below); inside a sheet or dialog, which blur what's behind them themselves, its boxes don't blur it a
   second time, and with Reduce Transparency it's all solid dark glass instead;
   the top bars are clear. White words, the second shade a cool gray. What's on or tapped (the send button, the main
   buttons, chips and tabs that are on, switches, Create, the upgrade cards) is its violet, flat. Links and unread are
   periwinkle. */
:root.cosmos[data-theme] {
  --sky:
    linear-gradient(rgba(2, 3, 6, .25), rgba(2, 3, 6, .25)),
    radial-gradient(circle at 12% 9%, rgba(255, 255, 255, .7) 0 .8px, transparent 1.4px),
    radial-gradient(circle at 37% 4%, rgba(255, 255, 255, .45) 0 .6px, transparent 1.2px),
    radial-gradient(circle at 63% 15%, rgba(255, 255, 255, .6) 0 .7px, transparent 1.3px),
    radial-gradient(circle at 86% 7%, rgba(220, 230, 255, .55) 0 .8px, transparent 1.4px),
    radial-gradient(circle at 93% 27%, rgba(255, 255, 255, .4) 0 .6px, transparent 1.2px),
    radial-gradient(circle at 6% 36%, rgba(255, 255, 255, .45) 0 .6px, transparent 1.2px),
    radial-gradient(circle at 24% 58%, rgba(230, 220, 255, .35) 0 .6px, transparent 1.2px),
    radial-gradient(circle at 78% 49%, rgba(255, 255, 255, .4) 0 .7px, transparent 1.3px),
    radial-gradient(circle at 51% 71%, rgba(255, 255, 255, .3) 0 .6px, transparent 1.2px),
    radial-gradient(circle at 9% 83%, rgba(255, 245, 225, .4) 0 .7px, transparent 1.3px),
    radial-gradient(circle at 70% 88%, rgba(255, 255, 255, .35) 0 .6px, transparent 1.2px),
    radial-gradient(circle at 95% 76%, rgba(255, 230, 245, .4) 0 .7px, transparent 1.3px),
    radial-gradient(80% 52% at 28% 4%, rgba(88, 132, 156, .78), rgba(88, 132, 156, 0) 70%),
    radial-gradient(58% 44% at 98% 34%, rgba(138, 64, 126, .62), rgba(138, 64, 126, 0) 70%),
    radial-gradient(58% 42% at 0% 52%, rgba(64, 68, 140, .55), rgba(64, 68, 140, 0) 70%),
    radial-gradient(68% 40% at 12% 100%, rgba(104, 106, 62, .58), rgba(104, 106, 62, 0) 70%),
    radial-gradient(58% 38% at 92% 98%, rgba(128, 60, 104, .5), rgba(128, 60, 104, 0) 70%),
    linear-gradient(180deg, #0b1016 0%, #07080c 55%, #0a090c 100%);
  --glass-rim: rgba(255, 255, 255, .12);
  --glass-blur: blur(12px) saturate(170%);
  --glass-blur-strong: blur(18px) saturate(180%);
  --box-blur: blur(16px) saturate(170%);
  --nebula-glass:
    radial-gradient(44% 80% at 56% 0%, rgba(200, 104, 238, .15), rgba(200, 104, 238, 0)),
    radial-gradient(44% 80% at 40% 100%, rgba(78, 58, 226, .17), rgba(78, 58, 226, 0)),
    linear-gradient(90deg, rgba(51, 68, 240, .3) 0%, rgba(74, 50, 230, .3) 9%, rgba(104, 40, 222, .3) 22%, rgba(122, 54, 228, .3) 37%,
      rgba(144, 78, 235, .3) 50%, rgba(189, 76, 230, .3) 64%, rgba(221, 61, 220, .3) 80%, rgba(224, 26, 210, .3) 95%, rgba(216, 12, 200, .3) 100%);
  --frost: rgba(150, 165, 200, .085);
  --frost-sheen: none;
  --frost-edge: inset 1px 1px 0 rgba(255, 255, 255, .15), inset -1px -1px 0 rgba(255, 255, 255, .045), inset 0 0 26px rgba(255, 255, 255, .04);
  --frost-line: rgba(255, 255, 255, .12);
  --frost-on: rgba(190, 200, 235, .17);
  --frost-hover: rgba(170, 182, 215, .12);
  --frost-press: rgba(190, 200, 235, .2);
  --pane: rgba(255, 255, 255, .03);
  --pane-sheen: none;
  --head-tint: var(--bg);
  --g-side-line: rgba(255, 255, 255, .1);
  --g-unread: #8fa8ff;
  --g-card: rgba(150, 165, 200, .085);
  --g-card-press: rgba(170, 182, 215, .13);
  --g-card-line: rgba(255, 255, 255, .08);
  --g-meter: rgba(255, 255, 255, .16);
  --g-pop: #181a24;
  --solid: #1a1c24;
  --solid-sheet: #13151c;
  --solid-raised: #20222c;
  --bg: #07090d;
  --surface: #191a1e;
  --surface-2: #212225;
  --surface-3: #2d2e31;
  --elevated: #17181c;
  --border: rgba(255, 255, 255, .1);
  --border-strong: rgba(255, 255, 255, .18);
  --text: #f3f3f7;
  --muted: #a5a8b6;
  --dim: #626676;
  --blue: #8fa8ff;
  --bot-bubble: #191a1e;
  --user-bubble: #7a36e4;
  --code-bg: rgba(0, 0, 0, .35);
  --scrim: rgba(2, 3, 8, .5);
  --sheet: rgba(18, 20, 28, .74);
  --sheet-btn: rgba(255, 255, 255, .08);
  --raised: rgba(22, 25, 35, .72);
  --raised-line: rgba(255, 255, 255, .14);
  --track: rgba(255, 255, 255, .2);
  --drawer-bg: var(--bg);
  --drawer-backdrop: #05060a;
  --drawer-page: #05060a;
}
:root.cosmos[data-theme] .pbar { --pb-accent: #7a36e4; --pb-accent-ring: rgba(122, 54, 228, .32); --pb-accent-ink: #ffffff; }
/* The prompt bar is liquid glass, the one see-through thing on the page: all but clear, the sky behind it blurred,
   richer and a touch brighter, and a soft glow inside, no light falling across it (no gradients), with
   no rim or edge (No borders, at the end); its + and mic stay the solid glass the rest is. Dropping files on it, what it says is on solid glass. With Reduce Transparency it's solid. */
:root.cosmos[data-theme] .pbar {
  background-color: rgba(255, 255, 255, .035);
  border-color: transparent; -webkit-backdrop-filter: blur(12px) saturate(180%) brightness(1.12); backdrop-filter: blur(12px) saturate(180%) brightness(1.12);
  box-shadow: inset 0 0 20px rgba(255, 255, 255, .05); }
:root.cosmos[data-theme] .pbar-drop { background: var(--solid-raised); }
@media (prefers-reduced-transparency: reduce) {
  :root.cosmos[data-theme] .pbar { background: var(--solid); border-color: var(--frost-line); -webkit-backdrop-filter: none; backdrop-filter: none; }
  :root.cosmos[data-theme] .pbar::before { content: none; }
}
/* Boxes are black liquid glass (the tokens above): they blur what's behind them (--box-blur, in the shared glass rules),
   except inside a sheet, a dialog or a held message's menus, which blur it themselves already. */
:root.cosmos[data-theme] :is(.sheet, .dialog, .mm):not(.screen-sheet) { --box-blur: none; }
@media (prefers-reduced-transparency: reduce) {
  :root.cosmos[data-theme] {
    --frost: #17181c; --frost-edge: inset 0 0 22px rgba(255, 255, 255, .025); --frost-on: #2b2b2f; --frost-hover: #212225; --frost-press: #0f1014;
    --g-card: #17181c; --g-card-press: #191a1e; --sheet: #101219; --raised: #181a22; --box-blur: none; --glass-blur-strong: blur(12px) saturate(180%); }
}
/* The accent: on what's on or tapped, its violet, flat, white on it. Your bubbles are black liquid glass, as the agents'
   are, tinted with the owner's blue and magenta mix at 30% (--nebula-glass: royal blue into hot magenta through violet,
   left to right, lighter orchid toward its top and the blue reaching further along its bottom), so the sky still shows
   through them, blurred. */
:root.cosmos[data-theme] :is(.btn.primary, .chip.on, .tab.on, .plug-installed.on, .plug-filter.on, .pbar-go, .ws-start-go, .toggle.on) {
  background: #7a36e4; border-color: transparent; color: #fff; }
:root.cosmos[data-theme] :is(.btn.primary, .pbar-go, .ws-start-go) { box-shadow: none; }
:root.cosmos[data-theme] .msg.user .bubble { background-color: var(--frost); background-image: var(--nebula-glass); color: #fff; box-shadow: var(--frost-edge);
  -webkit-backdrop-filter: var(--box-blur, none); backdrop-filter: var(--box-blur, none); }
:root.cosmos[data-theme] :is(.upgrade-card, .upsell-icon, .pc-upgrade-icon, .memory-locked .lock) { background: #7a36e4; }
/* No gradient anywhere but the sky's glows and your bubbles' tint (the owner's calls): besides the glass, the top bars (clear
   glass, what scrolls up under them fading out into the sky on one clean curve: below) and the accent (above), the upgrade cards' sheen, the computer page's glow, the gallery's shades, the voice's backdrop, the
   loading and "Working" shimmers (it pulses instead), the Screen's dock, the store's tiles (and its backgrounds:
   src/ui/store.js tint), the shade behind the expand button on an agent's screen, the plan page's dock, and the
   glows in the pictures (the table's, the plane's trail) go flat or go. What fades out at the end of a line too long
   for it still fades: that's a mask over the words, not a color. */
/* What scrolls up under the agent list's or a chat's top bar fades out cleanly, after Claude Code's (the owner's call):
   the list or the chat itself fades, on one eased curve, from all there at the bar's bottom edge to gone a little above
   it, behind the buttons (a mask over the scroller, --head-fade, so it's the words and bubbles that fade, into the
   sky, rather than a band laid over them). Nothing is blurred, tinted or stepped there, and the bar's own glass
   (.head-glass) isn't drawn; the stepped blurs left bands of smeared words. The first chat or message, resting just
   under the bar, is untouched. Other bars (Plugins') keep their glass. */
:root.cosmos[data-theme] :is(.topbar, .chat-topbar) > .head-glass { display: none; }
:root.cosmos[data-theme] :is(.topbar ~ .home-scroll, .chat-body > .chat-scroll) {
  --head-fade: linear-gradient(to bottom, transparent calc(var(--head-h) - 34px), rgba(0, 0, 0, .06) calc(var(--head-h) - 29px),
    rgba(0, 0, 0, .2) calc(var(--head-h) - 23px), rgba(0, 0, 0, .45) calc(var(--head-h) - 17px), rgba(0, 0, 0, .72) calc(var(--head-h) - 11px),
    rgba(0, 0, 0, .92) calc(var(--head-h) - 5px), #000 var(--head-h));
  -webkit-mask-image: var(--head-fade); mask-image: var(--head-fade); }
:root.cosmos[data-theme] .upgrade-card::after, :root.cosmos[data-theme] .glass::before, :root.cosmos[data-theme] .sb-tile::after,
:root.cosmos[data-theme] .usage-skel::after { content: none; }
:root.cosmos[data-theme] .pc-hero { background: var(--card); }
:root.cosmos[data-theme] :is(.gal-top, .gal-strip) { background: none; }
:root.cosmos[data-theme] .voice { background: var(--bg); }
:root.cosmos[data-theme] .screen-dock { background: rgba(28, 28, 30, .62); }
:root.cosmos[data-theme] .sub-dock { background: var(--bg); }
:root.cosmos[data-theme] .activity.running .lbl { background: none; color: var(--muted); animation: cosmos-working 1.6s ease-in-out infinite; }
@keyframes cosmos-working { 50% { opacity: .5; } }
:root.cosmos[data-theme] .table-scene [fill^="url("] { display: none; }
:root.cosmos[data-theme] .fly-trail { stroke: rgba(95, 163, 171, .55); }
@media (prefers-reduced-motion: reduce) { :root.cosmos[data-theme] .activity.running .lbl { animation: none; } }
/* Holding a message: the app blurs behind it under a deep dimming, and its menus are black glass. */
:root.cosmos[data-theme] .mm {
  --mm-glass: var(--raised); --mm-line: rgba(255, 255, 255, .14); --mm-press: rgba(255, 255, 255, .08); --mm-on: rgba(122, 54, 228, .3);
  --mm-scrim: rgba(2, 3, 8, .55); }
:root.cosmos[data-theme] .sheet.store { --sb-pill: rgba(255, 255, 255, .08); --sb-blue: #8fa8ff; }

/* ---------- No borders ----------
   Nothing in the app has a border, in any look (the owner's call): no line round a box, a card, a field, a button, a
   chip, a badge, a menu, a sheet, a bubble or a pane, nor the ring round a face's dot, nor a glass edge or rim. The
   borders are made clear rather than taken away (!important, over every look's own), so nothing shifts. Kept: the
   lines between rows and sections, which are dividers, not borders (the menu's and Settings' rows, the store's, a
   profile's stats and tabs, the files and activity lists, the usage table, a code block's header); the lines in what
   an agent writes (its tables, quotes and rules); and what shows a state: what's picked or has the keyboard's focus,
   a face that's thinking, a spinner, a file dragged over the prompt bar, the camera's shutter, the computer's mic;
   and the round buttons, which have a line of their own (Round buttons, below). */
:root :not(.spinner, .cam-shutter, .pick, .swatch, .screen-mic, .bubble :is(th, td, blockquote, hr), .agent-card .a :is(blockquote, hr),
  .bp-stat, .bp-tabbar, .file-row, .act-item, .usage-table :is(td, th), .sb-info div, .sb-facts, .sb-story, .pc-gauges, .usage-card-sub, .bw-body, .code-head,
  .circle-btn, .top-btn, .pbar-btn, .pbar-go, .plug-filter, .secret-add, .member-add, .side-ic, .rail-me, .voice .big, .voice .end, .gal-btn, .cam-btn, .att-video .play) {
  border-color: transparent !important; }
/* What a line alone drew is filled instead: check boxes and radio buttons, the little badges, the quote above a reply,
   an outlined button, the box to type who to invite in. */
:where(.ws-check, .ws-radio, .invite-box, .sub-radio) { background-color: var(--surface-3); }
:where(.mem .badge, .notes-group .gone, .row-bot .kind-badge, .ws-kind) { background: var(--surface-2); }
:where(.reply-quote) { background: color-mix(in srgb, var(--text) 6%, transparent); }
:root .hello-cta.secondary { background: color-mix(in srgb, var(--text) 8%, transparent); }
.btn.wsf-option, .btn.wsf-option:disabled { background: var(--surface-2); box-shadow: none; }
.invite-input { padding: 14px 12px; border-radius: 12px; background: var(--surface-2); }
.bp-name:focus { background: var(--surface-2); }
/* Rings drawn as shadows go the same way, and nothing has a shadow under it (No shadows, also the owner's call). */
.sub-radio, .plug-logo::after, .pbar-go, .screen-dock .circle-btn.on, .app.rail .row-bot > .dot, .rail-alert::after, .plush-dot, .plug-stack .plug-logo { box-shadow: none; }
.app.rail .row-bot:active > .dot, .app.rail .row-bot.active > .dot, .app.rail .row-bot:hover:not(.active) > .dot, .plug-installed.on .plug-stack .plug-logo { box-shadow: none; }
.sub-plan:not(.on), .pc-card:not(.hosted) { box-shadow: none; }
.ws-start { box-shadow: none; }
.reacts { box-shadow: none; }
.sb-tile { box-shadow: none; }
.glass { box-shadow: inset 0 0 8px rgba(255, 255, 255, .04), inset 0 4px 8px -4px rgba(0, 0, 0, .22); }
.glass::after { content: none; }
.screen-dock { box-shadow: none; }
:root.mac-glass:not([data-theme="light"], .cosmos) .app.wide .pane-list { box-shadow: none; }
/* In Light, what's white on the white page (or a sheet) had a hairline to show where it is, and then a soft shadow;
   now neither. */
:root[data-theme="light"] :is(.drawer, .settings-page) .group, :root[data-theme="light"] :is(.pc-hero, .pc-upgrade) { box-shadow: none; }
:root[data-theme="light"] :is(.card, .agent-card, .file-card, .activity-detail, .source, .pending-att .file, .list-notice, .search-bar, .ws-list, .xa-link,
  .reply-bar, .btn:not(.primary, .danger), .chip:not(.on), .tab:not(.on), .input, .textarea, .select, .btn.wsf-option) {
  box-shadow: none; }
:root[data-theme="light"] .btn.wsf-option:disabled { box-shadow: none; }
/* Signing in is always on Light's page: Google's white button has neither too. */
.auth-btn.google { box-shadow: none; }
:root[data-theme="light"] .ws-start { box-shadow: none; }
:root[data-theme="light"] .reacts { box-shadow: none; }
:root[data-theme="light"] .sb-tile { box-shadow: none; }

/* ---------- Round buttons ----------
   Every round button has a thin line round it, so it stands out from what's behind it, in every look (the owner's
   call, over No borders): its own icon's color, faint (20%), so it's dark round a light button and light round a dark
   one, and white over a picture or the camera: the round buttons at the top and on sheets and pages (X, Back, search, +, the computer and ⋯), the prompt bar's
   +, mic and send, the scroll-down arrow, the sidebar's and the rail's, Plugins' filters, the add buttons, the
   voice's, the gallery's and the camera's and the play button on a video. The Screen's dock keeps its plain icons,
   as do the round buttons that are only an icon (a field's clear, a reply's X, the menu's gear, the trash cans in
   lists, the expand icon in the middle of an agent's screen). */
:root :is(.circle-btn, .top-btn, .pbar-btn, .pbar-go, .plug-filter, .secret-add, .member-add, .side-ic, .rail-me, .voice .big, .voice .end):not(.screen-dock *) {
  border: 1px solid color-mix(in srgb, currentColor 20%, transparent) !important; }
:root :is(.gal-btn, .cam-btn, .att-video .play) { border: 1px solid rgba(255, 255, 255, .28) !important; }

/* ---------- Settings and its pages as a window, on a computer ----------
   In a window 900 px wide or more (the website, Holli Bot for Windows and Mac; where sheets are in the middle too),
   Settings and every page the account menu opens (Usage, Plugins, MCP and SSH servers, Secrets, Saved logins & keys,
   Computer) open in the middle of the screen, exactly centered across and down, on a dark overlay (darker than a
   sheet's, --settings-scrim), rather than sliding over everything. The overlay fades in (400 ms) and the window, a beat
   behind it, fades in as it settles up and out of a hair smaller into its place (450 ms, easing off as it arrives);
   closing is quicker and gentler, both fading out together (260 ms): usePushPage takes .page-open off, which starts
   it, and removes the page once it's gone. The window is the same size on every page (640 px by 720, or as much of
   the screen as there is less 80 px), so the page changing doesn't make it jump, and what's on a page scrolls in it.
   It has the page's own color (--bg: Light's pale gray, the dark looks' own), so the groups on it stand out as they
   always have; in Pearl and Cosmos it's their glass's solid shade, with its sheen. In every look the overlay blurs the
   app behind it slightly too (more in Pearl and Cosmos). With Reduce Motion it's there and gone at once; with Reduce
   Transparency nothing blurs. */
/* The overlay behind it is darker than a sheet's (the owner's call), in each look's own shade. */
:root { --settings-scrim: rgba(0, 0, 0, .7); }
:root[data-theme="light"] { --settings-scrim: rgba(12, 32, 38, .5); }
:root.pearl[data-theme] { --settings-scrim: rgba(36, 22, 70, .45); }
:root.cosmos[data-theme] { --settings-scrim: rgba(2, 3, 8, .66); }
@media (min-width: 900px) {
  .push-page.settings-page { align-items: center; justify-content: center; background: var(--settings-scrim); transform: none; opacity: 0; pointer-events: none;
    transition: opacity .26s ease-in; touch-action: auto; }
  .page-open .push-page.settings-page { opacity: 1; pointer-events: auto; transition: opacity .4s cubic-bezier(.2, .8, .2, 1); }
  .settings-page .page-panel { position: relative; flex: none; width: min(640px, calc(100vw - 64px)); height: min(720px, calc(100vh - 80px)); border-radius: 28px; overflow: hidden;
    background: var(--bg); opacity: 0; transform: translateY(14px) scale(.97); transition: opacity .22s ease-in, transform .26s ease-in; }
  .page-open .settings-page .page-panel { opacity: 1; transform: none;
    transition: opacity .45s cubic-bezier(.2, .8, .2, 1) .05s, transform .55s cubic-bezier(.2, .8, .2, 1) .05s; }
  .settings-page .page-head { padding: 16px 18px 12px; }
  .settings-page .page-scroll { padding: 0 18px 28px; }
  .settings-page.own-head { padding-top: 0; }
  .settings-page .plug-top { margin: 0 -18px; padding: 16px 18px 12px; }
  /* Behind it, in every look, the app dims under the overlay and blurs slightly (the owner's call); in Pearl and Cosmos a
     little more, through their glass. */
  .push-page.settings-page { -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px); }
  :root:is(.pearl, .cosmos)[data-theme] .push-page.settings-page { background: var(--settings-scrim); -webkit-backdrop-filter: blur(12px) saturate(160%); backdrop-filter: blur(12px) saturate(160%); }
  :root:is(.pearl, .cosmos)[data-theme] .push-page.settings-page::before { display: none; }
  /* In Pearl and Cosmos the window is their glass's solid shade (--solid-sheet), with its sheen and edge light: see-through,
     it would take on the dark overlay behind it and look dim. */
  :root:is(.pearl, .cosmos)[data-theme] .settings-page .page-panel { background-color: var(--solid-sheet); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); isolation: isolate; --box-blur: none; }
}
/* ---------- Settings with a side bar, on a computer (src/ui/settings.js SettingsSheet, TABS) ----------
   Settings itself is a wider window (900 px by 720, or as much of the screen as there is): down its left, a side
   bar of its sections, each an icon and its name, the one open on the groups' own surface (--card), the rest
   showing a lighter one under the pointer; a hairline between it and the rest. There, the section's name at the
   top left, big, an X at the top right, and the section under them, its groups across the whole width. A row's
   choice (Appearance, Language) is a rounded button with what's picked and a chevron, opening a menu under it;
   the account's surface has the initials, name and email (a copy button beside it) and Sign Out on a rounded
   button. Every color is the look's own: the surfaces, lines and words of Light, Dark, Black, Pearl and Cosmos
   (in Pearl and Cosmos the window and the groups are their frosted glass), and the rounded buttons a tint of
   the words' color, so they show on all of them. */
@media (min-width: 900px) {
  .settings-page.tabbed .page-panel { flex-direction: row; width: min(900px, calc(100vw - 64px)); height: min(720px, calc(100vh - 80px)); border-radius: 22px; }
  .set-nav { flex: none; width: 224px; display: flex; flex-direction: column; gap: 2px; padding: 18px 12px; overflow-y: auto; border-right: 1px solid var(--card-line); }
  .set-tab { display: flex; align-items: center; gap: 12px; width: 100%; min-height: 40px; padding: 8px 12px; border-radius: 12px; color: var(--text);
    font-size: var(--fs-md); line-height: 20px; font-weight: 500; text-align: left; transition: background .15s; }
  .set-tab svg { flex: none; width: 20px; height: 20px; color: var(--text-2); stroke-width: 1.75; }
  .set-tab span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  @media (hover: hover) { .set-tab:hover { background: color-mix(in srgb, var(--text) 6%, transparent); } }
  .set-tab:active { background: color-mix(in srgb, var(--text) 9%, transparent); }
  .set-tab[aria-current="page"] { background: var(--card); }
  :root[data-theme="light"] .set-tab[aria-current="page"] { box-shadow: 0 0 0 1px var(--card-line); }
  :root:is(.pearl, .cosmos)[data-theme] .set-tab[aria-current="page"] { background-color: var(--card); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); }
  .settings-page.tabbed .set-main { position: relative; }
  .settings-page.tabbed .set-head { display: flex; align-items: center; gap: 12px; min-height: 76px; padding: 26px 76px 4px 32px; }
  .settings-page.tabbed .set-head h2 { flex: 1; min-width: 0; text-align: left; font-size: var(--fs-lg); line-height: 30px; font-weight: 700; }
  .set-close { display: inline-flex; position: absolute; top: 18px; right: 18px; z-index: 2; }
  .settings-page.tabbed .page-scroll { padding: 0 32px 32px; }
  .settings-page.tabbed .page-body { max-width: none; }
  /* Plugins beside the side bar: its search row (how many are installed, the box, the filter) stays at the top as it scrolls. */
  .settings-page.tabbed .plug-top { margin: 0 -32px; padding: 4px 32px 12px; }
  .settings-page.tabbed .plug-top.in-pane .plug-search { margin-top: 0; }
  .settings-page.tabbed .group-label { margin: 26px 4px 9px; }
  .settings-page.tabbed .group-note { margin: 8px 4px 12px; }
  .settings-page.tabbed .settings-foot { margin-top: 22px; }
  /* The account: initials, name and email (with its copy button), and Sign Out. */
  .settings-page .set-account { min-height: 76px; padding: 14px 16px; gap: 14px; }
  .set-account .initials { width: 46px; height: 46px; border: 0; background: color-mix(in srgb, var(--text) 10%, transparent); color: var(--text-2); font-size: var(--fs-md); font-weight: 500; }
  .settings-page .set-account .label .s { display: flex; align-items: center; gap: 6px; -webkit-mask-image: none; mask-image: none; }
  .set-account .label .s span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .set-copy { flex: none; display: grid; place-items: center; width: 26px; height: 26px; margin: -4px 0; border-radius: 8px; color: var(--muted); transition: background .15s, color .15s; }
  .set-copy svg { width: 15px; height: 15px; }
  @media (hover: hover) { .set-copy:hover { background: color-mix(in srgb, var(--text) 8%, transparent); color: var(--text); } }
}
/* Sign Out and a row's choice, on a computer and in an agent's settings on a phone alike (below). */
/* Rounded buttons, a tint of the words' color with a hairline round them. */
.set-pill, .set-pick { flex: none; display: inline-flex; align-items: center; height: 38px; border-radius: 19px; border: 1px solid var(--card-line);
  background: color-mix(in srgb, var(--text) 5%, transparent); color: var(--text); font-size: var(--fs-md); line-height: 20px; font-weight: 500; white-space: nowrap; transition: background .15s; }
.set-pill { padding: 0 18px; }
.set-pick { gap: 6px; max-width: 260px; padding: 0 11px 0 15px; }
.set-pick span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.set-pick svg { flex: none; width: 16px; height: 16px; color: var(--muted); stroke-width: 2; }
@media (hover: hover) { :is(.set-pill, .set-pick):hover { background: color-mix(in srgb, var(--text) 10%, transparent); } }
:is(.set-pill, .set-pick):active, .set-pick[aria-expanded="true"] { background: color-mix(in srgb, var(--text) 12%, transparent); }
/* A choice's menu, hanging from its button's right edge. */
.popover.set-menu { min-width: 200px; padding: 6px; border-radius: 18px; }
.popover.set-menu.up { transform-origin: bottom right; }
.set-menu button { justify-content: space-between; gap: 12px; height: 42px; padding: 0 12px 0 14px; border-radius: 12px; font-size: var(--fs-md); }
.set-menu button svg { width: 18px; height: 18px; color: var(--text); }
@media (hover: hover) { .set-menu button:hover { background: color-mix(in srgb, var(--text) 7%, transparent); } }

/* ---------- Settings → Plan (src/ui/subscribe.js PlanPanel) ----------
   The plan page inside Settings, on a phone and in the window on a computer alike, in Settings' own look rather than
   the plan page's: a line on what a plan adds, Monthly | Yearly on a switch, the plans on one surface (a row each: a
   round tick at the left, filled as the main buttons are in the look, the name with its tags, its note and what it
   includes under it, and its price at the right), what every plan includes as rows with icons, and Subscribe or
   Manage Billing, a button across the page, with the fine print under it. A payment that didn't go through is a
   note on a tint of red. Every color is the look's: its surfaces, lines and words, and its main buttons' fill. */
.set-plan-lead { margin: 0 16px 14px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; font-weight: 450; }
.set-plan-alert { display: flex; align-items: flex-start; gap: 12px; margin: 0 0 14px; padding: 14px 16px; border-radius: var(--radius-group, 16px);
  background: color-mix(in srgb, var(--red) 11%, transparent); color: var(--text); }
.set-plan-alert > svg { flex: none; margin-top: 1px; color: var(--red); }
.set-plan-alert .t { font-size: var(--fs-md); line-height: 21px; font-weight: 700; }
.set-plan-alert .s { color: var(--text-2); font-size: var(--fs-sm); line-height: 1.45; }
.set-plan-alert .t + .s { margin-top: 2px; }
.set-plan-cycle button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-height: 36px; }
.set-plan-cycle button:disabled { opacity: .55; }
.set-plan-tag { display: inline-flex; align-items: center; height: 20px; padding: 0 7px; border-radius: 10px; background: color-mix(in srgb, var(--text) 8%, transparent);
  color: var(--muted); font-size: var(--fs-sm); line-height: 20px; font-weight: 500; white-space: nowrap; }
.set-plan-tag.save { background: color-mix(in srgb, var(--green) 16%, transparent); color: var(--green); }
:root[data-theme="light"] .set-plan-tag.save { background: color-mix(in srgb, var(--teal) 13%, transparent); color: var(--teal); }
:is(.drawer, .settings-page) .row.set-plan { align-items: flex-start; gap: 14px; padding: 14px 16px; }
.settings-page .set-plans .row + .row::before { left: 52px; right: 16px; }
.settings-page button.set-plan:disabled { cursor: default; background: transparent; }
.set-plan.soon { opacity: .55; }
.set-plan-radio { flex: none; display: grid; place-items: center; width: 22px; height: 22px; margin-top: 0; border-radius: 50%; border: 1.5px solid var(--dim); color: #000; }
.set-plan.on .set-plan-radio { background: #fff; border-color: #fff; }
:root[data-theme="light"] .set-plan.on .set-plan-radio { background: var(--teal); border-color: var(--teal); color: #fff; }
:root.cosmos[data-theme] .set-plan.on .set-plan-radio { background: #7a36e4; border-color: #7a36e4; color: #fff; }
.set-plan-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.set-plan-name { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; color: var(--text); font-size: var(--fs-md); line-height: 21px; font-weight: 500; }
.set-plan-note { color: var(--text-2); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; }
.set-plan-specs { color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; }
.set-plan-price { flex: none; text-align: right; color: var(--muted); font-size: var(--fs-sm); line-height: 21px; font-weight: 450; font-variant-numeric: tabular-nums; }
.set-plan-price b { color: var(--text); font-size: var(--fs-lg); line-height: 30px; font-weight: 700; }
.set-plan-price small { display: block; font-size: var(--fs-sm); line-height: 18px; }
.set-plan-include { flex: 1; min-width: 0; color: var(--text); font-size: var(--fs-md); line-height: 21px; font-weight: 450; }
.set-plan-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 24px; }
.set-plan-error { margin: 12px 16px 0; }
.set-plan-fine { margin: 0 16px; color: var(--muted); font-size: var(--fs-sm); line-height: 1.45; font-weight: 450; text-align: center; }
.set-plan-legal { margin: 18px 16px 0; color: var(--muted); font-size: var(--fs-sm); line-height: 20px; text-align: center; }
.set-plan-legal a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
@media (min-width: 900px) {
  .settings-page.tabbed :is(.set-plan-lead, .set-plan-fine, .set-plan-legal) { margin-left: 4px; margin-right: 4px; }
  .settings-page.tabbed .set-plan-error { margin-left: 4px; margin-right: 4px; }
}
/* ---------- Settings → Agents (src/ui/bot-profile.js AgentSettings) ----------
   Every agent, and the one picked's whole profile, fitted to Settings. At the top, held there on frosted glass as the
   rest scrolls under it, the agents' faces in a row that scrolls sideways, each with its name under it, the one picked
   ringed in the look's accent. Under them, its face (big, its loop playing) and its name in the middle, its four
   counts on small surfaces in a row (two by two on a narrow phone), its tabs on a line, and the tab under them,
   scrolling with Settings rather than in a box of its own. Every color is the look's. */
.ag-top { position: sticky; top: 0; z-index: 4; margin: 0 -16px; padding: 2px 16px 10px; }
.ag-strip { display: flex; gap: 4px; overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; padding: 4px 2px; }
.ag-strip::-webkit-scrollbar { display: none; }
.ag-pick { flex: none; display: flex; flex-direction: column; align-items: center; gap: 7px; width: 78px; padding: 6px 4px 8px; border-radius: 14px;
  color: var(--muted); transition: background .15s, color .15s; }
.ag-pick-face { display: grid; place-items: center; border-radius: 50%; outline: 2px solid transparent; outline-offset: 3px; transition: outline-color .15s; }
.ag-pick.on { color: var(--text); }
.ag-pick.on .ag-pick-face { outline-color: var(--blue); }
@media (hover: hover) { .ag-pick:hover:not(.on) { background: color-mix(in srgb, var(--text) 5%, transparent); color: var(--text-2); } }
.ag-pick-name { max-width: 100%; font-size: var(--fs-sm); line-height: 18px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ag-hero { display: flex; flex-direction: column; align-items: center; padding: 10px 0 2px; text-align: center; }
.ag-hero .ag-name { max-width: 440px; margin-top: 10px; font-size: var(--fs-lg); line-height: 30px; font-weight: 700; }
.ag-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin: 16px 0 4px; }
.ag-stat { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; min-height: 64px; padding: 8px 6px; border-radius: 14px;
  background: var(--card); transition: background .15s; }
.ag-stat b { color: var(--text); font-size: var(--fs-lg); line-height: 30px; font-weight: 700; font-variant-numeric: tabular-nums; }
.ag-stat span { color: var(--muted); font-size: var(--fs-sm); line-height: 18px; font-weight: 450; text-align: center; }
.ag-stat:disabled { cursor: default; }
.ag-stat:not(:disabled):active { background: var(--card-press); }
@media (hover: hover) { .ag-stat:not(:disabled):hover { background: var(--card-press); } }
:root:is(.pearl, .cosmos)[data-theme] .ag-stat { background-color: var(--card); background-image: var(--frost-sheen); box-shadow: var(--frost-edge); }
@media (max-width: 480px) { .ag-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.settings-page .bp-tabbar.ag-tabbar { position: static; margin: 12px 0 0; padding: 0 0 0 4px; background: none; border-bottom-color: var(--card-line); }
.settings-page .ag-tabbar .bp-tabs { gap: 22px; }
.settings-page .ag-tabbar .bp-tabs button { padding: 14px 0 12px; }
.settings-page .bp-pane.ag-pane { flex: none; min-height: 0; overflow: visible; padding: 16px 0 0; }
.settings-page .ag-pane .bp-card { background: var(--card); }
.ag-screen .bc-card { margin: 0 0 14px; }
@media (min-width: 900px) {
  .settings-page.tabbed .ag-top { margin: 0 -32px; padding: 2px 32px 10px; }
}
@media (min-width: 900px) and (prefers-reduced-motion: reduce) {
  .push-page.settings-page, .page-open .push-page.settings-page, .settings-page .page-panel, .page-open .settings-page .page-panel { transition: none; }
  .settings-page .page-panel { transform: none; }
}
@media (min-width: 900px) and (prefers-reduced-transparency: reduce) {
  .push-page.settings-page, :root:is(.pearl, .cosmos)[data-theme] .push-page.settings-page { -webkit-backdrop-filter: none; backdrop-filter: none; }
}

/* ---------- Pop-up menus on a computer: a drop shadow, and a line round them ----------
   The owner's call, in every look: on the website and in Holli Bot for Mac and Windows (a mouse or trackpad; not
   Holli Bot for iPhone or iPad, nor a phone), every pop-up menu floats over the app on a soft drop shadow, and has a
   hairline round it in a shade that stands out from what's under it (darker in Light and Pearl, lighter in Black, Dark
   and Cosmos), so a menu stands apart even over something of its own color: the account menu, + and ⋯ and the chat's
   menus, a held message's menu, its reactions and more reactions, @mentions, a look's picker, Settings' choices and
   Plugins' filter. In Pearl and Cosmos they keep their frosted glass's light along their edges too. */
:root { --menu-ring: rgba(255, 255, 255, .14); --menu-shadow: 0 16px 40px -8px rgba(0, 0, 0, .65), 0 4px 12px rgba(0, 0, 0, .35); }
:root[data-theme="light"] { --menu-ring: rgba(16, 40, 48, .14); --menu-shadow: 0 16px 40px -8px rgba(16, 40, 48, .24), 0 4px 12px rgba(16, 40, 48, .1); }
:root.pearl[data-theme] { --menu-ring: rgba(84, 56, 150, .18); --menu-shadow: 0 16px 40px -8px rgba(70, 46, 140, .3), 0 4px 12px rgba(70, 46, 140, .12); }
@media (hover: hover) and (pointer: fine) {
  :root:not(.ios-app) :is(.popover, .desk-menu, .mention-list, .mm-menu, .mm-more, .mm-bar):not(.screen-sheet *) { box-shadow: 0 0 0 1px var(--menu-ring), var(--menu-shadow); }
  :root:is(.pearl, .cosmos)[data-theme]:not(.ios-app) :is(.popover, .desk-menu, .mention-list, .mm-menu, .mm-more, .mm-bar):not(.screen-sheet *) {
    box-shadow: var(--frost-edge), 0 0 0 1px var(--menu-ring), var(--menu-shadow); }
  /* A look's picker has a point at its top, drawn with its border: the line is its border, so the point has it too. */
  :root:not(.ios-app) .look-pop, :root[data-theme]:not(.ios-app) .look-pop { border-color: var(--menu-ring); box-shadow: var(--menu-shadow); }
}
