/*
 * THE SHELL'S OWN CSS (ADR-027 §8, D8): what the page is before the app draws — the reset Expo's
 * template carries inline (refused by the CSP, so it is here), the self-hosted faces, and the ink
 * of each theme on <html> so the first paint is never the browser's white or black. The colours
 * come from @builds/tokens at export (scripts/export-web.ts fills %TOKENS%); nothing is typed twice.
 * One small file, hashed, cached for good and precached by the service worker.
 */
@font-face {
  font-family: "Space Grotesk";
  src: url(../fonts/space-grotesk-latin-wght-normal.woff2) format("woff2");
  font-weight: 300 700;
  font-display: swap;
}
@font-face {
  font-family: "Inter";
  src: url(../fonts/inter-latin-wght-normal.woff2) format("woff2");
  font-weight: 400 600;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url(../fonts/jetbrains-mono-latin-400-normal.woff2) format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url(../fonts/jetbrains-mono-latin-500-normal.woff2) format("woff2");
  font-weight: 500;
  font-display: swap;
}
:root{--ink:#07080c;--surface:#0e1118;--surface-2:#141925;--surface-3:#1a2030;--surface-4:#202839;--rule:#1a2030;--rule-2:#252d40;--edge:#66718d;--paper:#eef1f6;--dim:#b4bccb;--mute:#8a93a6;--violet:#8b7cff;--cyan:#4de1d2;--lime:#a6f25c;--amber:#ffb547;--coral:#ff6b5a;--sky:#55c4fe;--fill-amber:#ffb547;--fill-violet:#8b7cff;--fill-cyan:#4de1d2;--fill-coral:#ff6b5a;--fill-lime:#a6f25c;--fill-sky:#55c4fe;--on-accent:#07080c;--scrim-base:#030407;--claude:#d97757;--claude-text:#e6a98f;color-scheme:dark;}
:root[data-theme="dusk"]{--ink:#23262d;--surface:#2a2e36;--surface-2:#30343d;--surface-3:#373c47;--surface-4:#3e4350;--rule:#363a44;--rule-2:#454b57;--edge:#7a8295;--paper:#f2f4f7;--dim:#c9ced8;--mute:#a8afbd;--violet:#ab9cff;--cyan:#4fd8ca;--lime:#b4e86a;--amber:#ffc166;--coral:#ff8878;--sky:#69caff;--fill-amber:#ffc166;--fill-violet:#ab9cff;--fill-cyan:#4fd8ca;--fill-coral:#ff8878;--fill-lime:#b4e86a;--fill-sky:#69caff;--on-accent:#1b1d22;--scrim-base:#101113;--claude:#d97757;--claude-text:#f0b49b;color-scheme:dark;}
:root[data-theme="graphite"]{--ink:#30343c;--surface:#383c45;--surface-2:#3f434d;--surface-3:#464b56;--surface-4:#4e535f;--rule:#454a54;--rule-2:#555b67;--edge:#8a92a2;--paper:#f5f6f8;--dim:#d6dae1;--mute:#bfc5cf;--violet:#c2b7ff;--cyan:#63e2d5;--lime:#bdee78;--amber:#ffc873;--coral:#ffa396;--sky:#8cd5ff;--fill-amber:#ffc873;--fill-violet:#c2b7ff;--fill-cyan:#63e2d5;--fill-coral:#ffa396;--fill-lime:#bdee78;--fill-sky:#8cd5ff;--on-accent:#1c1f25;--scrim-base:#16171b;--claude:#df8465;--claude-text:#f7c3ad;color-scheme:dark;}
:root[data-theme="light"]{--ink:#e8ebf1;--surface:#f1f3f7;--surface-2:#e2e6ee;--surface-3:#d6dbe5;--surface-4:#cdd3de;--rule:#d5dae4;--rule-2:#c3cad7;--edge:#6f7a90;--paper:#232833;--dim:#3a4150;--mute:#525b6d;--violet:#5646d0;--cyan:#095f58;--lime:#356d17;--amber:#7a4c0a;--coral:#9a3829;--sky:#0c60a3;--fill-amber:#f0a93f;--fill-violet:#8f82f5;--fill-cyan:#3cc4b7;--fill-coral:#f38a7b;--fill-lime:#8fd14f;--fill-sky:#3aa9ec;--on-accent:#10131a;--scrim-base:#5d6577;--claude:#d97757;--claude-text:#8a4029;color-scheme:light;}
@media (prefers-color-scheme: light){:root:not([data-theme]){--ink:#e8ebf1;--surface:#f1f3f7;--surface-2:#e2e6ee;--surface-3:#d6dbe5;--surface-4:#cdd3de;--rule:#d5dae4;--rule-2:#c3cad7;--edge:#6f7a90;--paper:#232833;--dim:#3a4150;--mute:#525b6d;--violet:#5646d0;--cyan:#095f58;--lime:#356d17;--amber:#7a4c0a;--coral:#9a3829;--sky:#0c60a3;--fill-amber:#f0a93f;--fill-violet:#8f82f5;--fill-cyan:#3cc4b7;--fill-coral:#f38a7b;--fill-lime:#8fd14f;--fill-sky:#3aa9ec;--on-accent:#10131a;--scrim-base:#5d6577;--claude:#d97757;--claude-text:#8a4029;color-scheme:light;}}
html,
body {
  height: 100%;
  margin: 0;
}
html {
  background: var(--ink);
  color: var(--paper);
  -webkit-text-size-adjust: 100%;
}
body {
  overflow: hidden;
  /* The classic's body: grey-scale antialiasing on macOS, so its words are as light as the
     classic's (subpixel drew every line a shade heavier side by side, 2026-09-25). */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
#root {
  display: flex;
  height: 100%;
  flex: 1;
}
/* Nothing of the app shows while the splash is up: the classic's splash is replaced by its page,
   never drawn over it. The splash fades in only after 150 ms (below), so without this what the app
   draws under it (the list before its FrontierScore and usage answers, a thread's placeholder)
   showed through (a cold visit, 2026-09-25). Laid out as ever, so it measures as it will show. */
#splash ~ #root {
  visibility: hidden;
}
/*
 * INCREASE CONTRAST (B15, KI-066; the classic's `prefers-contrast: more` block): the quiet tier
 * reads as the secondary one, hairlines as edges — Tokens v2's increaseContrast(), for the shell's
 * own properties and for Unistyles' (its themes on <html>, in the scheme's media queries too, so
 * the selector outweighs each), with the quiet tier's tints following it. test/contrast.test.ts
 * holds this block to the package's derivation.
 */
@media (prefers-contrast: more) {
  :root:root:root {
    --mute: var(--dim);
    --rule-2: var(--edge);
    --colour-mute: var(--colour-dim);
    --colour-rule2: var(--colour-edge);
    --tints-mute-a1: color-mix(in srgb, var(--colour-dim) 6%, transparent);
    --tints-mute-a2: color-mix(in srgb, var(--colour-dim) 14%, transparent);
    --tints-mute-a3: color-mix(in srgb, var(--colour-dim) 40%, transparent);
    --tints-mute-a4: color-mix(in srgb, var(--colour-dim) 65%, transparent);
  }
}
/*
 * THE KEYBOARD (B7, the classic's `.app` under `data-kb="up"`): platform/viewport.web.ts measures
 * the visible area; while a keyboard is up the app is sized to it, so the composer rides on top of
 * the keyboard and the header stays at the top of what can be seen.
 */
:root[data-kb="up"] #root {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--vv-top);
  height: var(--vv-height);
}
/*
 * BETWEEN THE LIST AND A CONVERSATION (B8, the classic's view transitions; features/nav/slide.web.ts
 * starts them): the page slides, the top bar stays put. Into a conversation from the right, back
 * from the left. Movement only, never a fade: two half-faded screens let what is behind them show
 * through — white, on iOS, coming back from a session (the owner, 2026-09-24). The incoming screen
 * covers the outgoing one; everything underneath is the app's own ink.
 */
[data-testid="list-bar"],
[data-testid="thread-head"],
[data-testid="usage-bar"],
[data-testid="settings-bar"] {
  view-transition-name: topbar;
}
/* F114: Send dims at rest (nothing to send) and comes up with the first character, softly. */
[data-testid="send"],
[data-testid="stop"] {
  transition: opacity 160ms ease-out;
}
@media (prefers-reduced-motion: reduce) {
  [data-testid="send"],
  [data-testid="stop"] {
    transition: none;
  }
}
/* Presence.web.tsx (F32, UXT-1): a pinned bar coming fades in, one going fades out holding its
   last words, turned back from where it is if something new comes; instant under Reduce Motion. */
[data-presence="out"] {
  opacity: 0;
}
@media (prefers-reduced-motion: no-preference) {
  [data-presence="in"],
  [data-presence="out"] {
    transition: opacity 0.18s ease-out;
  }
  [data-presence="in"] {
    @starting-style {
      opacity: 0;
    }
  }
}
@media (prefers-reduced-motion: no-preference) {
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 0.22s;
    animation-timing-function: cubic-bezier(0.22, 0.8, 0.24, 1);
    mix-blend-mode: normal;
  }
  :root[data-nav="forward"]::view-transition-old(root) {
    animation-name: vt-out-left;
  }
  :root[data-nav="forward"]::view-transition-new(root) {
    animation-name: vt-in-right;
  }
  :root[data-nav="back"]::view-transition-old(root) {
    animation-name: vt-out-right;
  }
  :root[data-nav="back"]::view-transition-new(root) {
    animation-name: vt-in-left;
  }
  ::view-transition-old(topbar),
  ::view-transition-new(topbar) {
    animation-duration: 0.22s;
  }
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
::view-transition,
::view-transition-group(root),
::view-transition-image-pair(root),
::view-transition-old(root),
::view-transition-new(root) {
  background: var(--ink);
}
::view-transition-new(root) {
  box-shadow: -18px 0 30px rgba(0, 0, 0, 0.45);
}
:root[data-nav="back"]::view-transition-old(root) {
  z-index: 2;
  box-shadow: -18px 0 30px rgba(0, 0, 0, 0.45);
}
:root[data-theme="light"]::view-transition-new(root),
:root[data-theme="light"][data-nav="back"]::view-transition-old(root) {
  box-shadow: -18px 0 30px rgba(40, 50, 70, 0.16);
}
@keyframes vt-in-right {
  from {
    transform: translateX(100%);
  }
}
@keyframes vt-out-left {
  to {
    transform: translateX(-24%);
  }
}
@keyframes vt-in-left {
  from {
    transform: translateX(-24%);
  }
}
@keyframes vt-out-right {
  to {
    transform: translateX(100%);
  }
}
/*
 * THE SPLASH (S27, the classic's index.html and styles.css `.splash`): on the theme's own ink from
 * the first frame (theme.js set <html data-theme> before this sheet applied), the mark assembles —
 * each path drawn in, then settled to dim rails while the light runs along them — above the
 * wordmark, until the app draws and removes it.
 */
#splash {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 18px;
  background: radial-gradient(
      40% 32% at 50% 46%,
      color-mix(in srgb, var(--fill-violet) 14%, transparent),
      transparent 70%
    ),
    radial-gradient(circle, color-mix(in srgb, var(--mute) 14%, transparent) 1px, transparent 1.5px)
    0 0 / 28px 28px, var(--ink);
  z-index: 1;
  /* The classic's `.splash`: it fades in only after 150 ms (--dur-fast), over 220 (--dur), so a
     page that draws sooner never shows it — its cold visit goes from ink straight to the list.
     Shown at once, ours was the frame the classic never has (2026-09-25). */
  animation: splash-in 0.22s cubic-bezier(0.22, 0.8, 0.24, 1) 0.15s both;
}
/* F27: after a reload whose page already showed the splash (update.web.ts hardReload), it is up
   from the first frame, so the mark carries on from one page to the next. */
[data-reloading] #splash {
  animation: none;
}
/* …and on the page going (splashAgain): it fades in at once, over the page, which stays drawn
   under it until the next one replaces them both. */
#splash.again {
  animation-delay: 0s;
}
#splash.again ~ #root {
  visibility: visible;
}
@keyframes splash-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
#splash .bm .bm-rail {
  opacity: 1;
  stroke-dasharray: 1 1;
  stroke-dashoffset: 1;
  animation: bm-draw 0.8s cubic-bezier(0.22, 0.8, 0.24, 1) forwards, bm-settle 0.8s
    cubic-bezier(0.22, 0.8, 0.24, 1) 1.6s forwards;
}
#splash .bm .bm-rail.bm-violet,
#splash .bm .bm-rail.bm-lime {
  animation-delay: 0.32s, 1.2s;
}
#splash .bm .bm-rail.bm-cyan {
  animation-delay: 0.46s, 1.2s;
}
#splash .bm .bm-flow {
  animation-delay: 1.2s;
}
#splash .bm .bm-flow.bm-violet,
#splash .bm .bm-flow.bm-lime {
  animation-delay: 1.5s;
}
#splash .bm .bm-flow.bm-cyan {
  animation-delay: 1.62s;
}
#splash .bm-node,
#splash .bm-ring {
  animation-delay: 1.2s;
}
.splash-wm {
  font: 600 18px / 1 "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  letter-spacing: -0.02em;
  color: var(--paper);
}
.splash-wm em {
  font-style: normal;
  color: var(--amber);
}
@keyframes bm-draw {
  to {
    stroke-dashoffset: 0;
  }
}
@keyframes bm-settle {
  to {
    opacity: 0.32;
  }
}

/*
 * THE MARK, ALIVE (the classic's mark.ts and styles.css `.bm`; ui/Mark.web.tsx draws the paths):
 * dim rails, a short streak of light down each path in turn, the node igniting as it lands, on the
 * one tempo (1.6 s). `.idle` is the whole mark, still. Under Reduce Motion nothing runs.
 */
.bm {
  flex: none;
  display: block;
  overflow: visible;
}
.bm .bm-rail {
  opacity: 0.3;
}
.bm-tile {
  fill: var(--ink);
}
/* The app icon's form (the classic's MARK_SVG, the hero's): the dark palette in either theme. */
.bm-icon .bm-tile {
  fill: #07080c;
}
.bm-icon .bm-paper {
  stroke: #eef1f6;
}
.bm-icon .bm-violet {
  stroke: #8b7cff;
}
.bm-icon .bm-lime {
  stroke: #a6f25c;
}
.bm-icon .bm-cyan {
  stroke: #4de1d2;
}
.bm-icon .bm-node {
  fill: #ffb547;
}
.bm-paper {
  stroke: var(--paper);
}
.bm-violet {
  stroke: var(--violet);
}
.bm-lime {
  stroke: var(--lime);
}
.bm-cyan {
  stroke: var(--cyan);
}
.bm .bm-flow {
  stroke-dasharray: 0.34 1.4;
  stroke-dashoffset: 0.34;
  animation: bm-flow 1.6s cubic-bezier(0.45, 0, 0.3, 1) infinite;
}
.bm .bm-flow.bm-violet,
.bm .bm-flow.bm-lime {
  animation-delay: 0.3s;
}
.bm .bm-flow.bm-cyan {
  animation-delay: 0.42s;
}
.bm-node {
  fill: var(--amber);
  transform-box: fill-box;
  transform-origin: center;
  animation: bm-ignite 1.6s cubic-bezier(0.22, 0.8, 0.24, 1) infinite;
}
.bm-ring {
  fill: none;
  stroke: var(--amber);
  stroke-width: 1.2;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
  animation: bm-ring 1.6s cubic-bezier(0.22, 0.8, 0.24, 1) infinite;
}
.bm.idle .bm-rail {
  opacity: 1;
}
.bm.idle .bm-flow,
.bm.idle .bm-ring {
  animation: none;
  opacity: 0;
}
.bm.idle .bm-node {
  animation: none;
}
@keyframes bm-flow {
  0% {
    stroke-dashoffset: 0.34;
  }
  62%,
  100% {
    stroke-dashoffset: -1;
  }
}
@keyframes bm-ignite {
  0%,
  52% {
    transform: scale(0.9);
  }
  64% {
    transform: scale(1.28);
  }
  82%,
  100% {
    transform: scale(1);
  }
}
@keyframes bm-ring {
  0%,
  54% {
    opacity: 0;
    transform: scale(1);
  }
  58% {
    opacity: 0.9;
  }
  100% {
    opacity: 0;
    transform: scale(2.8);
  }
}
@media (prefers-reduced-motion: reduce) {
  .bm .bm-flow,
  .bm .bm-ring {
    display: none;
  }
  .bm .bm-rail,
  #splash .bm .bm-rail {
    opacity: 1;
    stroke-dashoffset: 0;
    animation: none;
  }
  .bm-node {
    animation: none;
  }
}
.boot-stuck {
  margin-top: 16px;
  font: 14px / 1.4 "Inter", system-ui, sans-serif;
  color: var(--dim);
  text-align: center;
}
.boot-stuck button {
  margin-top: 10px;
  font: inherit;
  padding: 8px 16px;
  border-radius: 10px;
  border: 1px solid var(--edge);
  background: var(--surface-2);
  color: var(--paper);
}

/*
 * THE DOT, THE SHEET AND THE TOAST ON THE WEB (ui/Dot.web.tsx, Sheet.web.tsx, Toast.web.tsx): the
 * classic's own CSS, so the web draws them with no Animated. Colours are the theme's custom
 * properties; every motion stops under Reduce Motion.
 */
.dot {
  display: block;
  flex: none;
  border-radius: 50%;
  background: var(--edge);
}
/* DDW-16: a glow is the fill's hue (in light the text accents are deepened to browns and bricks). */
.dot.cyan {
  background: var(--cyan);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fill-cyan) 14%, transparent);
}
.dot.violet {
  background: var(--violet);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fill-violet) 14%, transparent);
}
.dot.amber {
  background: var(--amber);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fill-amber) 14%, transparent);
}
.dot.hollow {
  background: transparent;
  box-shadow: inset 0 0 0 1.5px var(--edge);
}
.dot.breathe {
  animation: dot-breathe 1.6s ease-in-out infinite;
}
@keyframes dot-breathe {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}
.sh-layer {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
}
.sh-scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 62%, transparent);
  animation: sh-fade 0.22s cubic-bezier(0.22, 0.8, 0.24, 1);
}
.sh-panel {
  position: absolute;
  /* The classic's every box is border-box (its `*` rule): its 88dvh and 560 px include the
     border. Content-box made ours 2 px taller and wider (the parity run, 2026-09-25). */
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--surface);
  border: 0 solid var(--rule-2);
}
.sh-panel.bottom {
  left: 0;
  right: 0;
  bottom: 0;
  max-width: 560px;
  max-height: 88dvh;
  margin: 0 auto;
  border-width: 1px 1px 0;
  border-radius: 16px 16px 0 0;
  padding-bottom: env(safe-area-inset-bottom);
  animation: sh-rise 0.22s cubic-bezier(0.22, 0.8, 0.24, 1);
}
/* The classic's hour pickers (`.set select.field`), the notifications sheet's quiet hours. */
select.field {
  flex: none;
  padding: 6px 8px;
  border-radius: 10px;
  background: var(--surface-2);
  border: 1px solid var(--edge);
  font: 500 12.5px / 1 "JetBrains Mono", ui-monospace, monospace;
  color: var(--paper);
}
.sh-panel.center {
  left: 0;
  right: 0;
  top: 50%;
  max-width: 560px;
  max-height: 88dvh;
  margin: 0 auto;
  border-width: 1px;
  border-radius: 16px;
  transform: translateY(-50%);
  animation: sh-fade 0.22s cubic-bezier(0.22, 0.8, 0.24, 1);
}
.sh-panel.right,
.sh-panel.left {
  top: 0;
  bottom: 0;
  padding: env(safe-area-inset-top) 0 env(safe-area-inset-bottom);
}
.sh-panel.right {
  right: 0;
  border-left-width: 1px;
  animation: sh-from-right 0.22s cubic-bezier(0.22, 0.8, 0.24, 1);
}
.sh-panel.left {
  left: 0;
  border-right-width: 1px;
  animation: sh-from-left 0.22s cubic-bezier(0.22, 0.8, 0.24, 1);
}
/* A menu (the account's, F33): a small card under its button, grown from its top right corner,
   over a lighter scrim. Its top and left come from the button (Sheet.web.tsx). */
.sh-layer:has(> .sh-panel.menu) > .sh-scrim {
  background: color-mix(in srgb, var(--ink) 30%, transparent);
}
.sh-panel.menu {
  width: 248px;
  border-width: 1px;
  border-radius: 12px;
  padding: 4px 0;
  transform-origin: top right;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
  animation: sh-grow 0.16s cubic-bezier(0.22, 0.8, 0.24, 1);
}
html[data-theme="light"] .sh-panel.menu {
  box-shadow: 0 10px 30px rgba(40, 50, 70, 0.18);
}
@keyframes sh-grow {
  from {
    opacity: 0;
    transform: scale(0.94);
  }
}
@keyframes sh-fade {
  from {
    opacity: 0;
  }
}
@keyframes sh-rise {
  from {
    opacity: 0;
    transform: translateY(40px);
  }
}
@keyframes sh-from-right {
  from {
    opacity: 0;
    transform: translateX(40px);
  }
}
@keyframes sh-from-left {
  from {
    opacity: 0;
    transform: translateX(-40px);
  }
}
.toast {
  position: fixed;
  left: 50%;
  bottom: calc(96px + env(safe-area-inset-bottom));
  z-index: 60;
  max-width: calc(100% - 32px);
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--rule-2);
  background: var(--surface-2);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  font: 400 12.5px / 18px "Inter", ui-sans-serif, system-ui, sans-serif;
  color: var(--paper);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, 6px);
  transition: opacity 0.22s cubic-bezier(0.22, 0.8, 0.24, 1), transform 0.22s
    cubic-bezier(0.22, 0.8, 0.24, 1);
}
html[data-theme="light"] .toast {
  box-shadow: 0 8px 24px rgba(40, 50, 70, 0.18);
}
.toast.on {
  opacity: 1;
  transform: translate(-50%, 0);
}
/* ADR-140: a word with an action ("Archived · Undo"): the pointer is its while it shows. */
.toast.act {
  pointer-events: auto;
}
.toast-act {
  margin: -6px -8px -6px 10px;
  padding: 6px 8px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  font: 600 12.5px / 18px "Inter", ui-sans-serif, system-ui, sans-serif;
  color: var(--cyan);
  cursor: pointer;
}
.toast-act:hover {
  background: color-mix(in srgb, var(--paper) 8%, transparent);
}
/* ADR-115 §17: a caught error's line under the words (what threw, where, which build). */
.toast-detail {
  display: block;
  margin-top: 2px;
  font: 400 10.5px / 15px ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color: var(--mute);
  overflow-wrap: anywhere;
}
.toast:has(.toast-detail) {
  border-radius: 14px;
}
/* F119: with a keyboard up, 96 px above its top — the visible area's bottom (viewport.web.ts's
   --vv-top + --vv-height), not the layout's, which iOS leaves under the keyboard. */
html[data-kb="up"] .toast {
  bottom: auto;
  top: calc(var(--vv-top, 0px) + var(--vv-height, 100vh) - 96px);
  transform: translate(-50%, calc(6px - 100%));
}
html[data-kb="up"] .toast.on {
  transform: translate(-50%, -100%);
}
@media (prefers-reduced-motion: reduce) {
  .dot.breathe,
  .sh-scrim,
  .sh-panel {
    animation: none;
  }
  .toast {
    transition: none;
  }
}

/* The thread: a card brought into view (the classic's dk-flash): a 2 px violet ring at a4 that
   fades over the beat, from the moment it is drawn (features/thread/cells/Ring.web.tsx). */
.thread-ring {
  position: absolute;
  inset: -2px -4px;
  border: 2px solid color-mix(in srgb, var(--fill-violet) 65%, transparent);
  border-radius: 12px;
  pointer-events: none;
  animation: thread-ring 1.6s ease-in forwards;
}
@keyframes thread-ring {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
@media (prefers-reduced-motion: reduce) {
  .thread-ring {
    animation-duration: 0.01s;
    animation-delay: 1.2s;
  }
}

/* The classic's .breathe: the status chip while a turn works or asks. */
[data-breathe="1"] {
  animation: breathe 1.6s ease-in-out infinite;
}
@keyframes breathe {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.35;
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-breathe="1"] {
    animation: none;
  }
}

/* The classic's .chip .spin: the status chip's ring while the connection comes up. */
[data-spin="1"] {
  animation: chip-spin 0.8s linear infinite;
}
@keyframes chip-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ADR-038, the classic's .lrun-bar: a command's bar, filled to how far its output said, or a
   piece sweeping when it did not; still and dim under reduced motion. */
.lrun-bar {
  position: relative;
  display: block;
  height: 4px;
  border-radius: 3px;
  background: var(--rule-2);
  overflow: hidden;
}
.lrun-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  border-radius: 3px;
  background: var(--cyan);
  transition: width 0.4s cubic-bezier(0.22, 0.8, 0.24, 1);
}
.lrun-bar.unknown i {
  width: 28%;
  animation: lrun-sweep 1.6s cubic-bezier(0.22, 0.8, 0.24, 1) infinite;
}
@keyframes lrun-sweep {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(360%);
  }
}
@media (prefers-reduced-motion: reduce) {
  .lrun-bar.unknown i {
    animation: none;
    width: 100%;
    opacity: 0.35;
  }
}

/* The composer's tray: a picture being prepared pulses (the classic's .pic.loading shimmer). */
[data-shimmer="1"] {
  animation: shimmer 1.6s infinite;
}
@keyframes shimmer {
  0%,
  100% {
    opacity: 0.45;
  }
  50% {
    opacity: 0.85;
  }
}

/* The question card (the classic's .ask): it rises into place as it arrives, casts its shadow over
   the thread above the composer (.foot .ask), and shows the amber ring while it has the keys
   (.ask:focus-visible; it takes them as it arrives at a desk). */
[data-ask="1"] {
  animation: ask-rise 0.22s cubic-bezier(0.22, 0.8, 0.24, 1) both;
  box-shadow: 0 -8px 24px color-mix(in srgb, var(--ink) 70%, transparent);
  outline: none;
}
[data-ask="1"]:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
}
@keyframes ask-rise {
  from {
    transform: translateY(8px);
    opacity: 0;
  }
  to {
    transform: none;
    opacity: 1;
  }
}
@media (prefers-reduced-motion: reduce) {
  [data-shimmer="1"],
  [data-ask="1"] {
    animation: none;
  }
}

/* The composer's field (the classic's textarea:focus): the cyan edge alone. WebKit and Chromium
   draw their own ring for outline-style auto whatever its width, so it is none here. */
[data-field="1"],
[data-field="1"]:focus,
[data-field="1"]:focus-visible {
  outline: none;
}
/* Its placeholder is one line, cut with an ellipsis: the hint that says which key sends (F102)
   would wrap in a narrow field. */
[data-field="1"]::placeholder {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The classic's buttons keep the engine's own padding (WebKit 2 6 3, Chromium 1 6), which sets
   their glyph half a pixel off centre in WebKit; react-native-web zeroes it. The header's, the
   composer's and the question card's buttons take it back, so each glyph lands where the
   classic's does in either engine. */
button[data-ua="1"] {
  padding: revert;
}

/* A touch target past what is drawn (ui/aria.ts webHit, DDW-30): react-native-web drops hitSlop, so
   an invisible box reaches that far past each edge; its clicks are the button's. Nothing drawn. */
[data-hit] {
  position: relative;
}
[data-hit]::after {
  content: "";
  position: absolute;
}
[data-hit="4"]::after {
  inset: -4px;
}
[data-hit="12"]::after {
  inset: -12px;
}
[data-hit="4x5"]::after {
  inset: -4px -5px;
}
/* F506: a phone header's 24 px button on the name's line: 14 over, 6 under (44), 2 at the sides. */
[data-hit="pill"]::after {
  inset: -14px -2px -6px;
}

/*
 * AN ANSWER'S MARKDOWN (features/thread/md/Prose.web.tsx): the classic's .prose rules as they are
 * at d374aa1 (apps/remote/src/web/styles.css), on the DOM md.ts builds, so the web draws an answer
 * as the classic does — its sizes written out (--t-body 0.875rem, --t-note 0.78125rem, --t-small
 * 0.71875rem, --t-meta 0.65625rem; --r-s 6px, --r-m 10px, --r-l 12px), with what the classic's page
 * gives it from outside (body's face, box-sizing, its button and link resets) set on .prose.
 */
.prose {
  min-width: 0;
  font-family: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  font-weight: 400;
  font-size: 0.875rem;
  line-height: 21px;
  color: var(--paper);
  overflow-wrap: anywhere;
  word-break: break-word;
  -webkit-font-smoothing: antialiased;
  -webkit-user-select: text;
  user-select: text;
}
.prose *,
.prose *::before,
.prose *::after {
  box-sizing: border-box;
}
.prose.prose-mute {
  color: var(--mute);
}
/* The classic's .think-body: 12.5/18. */
.prose.prose-note {
  font-size: 0.78125rem;
  line-height: 18px;
}
.prose.prose-i {
  font-style: italic;
}
.prose p {
  margin: 0 0 8px;
}
.prose p:last-child {
  margin-bottom: 0;
}
.prose ul,
.prose ol {
  margin: 0 0 8px;
  padding-left: 20px;
}
.prose li {
  margin: 2px 0;
}
.prose li::marker {
  color: var(--mute);
}
.prose a {
  color: var(--cyan);
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* F164: an answer's link ends in a small chain glyph, the link's colour, sat on the text. */
.prose a .lk {
  display: inline-block;
  width: 13px;
  height: 13px;
  margin-left: 3px;
  vertical-align: -2px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.prose strong {
  font-weight: 600;
}
.prose .md-h {
  margin-top: 12px;
}
.prose li.d1 {
  margin-left: 16px;
}
.prose li.d2 {
  margin-left: 32px;
}
.prose li.d3 {
  margin-left: 48px;
}
:where(.prose) button {
  font: inherit;
  color: inherit;
  margin: 0;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}
:where(.prose) :focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 2px;
  border-radius: 10px;
}
.ic {
  font-family: "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  font-size: 0.71875rem;
  padding: 1px 4px;
  border-radius: 6px;
  background: var(--surface-2);
  border: 1px solid var(--rule);
  /* DDW-46: spaces kept, and one at a line's end takes its room instead of hanging past the edge
     (pre-wrap drew the pill 8 px outside the text column at 402). */
  white-space: break-spaces;
  /* DDW-1: a name breaks where a word may (a space, a slash, a hyphen), and inside itself only when
     it is wider than the line: break-all split "summar|y.pdf" at 402 px with room on the next line. */
  overflow-wrap: anywhere;
  word-break: normal;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.md-hr {
  border: 0;
  border-top: 1px solid var(--rule-2);
  margin: 12px 0;
}
.md-quote {
  border-left: 2px solid var(--rule-2);
  padding-left: 10px;
  color: var(--mute);
  margin: 0 0 8px;
}
.md-table {
  max-width: 100%;
  overflow-x: auto;
  margin: 0 0 8px;
  border: 1px solid var(--rule);
  border-radius: 10px;
}
.md-table table {
  /* DDW-28: at least as wide as its frame — a narrow table's row rules stopped short of the box's
     right edge (1280, a five-column table in a 558 px column). */
  min-width: 100%;
  border-collapse: collapse;
  font-size: 0.78125rem;
  line-height: 17px;
}
.md-table th,
.md-table td {
  padding: 5px 8px;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--rule);
  /* Words stay whole in a table: the table scrolls inside its box, the page never does. */
  overflow-wrap: normal;
  word-break: normal;
  max-width: 260px;
}
.md-table .ic,
.md-table [data-nw],
.ic[data-nw] {
  white-space: nowrap;
}
/* DDW-42: in a table that scrolls sideways anyway, a column of short phrases is not squeezed to
   two lines (the rows doubled, every other column blank under its words). Prose.web.tsx sets it. */
.md-table[data-wide] [data-ph] {
  white-space: nowrap;
}
.md-table th {
  font-weight: 600;
  background: var(--surface-2);
}
.md-table tr:last-child td {
  border-bottom: 0;
}
/* A table that mounted while its answer streamed (Prose.web.tsx): each column as wide as its
   widest cell, a cell at most 260 px, never squeezed to the box, so a new row only widens. */
.md-table.grow table {
  width: max-content;
}
.md-table.grow .md-c {
  max-width: 260px;
}
.prose.live > :last-child::after,
.prose.live:empty::after {
  content: "▍";
  color: var(--violet);
  margin-left: 1px;
  animation: breathe 1s steps(2) infinite;
}
/* Every other delta the caret's content differs (by its alternative text, never seen), so the
   engine makes it a new box rather than moving the old one, as the classic's rebuilt DOM does:
   the same caret, and no layout shift for each character it moves along (Chromium). */
.prose.live[data-tick="1"] > :last-child::after,
.prose.live[data-tick="1"]:empty::after {
  content: "▍" / "";
}
/* After a list the caret is a line of its own, as in the classic, but that line also keeps the
   last item's 2 px margin from folding into the list's: the next block took it back (the perf
   corpus, 6 drops of 2 px in both engines). A box 2 px shorter than its line draws the caret where
   the classic does and leaves the list as tall as it will be once the next block comes. As wide
   as the caret, not the line: moved down by each new item, a full-width box was a layout shift
   (Chromium, the perf corpus: 0.29 in the live message). */
.prose.live > :is(ul, ol):last-child::after {
  display: block;
  width: max-content;
  height: 19px;
}
.cb {
  margin: 4px 0 8px;
  max-width: 100%;
  min-width: 0;
  border: 1px solid var(--rule-2);
  border-radius: 12px;
  background: var(--ink);
  overflow: hidden;
}
.cb-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2px 4px 2px 10px;
  font: 500 0.65625rem / 1 "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  color: var(--mute);
  border-bottom: 1px solid var(--rule);
}
.cb-btn {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 30px;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--mute);
}
.cb-pre {
  margin: 0;
  padding: 9px 10px;
  overflow-x: auto;
  font: 400 0.71875rem / 17px "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  white-space: pre;
  -webkit-overflow-scrolling: touch;
}
.cb-more {
  display: block;
  width: 100%;
  padding: 7px;
  border: 0;
  border-top: 1px solid var(--rule);
  background: var(--surface);
  color: var(--cyan);
  font: 500 0.65625rem / 1 "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
}
.hl-k {
  color: var(--violet);
}
.hl-s {
  color: var(--lime);
}
.hl-n {
  color: var(--amber);
}
.hl-c {
  color: var(--mute);
  font-style: italic;
}
@media (prefers-reduced-motion: reduce) {
  .prose.live > :last-child::after,
  .prose.live:empty::after {
    animation: none;
  }
}

/* ?vp=1 (ui/Viewport.web.tsx): the classic's viewport panel, as its styles.css draws it. */
.vp-probe {
  position: fixed;
  visibility: hidden;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom)
    env(safe-area-inset-left);
}
.vp-panel {
  position: fixed;
  left: 8px;
  right: 8px;
  top: 45%;
  z-index: 99;
  margin: 0;
  padding: 10px;
  border-radius: 10px;
  background: color-mix(in srgb, var(--surface-2) 95%, transparent);
  border: 1px solid var(--amber);
  color: var(--amber);
  font: 500 0.71875rem / 18px "JetBrains Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  white-space: pre-wrap;
  pointer-events: none;
}

/* The grabber and the title of a sheet: a finger's swipe down there is the sheet's, not the
   page's (F59, Sheet.web.tsx useDragAway). A column, as a View is: the grip centres itself. */
.sh-handle {
  display: flex;
  flex-direction: column;
  flex: none;
  touch-action: none;
}

/* DDX-7: one keyboard focus ring, the theme's — the composer's own cyan (its field keeps its
   edge alone, above; the question card its amber ring) — not each engine's blue "auto" ring. A tab
   draws it inside itself: the tab row clips what stands outside it. */
:where(
    [role="button"],
    [role="link"],
    [role="tab"],
    [role="switch"],
    [role="checkbox"],
    [role="radio"],
    [role="option"],
    [role="menuitem"],
    [role="slider"],
    a[href],
    button,
    [tabindex]
  ):focus-visible {
  outline: 2px solid var(--colour-cyan);
  outline-offset: 2px;
}
:where([role="tab"]):focus-visible {
  outline-offset: -2px;
}

/* ADR-140: a list row's ⋯ (its menu, list/RowSwipe.web.tsx): under a pointer, shown while the row
   is hovered or holds the focus, and the flags' line keeps room for it. Under a finger (an
   iPhone's or an iPad's browser) never — the owner, 2026-10-07: "hide it on the phone"; a long
   press is the menu there (list/longPress.ts), so Safari's own link preview and selection must
   not take that press. */
@media (hover: hover) and (pointer: fine) {
  [data-row-more] {
    opacity: 0;
  }
  [data-row-wrap]:hover [data-row-more],
  [data-row-wrap]:focus-within [data-row-more] {
    opacity: 1;
  }
  [data-row-wrap] [data-row-flags] {
    padding-right: 26px;
  }
}
@media not all and (hover: hover) and (pointer: fine) {
  /* Two attributes: react-native-web's own display class comes after this sheet and outweighs one. */
  [data-row-wrap] [data-row-more] {
    display: none;
  }
  [data-row-wrap] {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
}

/* DDX-6: a pointer's hover, on everything pressed (the Mac console, a desktop browser, an iPad's
   trackpad in Safari): the ink's own paper washed over it at 5%, as ::after so a card's gradient,
   ring and shadow stay its own. Every react-native-web view is position: relative, so the wash
   covers the element alone, its corners included. Not on a touch screen, whose "hover" sticks. */
@media (hover: hover) and (pointer: fine) {
  :where(
      [role="button"],
      [role="link"],
      [role="tab"],
      [role="switch"],
      [role="checkbox"],
      [role="radio"],
      [role="option"],
      [role="menuitem"],
      button
    ):not([aria-disabled="true"], :disabled):hover::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: color-mix(in srgb, var(--colour-paper) 5%, transparent);
    pointer-events: none;
  }
}
