/* The Workbench — light bg #F3F5F7, ink #16202A, teal accent, amber for you.
   Sora (headings) + IBM Plex Sans (body) + IBM Plex Mono (code).

   Density: the root font size is the one lever that tightens everything at
   once, because nearly every padding below is in rem. 15px on a computer,
   14px on a phone. Anything that still felt loose after that is trimmed by
   hand underneath. */

:root {
  color-scheme: light;
  --bg:         #F3F5F7;
  --surface:    #FFFFFF;
  --surface-2:  #EAEFF2;
  --ink:        #16202A;
  --ink-soft:   #55606B;
  --ink-faint:  #8A96A2;
  --line:       #DDE3E8;
  --line-soft:  #E9EDF1;
  --accent:     #0F7C86;
  --accent-dk:  #0B5D65;
  --accent-wash:#E3F1F2;
  --amber:      #B36B00;
  --amber-wash: #FBF0DE;
  --danger:     #A32C24;
  --good:       #1F7A45;
  --radius:     10px;
  --radius-lg:  12px;
  --shadow:     0 1px 2px rgb(22 32 42 / 6%), 0 8px 24px rgb(22 32 42 / 6%);
  --font-head:  'Sora', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-body:  'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono:  'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  --nav-w:      232px;

  /* Nothing may be drawn under the bar along the bottom of a phone. There is
     exactly one number for how tall that bar is, and every scroll container
     that could reach the bottom of the screen pads itself by it. On a computer
     there is no bar, so it is zero and the same rules cost nothing. */
  --safe-b:     env(safe-area-inset-bottom, 0px);
  --tabbar-h:   0px;
  /* The visible height of the window, kept honest by visualViewport — dvh is
     wrong the moment a soft keyboard is up inside a WebView. */
  --app-h:      100dvh;
}

@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --bg:         #0F151B;
    --surface:    #18212A;
    --surface-2:  #131B22;
    --ink:        #E8EDF2;
    --ink-soft:   #9AA7B4;
    --ink-faint:  #6C7A88;
    --line:       #2A3641;
    --line-soft:  #222C36;
    --accent:     #4FBFC9;
    --accent-dk:  #7FD6DE;
    --accent-wash:#132C30;
    --amber:      #E0A44A;
    --amber-wash: #2C2216;
    --danger:     #E4796F;
    --good:       #6FD39B;
    --shadow:     0 1px 2px rgb(0 0 0 / 30%), 0 10px 30px rgb(0 0 0 / 28%);
  }
}

*, *::before, *::after { box-sizing: border-box; }

html { font-size: 15px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { font-family: var(--font-head); line-height: 1.25; margin: 0 0 .45em; letter-spacing: -0.015em; }
h1 { font-size: 1.35rem; }
h2 { font-size: 1.05rem; }
h3 { font-size: .95rem; }
p  { margin: 0 0 .7em; }
a  { color: var(--accent-dk); }
code, pre, .mono { font-family: var(--font-mono); }
.muted { color: var(--ink-soft); }
.faint { color: var(--ink-faint); }
.small { font-size: .85rem; }
.hidden, [hidden] { display: none !important; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

button { font-family: var(--font-body); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: .4rem; justify-content: center;
  padding: .4rem .85rem; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  font-weight: 600; font-size: .88rem; text-decoration: none; line-height: 1.35;
  transition: background .12s ease, border-color .12s ease;
}
.btn:hover:not(:disabled) { border-color: var(--ink-faint); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.primary:hover:not(:disabled) { background: var(--accent-dk); border-color: var(--accent-dk); }
@media (prefers-color-scheme: dark) { .btn.primary { color: #08171A; } }
.btn.ghost { background: transparent; border-color: transparent; color: var(--ink-soft); }
.btn.ghost:hover:not(:disabled) { background: var(--surface-2); border-color: transparent; }
.btn.danger { color: var(--danger); border-color: var(--line); }
.btn.small { padding: .28rem .65rem; font-size: .8rem; }
.btn.big { padding: .55rem 1.1rem; font-size: .95rem; }
.btn.block { width: 100%; }

/* A square icon button: the ☰, the ⋯, the ＋ next to Chats. One size, so a
   row of them lines up without anybody thinking about it. */
.icon-btn {
  flex: none; display: inline-grid; place-items: center;
  width: 1.9rem; height: 1.9rem; padding: 0; cursor: pointer;
  border: 1px solid transparent; border-radius: 8px;
  background: transparent; color: var(--ink-soft);
  font-size: .95rem; line-height: 1; font-family: var(--font-body);
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }

/* ── Centred card pages (join / locked out) ─────────────────────────────── */
body.centred { display: grid; place-items: center; min-height: 100vh; padding: 1.5rem; }
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: 2rem; max-width: 30rem; text-align: center; box-shadow: var(--shadow);
}
.card .mark { font-size: 2rem; color: var(--accent); margin-bottom: .6rem; }
.card p { color: var(--ink-soft); }

/* ── App shell ──────────────────────────────────────────────────────────── */
.app { display: grid; grid-template-columns: var(--nav-w) 1fr; min-height: 100vh; }

.sidebar {
  border-right: 1px solid var(--line); background: var(--surface-2);
  display: flex; flex-direction: column; gap: .2rem;
  padding: .55rem .5rem calc(.55rem + var(--safe-b));
  position: sticky; top: 0; height: 100vh; height: var(--app-h);
  min-height: 0; overflow: hidden;
}
.side-head { display: flex; align-items: center; gap: .3rem; padding: .1rem .1rem .35rem .25rem; }
.brand {
  display: flex; align-items: center; gap: .4rem; min-width: 0; flex: 1;
  font-family: var(--font-head); font-weight: 700; font-size: .92rem; letter-spacing: -0.02em;
}
.brand .mark { color: var(--accent); font-size: 1rem; flex: none; }
.brand-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.project-switch { position: relative; display: flex; align-items: center; margin-bottom: .45rem; }
.project-switch select {
  width: 100%; appearance: none; font: inherit; font-weight: 600; font-size: .88rem;
  padding: .4rem 1.7rem .4rem .55rem; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
  cursor: pointer; text-overflow: ellipsis;
}
.project-switch::after {
  content: '⌄'; position: absolute; right: .6rem; top: .05rem;
  color: var(--ink-faint); pointer-events: none; font-size: .95rem;
}

.side-nav { display: flex; flex-direction: column; gap: .05rem; }
.nav-item {
  display: flex; align-items: center; gap: .55rem; width: 100%;
  padding: .35rem .55rem; border-radius: 8px; border: none;
  background: transparent; color: var(--ink-soft); cursor: pointer;
  font-size: .88rem; font-weight: 500; text-align: left;
}
.nav-item .ic { width: 1.05rem; text-align: center; flex: none; color: var(--ink-faint); }
.nav-item .lbl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-item:hover { background: var(--surface); color: var(--ink); }
.nav-item.active { background: var(--accent-wash); color: var(--accent-dk); font-weight: 600; }
.nav-item.active .ic { color: var(--accent-dk); }

/* ── The chats rail ─────────────────────────────────────────────────────────
   Every conversation in this project, newest at the top, the way every other
   assistant does it. It is the only list that grows, so it is the only thing
   in the rail that scrolls. */
.side-chats {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  margin-top: .55rem; padding-top: .45rem; border-top: 1px solid var(--line);
}
.side-section-head {
  display: flex; align-items: center; gap: .3rem; padding: 0 .2rem .2rem .55rem;
}
.side-section-title {
  flex: 1; font-size: .68rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint);
}
.chat-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; }
.chat-row {
  display: flex; align-items: center; gap: .1rem; border-radius: 8px;
  padding-right: .1rem;
}
.chat-row:hover { background: var(--surface); }
.chat-row.on { background: var(--accent-wash); }
.chat-open {
  flex: 1; min-width: 0; border: 0; background: transparent; cursor: pointer;
  text-align: left; padding: .35rem .5rem; font: inherit; font-size: .85rem;
  color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border-radius: 8px;
}
.chat-row.on .chat-open { color: var(--accent-dk); font-weight: 600; }
.chat-row:hover .chat-open { color: var(--ink); }
.chat-act {
  flex: none; width: 1.5rem; height: 1.5rem; display: grid; place-items: center;
  border: 0; background: transparent; color: var(--ink-faint); cursor: pointer;
  border-radius: 6px; font-size: .78rem; line-height: 1; opacity: 0;
  font-family: var(--font-body);
}
.chat-row:hover .chat-act, .chat-row:focus-within .chat-act { opacity: 1; }
.chat-act:hover { background: var(--surface-2); color: var(--ink); }
.chat-act:disabled { opacity: 0 !important; pointer-events: none; }
/* No hover on a touch screen, so the two controls are simply always there. */
@media (hover: none) { .chat-act { opacity: 1; } }
.chat-empty { padding: .3rem .55rem; font-size: .8rem; color: var(--ink-faint); }

.side-foot { flex: none; padding-top: .45rem; border-top: 1px solid var(--line); margin-top: .45rem; }

.usage-chip {
  margin-top: .45rem; padding: .4rem .5rem; border-radius: 8px;
  background: var(--surface); border: 1px solid var(--line); font-size: .76rem; color: var(--ink-soft);
}
.usage-chip .meter { height: 3px; border-radius: 2px; background: var(--line); margin-top: .35rem; overflow: hidden; }
.usage-chip .meter i { display: block; height: 100%; background: var(--accent); }
.usage-chip.warn .meter i { background: var(--amber); }
.usage-chip.over .meter i, .usage-chip.off .meter i { background: var(--danger); }

/* ── Collapsed rail (a computer) ────────────────────────────────────────────
   Icons only, and the two lists that need words disappear rather than being
   squeezed into three letters. */
body.side-collapsed { --nav-w: 3.4rem; }
body.side-collapsed .sidebar { padding-left: .35rem; padding-right: .35rem; align-items: stretch; }
body.side-collapsed .brand-name,
body.side-collapsed .project-switch,
body.side-collapsed .side-chats,
body.side-collapsed .side-foot,
body.side-collapsed .nav-item .lbl { display: none; }
/* Collapsed, the ⟩ takes the brand's place rather than hiding until hovered:
   a way back that is only there once you have found it is not a way back. */
body.side-collapsed .side-head { justify-content: center; padding: .1rem 0 .4rem; }
body.side-collapsed .brand { display: none; }
body.side-collapsed .nav-item { justify-content: center; padding: .45rem 0; }
body.side-collapsed .nav-item .ic { width: auto; font-size: 1.05rem; }

.scrim { display: none; }

/* ── Main pane ──────────────────────────────────────────────────────────── */
.main { display: flex; flex-direction: column; min-width: 0; }

/* One bar. The tab's own controls, whatever they are, and nothing that merely
   repeats the name of the tab you just pressed. */
.topbar {
  display: flex; align-items: center; gap: .4rem;
  padding: .3rem .6rem .3rem clamp(.6rem, 1.5vw, 1rem);
  min-height: 2.5rem;
  border-bottom: 1px solid var(--line); background: var(--bg);
  position: sticky; top: 0; z-index: 5;
}
.topbar .grow { flex: 1; }
.tab-lead { display: flex; align-items: center; gap: .4rem; min-width: 0; }
.tab-name {
  font-family: var(--font-head); font-weight: 600; font-size: .95rem;
  letter-spacing: -0.01em; white-space: nowrap;
}
.crumb {
  display: none; align-items: center; gap: .25rem; min-width: 0; max-width: 12rem;
  border: 0; background: transparent; cursor: pointer; padding: .2rem .2rem;
  font-family: var(--font-head); font-weight: 600; font-size: .92rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.drawer-btn { display: none; }
#tabActions { display: flex; align-items: center; gap: .35rem; justify-content: flex-end; min-width: 0; }
.pane { padding: clamp(.8rem, 2vw, 1.4rem); flex: 1; min-height: 0; }
.pane.flush { padding: 0; display: flex; flex-direction: column; }

.notice {
  display: flex; gap: .55rem; align-items: flex-start;
  padding: .6rem .75rem; border-radius: var(--radius); font-size: .88rem;
  background: var(--amber-wash); border: 1px solid color-mix(in srgb, var(--amber) 35%, transparent);
  color: var(--ink); margin-bottom: .8rem;
}
.notice.good { background: var(--accent-wash); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }
.notice.bad  { background: color-mix(in srgb, var(--danger) 12%, var(--surface)); border-color: color-mix(in srgb, var(--danger) 35%, transparent); }
.notice .ic { font-size: 1rem; line-height: 1.4; }
.notice p:last-child { margin-bottom: 0; }

/* ── Build (chat) ───────────────────────────────────────────────────────── */
.chat {
  flex: 1; overflow-y: auto; padding: clamp(.7rem, 2vw, 1.2rem);
  display: flex; flex-direction: column; gap: .55rem;
  overscroll-behavior: contain;
}
.chat-inner { max-width: 48rem; width: 100%; margin: 0 auto; display: flex; flex-direction: column; gap: .55rem; }

.bubble {
  padding: .5rem .7rem; border-radius: 10px; max-width: 48rem;
  border: 1px solid var(--line); background: var(--surface);
  white-space: pre-wrap; overflow-wrap: anywhere; font-size: .92rem; line-height: 1.5;
}
.bubble.you {
  align-self: flex-end; max-width: 40rem; background: var(--amber-wash);
  border-color: color-mix(in srgb, var(--amber) 30%, transparent);
}
.bubble.you .who { color: var(--amber); }
.bubble .who {
  display: block; font-family: var(--font-head); font-size: .66rem; font-weight: 600;
  text-transform: uppercase; letter-spacing: .09em; color: var(--accent-dk); margin-bottom: .2rem;
}
.bubble.system { background: var(--surface-2); }
.bubble.system .who { color: var(--danger); }

.activity {
  align-self: flex-start; display: flex; align-items: center; gap: .5rem;
  color: var(--ink-soft); font-size: .85rem; padding: 0 .2rem;
}
.activity .spinner {
  width: 11px; height: 11px; border-radius: 50%; flex: none;
  border: 2px solid var(--line); border-top-color: var(--accent);
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .activity .spinner { animation-duration: 2.5s; } }
.activity-log { display: flex; flex-direction: column; gap: .05rem; }
.activity-log .past { color: var(--ink-faint); font-size: .8rem; padding: 0 .2rem; }

.composer {
  flex: none;
  border-top: 1px solid var(--line); background: var(--bg);
  padding: .5rem clamp(.7rem, 2vw, 1.2rem) calc(.55rem + var(--tabbar-h));
}
.composer-inner { max-width: 48rem; margin: 0 auto; display: flex; gap: .4rem; align-items: flex-end; }
.composer textarea {
  flex: 1; resize: none; font: inherit; font-size: .92rem; color: var(--ink);
  padding: .45rem .6rem; border-radius: 9px; min-height: 2.2rem; max-height: 10rem;
  border: 1px solid var(--line); background: var(--surface); line-height: 1.45;
}
.composer .hint {
  max-width: 48rem; margin: .35rem auto 0; font-size: .75rem; color: var(--ink-faint);
  display: flex; align-items: center; gap: .5rem;
}
.composer .hint > span { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* The way back to the model picker from where you are typing. It says which
   mind is building; pressing it opens the rail the picker lives in. */
.model-chip {
  flex: none; max-width: 11rem; display: inline-flex; align-items: center; gap: .25rem;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
  border-radius: 999px; padding: .12rem .5rem; font: inherit; font-size: .72rem;
  cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.model-chip:hover { color: var(--ink); border-color: var(--ink-faint); }
.model-chip .ic { font-size: .7rem; color: var(--ink-faint); flex: none; }
.model-chip-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── What builds this project ────────────────────────────────────────────────
   A popover on a computer, pinned above the chip it came from; a sheet along
   the bottom on a phone. One <dialog>, so Escape, the backdrop and the focus
   trap are the browser's job — only the box moves. */
dialog.model-sheet {
  position: fixed; margin: 0; padding: 0; right: auto; top: auto;
  width: min(26rem, calc(100vw - 1.5rem));
  max-height: min(78vh, 34rem); overflow: auto; overscroll-behavior: contain;
  border-radius: var(--radius-lg);
}
dialog.model-sheet.as-sheet {
  left: 0; right: 0; bottom: 0; top: auto; width: 100%; max-width: none;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0; border-bottom: 0;
  max-height: 82vh; padding-bottom: var(--safe-b);
}

.model-sheet-head {
  position: sticky; top: 0; z-index: 1; display: flex; align-items: center; gap: .5rem;
  padding: .75rem .85rem .5rem; background: var(--surface); border-bottom: 1px solid var(--line-soft);
}
.model-sheet-head h2 {
  margin: 0; flex: 1; font-family: var(--font-head); font-size: .95rem; font-weight: 600;
}

.model-group { padding: .7rem .85rem .2rem; }
.model-group + .model-group { border-top: 1px solid var(--line); margin-top: .5rem; }
.model-group-head { margin-bottom: .45rem; }
.model-group-title {
  display: flex; align-items: center; gap: .4rem;
  font-family: var(--font-head); font-size: .74rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .08em; color: var(--ink);
}
.model-group-title .ic { color: var(--accent); font-size: .85rem; }
.model-group-blurb { margin: .18rem 0 0; font-size: .78rem; color: var(--ink-soft); line-height: 1.45; }
.model-group-off {
  margin: .1rem 0 .5rem; font-size: .8rem; color: var(--ink-faint);
  background: var(--surface-2); border: 1px solid var(--line-soft);
  border-radius: var(--radius); padding: .45rem .55rem;
}
.model-provider {
  margin: .6rem 0 .2rem; font-size: .7rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-faint);
}

.model-row { display: flex; align-items: center; gap: .3rem; border-radius: 9px; }
.model-row:hover:not(.off) { background: var(--surface-2); }
.model-row.on { background: var(--accent-wash); }
.model-row-btn {
  flex: 1; min-width: 0; display: flex; align-items: flex-start; gap: .45rem;
  border: 0; background: transparent; cursor: pointer; text-align: left;
  padding: .45rem .5rem; border-radius: 9px; font: inherit; color: var(--ink);
}
.model-row-btn:disabled { cursor: default; color: var(--ink-faint); }
.model-row-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.model-tick { flex: none; width: .95rem; color: var(--accent-dk); font-size: .8rem; line-height: 1.35; }
.model-row-text { min-width: 0; display: flex; flex-direction: column; gap: .08rem; }
.model-row-name { font-size: .87rem; font-weight: 600; line-height: 1.3; }
.model-row.on .model-row-name { color: var(--accent-dk); }
.model-row-meta { display: flex; align-items: center; gap: .35rem; font-size: .76rem; color: var(--ink-soft); }
.model-row.off .model-row-meta { color: var(--ink-faint); }
.model-dot { width: .5rem; height: .5rem; border-radius: 50%; flex: none; background: var(--ink-faint); }
.model-dot.on { background: var(--good); }
.model-dot.off { background: var(--ink-faint); }
.model-row-link {
  flex: none; margin-right: .45rem; border: 1px solid var(--line); background: var(--surface);
  color: var(--accent-dk); border-radius: 999px; padding: .18rem .6rem;
  font: inherit; font-size: .74rem; font-weight: 600; cursor: pointer;
}
.model-row-link:hover { border-color: var(--accent); }

.prompt-chips { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .5rem; }
.chip {
  padding: .25rem .65rem; border-radius: 999px; border: 1px solid var(--line);
  background: var(--surface); color: var(--ink-soft); font-size: .8rem; cursor: pointer;
  font-family: var(--font-body);
}
.chip:hover { color: var(--ink); border-color: var(--ink-faint); }

/* ── Preview ────────────────────────────────────────────────────────────── */
.preview-body { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.preview-frame {
  flex: 1; width: 100%; border: 0; background: var(--surface);
  border-top: 1px solid var(--line); min-height: 50vh;
}

/* The bar above the preview: is it running, what am I looking at, turn it
   on and off. Everything a person can do to a running project lives here. */
.runbar {
  display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
  padding: .35rem clamp(.7rem, 2vw, 1.2rem); border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.runbar .grow { flex: 1; }
.run-state { display: inline-flex; align-items: center; gap: .4rem; font-size: .82rem; font-weight: 600; }
.run-state .dot { width: .5rem; height: .5rem; border-radius: 50%; background: var(--ink-faint); flex: none; }
.run-state.good .dot { background: var(--good); }
.run-state.warn .dot { background: var(--amber); animation: pulse 1.4s ease-in-out infinite; }
.run-state.bad  .dot { background: var(--danger); }
.run-state.off  .dot { background: var(--ink-faint); }
@keyframes pulse { 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .run-state.warn .dot { animation: none; } }
.run-why {
  font-size: .8rem; color: var(--ink-soft); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 24rem;
}

.segmented { display: inline-flex; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); overflow: hidden; }
.segmented button {
  border: 0; background: transparent; cursor: pointer; color: var(--ink-soft);
  padding: .22rem .7rem; font-size: .78rem; font-weight: 600; font-family: var(--font-body);
}
.segmented button.on { background: var(--accent-wash); color: var(--accent-dk); }

/* The console: what a running project is saying. Not a terminal — no prompt,
   nothing to type, and a timestamp on every line so it reads as a record. */
.console {
  flex: 1; min-height: 30vh; overflow: auto; margin: 0 clamp(.8rem, 2vw, 1.4rem) clamp(.8rem, 2vw, 1.4rem);
  padding: .6rem .7rem; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); font-family: var(--font-mono); font-size: .78rem; line-height: 1.55;
}
.console-lede { margin: .7rem clamp(.8rem, 2vw, 1.4rem) .5rem; max-width: 46rem; }
.console-line { display: flex; gap: .7rem; }
.console-line .ts { color: var(--ink-faint); flex: none; font-variant-numeric: tabular-nums; }
.console-line .txt { color: var(--ink-soft); white-space: pre-wrap; overflow-wrap: anywhere; }

/* A public address, made easy to copy and hard to mistype. */
.live-url {
  display: flex; align-items: center; gap: .5rem; flex-wrap: wrap;
  padding: .45rem .6rem; margin-bottom: .55rem; border-radius: var(--radius);
  background: var(--surface-2); border: 1px solid var(--line);
}
.live-url a { font-family: var(--font-mono); font-size: .8rem; overflow-wrap: anywhere; flex: 1; }
.mono { font-family: var(--font-mono); }
.log {
  font-family: var(--font-mono); font-size: .76rem; color: var(--ink-soft);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--radius);
  padding: .6rem; max-height: 14rem; overflow: auto; white-space: pre-wrap;
}

/* ── Lists / rows ───────────────────────────────────────────────────────── */
.list { display: flex; flex-direction: column; gap: .35rem; max-width: 48rem; }
.row {
  display: flex; align-items: center; gap: .7rem; padding: .5rem .7rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
}
.row .grow { flex: 1; min-width: 0; }
.row .title { font-weight: 600; font-size: .9rem; }
.row .sub { font-size: .78rem; color: var(--ink-faint); }
.row.current { border-color: var(--accent); background: var(--accent-wash); }
.badge {
  font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  padding: .12rem .45rem; border-radius: 999px; background: var(--accent); color: #fff;
}
@media (prefers-color-scheme: dark) { .badge { color: #08171A; } }

/* ── Home: the gallery ──────────────────────────────────────────────────────
   A card per project, each with a real picture of it. The card is a div, not a
   button, because it holds two controls: the name (a real button, stretched
   over the whole card by ::after) and the ⟳, which sits above it. */

.gallery {
  display: grid; gap: .7rem; max-width: 78rem;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
.gallery-card {
  position: relative; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); overflow: hidden;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.gallery-card:hover { border-color: var(--ink-faint); box-shadow: var(--shadow); }
.gallery-card:focus-within { border-color: var(--accent); }
.gallery-card.is-online { border-color: color-mix(in srgb, var(--good) 40%, var(--line)); }

.card-shot {
  aspect-ratio: 3 / 2; background: var(--surface-2);
  border-bottom: 1px solid var(--line-soft); overflow: hidden;
  display: grid; place-items: center;
}
.card-img { width: 100%; height: 100%; object-fit: cover; object-position: top center; display: block; }

/* Drawn art, for a project there is no picture of: not running, no page at
   all, or simply not photographed yet. The hue comes from the name, so it is
   the same card every time. */
.card-art {
  width: 100%; height: 100%; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: .25rem;
  background:
    linear-gradient(145deg,
      hsl(var(--art-hue) 42% 92%) 0%,
      hsl(calc(var(--art-hue) + 40) 38% 86%) 100%);
}
.art-letter {
  font-family: var(--font-head); font-size: 1.9rem; font-weight: 600; line-height: 1;
  color: hsl(var(--art-hue) 45% 32%);
}
.art-note { font-size: .7rem; font-weight: 600; color: hsl(var(--art-hue) 30% 38%); }
@media (prefers-color-scheme: dark) {
  .card-art {
    background:
      linear-gradient(145deg,
        hsl(var(--art-hue) 26% 22%) 0%,
        hsl(calc(var(--art-hue) + 40) 24% 16%) 100%);
  }
  .art-letter { color: hsl(var(--art-hue) 45% 78%); }
  .art-note { color: hsl(var(--art-hue) 22% 68%); }
}

.card-body { padding: .5rem .6rem .6rem; display: flex; flex-direction: column; gap: .1rem; }
.card-open {
  border: 0; background: transparent; padding: 0; text-align: left; cursor: pointer;
  font-family: var(--font-head); font-weight: 600; font-size: .92rem; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* The stretched link: the name is the control, the whole card is its target. */
.card-open::after { content: ''; position: absolute; inset: 0; z-index: 1; }
.card-when { line-height: 1.3; font-size: .74rem; }

.card-badge {
  position: absolute; top: .45rem; left: .45rem; z-index: 2;
  display: inline-flex; align-items: center; gap: .28rem;
  font-size: .64rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  padding: .15rem .4rem; border-radius: 999px;
  background: var(--surface); border: 1px solid var(--line); color: var(--good);
  box-shadow: 0 1px 3px rgb(22 32 42 / 12%);
}
.card-badge .dot { width: .38rem; height: .38rem; border-radius: 50%; background: var(--good); }

.card-refresh {
  position: absolute; top: .4rem; right: .4rem; z-index: 2;
  width: 1.7rem; height: 1.7rem; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
  font-size: .85rem; line-height: 1; display: grid; place-items: center;
  opacity: 0; transition: opacity .12s ease, color .12s ease;
}
.gallery-card:hover .card-refresh,
.gallery-card:focus-within .card-refresh,
.card-refresh:focus-visible { opacity: 1; }
.card-refresh:hover { color: var(--ink); }
.card-refresh.spinning { opacity: 1; animation: card-spin 1s linear infinite; }
@keyframes card-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .card-refresh.spinning { animation: none; } }
/* No hover on a touch screen, so the button is simply always there. */
@media (hover: none) { .card-refresh { opacity: 1; } }

.gallery-card.new-card {
  cursor: pointer; align-items: center; justify-content: center; gap: .3rem;
  min-height: 11rem; border-style: dashed; color: var(--accent-dk);
  background: transparent; font-family: var(--font-head); font-weight: 600;
}
.gallery-card.new-card:hover { background: var(--accent-wash); border-color: var(--accent); }
.gallery-card.new-card .plus { font-size: 1.5rem; line-height: 1; }
.new-card-label { font-size: .88rem; }

.home-empty-actions { display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .3rem; }

/* Phone: two columns, as asked. A card at 165px still reads. */
@media (max-width: 760px) {
  .gallery { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .55rem; }
  .card-body { padding: .4rem .5rem .5rem; }
  .card-open { font-size: .85rem; }
  .card-when { font-size: .7rem; }
  .gallery-card.new-card { min-height: 0; padding: 1.4rem .5rem; }
  .new-card-label { font-size: .78rem; text-align: center; }
}

/* ── Is it online? The chip in the Build bar ────────────────────────────────
   What used to be a tab. One control, three honest words, and a sheet behind
   it with the whole story. */

.live-chip {
  display: inline-flex; align-items: center; gap: .35rem; cursor: pointer;
  padding: .22rem .6rem; border-radius: 999px; font: inherit; font-size: .78rem;
  font-weight: 600; border: 1px solid var(--line); background: var(--surface);
  color: var(--ink-soft); white-space: nowrap;
}
.live-chip:hover { border-color: var(--ink-faint); color: var(--ink); }
.live-chip .dot { width: .45rem; height: .45rem; border-radius: 50%; background: var(--ink-faint); flex: none; }
.live-chip.good { color: var(--good); border-color: color-mix(in srgb, var(--good) 40%, var(--line)); }
.live-chip.good .dot { background: var(--good); }
.live-chip.warn { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 40%, var(--line)); }
.live-chip.warn .dot { background: var(--amber); }
.live-chip.bad  { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--line)); }
.live-chip.bad .dot { background: var(--danger); }

dialog.live-sheet { max-width: 32rem; }
dialog.live-sheet h2 { margin-top: 0; }
dialog.live-sheet .advanced-fold { margin-top: .9rem; border-top: 1px solid var(--line-soft); padding-top: .8rem; }
dialog.live-sheet .advanced-fold summary { cursor: pointer; font-size: .82rem; color: var(--ink-soft); }

.history-top { max-width: 48rem; margin-bottom: .8rem; }

/* ── Files ──────────────────────────────────────────────────────────────── */
.files-layout { display: grid; grid-template-columns: minmax(170px, 240px) 1fr; gap: .7rem; align-items: start; }
@media (max-width: 780px) { .files-layout { grid-template-columns: 1fr; } }
.tree { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: .45rem; max-height: 70vh; overflow: auto; }
.tree ul { list-style: none; margin: 0; padding-left: .8rem; }
.tree > ul { padding-left: 0; }
.tree button {
  display: block; width: 100%; text-align: left; border: 0; background: transparent;
  color: var(--ink-soft); cursor: pointer; padding: .16rem .35rem; border-radius: 6px;
  font-size: .84rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: var(--font-body);
}
.tree button:hover { background: var(--surface-2); color: var(--ink); }
.tree button.active { background: var(--accent-wash); color: var(--accent-dk); font-weight: 600; }
.tree .dirname { font-weight: 600; color: var(--ink); }
.viewer { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.viewer header { padding: .45rem .7rem; border-bottom: 1px solid var(--line); font-weight: 600; font-size: .85rem; display: flex; gap: .5rem; align-items: center; }
.viewer pre { margin: 0; max-height: 66vh; overflow: auto; font-size: .8rem; line-height: 1.55; }
.viewer pre code { padding: .7rem !important; display: block; }

/* ── Forms / dialogs ────────────────────────────────────────────────────── */
.form-grid { display: flex; flex-direction: column; gap: .7rem; max-width: 34rem; }
label.field { display: block; }
label.field > span { display: block; font-weight: 600; font-size: .85rem; margin-bottom: .28rem; }
input[type=text], input[type=email], input[type=password], textarea, select {
  width: 100%; font: inherit; font-size: .9rem; color: var(--ink); padding: .45rem .6rem;
  border: 1px solid var(--line); border-radius: 9px; background: var(--surface);
}
textarea { resize: vertical; }

.starter-grid { display: grid; gap: .5rem; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.starter {
  text-align: left; padding: .7rem .8rem; border-radius: var(--radius); cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink);
}
.starter.selected { border-color: var(--accent); background: var(--accent-wash); }
.starter strong { display: block; font-family: var(--font-head); font-size: .9rem; margin-bottom: .15rem; }
.starter span { font-size: .8rem; color: var(--ink-soft); }

dialog {
  border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 1.1rem;
  background: var(--surface); color: var(--ink); max-width: 34rem; width: calc(100% - 1.6rem);
  box-shadow: var(--shadow);
}
dialog::backdrop { background: rgb(22 32 42 / 45%); backdrop-filter: blur(2px); }

/* ── Help ───────────────────────────────────────────────────────────────── */
.help-grid { display: grid; gap: .8rem; max-width: 52rem; }
.panel {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: .9rem 1rem;
}
.status-line { display: flex; align-items: center; gap: .5rem; padding: .35rem 0; }
.tick { width: 1.05rem; height: 1.05rem; border-radius: 50%; display: grid; place-items: center; font-size: .66rem; color: #fff; flex: none; }
.tick.on  { background: var(--good); }
.tick.off { background: var(--ink-faint); }
@media (prefers-color-scheme: dark) { .tick { color: #08171A; } }
.glossary dt { font-weight: 600; margin-top: .55rem; font-size: .9rem; }
.glossary dd { margin: 0; color: var(--ink-soft); font-size: .86rem; }
.connect-out {
  font-family: var(--font-mono); font-size: .76rem; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--radius); padding: .55rem;
  max-height: 11rem; overflow: auto; white-space: pre-wrap; color: var(--ink-soft);
}
.code-box {
  font-family: var(--font-mono); font-size: 1.25rem; font-weight: 600; letter-spacing: .12em;
  padding: .55rem .8rem; background: var(--accent-wash); border: 1px dashed var(--accent);
  border-radius: var(--radius); display: inline-block; color: var(--accent-dk);
}

/* ── Account ─────────────────────────────────────────────────────────────── */
/* The face. One character in a circle: in the bar at the top (small, a button)
   and on the Account page itself (large, not a button). Sized in rem so it
   tracks the root font size that tightens the whole app on a phone. */
.avatar {
  position: relative;
  display: inline-grid; place-items: center; flex: none;
  width: 1.85rem; height: 1.85rem; border-radius: 50%;
  border: 1px solid var(--line); background: var(--surface-2);
  color: var(--ink); font-family: var(--font-head); font-weight: 600;
  font-size: .95rem; line-height: 1; cursor: pointer; padding: 0;
  /* Deliberately NOT overflow:hidden — the circle is comfortably bigger than
     the glyph in it, and clipping here would also clip the ::after that gives
     the button its tap area on a phone. */
}
.avatar:hover { background: var(--accent-wash); border-color: var(--accent); }
.avatar.active { border-color: var(--accent); background: var(--accent-wash); color: var(--accent-dk); }
.avatar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.avatar.lg {
  width: 3.1rem; height: 3.1rem; font-size: 1.6rem; cursor: default;
  background: var(--accent-wash); border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.avatar.lg:hover { background: var(--accent-wash); border-color: color-mix(in srgb, var(--accent) 35%, transparent); }

.profile-head { display: flex; align-items: center; gap: .8rem; }
.profile-head .grow { flex: 1; min-width: 0; }
.profile-name {
  font-family: var(--font-head); font-weight: 600; font-size: 1.05rem;
  letter-spacing: -0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* A hostname is long and is the one thing here worth copying, so let it wrap
   and let it be selected rather than truncating it into a lie. */
.profile-host { word-break: break-all; margin-top: .15rem; user-select: text; }

.avatar-input { max-width: 5rem; text-align: center; font-size: 1.2rem; }
.avatar-picks { display: flex; flex-wrap: wrap; gap: .3rem; }
.avatar-pick {
  width: 2.1rem; height: 2.1rem; border-radius: 50%; font-size: 1.1rem; line-height: 1;
  border: 1px solid var(--line); background: var(--surface-2); cursor: pointer;
  display: grid; place-items: center; padding: 0; color: var(--ink);
}
.avatar-pick:hover { border-color: var(--accent); background: var(--accent-wash); }
.avatar-pick.clear { color: var(--ink-faint); font-size: 1rem; }

/* ── Phone: the bar along the bottom, and the drawer ─────────────────────── */
.tabbar { display: none; }

@media (max-width: 760px) {
  html { font-size: 14px; }

  /* One number, used everywhere. 2.9rem of bar plus whatever the notch wants. */
  :root { --tabbar-h: calc(3.9rem + var(--safe-b)); }

  .app { grid-template-columns: 1fr; }

  /* The rail becomes a drawer. Off screen until the ☰ is pressed. */
  .sidebar {
    position: fixed; z-index: 60; top: 0; bottom: 0; left: 0;
    width: min(84vw, 19rem); height: 100%; border-right: 1px solid var(--line);
    padding: calc(.6rem + env(safe-area-inset-top, 0px)) .55rem calc(.6rem + var(--safe-b));
    transform: translateX(-101%); transition: transform .18s ease; will-change: transform;
    box-shadow: none;
  }
  body.drawer-open .sidebar { transform: none; box-shadow: 0 0 40px rgb(12 18 24 / 28%); }
  @media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }
  #sideCollapse { display: none; }

  .scrim {
    display: block; position: fixed; inset: 0; z-index: 55;
    background: rgb(12 18 24 / 40%); border: 0; padding: 0;
  }

  /* The six sections are along the bottom already; repeating them inside the
     drawer would be two navigations for one job. */
  .side-nav { display: none; }
  .side-chats { margin-top: .3rem; border-top: 0; padding-top: 0; }

  .drawer-btn { display: inline-grid; }
  .crumb { display: inline-flex; }
  /* The bar at the top is the only place Account lives on a phone, so the face
     keeps a real tap target even where everything else has tightened. The
     circle stays the size of every other icon in that bar — a lone oversized
     one would just look wrong — and grows its *hit* area instead, by less
     than the gap to its neighbour so it can never steal that button's tap. */
  .avatar { width: 2rem; height: 2rem; font-size: 1rem; }
  .avatar::after { content: ''; position: absolute; inset: -6px -5px; }
  .avatar.lg { width: 3rem; height: 3rem; font-size: 1.5rem; }
  /* The big one on the Account page is decoration, not a button. */
  .avatar.lg::after { content: none; }
  .tab-lead { display: none; }
  .topbar { padding: .25rem .5rem; min-height: 2.4rem; }

  .main { min-height: 100vh; padding-bottom: var(--tabbar-h); }

  .tabbar {
    display: flex; position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
    background: var(--surface); border-top: 1px solid var(--line);
    padding: .25rem .15rem calc(.15rem + var(--safe-b));
    height: var(--tabbar-h);
  }
  .tabbar button {
    flex: 1; min-width: 0; border: 0; background: transparent; cursor: pointer;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .05rem;
    padding: .1rem .1rem; color: var(--ink-faint); font-size: .7rem; font-weight: 600;
    font-family: var(--font-body);
  }
  .tabbar button .ic { font-size: 1.55rem; line-height: 1.1; }
  .tabbar button.active { color: var(--accent-dk); }

  .chat, .pane { padding: .7rem; }
  .bubble { max-width: 100%; }
  .bubble.you { max-width: 92%; }
  .files-layout { grid-template-columns: 1fr; }

  /* A soft keyboard hides the bar along the bottom — nothing is reachable
     under a keyboard anyway, and leaving it there is what pushes the box you
     are typing in off the screen. */
  body.kb-open { --tabbar-h: 0px; }
  body.kb-open .tabbar { display: none; }
}

/* ── Build: chat, a line, one panel ─────────────────────────────────────────
   There is no tiling here any more. Two columns on a computer, two swipeable
   cards on a phone, and a small row of tabs at the top of the panel. Every
   rule is written so that changing the shape changes only sizes: nothing is
   rebuilt when the divider moves, and a tab that is not showing is hidden
   rather than thrown away, because a preview that reloads loses whatever the
   person was looking at. */

.desk-host { overflow: hidden; }
.desk { flex: 1; min-height: 0; min-width: 0; display: flex; background: var(--surface-2); }
.desk-cols { display: flex; flex: 1; min-width: 0; min-height: 0; }

.desk-col {
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  margin: 5px; overflow: hidden;
}
.desk-panel:fullscreen { margin: 0; border-radius: 0; }

/* The line between the two. It is 7px of grab area with a 1px rule down the
   middle, so it is easy to hit and quiet to look at. */
.desk-divider {
  flex: 0 0 7px; background: transparent; position: relative; cursor: col-resize;
  touch-action: none;
}
.desk-divider::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 1px; height: 100%; background: var(--line); transition: background .12s ease;
}
.desk-divider:hover::after, .desk-divider.dragging::after, .desk-divider:focus-visible::after {
  background: var(--accent); width: 3px;
}
.desk-divider:focus-visible { outline: none; }

/* The panel's tabs: Preview, Console, and — only for somebody who asked for
   them — Code and Terminal. Small, at the top, above whatever tools that tab
   has of its own. */
.panel-tabs {
  display: flex; gap: .2rem; flex: none; padding: .25rem .3rem;
  background: var(--surface-2); border-bottom: 1px solid var(--line);
  overflow-x: auto; scrollbar-width: none;
}
.panel-tabs::-webkit-scrollbar { display: none; }
.panel-tab {
  display: inline-flex; align-items: center; gap: .3rem; flex: none;
  border: 1px solid transparent; background: transparent; color: var(--ink-soft);
  border-radius: 999px; padding: .2rem .6rem; font: 600 .76rem/1 var(--font-body);
  font-family: var(--font-body); cursor: pointer;
}
.panel-tab .ic { color: var(--ink-faint); font-size: .85rem; }
.panel-tab:hover { background: var(--surface); color: var(--ink); }
.panel-tab.on {
  background: var(--accent-wash); color: var(--accent-dk);
  border-color: color-mix(in srgb, var(--accent) 30%, transparent);
}
.panel-tab.on .ic { color: var(--accent-dk); }

.desk-pane-head {
  display: flex; align-items: center; gap: .2rem; flex: none;
  padding: .15rem .25rem .15rem .3rem; border-bottom: 1px solid var(--line);
  background: var(--surface-2); min-height: 1.85rem;
}
.desk-pane-head[hidden] { display: none; }
.desk-pane-tools { display: flex; align-items: center; gap: .2rem; flex: 1; min-width: 0; overflow: hidden; }
.desk-pane-tools-rest { display: flex; align-items: center; gap: .2rem; }

.desk-pane-btn {
  border: 0; background: transparent; cursor: pointer; color: var(--ink-faint);
  font-size: .82rem; line-height: 1; padding: .28rem .32rem; border-radius: 6px;
  text-decoration: none; display: inline-flex; align-items: center; flex: none;
  font-family: var(--font-body);
}
.desk-pane-btn:hover { background: var(--surface); color: var(--ink); }

.desk-pane-body { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; position: relative; }
.desk-pane-body > .chat { padding: .7rem; }
.desk-pane-body > .composer { padding: .45rem .6rem .55rem; }
.desk-pane-body .chat-inner, .desk-pane-body .composer-inner, .desk-pane-body .composer .hint { max-width: none; }

/* One tab's contents. Hidden, not removed: coming back to Preview must show
   the page as it was left, not load it again. */
.panel-view { flex: 1; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
.panel-view[hidden], .desk-pane-tools[hidden] { display: none; }

.pane-empty {
  flex: 1; display: flex; flex-direction: column; gap: .4rem;
  align-items: center; justify-content: center; text-align: center;
  padding: 1rem; color: var(--ink-soft); font-size: .85rem;
}
.pane-empty p { margin: 0; max-width: 26rem; }

.segmented.tiny button { padding: .18rem .5rem; font-size: .72rem; }
.btn.tiny { padding: .18rem .55rem; font-size: .74rem; border-radius: 999px; }
.runbar.slim { padding: .25rem .45rem; background: transparent; border-bottom: 1px solid var(--line-soft); gap: .35rem; }
.runbar.slim .run-state { font-size: .76rem; }
.runbar.slim .btn.small { padding: .15rem .5rem; font-size: .74rem; }

/* The device frame. The page inside really is 390px wide — it is scaled to fit
   with a transform, never resized, so nothing inside it reloads or reflows. */
.frame-stage {
  flex: 1; min-height: 0; overflow: auto; display: flex;
  align-items: center; justify-content: center; background: var(--surface-2);
}
.frame-stage.framed { padding: 10px; }
.device-frame {
  flex: none; transform-origin: center center; background: var(--surface);
  border-radius: 16px; box-shadow: var(--shadow); overflow: hidden;
  border: 1px solid var(--line); display: flex;
}
.device-frame.fill {
  width: 100%; height: 100%; border-radius: 0; border: 0; box-shadow: none;
}
.device-frame > iframe { flex: 1; width: 100%; height: 100%; border: 0; min-height: 0; background: var(--surface); }
.panel-view .preview-frame { border-top: 0; min-height: 0; }
.editor-frame { flex: 1; width: 100%; border: 0; background: var(--surface); min-height: 0; }
.term-host { flex: 1; min-height: 0; background: #10161C; padding: .3rem .4rem; overflow: hidden; }
.term-host .xterm { height: 100%; }

.panel-view > .console { margin: 0; border: 0; border-radius: 0; min-height: 0; }

/* What is going with the next message, and what the page is complaining about.
   Both sit directly above the box the person types in, because that is the one
   place they are already looking. */
.attach-chip, .problem-strip {
  display: flex; align-items: center; gap: .5rem; max-width: 48rem; flex-wrap: wrap;
  margin: 0 auto .45rem; padding: .35rem .55rem; border-radius: 9px;
  font-size: .8rem; border: 1px solid var(--line);
}
.attach-chip { background: var(--accent-wash); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
.attach-chip .grow, .problem-strip .grow { flex: 1 1 13rem; min-width: 0; }
.problem-strip { background: var(--amber-wash); border-color: color-mix(in srgb, var(--amber) 32%, transparent); }
.problem-strip .ic { color: var(--amber); font-weight: 700; }
.problem-strip .btn.tiny { margin-left: auto; }
.desk-pane-body .attach-chip, .desk-pane-body .problem-strip { max-width: none; }
.bubble-attached { margin-top: .3rem; color: var(--ink-faint); }
.storage-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .25rem; }
.storage-list li { display: flex; justify-content: space-between; gap: 1rem; font-size: .86rem; }

.switch-row { display: flex; align-items: center; gap: .5rem; font-size: .88rem; cursor: pointer; }
.switch-row input { width: 1rem; height: 1rem; accent-color: var(--accent); }

/* ── Build on a phone: two cards you swipe between ───────────────────────── */
.desk-phone { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.desk-phone-tabs {
  display: flex; gap: .25rem; overflow-x: auto; flex: none;
  padding: .3rem .45rem; background: var(--surface); border-bottom: 1px solid var(--line);
  scrollbar-width: none;
}
.desk-phone-tabs::-webkit-scrollbar { display: none; }
.desk-phone-tab {
  display: inline-flex; align-items: center; gap: .3rem; flex: none;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
  border-radius: 999px; padding: .22rem .65rem; font-size: .76rem; font-weight: 600;
  cursor: pointer; font-family: var(--font-body);
}
.desk-phone-tab.on { background: var(--accent-wash); color: var(--accent-dk); border-color: transparent; }
.desk-phone-track {
  flex: 1; min-height: 0; display: flex; overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory; scrollbar-width: none;
}
.desk-phone-track::-webkit-scrollbar { display: none; }
.desk-phone-card {
  flex: 0 0 100%; width: 100%; scroll-snap-align: start;
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
  background: var(--surface); border-right: 1px solid var(--line);
}

@media (max-width: 760px) {
  .desk-col { margin: 0; border: 0; border-radius: 0; }
  /* A tab with no tools of its own has an empty strip, and an empty strip is a
     row of a small screen spent on nothing. */
  .desk-phone-card > .desk-pane-head { padding: .1rem .3rem; min-height: 0; }
}

/* Build is the one screen that must not scroll the window: its columns scroll
   themselves. Everywhere else the page grows and the window scrolls, which is
   right for a chat and wrong for a workbench. */
body.desk-mode .main { height: var(--app-h); overflow: hidden; }
body.desk-mode .topbar { position: static; flex: none; }
@media (max-width: 760px) {
  /* The bar along the bottom is fixed over the window, so Build stops short of
     it rather than running under it. */
  body.desk-mode .main { height: var(--app-h); padding-bottom: var(--tabbar-h); }
  /* Inside Build the composer is already above the bar, so it must not pad
     itself by the bar a second time. */
  body.desk-mode .desk-pane-body > .composer { padding-bottom: .5rem; }
}

/* A column is narrower than a page. The composer stacks, and a header with more
   tools than room scrolls sideways rather than hiding half of them. */
.desk-pane-body .composer-inner { flex-wrap: wrap; }
/* Wide enough and Send sits beside the box, as it does on a full page; only a
   genuinely narrow column drops it onto a line of its own. */
.desk-pane-body .composer textarea { min-height: 2.4rem; flex: 1 1 9rem; }
.desk-pane-body .composer .btn { flex: none; }
.desk-pane-tools { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
.desk-pane-tools::-webkit-scrollbar { display: none; }

.frame-size { flex: none; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* Phone: a flex child defaults to min-width:auto, which lets a wide card push
   the whole deck past the edge of the screen instead of scroll-snapping. */
.desk-phone { min-width: 0; width: 100%; }
.desk-phone-track { min-width: 0; width: 100%; }
.desk-phone-card { min-width: 0; max-width: 100%; }


/* ── add-to-home-screen hint ──────────────────────────────────────────────── */
.a2hs-hint {
  position: fixed; left: .5rem; right: .5rem; bottom: calc(var(--tabbar-h) + .5rem);
  z-index: 40; display: flex; align-items: center; gap: .5rem;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); padding: .5rem .6rem; font-size: .8rem; color: var(--ink);
}
.a2hs-hint .mark { color: var(--accent); font-size: 1rem; flex: none; }
.a2hs-hint > span:nth-child(2) { flex: 1 1 auto; line-height: 1.3; }
.a2hs-hint .a2hs-close { flex: none; padding: 0 .35rem; font-size: 1rem; line-height: 1; }

/* ── The first-run walk ─────────────────────────────────────────────────────
   One veil, one hole, one card. The hole is not a hole: it is a transparent
   box with an enormous shadow, which is the only way to punch light through a
   dark layer without a canvas or a mask that half the phones get wrong. */

.tour-veil {
  position: fixed; inset: 0; z-index: 100;
  background: transparent;
}
/* Only when there is nothing to spotlight does the veil do the dimming
   itself — otherwise the hole's shadow already darkens everything else. */
.tour-veil.dim { background: rgb(12 18 24 / 58%); }
.tour-hole {
  position: fixed; z-index: 101; pointer-events: none;
  border-radius: 10px;
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 9999px rgb(12 18 24 / 58%);
  transition: top .18s ease, left .18s ease, width .18s ease, height .18s ease;
}
@media (prefers-reduced-motion: reduce) { .tour-hole { transition: none; } }
.tour-card {
  position: fixed; z-index: 102; width: min(340px, calc(100vw - 1.6rem));
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: .8rem .9rem .7rem;
}
.tour-card.centred { top: 50%; left: 50%; transform: translate(-50%, -50%); }
.tour-card h3 { margin: .1rem 0 .3rem; font-size: 1rem; }
.tour-card p { margin: 0 0 .7rem; color: var(--ink-soft); font-size: .86rem; line-height: 1.45; }
.tour-count {
  font-size: .68rem; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--ink-faint);
}
.tour-actions { display: flex; align-items: center; gap: .4rem; }
.tour-actions .grow { flex: 1; }

@media (max-width: 760px) {
  /* A 390px screen has nowhere to dodge the spotlight to, so the card lives at
     the bottom, clear of the bar along the bottom, where a thumb already is. */
  .tour-card {
    top: auto !important; left: .5rem !important; right: .5rem;
    bottom: calc(var(--tabbar-h) + .5rem);
    width: auto; transform: none;
  }
  .tour-card.centred { top: auto !important; left: .5rem !important; transform: none; }
}

/* ── Publish: two cards ─────────────────────────────────────────────────── */
.publish-cards { display: grid; gap: .9rem; max-width: 42rem; }
.state-line { display: flex; align-items: center; gap: .45rem; font-weight: 600; font-size: .89rem; margin: .15rem 0 .4rem; }
.state-line .dot { width: .55rem; height: .55rem; border-radius: 50%; background: var(--ink-faint); flex: none; }
.state-line.good .dot { background: var(--good); }
.state-line.warn .dot { background: var(--amber); }
.state-line.bad  .dot { background: var(--danger); }
.state-line.off  .dot { background: var(--ink-faint); }

.mini-list { display: flex; flex-direction: column; gap: .25rem; }
.mini-row {
  display: flex; align-items: center; gap: .6rem;
  padding: .4rem .6rem; border: 1px solid var(--line); border-radius: 9px;
  background: var(--surface-2); font-size: .85rem;
}
.mini-row .grow { flex: 1; min-width: 0; }

.advanced-fold { padding: .7rem .9rem; }
.advanced-fold > summary {
  cursor: pointer; font-weight: 600; font-size: .85rem; color: var(--ink-soft);
}
.advanced-fold[open] > summary { margin-bottom: .6rem; }

/* ── Help: the written tutorial ─────────────────────────────────────────── */
.how-section { border-top: 1px solid var(--line); padding: .45rem 0; }
.how-section > summary { cursor: pointer; font-weight: 600; font-size: .89rem; }
.how-section[open] > summary { margin-bottom: .4rem; }
.how-section p { line-height: 1.5; }
.how-section ol { margin: 0 0 .6rem; padding-left: 1.1rem; line-height: 1.5; }
.how-quote {
  margin: .1rem 0 .6rem; padding: .35rem .7rem;
  border-left: 3px solid var(--accent); background: var(--surface-2);
  border-radius: 0 8px 8px 0; font-size: .85rem; color: var(--ink);
}

/* ── Deleting a project ─────────────────────────────────────────────────── */
.danger-zone {
  margin-top: 1.2rem; padding: .8rem .9rem;
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--line));
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--danger) 7%, var(--surface));
}
.danger-zone h3 { margin: 0 0 .4rem; font-size: .94rem; color: var(--danger); }
.danger-zone p { margin: 0 0 .5rem; color: var(--ink-soft); line-height: 1.45; }
.danger-zone .field { margin-bottom: .6rem; }

/* ── pinned versions ────────────────────────────────────────────────────── */
.pin-star { color: var(--amber); font-size: .95rem; line-height: 1; flex: none; }
.row.pinned { border-color: color-mix(in srgb, var(--amber) 45%, var(--line)); }
