:root {
  --bg: #0b0b0f; --panel: #14141b; --line: #262633;
  --fg: #e7e7ee; --muted: #8b8b9e; --accent: #6f7bff; --err: #ff6b6b; --ok: #4ade80;
  /* --accent is a colour for text and borders: 5.6:1 against the page, and
     only 3.5:1 under white, which is what every filled button was using. A
     filled shape with a label on it uses --accent-fill, where white sits at
     5.5:1 and clears AA. The two are close enough to read as one colour. */
  --accent-fill: #4a57e6;
  /* Bright enough for the darkest panel and for the accent fill both, so one
     ring serves everywhere instead of disappearing on the primary button. */
  --focus: #c9cdff;
  color-scheme: dark;
}

/* One focus ring, for everything that can take focus.
   Declared early, and with a zero-specificity subject, so any control needing
   a different ring only has to say so. :focus-visible rather than :focus, so a
   mouse click does not leave one behind. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  display: flex; flex-direction: column; height: 100vh;
}
header {
  display: flex; align-items: center; gap: 12px;
  padding: 10px 16px; border-bottom: 1px solid var(--line);
}
h1 { font-size: 17px; font-weight: 600; letter-spacing: -.01em; margin: 0; }
.model { color: var(--muted); font-size: 12px; font-family: ui-monospace, monospace; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); margin-left: auto; }
.dot[data-state="busy"] { background: var(--accent); animation: pulse 1s ease-in-out infinite; }
.dot[data-state="error"] { background: var(--err); }
.dot[data-state="done"] { background: var(--ok); }
@keyframes pulse { 50% { opacity: .25; } }

main { flex: 1; display: grid; grid-template-columns: 1fr 380px; min-height: 0; }
.screens { padding: 16px; overflow: auto; display: flex; flex-wrap: wrap; gap: 16px; align-content: flex-start; }
.empty { color: var(--muted); }

.log {
  border-left: 1px solid var(--line); background: var(--panel);
  padding: 12px; overflow: auto; font: 12px/1.6 ui-monospace, monospace;
}
.log div { white-space: pre-wrap; word-break: break-word; }
.log .t-log { color: var(--muted); }
.log .t-event { color: #787f99; }
.log .t-error { color: var(--err); }
.log .t-status { color: var(--accent); }
.log .t-message { color: var(--fg); border-left: 2px solid var(--accent); padding-left: 8px; margin: 6px 0; }

.composer { border-top: 1px solid var(--line); padding: 12px 16px; background: var(--panel); }
.target { font-size: 12px; color: var(--muted); margin-bottom: 6px; }
.target strong { color: var(--accent); }
.target button { background: none; border: 0; color: var(--muted); cursor: pointer; }
textarea {
  width: 100%; background: var(--bg); color: var(--fg);
  border: 1px solid var(--line); border-radius: 8px; padding: 10px; resize: vertical;
  font: inherit;
}
textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.row { display: flex; gap: 8px; margin-top: 8px; }
button[type="submit"], #cancel {
  background: var(--accent-fill); color: #fff; border: 0; border-radius: 8px;
  padding: 8px 16px; font-weight: 600; cursor: pointer;
}
#cancel { background: transparent; border: 1px solid var(--line); color: var(--muted); }
button:disabled { opacity: .5; cursor: not-allowed; }

/* --- screen cards ---------------------------------------------------------
   Each card previews the real generated file in an iframe at true phone size,
   then scales it down. No screenshot pipeline, and it is never stale. */
.card {
  --w: 390px; --h: 844px; --scale: .34;
  width: calc(var(--w) * var(--scale));
  cursor: pointer; border: 0; padding: 0; background: none; text-align: left;
  font: inherit; color: inherit;
}
.card .frame {
  position: relative; overflow: hidden; border-radius: 12px;
  width: calc(var(--w) * var(--scale)); height: calc(var(--h) * var(--scale));
  border: 1px solid var(--line); background: #fff;
  transition: border-color .12s, box-shadow .12s;
}
.card iframe {
  width: var(--w); height: var(--h); border: 0;
  transform: scale(var(--scale)); transform-origin: top left;
  pointer-events: none;              /* the card is a button, not a live app */
}
.card .veil { position: absolute; inset: 0; }   /* keeps clicks off the iframe */
.card:hover .frame { border-color: var(--muted); }
.card[aria-pressed="true"] .frame {
  border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent);
}
.card .label { display: flex; align-items: baseline; gap: 6px; padding: 8px 2px 0; }
.card .label b { font-size: 13px; font-weight: 600; }
.card .label span { font-size: 11px; color: var(--muted); font-family: ui-monospace, monospace; }
.card.stale .frame { opacity: .45; }
.badge {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--accent); border: 1px solid var(--accent); border-radius: 4px;
  padding: 0 4px;
}
.log .t-reason { color: #7a7f95; font-style: italic; }
.log .t-cmd { color: #6f9f7f; }

/* --- design references ----------------------------------------------------- */
.pane { display: flex; flex-direction: column; min-height: 0; }
.toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 16px; border-bottom: 1px solid var(--line);
}
.toggle { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); cursor: pointer; }
.hint { font-size: 11px; color: var(--muted); }
.design { margin-bottom: 8px; font-size: 12px; }
.design summary { color: var(--muted); cursor: pointer; padding: 4px 0; }
.design input[type="text"] {
  width: 100%; margin: 6px 0; padding: 8px; font: inherit;
  background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: 6px;
}
.design .row { align-items: center; }
.filebtn {
  border: 1px solid var(--line); border-radius: 6px; padding: 6px 12px;
  cursor: pointer; color: var(--fg);
}
.filebtn:hover { border-color: var(--muted); }
#clearUploads { background: none; border: 1px solid var(--line); color: var(--muted); border-radius: 6px; padding: 4px 10px; cursor: pointer; }
.card .refimg {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; background: var(--panel);
}
.card .noref {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 11px; color: var(--muted); background: var(--panel);
}

/* --- flow groups ----------------------------------------------------------- */
.flows { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; margin: 8px 0; }
.screens h2.group {
  width: 100%; margin: 4px 0 -4px; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted);
}
.screens h2.group[data-note]::after {
  content: ' — ' attr(data-note);
  font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--err);
}

/* --- app switcher ---------------------------------------------------------- */
#appPicker {
  background: var(--bg); color: var(--fg); border: 1px solid var(--line);
  border-radius: 6px; padding: 4px 8px; font: inherit; font-size: 12px; max-width: 220px;
}
button.ghost {
  background: none; border: 1px solid var(--line); color: var(--muted);
  border-radius: 6px; padding: 4px 10px; font-size: 12px; cursor: pointer;
}
button.ghost:hover { border-color: var(--muted); color: var(--fg); }

/* --- login gate ------------------------------------------------------------ */
.gate {
  position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
  background: var(--bg);
}
.gate-card {
  width: min(360px, 90vw); padding: 24px; border: 1px solid var(--line);
  border-radius: 12px; background: var(--panel);
}
.gate-card h2 { margin: 0 0 8px; font-size: 19px; letter-spacing: -.01em; }
.gate-card p { margin: 0 0 14px; font-size: 12px; color: var(--muted); }
.gate-card input {
  width: 100%; padding: 10px; margin-bottom: 10px; font: inherit;
  background: var(--bg); color: var(--fg); border: 1px solid var(--line); border-radius: 8px;
}
.gate-card button {
  width: 100%; padding: 10px; background: var(--accent-fill); color: #fff;
  border: 0; border-radius: 8px; font-weight: 600; cursor: pointer;
}
.gate-error { color: var(--err) !important; margin: 10px 0 0 !important; }
/* Signing in with somebody else's proof instead of a password. A peer of the
   password form rather than a link, because it is a whole alternative route
   rather than a change of mind. */
.gate-or {
  display: flex; align-items: center; gap: 10px;
  margin: 14px 0 !important; font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
}
.gate-or::before, .gate-or::after { content: ""; flex: 1; border-top: 1px solid var(--line); }
.gate-google {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; padding: 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--bg); color: var(--fg); font-size: 13px; font-weight: 600;
  text-decoration: none;
}
.gate-google:hover { border-color: var(--accent); }
.gate-google svg { flex: none; }

/* The other way in — a link, not a second call to action, because only one of
   signing in and signing up is what a given person came to do. */
.gate-alt { margin: 12px 0 0 !important; text-align: center; }
.gate-alt button {
  width: auto; padding: 0; background: none; color: var(--accent);
  font-weight: 400; font-size: 12px; text-decoration: underline; cursor: pointer;
}

/* --- version history ------------------------------------------------------- */
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(380px, 92vw); z-index: 40;
  background: var(--panel); border-left: 1px solid var(--line);
  display: flex; flex-direction: column; box-shadow: -12px 0 40px rgba(0,0,0,.4);
}
.drawer-head {
  display: flex; align-items: center; gap: 8px;
  padding: 12px 14px; border-bottom: 1px solid var(--line); font-size: 13px;
}
.drawer-head strong { font-size: 15px; }
.drawer-head strong { flex: 1; }
.snapshots { list-style: none; margin: 0; padding: 8px; overflow: auto; flex: 1; }
.snapshots li {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px;
  margin-bottom: 8px; font-size: 12px;
}
.snapshots .when { color: var(--muted); font-size: 11px; }
.snapshots .what { margin: 3px 0 8px; }
.snapshots .row { display: flex; gap: 6px; }
.snapshots button { font-size: 11px; padding: 4px 10px; }
.snapshots .restore {
  background: var(--accent-fill); color: #fff; border: 0; border-radius: 6px; cursor: pointer;
}

/* --- remove control on a card ---------------------------------------------- */
.card .card-remove {
  position: absolute; top: 6px; right: 6px; z-index: 2;
  width: 22px; height: 22px; border-radius: 50%;
  border: 1px solid var(--line); background: var(--panel); color: var(--muted);
  font-size: 11px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity .12s;
}
.card:hover .card-remove, .card:focus-within .card-remove { opacity: 1; }
.card .card-remove:hover { color: var(--err); border-color: var(--err); }

/* --- stats drawer ----------------------------------------------------------
   Numbers a creator decides on, so the design job is making an approximate
   figure look approximate. Anything soft carries its caveat next to it rather
   than in a legend nobody reads. */
.stats { padding: 10px; overflow: auto; flex: 1; font-size: 12px; }
.stats .group { border: 1px solid var(--line); border-radius: 8px; padding: 10px; margin-bottom: 8px; }
.stats .group h4 {
  margin: 0 0 8px; font-size: 11px; font-weight: 600; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
}
.stats .figures { display: flex; flex-wrap: wrap; gap: 14px; }
.stats .figure { min-width: 62px; }
.stats .figure .n { font-size: 22px; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stats .figure .k { color: var(--muted); font-size: 11px; }
/* An estimate must not be able to pass for a count. */
.stats .figure.soft .n { color: var(--muted); }
.stats .caveat { color: var(--muted); font-size: 11px; margin: 8px 0 0; line-height: 1.4; }
.stats .missing { color: var(--muted); font-style: italic; }
.stats .bad { color: var(--danger, #e0817e); }

/* --- publish ---------------------------------------------------------------
   The drawer that takes an app from built to reachable. Everything a creator
   has to do before somebody else can open their app lives here, in the order
   it has to happen: an address, then the checks, then the button. */
button.publish {
  background: var(--accent-fill); color: #fff; border: 0; border-radius: 6px;
  padding: 4px 12px; font-size: 12px; font-weight: 600; cursor: pointer;
}
button.publish:hover { filter: brightness(1.1); }
/* Was an inline style on the element. It has to be overridable at narrow
   widths, and an inline style is not. */
#payBtn { margin-left: auto; }

.pub { overflow: auto; flex: 1; padding: 12px 14px; display: flex; flex-direction: column; gap: 20px; }
.pub h3 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 8px; }
.pub section { margin: 0; }

/* The answer to "is it live, and where" -- the thing a creator opens this
   drawer to find out, so it sits above everything else. */
.pub-live {
  padding: 10px 12px; border: 1px solid var(--ok); border-radius: 8px;
  background: color-mix(in srgb, var(--ok) 8%, transparent);
  display: flex; flex-direction: column; gap: 2px;
}
.pub-label { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.pub-live a { color: var(--ok); font-weight: 600; word-break: break-all; text-decoration: none; }
.pub-live a:hover { text-decoration: underline; }

.domains { list-style: none; margin: 0 0 10px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.domains li {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px;
}
.domains .host { flex: 1; font-family: ui-monospace, monospace; font-size: 12px; word-break: break-all; }

/* Status carries a shape as well as a colour: "pending" and "active" are the
   difference between a domain that works and one that only looks attached. */
.pill {
  font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  padding: 2px 7px; border-radius: 999px; border: 1px solid var(--line); color: var(--muted);
  white-space: nowrap;
}
.pill[data-s="active"] { color: var(--ok); border-color: var(--ok); }
.pill[data-s="pending"] { color: var(--accent); border-color: var(--accent); }
.pill[data-s="error"], .pill[data-s="refused"] { color: var(--err); border-color: var(--err); }

.domain-add { display: flex; gap: 6px; }
.domain-add input {
  flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--line);
  color: var(--fg); border-radius: 6px; padding: 6px 10px;
  font-family: ui-monospace, monospace; font-size: 12px;
}
.domain-add button {
  background: none; border: 1px solid var(--line); color: var(--muted);
  border-radius: 6px; padding: 6px 12px; font-size: 12px; cursor: pointer;
}
.domain-add button:hover { border-color: var(--muted); color: var(--fg); }

.check { display: flex; gap: 8px; align-items: flex-start; padding: 4px 0; font-size: 12px; }
.check .mark { flex: none; width: 14px; }
.check.bad .mark { color: var(--err); }
.check.warn .mark { color: var(--accent); }
.check.good .mark { color: var(--ok); }

.pub-go { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pub-go button {
  background: var(--accent-fill); color: #fff; border: 0; border-radius: 8px;
  padding: 9px 20px; font-weight: 600; cursor: pointer;
}
.pub-notes {
  list-style: none; margin: 0; padding: 10px 12px; border: 1px solid var(--line);
  border-radius: 8px; font: 11px/1.7 ui-monospace, monospace; color: var(--muted);
}

/* --- payments --------------------------------------------------------------
   Connecting an account and naming a price. Both are the creator's, held on
   the creator's own Stripe account, which is why the copy says so out loud. */
#connectStripe {
  background: var(--accent-fill); color: #fff; border: 0; border-radius: 8px;
  padding: 9px 18px; font-weight: 600; cursor: pointer;
}
#connectStripe:hover { filter: brightness(1.1); }

.price-form { display: flex; flex-direction: column; gap: 10px; }
.price-row { display: flex; align-items: center; gap: 10px; }
.price-row label { width: 66px; flex: none; color: var(--muted); font-size: 12px; }
.price-row select,
.price-row input[type="text"] {
  flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--line);
  color: var(--fg); border-radius: 6px; padding: 7px 10px; font-size: 13px;
}

/* The currency sits inside the field rather than beside it, so the amount
   cannot be read as anything but money. */
.money { flex: 1; display: flex; align-items: center; gap: 0;
  background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 0 10px; }
.money .cur { color: var(--muted); font-size: 13px; }
.money input {
  flex: 1; min-width: 0; background: none; border: 0; color: var(--fg);
  padding: 7px 6px; font-size: 13px; font-variant-numeric: tabular-nums;
}
/* No `outline: none` here: the wrapper's border change is a 1px cue, not a
   focus indicator. The global :focus-visible ring draws outside the field. */
.money:focus-within { border-color: var(--accent); }

#savePrice {
  align-self: flex-start; background: none; border: 1px solid var(--line);
  color: var(--fg); border-radius: 6px; padding: 7px 16px; font-size: 13px; cursor: pointer;
}
#savePrice:hover { border-color: var(--muted); }

/* --- gating ----------------------------------------------------------------
   The lock sits on the card because that is where the decision is made. It is
   always visible when on -- a paywalled screen is a fact about the app, not a
   hover state -- and fades in beside the remove button otherwise. */
.card-gate {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  width: 22px; height: 22px; padding: 0; line-height: 1;
  border: 1px solid var(--line); border-radius: 6px;
  background: rgba(11,11,15,.82); color: var(--muted);
  font-size: 11px; cursor: pointer; opacity: 0; transition: opacity .12s;
}
.card:hover .card-gate, .card-gate:focus-visible { opacity: 1; }
.card-gate.on { opacity: 1; border-color: var(--accent); }
.card-gate:hover { border-color: var(--muted); }

/* The entitlement's name, not a padlock: an app can sell more than one thing. */
.badge.paid { border-color: var(--accent); color: var(--accent); }

/* --- take it with you ------------------------------------------------------
   Separated by a rule rather than a heading weight: leaving is a different
   kind of action from publishing, and should not look like a step in it. */
.take-away { border-top: 1px solid var(--line); padding-top: 16px; }
.take-away .row { display: flex; gap: 8px; margin: 10px 0 0; }
#exportResult:not(:empty) { margin-top: 10px; }

/* --- moderation ------------------------------------------------------------
   An operator surface, not a creator one: reached by ?moderate=1 rather than a
   toolbar button, so a creator is never shown a door they cannot open. Wider
   than the other drawers because it is a list of other people's apps rather
   than a form about this one. */
.drawer.wide { width: min(560px, 96vw); }

.queue { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.queue li {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
}
/* Suspended is a state of the world, not a warning about one, so it marks the
   whole row rather than sitting in it. */
.queue li.suspended { border-color: var(--err); background: color-mix(in srgb, var(--err) 7%, transparent); }

.queue .top { display: flex; align-items: center; gap: 8px; }
.queue .who { flex: 1; min-width: 0; }
.queue .who b { display: block; }
.queue .who span { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; }
.queue .hosts { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; word-break: break-all; }
.queue .findings { display: flex; flex-direction: column; gap: 3px; }
.queue button.act { background: none; border: 1px solid var(--line); color: var(--muted);
  border-radius: 6px; padding: 5px 12px; font-size: 12px; cursor: pointer; white-space: nowrap; }
.queue button.act:hover { border-color: var(--err); color: var(--err); }
.queue li.suspended button.act:hover { border-color: var(--ok); color: var(--ok); }

/* The queue's own states. Without these a "blocked" pill renders in the same
   muted grey as everything else, which is the one thing it must not do. */
.pill[data-s="blocked"], .pill[data-s="suspended"] { color: var(--err); border-color: var(--err); }
.pill[data-s="flagged"] { color: var(--accent); border-color: var(--accent); }

/* --- account hygiene -------------------------------------------------------
   Deleting an app and signing out: both rare, both consequential, so neither
   should sit where a hand lands by accident. Delete is only tinted on hover,
   because a permanently red control in the header reads as an alarm. */
button.ghost.danger:hover { border-color: var(--err); color: var(--err); }
.who { color: var(--muted); font-size: 12px; max-width: 22ch; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* --- hidden means hidden ----------------------------------------------------
   `hidden` is a UA-stylesheet rule (`display: none`), and ANY author rule that
   sets display beats it. Every overlay here does exactly that -- the gate is
   `display: grid`, each drawer is `display: flex` -- so `el.hidden = true`
   set the attribute, changed nothing on screen, and left the gate covering the
   whole viewport with the static markup it shipped with: "Builder locked. This
   builder requires a password." On a builder that requires no password.

   It also swallowed clicks. An early attempt to open the publish drawer by
   clicking did nothing, and the click landed on this overlay rather than the
   button -- which read as a flaky click and was not.

   Declared last and with !important so no later rule reintroduces it. */
[hidden] { display: none !important; }

/* --- the builder's own plan -------------------------------------------------
   A meter rather than a number alone. "$3.90 of $5.00" is precise and easy to
   read past; a bar that is nearly full is the thing somebody notices before
   the build that fails to start. */
.pill {
  background: none; border: 1px solid var(--line); border-radius: 999px;
  color: var(--muted); font: inherit; font-size: 11px; padding: 3px 10px;
  cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 7px;
}
.pill:hover { border-color: var(--muted); color: var(--fg); }
.pill .track { width: 42px; height: 4px; border-radius: 2px; background: var(--line); overflow: hidden; }
.pill .track i { display: block; height: 100%; background: var(--accent); }
/* Low, then empty. The colour changes before the wall, not at it. */
.pill[data-level="low"] { color: var(--fg); border-color: #6b5f2a; }
.pill[data-level="low"] .track i { background: #e0b83c; }
.pill[data-level="out"] { color: var(--err); border-color: var(--err); }
.pill[data-level="out"] .track i { background: var(--err); }

.meter { height: 8px; border-radius: 4px; background: var(--line); overflow: hidden; margin-bottom: 10px; }
.meter span { display: block; height: 100%; width: 0; background: var(--accent); transition: width .3s ease; }
.meter[data-level="low"] span { background: #e0b83c; }
.meter[data-level="out"] span { background: var(--err); }
.usage { margin: 0 0 4px; font-size: 13px; font-variant-numeric: tabular-nums; }

.plans { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.plans li {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; font-size: 12px;
  display: flex; align-items: baseline; gap: 8px;
}
.plans li.current { border-color: var(--accent); }
.plans .name { font-weight: 600; font-size: 13px; color: var(--fg); }
.plans .price { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--fg); }
.plans .on { font-size: 11px; color: var(--accent); text-transform: uppercase; letter-spacing: .06em; }

/* A message that is not an error: "check your email" is good news, and the
   red used for a failed sign-in would read as another rejection. */
.gate-note {
  font-size: 12px; color: var(--ok); margin: 8px 0 0;
  border-left: 2px solid var(--ok); padding-left: 8px; text-align: left;
}

/* --- behind an open drawer --------------------------------------------------
   Six drawers share one edge, one width and one z-index, so a second one does
   not sit beside the first -- it covers it exactly, close button included. The
   fix is that only one is ever open, and this is the other half of it: a
   surface that says which layer is live and gives the click-away that a
   covering panel has to answer to. Below the drawers, above everything else. */
.scrim { position: fixed; inset: 0; z-index: 39; background: rgba(5, 5, 8, .55); }

/* --- narrow screens ---------------------------------------------------------
   There was nothing here, and `main` was a hard 1fr + 380px at every width. On
   a phone that is not a narrow two-column layout, it is an 846px one the
   browser zooms out to fit: 11px labels arrive at about 5px. So the log stops
   being a column and becomes a band under the canvas -- still live, still
   scrolling, no longer taking half the screen -- and the two rows of controls
   are allowed to wrap. */
@media (max-width: 900px) {
  /* dvh rather than vh: on mobile the address bar is part of the viewport
     until it isn't, and 100vh puts the composer under it. */
  body { height: 100dvh; }

  header { flex-wrap: wrap; row-gap: 8px; padding: 10px 12px; }
  /* The title takes its own line, so the controls below it wrap as a group
     rather than around it. */
  h1 { flex-basis: 100%; }
  /* `margin-left: auto` pushes the dot and its neighbours to the right edge of
     the line. Once the line can wrap that is the wrong instruction. */
  .dot, #payBtn { margin-left: 0; }
  #appPicker { max-width: 100%; }

  .toolbar { flex-wrap: wrap; row-gap: 8px; padding: 8px 12px; }

  main { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) auto; }
  .log {
    border-left: 0; border-top: 1px solid var(--line);
    max-height: 30vh; min-height: 0; padding: 10px 12px;
  }

  /* Bigger, not smaller: the cards are the content, and there is now a whole
     row for them rather than the left two thirds of one. */
  .screens { padding: 12px; gap: 12px; }
  .card { --scale: .42; }

  .composer { padding: 10px 12px; }
}

/* --- the look picker ------------------------------------------------------
   A chip per style, drawn from its swatch rather than from a screenshot: three
   of these are light-ground looks and would otherwise be three identical white
   squares, so the chip shows every colour in the swatch. */
.styles { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.style-chip {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 10px 5px 6px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg); color: var(--fg); font: inherit; font-size: 12px;
  cursor: pointer;
}
.style-chip:hover { border-color: var(--accent); }
.style-chip[aria-checked="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.style-chip .sw {
  display: flex; width: 26px; height: 18px; border-radius: 5px; overflow: hidden;
  border: 1px solid var(--line); flex: none;
}
.style-chip .sw i { flex: 1; }
