/* The Payama workspace — /app.
 *
 * Two modes in one page, exactly as the design has it:
 *   Projects mode  — 236px sidebar, big prompt box, fanned project cards
 *   Project mode   — sidebar goes, a 44% panel slides in, a 66px icon rail
 *
 * Follows the light/dark toggle, unlike the marketing and auth pages. This is
 * the tool, and people work in it for hours.
 *
 * The design file uses inline styles throughout; every one of them is lifted
 * into a class here so hover and focus states are real CSS.
 */

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

html,
body {
  margin: 0;
  height: 100%;
  background: var(--pa-bg);
  color: var(--pa-text);
  font-family: var(--font-body);
  font-size: 15px;
  overflow: hidden;
}

::selection {
  background: var(--pa-text-hi);
  color: var(--pa-bg);
}

a {
  color: var(--pa-text);
  text-decoration: none;
}

a:hover {
  color: var(--pa-text-hi);
}

textarea::placeholder {
  color: var(--pa-muted-3);
}

textarea {
  caret-color: var(--pa-accent);
}

textarea::selection {
  background: var(--pa-accent);
  color: var(--pa-on-accent);
}

::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-thumb {
  background: var(--pa-border);
  border-radius: 999px;
}

::-webkit-scrollbar-track {
  background: transparent;
}

[hidden] {
  display: none !important;
}

/* ── shell ────────────────────────────────────────────────────────────────── */

.ws {
  height: 100vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* One grid holds both modes.
 *
 * Projects mode is a single column with the log drawer under it. Project mode
 * splits into chat | panel, and the drawer sits under the panel only — so the
 * chat column keeps running past it to the bottom of the window. Nothing moves
 * in the DOM between the two; only these two rule blocks differ. */
.ws__body {
  position: relative;   /* anchors the floating projects pill */
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: minmax(0, 1fr) auto;
}

.ws--open .ws__body {
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  padding: 0 12px 12px;
}

.ws__stage {
  position: relative;
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* The chat column becomes a card once a project is open, and runs the full
   height of the window — the drawer is in the other column, so there is no
   second row to leave clear here. */
.ws--open .ws__stage {
  grid-row: 1 / span 2;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface);
  overflow: hidden;
}

.ws__main {
  flex: 1;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

/* ── chat column head ─────────────────────────────────────────────────────── */

.ws__chat-head {
  flex: none;
  height: 40px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
  border-bottom: 1px solid var(--pa-line);
}

.ws__chat-label {
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
}

.ws__chat-head .ws__status {
  margin-left: auto;
}

/* ── header ───────────────────────────────────────────────────────────────── */

.ws__header {
  flex: none;
  height: 56px;
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 0 16px;
  border-bottom: 1px solid var(--pa-line);
}

/* In project mode the header lifts off the top edge and becomes a card, so it
   reads as one of three panels rather than as chrome above them. */
/* All four margins, not three. The bottom one used to be 0, which left the
   header card touching the chat and panel cards below it while every other
   gutter in this layout was 12px -- so the header read as stuck to them
   rather than as a third card alongside. */
.ws--open .ws__header {
  height: 52px;
  margin: 12px;
  border: 1px solid var(--pa-line);
  border-radius: 14px;
  background: var(--pa-surface);
}

/* ── project switcher ─────────────────────────────────────────────────────── */

.ws__switch {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 30px;
  padding: 0 11px;
  border: 1px solid var(--pa-border);
  border-radius: 9px;
  background: var(--pa-input);
  color: var(--pa-dim);
  font-family: var(--font-body);
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s, color .16s;
}

.ws__switch:hover {
  background: var(--pa-hover-2);
  color: var(--pa-text-hi);
}

.ws__switch-icon {
  width: 15px;
  height: 15px;
  display: block;
}

.ws__switch-name {
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── plugin chip ──────────────────────────────────────────────────────────── */

.ws__plug {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 7px;
  height: 30px;
  padding: 0 12px;
  border: 1px solid var(--pa-border);
  border-radius: 999px;
  background: var(--pa-input);
  font-size: 12.5px;
  color: var(--pa-muted);
  white-space: nowrap;
}

.ws__plug i {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--pa-idle);
}

.ws__plug span {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Discord ──────────────────────────────────────────────────────────────── */

/* The one place on the product with a colour that is not the brand blue. It is
   an outbound link to someone else's service, and it is meant to look like it. */
.ws__discord {
  flex: none;
  display: flex;
  align-items: center;
  gap: 9px;
  height: 32px;
  padding: 0 15px 0 12px;
  border-radius: 999px;
  background: linear-gradient(120deg, #5865f2 0%, #7b6cf6 46%, #a07df0 100%);
  box-shadow: 0 10px 24px -12px rgba(88, 101, 242, .9),
    inset 0 1px 0 rgba(255, 255, 255, .35);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  text-decoration: none;
  transition: transform .16s, box-shadow .16s;
}

.ws__discord:hover {
  color: #fff;
  transform: translateY(-1px);
  box-shadow: 0 16px 30px -12px rgba(88, 101, 242, 1),
    inset 0 1px 0 rgba(255, 255, 255, .45);
}

.ws__discord:active {
  transform: none;
}

/* In a project the header has no room for the full pill -- shrink to an
   icon rather than disappearing outright, so the invite is still reachable. */
.ws--open .ws__discord {
  gap: 0;
  padding: 0;
  width: 32px;
  justify-content: center;
}

.ws--open .ws__discord-text,
.ws--open .ws__vip {
  display: none;
}

.ws__vip {
  padding: 1px 6px;
  border-radius: 999px;
  background: rgba(255, 255, 255, .22);
  font-size: 10px;
  letter-spacing: .08em;
  text-transform: uppercase;
}

/* ── notification bell ───────────────────────────────────────────────────
   Same class names and markup shape public/app.js's own bellHtml() uses
   on the standalone pages -- duplicated here rather than pulled from
   app.css (which this page doesn't load at all; workspace.js/.css is
   entirely self-contained) so the two never need to agree on anything
   beyond the class names themselves. */

.nav__bell-wrap {
  position: relative;
}

.nav__bell {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--pa-muted-2);
  cursor: pointer;
  transition: background .14s, color .14s;
}

.nav__bell:hover {
  background: var(--pa-hover-2);
  color: var(--pa-text-hi);
}

.nav__bell-dot {
  position: absolute;
  top: 2px;
  right: 3px;
  min-width: 15px;
  height: 15px;
  padding: 0 3px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  background: var(--pa-danger-strong);
  border: 2px solid var(--pa-bg);
  font-size: 9.5px;
  font-weight: 700;
  line-height: 1;
  color: #fff;
}

.nav__bell-pop {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 40;
  width: 320px;
  max-height: 400px;
  overflow-y: auto;
  padding: 10px;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface);
  box-shadow: 0 24px 44px -22px var(--pa-shadow);
}

.nav__bell-title {
  padding: 6px 8px 10px;
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
}

.nav__bell-item {
  position: relative;
  padding: 10px 8px 10px 18px;
  border-radius: 10px;
  font-size: 13px;
  color: var(--pa-soft);
}

.nav__bell-item a {
  color: inherit;
  text-decoration: none;
}

.nav__bell-item a:hover {
  text-decoration: underline;
}

.nav__bell-item--unread::before {
  content: "";
  position: absolute;
  left: 6px;
  top: 18px;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--pa-accent);
}

.nav__bell-time {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  color: var(--pa-muted-4);
}

.nav__bell-empty {
  padding: 14px 8px;
  font-size: 13px;
  color: var(--pa-muted-3);
  text-align: center;
}

.ws__logo {
  display: flex;
  align-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
}

.ws__logo img {
  width: 40px;
  height: 40px;
  display: block;
  transition: filter .2s, transform .3s var(--ease-spring);
}

/* Greyscale at rest in dark mode, colour on hover — the design's touch. */
.ws__logo img.is-dark {
  filter: grayscale(1);
}

.ws__logo:hover img {
  filter: none;
  transform: scale(1.08);
}

.ws__title {
  font-size: 14.5px;
  font-weight: 600;
  white-space: nowrap;
}

.ws__wordmark {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 20px;
  letter-spacing: .005em;
  color: var(--pa-text);
  white-space: nowrap;
}

.ws__chip {
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--pa-border);
  background: var(--pa-hover);
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--pa-muted);
  white-space: nowrap;
}

.ws__actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ── buttons ──────────────────────────────────────────────────────────────── */

.ws-btn {
  height: 32px;
  padding: 0 13px;
  border: 1px solid var(--pa-border);
  border-radius: 9px;
  background: transparent;
  color: var(--pa-dim);
  font-family: var(--font-body);
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s, border-color .16s, color .16s;
}

.ws-btn:hover:not(:disabled) {
  background: var(--pa-hover-2);
  border-color: var(--pa-border-3);
  color: var(--pa-text-hi);
}

.ws-btn:disabled {
  color: var(--pa-muted-4);
  cursor: default;
}

/* .ws-btn on an <a> (the header's Marketplace link) rather than a <button> --
   an anchor is inline by default and would otherwise ignore the height/
   padding above entirely. */
a.ws-btn {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
}

.ws-btn--on {
  background: var(--pa-hover);
}

.ws-btn--primary {
  height: 32px;
  padding: 0 15px;
  border: 0;
  border-radius: 9px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws-btn--primary:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

.ws-btn--primary:active {
  background: var(--pa-accent-active);
  transform: translateY(0);
}

.ws-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--pa-border);
  border-radius: 9px;
  background: transparent;
  color: var(--pa-dim);
  cursor: pointer;
  transition: background .16s, border-color .16s, color .16s;
}

.ws-icon:hover,
.ws-icon--on {
  background: var(--pa-hover-2);
  border-color: var(--pa-border-3);
  color: var(--pa-text-hi);
}

/* ── avatar ───────────────────────────────────────────────────────────────── */

.ws__avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--pa-border-2);
  border-radius: 999px;
  background: var(--pa-hover-2);
  overflow: hidden;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  color: var(--pa-dim-4);
  cursor: pointer;
  transition: border-color .16s;
}

.ws__avatar:hover {
  border-color: var(--pa-border-4);
}

.ws__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ── popovers ─────────────────────────────────────────────────────────────── */

.ws__pop-host {
  position: relative;
  flex: none;
}

.ws-pop {
  position: absolute;
  top: 38px;
  right: 0;
  z-index: 70;
  padding: 6px;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface);
  box-shadow: 0 26px 48px -26px var(--pa-shadow);
  animation: pa-gate .16s var(--ease-out) both;
}

.ws-pop--theme {
  width: 168px;
}

.ws-pop--account {
  width: 252px;
  padding: 8px;
  border-radius: 16px;
  box-shadow: 0 30px 54px -28px var(--pa-shadow);
}

.ws-pop__item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  height: 38px;
  padding: 0 12px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  color: var(--pa-soft);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background .14s, color .14s;
}

.ws-pop__item:hover {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

.ws-pop__item--on {
  background: var(--pa-hover);
}

.ws-pop__item--plain {
  font-weight: 400;
  padding: 0 10px;
}

.ws-pop__item--danger {
  color: var(--pa-danger-text);
  font-weight: 400;
}

.ws-pop__item--danger:hover {
  background: var(--pa-error-tint);
  color: var(--pa-danger-strong);
}

.ws-pop__tick {
  margin-left: auto;
}

.ws-pop__who {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px 12px;
}

.ws-pop__who-face {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 34px;
  height: 34px;
  border: 1px solid var(--pa-border-2);
  border-radius: 999px;
  background: var(--pa-hover-2);
  overflow: hidden;
  font-size: 12px;
  font-weight: 600;
  color: var(--pa-dim-4);
}

.ws-pop__who-face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ws-pop__who-text {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.ws-pop__name {
  font-size: 14px;
  font-weight: 600;
  color: var(--pa-text-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ws-pop__sub {
  font-size: 11.5px;
  color: var(--pa-muted-3);
  white-space: nowrap;
}

.ws-pop__rule {
  height: 1px;
  margin: 7px 4px;
  background: var(--pa-line);
}

/* ── projects pill ────────────────────────────────────────────────────────── */

/* This replaced a permanent 236px sidebar. In project mode that sidebar was
   dead weight, and in projects mode the scattered cards are already the list —
   so the roster moved behind a pill that floats over the stage. */
.ws__projects-host {
  position: absolute;
  z-index: 30;
  top: 14px;
  left: 16px;
}

/* Project mode has the switcher in the header instead. */
.ws--open .ws__projects-host {
  display: none;
}

.ws__projects-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  height: 34px;
  padding: 0 13px;
  border: 1px solid var(--pa-border);
  border-radius: 10px;
  background: var(--pa-surface-2);
  color: var(--pa-dim);
  font-family: var(--font-body);
  font-size: 13.5px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s, color .16s, border-color .16s;
}

.ws__projects-btn:hover,
.ws__projects-btn.is-on {
  background: var(--pa-hover-2);
  border-color: var(--pa-border-3);
  color: var(--pa-text-hi);
}

.ws__projects-count {
  font-family: ui-monospace, monospace;
  font-size: 12px;
  color: var(--pa-muted-3);
}

/* .ws-pop pins itself to the right by default, for the header menus. This one
   hangs off the left edge of its pill instead. */
.ws-pop--projects {
  top: 40px;
  right: auto;
  left: 0;
  width: 268px;
  padding: 8px;
}

/* The popover scrolls rather than growing past the window on a big roster. */
.ws-pop--projects .ws__list {
  max-height: 52vh;
  overflow-y: auto;
}

.ws__new {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  height: 40px;
  border: 0;
  border-radius: 12px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws__new:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

.ws__new:active {
  background: var(--pa-accent-active);
  transform: translateY(0);
}

.ws__side-label {
  padding: 14px 20px 6px;
  font-size: 12px;
  color: var(--pa-muted-2);
}

.ws__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 8px 8px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.ws__row {
  position: relative;
  display: flex;
  align-items: center;
  gap: 4px;
  padding-right: 2px;
  border-radius: 9px;
  background: transparent;
  transition: background .15s;
}

.ws__row--open {
  background: var(--pa-hover);
}

.ws__row-open {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 4px 8px 10px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--pa-dim-5);
  text-align: left;
  cursor: pointer;
  transition: color .15s;
}

.ws__row-open:hover {
  color: var(--pa-text-hi);
}

.ws__row--open .ws__row-open {
  color: var(--pa-text-hi);
  font-weight: 600;
}

.ws__row-icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
}

.ws__row-icon img {
  width: 17px;
  height: 17px;
  display: block;
  opacity: .9;
}

.ws__row-name {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws__row-dots {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--pa-muted);
  opacity: 0;
  cursor: pointer;
  transition: opacity .15s, background .15s, color .15s;
}

.ws__row:hover .ws__row-dots,
.ws__row-dots--on {
  opacity: 1;
}

.ws__row-dots:hover {
  background: var(--pa-hover-2);
  color: var(--pa-text-hi);
}

.ws__row-menu {
  position: absolute;
  top: 32px;
  right: -6px;
  z-index: 40;
  width: 186px;
  padding: 6px;
  border: 1px solid var(--pa-border);
  border-radius: 13px;
  background: var(--pa-surface);
  box-shadow: 0 24px 44px -22px var(--pa-shadow);
  animation: pa-gate .16s var(--ease-out) both;
}

.ws__row-menu button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 36px;
  padding: 0 10px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--pa-soft);
  font-family: var(--font-body);
  font-size: 13.5px;
  text-align: left;
  cursor: pointer;
  transition: background .14s;
}

.ws__row-menu button:hover {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

.ws__row-menu .is-danger {
  color: var(--pa-danger-text);
}

.ws__row-menu .is-danger:hover {
  background: var(--pa-error-tint);
  color: var(--pa-danger-strong);
}

.ws__side-empty {
  padding: 10px 20px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--pa-muted-3);
}

/* ── side panel ───────────────────────────────────────────────────────────── */

/* Replaced an icon rail plus a panel that slid in over the chat. Three tabs in
   a column that is always on screen costs no clicks to reach and never covers
   what you were reading. */
.ws__panel {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  overflow: hidden;
  background: var(--pa-surface);
}

.ws__tabs {
  flex: none;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border-bottom: 1px solid var(--pa-line);
}

.ws__tab {
  position: relative;
  height: 42px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--pa-muted-2);
  font-family: var(--font-body);
  font-size: 13.5px;
  cursor: pointer;
  transition: color .16s, border-color .16s, background .16s;
}

/* Models tab, until it's actually been opened once -- see
   MODELS_TAB_SEEN_KEY's own comment in workspace.js. */
.ws__tab-dot {
  position: absolute;
  top: 8px;
  right: 6px;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--pa-accent);
}

.ws__tab:hover {
  background: var(--pa-hover);
  color: var(--pa-text);
}

.ws__tab.is-on {
  border-bottom-color: var(--pa-accent);
  color: var(--pa-text-hi);
  font-weight: 600;
}

.ws__panel-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.ws__panel-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 10px 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Chat history */

.ws__newchat {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  height: 42px;
  padding: 0 13px;
  border: 1px solid var(--pa-border);
  border-radius: 12px;
  background: transparent;
  color: var(--pa-text);
  font-family: var(--font-body);
  font-size: 14px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .16s, border-color .16s;
}

.ws__newchat:hover {
  background: var(--pa-input);
  border-color: var(--pa-border-3);
}

.ws__chatrow {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 13px;
  border-radius: 12px;
  background: var(--pa-input);
  font-size: 13.5px;
  color: var(--pa-text);
}

.ws__chatrow span:nth-child(2) {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws__hint {
  margin: 6px 2px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--pa-muted-3);
}

/* Model library (the Models tab) -- cards mirror .ws__attach-chip's own
   layered image/icon + name + remove-button shape below, just bigger and
   in a grid, since this is a place to browse rather than a row of chips
   already picked. */

.ws__model-group + .ws__model-group {
  margin-top: 14px;
}

.ws__model-group-title {
  margin: 0 2px 8px;
  font-size: 12px;
  font-weight: 600;
  color: var(--pa-muted-2);
  text-transform: uppercase;
  letter-spacing: .03em;
}

.ws__model-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 8px;
}

.ws__model-card {
  display: block;
  position: relative;
  aspect-ratio: 1;
  border-radius: 12px;
  border: 1px solid var(--pa-border);
  background: var(--pa-deep-2);
  overflow: hidden;
  cursor: grab;
  user-select: none;
  transition: border-color .16s;
}

.ws__model-card:hover {
  border-color: var(--pa-accent);
}

.ws__model-card:active {
  cursor: grabbing;
}

.ws__model-card-thumb {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--pa-surface-2);
  color: var(--pa-accent);
}

.ws__model-card-thumb img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  pointer-events: none;
}

.ws__model-card-name {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  box-sizing: border-box;
  padding: 4px 6px;
  background: rgba(8, 8, 8, .72);
  font-size: 10px;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws__model-card-x {
  position: absolute;
  top: 4px;
  right: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 999px;
  background: rgba(8, 8, 8, .72);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  transition: opacity .16s;
}

.ws__model-card:hover .ws__model-card-x {
  opacity: 1;
}

.ws__model-card-x:hover {
  background: rgba(8, 8, 8, .9);
}

/* Settings toggles */

.ws__toggle {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px;
  border: 0;
  border-radius: 12px;
  background: var(--pa-surface-2);
  text-align: left;
  cursor: pointer;
  transition: background .16s;
}

.ws__toggle:hover {
  background: var(--pa-hover);
}

.ws__toggle-text {
  flex: 1;
  min-width: 0;
}

.ws__toggle-name {
  display: block;
  margin-bottom: 3px;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--pa-text);
}

.ws__toggle-desc {
  display: block;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--pa-muted-2);
}

.ws__track {
  position: relative;
  flex: none;
  width: 38px;
  height: 22px;
  border-radius: 999px;
  background: var(--pa-border-2);
  transition: background .2s;
}

.ws__toggle[aria-pressed="true"] .ws__track {
  background: var(--pa-accent);
}

.ws__track::after {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  border-radius: 999px;
  background: var(--pa-text-hi);
  transition: left .2s var(--ease-in-out);
}

.ws__toggle[aria-pressed="true"] .ws__track::after {
  left: 19px;
}

.ws__plugin-row {
  margin-top: 4px;
  padding: 13px 14px;
  border: 1px solid var(--pa-border);
  border-radius: 12px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.ws__plugin-row span:nth-child(2) {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  color: var(--pa-dim);
}

/* Game map */

.ws__map {
  flex: 1;
  min-height: 0;
  position: relative;
  overflow: hidden;
  border-top: 1px solid var(--pa-line);
}

.ws__map-grid {
  position: absolute;
  inset: 0;
  background-image: radial-gradient(var(--pa-grid) 1px, transparent 1px);
  background-size: 24px 24px;
}

.ws__map-bar {
  position: absolute;
  z-index: 3;
  top: 12px;
  left: 12px;
  right: 12px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.ws__map-chip {
  display: flex;
  align-items: center;
  gap: 7px;
  height: 28px;
  padding: 0 11px;
  border: 1px solid var(--pa-border);
  border-radius: 9px;
  background: var(--pa-input);
  font-size: 12px;
  color: var(--pa-dim);
  white-space: nowrap;
}

.ws__map-chip img {
  width: 13px;
  height: 13px;
  display: block;
}

.ws__map-tools {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 6px;
}

.ws__map-tools button {
  height: 28px;
  min-width: 28px;
  padding: 0 11px;
  border: 1px solid var(--pa-border);
  border-radius: 9px;
  background: var(--pa-input);
  color: var(--pa-dim);
  font-family: var(--font-body);
  font-size: 12.5px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s;
}

.ws__map-tools button:hover {
  background: var(--pa-hover-2);
}

/* Set apart from the zoom cluster: it does something to the project, not to
   the view of it. */
.ws__map-import {
  margin-left: 6px;
}

.ws__zoom {
  min-width: 38px;
  text-align: center;
  font-size: 12px;
  color: var(--pa-muted-2);
  font-variant-numeric: tabular-nums;
}

.ws__map-surface {
  position: absolute;
  inset: 50px 16px 78px;
  overflow: hidden;
  cursor: grab;
  /* Pan and pinch-zoom are handled by hand in workspace.js -- the browser's
     own scroll/zoom gestures on this element would fight them. */
  touch-action: none;
}

.ws__map-surface--dragging {
  cursor: grabbing;
}

/* Positioned by hand (left/top 50% + a JS-built transform) rather than by
   the parent's flexbox centring, so pan and zoom-toward-cursor can move it
   off centre. workspace.js writes `transform` directly on this element for
   every wheel tick and drag frame -- see mapTransform(). */
.ws__plot {
  position: absolute;
  left: 50%;
  top: 50%;
  height: min(100%, 420px);
  max-width: 100%;
  aspect-ratio: 1;
}

.ws__plot-base {
  position: absolute;
  inset: 0;
  border: 1px solid var(--pa-border-2);
  border-radius: 14px;
  background: var(--pa-surface-2);
}

.ws__plot-size {
  position: absolute;
  bottom: 6px;
  right: 9px;
  font-family: var(--font-mono);
  font-size: 10.5px;
  letter-spacing: .04em;
  color: var(--pa-muted-4);
}

.ws__map-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
}

.ws__map-empty img {
  width: 34px;
  height: 34px;
  display: block;
  opacity: .5;
}

.ws__map-stats {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 11px 12px;
  border-top: 1px solid var(--pa-line);
  background: var(--pa-bg);
  display: flex;
  align-items: center;
  gap: 8px;
}

.ws__stat {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ws__stat-label {
  font-size: 10.5px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
}

.ws__stat-value {
  font-family: var(--font-mono);
  font-size: 13px;
  color: var(--pa-text);
}

/* ── empty state ──────────────────────────────────────────────────────────── */

.ws__empty {
  position: relative;
  flex: 1;
  min-height: 0;
  /* overflow-y is a safety net, not the plan -- Aydin: "you shouldn't be
     able to scroll [here], bring everything higher." The real fix is
     .ws__hero-logo/.ws__hero-name/.ws__cards/.ws__chips below all being
     shrunk enough that the whole hero fits inside one screen without it
     on any normal viewport; auto stays only so a genuinely tiny window
     (browser resized narrow+short, an actual edge case, not the reported
     one) degrades to a scrollbar instead of silently clipping the
     composer or the cards off the bottom. */
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 10px 20px 8px;
  animation: pa-in .35s ease both;
}

.ws__empty-mid {
  margin: auto 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.ws__empty-grid {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: radial-gradient(var(--pa-grid-2) 1px, transparent 1px);
  background-size: 26px 26px;
  mask-image: radial-gradient(120% 90% at 50% 40%, #000 20%, transparent 78%);
}

.ws__hero-logo {
  position: relative;
  /* Was 156px -- part of the "bring everything higher" pass (see
     .ws__empty's own comment): the single biggest chunk of vertical
     space in the hero, so the first thing trimmed. */
  width: 128px;
  height: 128px;
  display: block;
  margin: 0 0 -12px;
}

.ws__hero-logo.is-dark {
  filter: grayscale(1);
}

.ws__hero-name {
  position: relative;
  margin: 0;
  font-family: var(--font-display);
  font-weight: 400;
  /* Was 68px -- see .ws__empty's own comment on the "bring everything
     higher" pass; the 620px media query below still drops this further
     on narrow/short screens on top of this smaller base. */
  font-size: 56px;
  line-height: 1;
  color: var(--pa-text-1);
}

.ws__hero-sub {
  position: relative;
  margin: 6px 0 14px;
  font-size: 14px;
  white-space: nowrap;
  color: var(--pa-muted-1);
}

/* ── gate ─────────────────────────────────────────────────────────────────── */

.ws__gate {
  position: relative;
  width: 100%;
  max-width: 420px;
  margin: 0 auto 14px;
  padding: 16px 18px 18px;
  border: 1px solid var(--pa-border);
  border-radius: 16px;
  background: var(--pa-deep-2);
  text-align: left;
  box-shadow: 0 26px 44px -26px var(--pa-shadow);
  animation: pa-gate .3s var(--ease-out) both;
}

.ws__gate-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
}

.ws__dots {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 14px;
}

.ws__dots i {
  width: 5px;
  height: 5px;
  border-radius: 999px;
  background: var(--pa-soft-2);
  animation: pa-dot 1s ease-in-out infinite;
}

.ws__dots i:nth-child(2) { animation-delay: .16s; }
.ws__dots i:nth-child(3) { animation-delay: .32s; }

.ws__gate-title {
  flex: 1;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--pa-text-2);
}

.ws__gate-x {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--pa-muted-2);
  cursor: pointer;
  transition: background .16s, color .16s;
}

.ws__gate-x:hover {
  background: var(--pa-input);
  color: var(--pa-text-hi);
}

.ws__gate p {
  margin: 0 0 14px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--pa-muted);
  text-wrap: pretty;
}

.ws__gate-go {
  width: 100%;
  height: 42px;
  border: 0;
  border-radius: 12px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14.5px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws__gate-go:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

/* ── composer ─────────────────────────────────────────────────────────────── */

.ws__composer {
  position: relative;
  /* Explicit, not auto -- :hover below sets a transform, and ANY
     transform (even a 2px nudge) silently opens its own stacking context
     per the CSS spec. Without a z-index of its own here, that context
     then stacks by DOM order against .ws__cards just below it (which
     carries its own explicit z-index: 2, for its own unrelated reason --
     see .ws__cards's own comment), and .ws__cards wins: any popover
     opened from inside the composer (mode/model/attach dropdowns) got
     visually clipped by the stat cards the instant the mouse hovered the
     composer to open one. Set unconditionally, not just on :hover, so
     stacking doesn't jump the moment the transform (dis)appears.
     Aydin's bug1.png: the mode dropdown's new Auto row (added this same
     session) cut off behind the "Projects" card -- this is that bug. */
  z-index: 3;
  width: 100%;
  max-width: 640px;
  padding: 13px;
  border: 1px solid var(--pa-accent);
  border-radius: 16px;
  background: var(--pa-input);
  box-shadow: 0 26px 50px -34px var(--pa-shadow-2), inset 0 1px 0 var(--pa-grid-3);
  transition: border-color .18s, transform .18s, box-shadow .18s, padding .2s cubic-bezier(.4,0,.2,1);
}

.ws__composer:hover {
  border-color: var(--pa-faint);
  transform: translateY(-2px);
  box-shadow: 0 32px 56px -32px var(--pa-shadow), inset 0 1px 0 var(--pa-grid-4);
}

/* A soft breathing ring while actively typing -- see the composerBox
   .is-typing toggle in the send()/textarea input handler. */
.ws__composer.is-typing {
  border-color: var(--pa-accent);
  box-shadow: 0 0 0 3px var(--pa-accent-tint);
}

/* Paused while the build-confirm panel (.ws__confirm, below) is answering a
   question -- see render()'s confirming toggle. Nothing else should happen
   until that panel is answered, the same rule Claude Code's own permission
   prompt follows. */
.ws__composer.is-confirming,
.ws__dock-inner.is-confirming {
  opacity: .5;
  pointer-events: none;
}

/* The mode chip sits beside the field now, not the upload button -- see
   .ws__composer-foot for where upload moved to. .ws__ghost's inset:0 still
   keeps aligning with the textarea exactly either way. */
.ws__composer-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.ws__field {
  position: relative;
  flex: 1;
  min-width: 0;
}

.ws__composer textarea {
  position: relative;
  z-index: 2;
  width: 100%;
  min-height: 28px;
  max-height: 260px;
  overflow-y: auto;
  resize: none;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--pa-text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  transition: height .16s cubic-bezier(.4,0,.2,1);
}

.ws__ghost {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  font-size: 16px;
  line-height: 1.6;
  color: var(--pa-ghost);
}

.ws__caret {
  display: inline-block;
  width: 2px;
  height: 17px;
  margin-left: 2px;
  vertical-align: -3px;
  background: var(--pa-accent);
  animation: pa-blink 1s steps(1) infinite;
}

.ws__composer-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--pa-line);
}

/* ── attach / generate-3D trigger ────────────────────────────────────────
   Round icon button in the foot row, same family as the model chip and
   credits pill beside it. Shared class with the plain attach button this
   used to be before it morphed into a single trigger for both attach and
   Generate 3D -- see #btnAttach's own comment in app.html and
   .ws__attach-wrap/.ws-pop--attach below for its own popover. */
.ws__upload {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid var(--pa-border);
  border-radius: 999px;
  background: transparent;
  color: var(--pa-muted-2);
  cursor: pointer;
  transition: background .14s, color .14s, border-color .14s;
}

.ws__upload:hover,
.ws__upload.is-on {
  background: var(--pa-hover-b);
  color: var(--pa-text-hi);
  border-color: var(--pa-border-3);
}

/* Pushes everything from the model chip onward to the right of the foot
   row -- same trick as .pa-spacer in the source design. */
.ws__spacer {
  margin-left: auto;
}

.ws__dock-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.ws__dock-row textarea {
  flex: 1;
  min-width: 0;
}

/* ── attachment thumbnails ────────────────────────────────────────────────
   What was picked, shown back -- not yet sent anywhere. No storage bucket
   or upload middleware exists yet (see lib/app.js), so this is exactly as
   far as a file gets today. Square preview cards, not pills -- an image
   you attached is worth actually seeing before you send it. */
.ws__attach {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}

.ws__attach-chip {
  display: block;
  position: relative;
  flex: none;
  width: 66px;
  height: 66px;
  border-radius: 12px;
  border: 1px solid var(--pa-border);
  background: var(--pa-deep-2);
  overflow: hidden;
  cursor: grab;
  user-select: none;
  animation: pa-thumb-in .18s var(--ease-out);
}

.ws__attach-chip:active {
  cursor: grabbing;
}

@keyframes pa-thumb-in {
  from { opacity: 0; transform: scale(.85); }
  to { opacity: 1; transform: scale(1); }
}

.ws__attach-chip img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 0;
  object-fit: cover;
  pointer-events: none;
}

.ws__attach-chip-icon {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  background: var(--pa-surface-2);
  color: var(--pa-accent);
}

.ws__attach-chip-name {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  box-sizing: border-box;
  padding: 3px 5px;
  background: rgba(8, 8, 8, .72);
  font-size: 9.5px;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws__attach-chip-x {
  position: absolute;
  top: 4px;
  right: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border: 0;
  border-radius: 999px;
  background: rgba(8, 8, 8, .72);
  color: #fff;
  cursor: pointer;
}

.ws__attach-chip-x:hover {
  background: rgba(8, 8, 8, .9);
}

/* Drag-to-reorder drop indicator -- see the attach-list mousedown handler
   in workspace.js. */
.ws__attach-line {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--pa-accent);
  border-radius: 999px;
  z-index: 2;
  pointer-events: none;
}

.ws__attach-line--before { left: -5px; }
.ws__attach-line--after { right: -5px; }

.ws__status {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--pa-muted-2);
  white-space: nowrap;
}

.ws__status i {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: var(--pa-idle);
}

.ws__spin {
  width: 10px;
  height: 10px;
  border: 2px solid var(--pa-border-2);
  border-top-color: var(--pa-dim-4);
  border-radius: 999px;
  animation: pa-spin .7s linear infinite;
}

.ws__send {
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 18px;
  border: 0;
  border-radius: 999px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 14px;
  white-space: nowrap;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws__send:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-2px);
}

.ws__send:active {
  background: var(--pa-accent-active);
  transform: translateY(0);
}

/* ── suggestion chips ─────────────────────────────────────────────────────── */

.ws__chips {
  position: relative;
  margin-top: 12px;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}

.ws__chip-btn {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 6px 14px 6px 6px;
  border: 1px solid var(--pa-border);
  border-radius: 999px;
  background: var(--pa-surface-2);
  color: var(--pa-dim);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  animation: pa-bob 8.4s ease-in-out infinite;
  transition: border-color .18s, color .18s, background .18s;
}

.ws__chip-btn:hover {
  border-color: var(--pa-border-4);
  color: var(--pa-text-hi);
  background: var(--pa-hover-b);
}

.ws__chip-btn:nth-child(1) { --tw: -3deg; transform: rotate(-3deg); margin-top: 6px; }
.ws__chip-btn:nth-child(2) { --tw: 2.5deg; transform: rotate(2.5deg); margin-bottom: 18px; animation-duration: 9.6s; animation-delay: .7s; }
.ws__chip-btn:nth-child(3) { --tw: -2deg; transform: rotate(-2deg); margin-top: 10px; animation-duration: 8.9s; animation-delay: 1.4s; }

.ws__chip-btn span {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  overflow: hidden;
}

.ws__chip-btn img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ── stat cards ───────────────────────────────────────────────────────────── */

.ws__cards {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 640px;
  /* Was 22px -- see .ws__empty's own comment on the "bring everything
     higher" pass. */
  margin: 14px auto 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}

.ws__card {
  padding: 13px 14px;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface-2);
  text-align: left;
}

.ws__card-label {
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
}

.ws__card-big {
  font-family: var(--font-display);
  font-size: 24px;
  line-height: 1;
  color: var(--pa-text);
}

/* Total build minutes, under the "Recent builds" label. lib/generate.js and
   message.js's handle() both measure a build's time; this is the sum. */
.ws__card-sub {
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--pa-muted-2);
}

.ws__bar {
  margin-top: 10px;
  height: 4px;
  border-radius: 999px;
  background: var(--pa-hover-2);
  overflow: hidden;
}

.ws__bar i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--pa-accent);
}

/* Sits on the same baseline as the big number: "6 of 10". */
.ws__card-of {
  margin-left: 6px;
  font-size: 12.5px;
  color: var(--pa-muted-3);
}

.ws__recent {
  margin-top: 7px;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.ws__recent-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 12.5px;
}

.ws__recent-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--pa-dim);
}

.ws__recent-when {
  flex: none;
  color: var(--pa-muted-3);
}

/* ── fanned project cards ─────────────────────────────────────────────────── */

.ws__fan {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
}

.ws__fan-item {
  display: block;
  position: absolute;
  perspective: 700px;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
}

/* The tilt target -- onFanTilt() in workspace.js sets its transform
   directly on mousemove, so this only needs the resting state and the
   transition back to it. */
.ws__fan-tilt {
  position: relative;
  display: block;
  box-sizing: border-box;
  border-radius: 19px;
  padding: 3px;
  background: linear-gradient(148deg,
    var(--pa-text-3) 0%, var(--pa-dim-3) 24%, var(--pa-muted-3c) 50%,
    var(--pa-text-4) 72%, var(--pa-muted) 100%);
  box-shadow: 0 18px 30px -18px var(--pa-shadow);
  transform-style: preserve-3d;
  transition: transform .3s cubic-bezier(.22, .68, .24, 1);
}

.ws__fan-shot {
  display: block;
  border-radius: 16px;
  overflow: hidden;
  aspect-ratio: 16 / 9;
}

.ws__fan-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* A soft static gloss over every card, on top of the shot. */
.ws__fan-gloss {
  position: absolute;
  inset: 3px;
  z-index: 2;
  pointer-events: none;
  border-radius: 16px;
  background:
    linear-gradient(122deg,
      rgba(255, 255, 255, .78) 0%, rgba(255, 255, 255, .2) 20%, rgba(255, 255, 255, .02) 42%,
      rgba(255, 255, 255, .16) 62%, rgba(255, 255, 255, .62) 86%, rgba(255, 255, 255, .24) 100%),
    repeating-linear-gradient(122deg,
      rgba(255, 255, 255, .07) 0px, rgba(255, 255, 255, 0) 2px, rgba(255, 255, 255, .07) 4px);
  mix-blend-mode: screen;
}

/* The specular highlight that follows the cursor -- --mx/--my are written
   by onFanTilt(), opacity toggled with it too. */
.ws__fan-sheen {
  position: absolute;
  inset: 3px;
  z-index: 3;
  pointer-events: none;
  border-radius: 16px;
  opacity: 0;
  background: radial-gradient(60% 60% at var(--mx, 50%) var(--my, 50%),
    rgba(255, 255, 255, .8) 0%, rgba(255, 255, 255, .3) 32%, rgba(255, 255, 255, 0) 68%);
  transition: opacity .25s ease;
}

.ws__fan-item:nth-child(1) { --tw: 4deg;  right: 2%;  top: 47%; margin-top: -54px; width: 192px; animation: pa-rise .6s cubic-bezier(.2,.75,.3,1) .5s  both, pa-bob 9.8s ease-in-out infinite 1.3s; }
.ws__fan-item:nth-child(2) { --tw: -8deg; left: 8%;   top: 13%;                    width: 232px; animation: pa-rise .6s cubic-bezier(.2,.75,.3,1) .18s both, pa-bob 8s   ease-in-out infinite .6s;  }
.ws__fan-item:nth-child(3) { --tw: 6deg;  left: 11%;  bottom: 12%;                 width: 210px; animation: pa-rise .6s cubic-bezier(.2,.75,.3,1) .26s both, pa-bob 9.2s ease-in-out infinite .9s;  }
.ws__fan-item:nth-child(4) { --tw: 5deg;  right: 8%;  top: 11%;                    width: 232px; animation: pa-rise .6s cubic-bezier(.2,.75,.3,1) .34s both, pa-bob 8.6s ease-in-out infinite .4s;  }
.ws__fan-item:nth-child(5) { --tw: -6deg; right: 11%; bottom: 14%;                 width: 210px; animation: pa-rise .6s cubic-bezier(.2,.75,.3,1) .42s both, pa-bob 9.8s ease-in-out infinite 1.3s; }

/* ── chat ─────────────────────────────────────────────────────────────────── */

.ws__chat {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.ws__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 24px 24px 6px;
}

.ws__thread {
  max-width: 700px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.ws__msg-user {
  display: flex;
  justify-content: flex-end;
  animation: pa-in .24s ease both;
}

/* Was a bare <span> -- fine while it only ever held text, but a block
   child (the image below) needs a real box. */
.ws__msg-user-bubble {
  max-width: 78%;
  padding: 11px 14px;
  border-radius: 14px 14px 5px 14px;
  background: var(--pa-accent);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--pa-on-accent);
  white-space: pre-wrap;
}

/* The picture an image-to-build message actually sent -- see the thumb
   markup in renderThread(). Capped well under the bubble's own max-width
   so a portrait photo never towers over the words next to it. */
.ws__msg-user-image {
  display: block;
  max-width: 100%;
  max-height: 200px;
  margin-bottom: 8px;
  border-radius: 10px;
  object-fit: cover;
}

.ws__msg-ai {
  display: flex;
  gap: 12px;
  animation: pa-in .24s ease both;
}

.ws__msg-face {
  flex: none;
  width: 24px;
  height: 24px;
  margin-top: 2px;
  border-radius: 8px;
  overflow: hidden;
}

.ws__msg-face img {
  width: 100%;
  height: 100%;
  display: block;
}

.ws__msg-face img.is-dark {
  filter: grayscale(1);
}

.ws__msg-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ws__prose {
  font-size: 14.5px;
  line-height: 1.65;
  color: var(--pa-soft);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* A system state in the thread ("out of credits"), not a reply Payama wrote
   -- boxed apart with a border and a warning tint so it doesn't read as one
   more paragraph among the AI's own messages. Scoped to the --credits
   modifier (the JS that builds this already applies both classes) rather
   than bare .ws__notice -- bare .ws__notice used to also be the admin
   broadcast banner's own class (since removed in favour of showing the
   broadcast in the bell instead); sharing it was a bug even then, not
   deliberate reuse, so kept scoped here rather than un-doing the fix. */
.ws__notice--credits {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  margin: 4px 0;
  border: 1px solid var(--pa-warning);
  border-radius: 12px;
  background: var(--pa-warning-tint);
  animation: pa-in .24s ease both;
}

.ws__notice--credits svg {
  flex: none;
  color: var(--pa-warning);
}

.ws__notice-text {
  flex: 1;
  min-width: 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--pa-text);
}

.ws__notice-cta {
  flex: none;
  padding: 7px 12px;
  border: 0;
  border-radius: 8px;
  background: var(--pa-warning);
  color: var(--pa-bg);
  font: inherit;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: opacity .14s;
}

.ws__notice-cta:hover {
  opacity: .88;
}

/* Only ever appears on a reply that actually built something -- see
   send()'s own comment on `share`. align-self stops it stretching to the
   full width .ws__msg-body's flex-column would otherwise give it. */
.ws__msg-share {
  align-self: flex-start;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 11px;
  border: 1px solid var(--pa-border);
  border-radius: 8px;
  background: transparent;
  color: var(--pa-muted-3);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: color .14s, border-color .14s, background .14s;
}

.ws__msg-share:hover {
  color: var(--pa-accent);
  border-color: var(--pa-accent);
  background: var(--pa-accent-tint);
}

.ws__msg-share:disabled {
  opacity: .5;
  cursor: default;
}

.ws__code {
  border: 1px solid var(--pa-border);
  border-radius: 12px;
  background: var(--pa-bg-2);
  overflow: hidden;
}

.ws__code-head {
  padding: 6px 12px;
  border-bottom: 1px solid var(--pa-hover);
  font-size: 10.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ws__code pre {
  margin: 0;
  padding: 11px 13px;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.65;
  color: var(--pa-dim-2);
  white-space: pre;
  tab-size: 2;
}

.ws__typing {
  display: block;
  width: 7px;
  height: 15px;
  background: var(--pa-dim-4);
  animation: pa-blink 1s steps(1) infinite;
}

.ws__thinking {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  color: var(--pa-muted-3c);
}

.ws__thinking span:last-child {
  animation: pa-pulse 1.6s ease-in-out infinite;
}

/* "Bricklaying...", "Faffing..." -- see WORKING_WORDS in workspace.js.
   Fraunces italic against Archivo's sans, so it reads as a flourish rather
   than another line of interface text. */
.ws__working {
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 600;
  font-size: 14.5px;
  letter-spacing: .01em;
}

.ws__working::after {
  content: "…";
}

/* The recap bubble a new chat opens with when it carries context over from
   an older one -- POST /api/recap, see requestRecap() in workspace.js.
   Fraunces italic, the same pairing .ws__working uses above, but its own
   colour: deliberately not the near-white of a real reply (--pa-soft on
   .ws__prose) so it reads as a caption about the thread rather than a line
   Payama actually said -- the same job the muted colour of Claude Code's own
   compaction notice does. */
.ws__recap {
  display: flex;
  align-items: baseline;
  gap: 9px;
  margin-bottom: 6px;
  font-family: "Fraunces", Georgia, "Times New Roman", serif;
  font-style: italic;
  font-weight: 500;
  font-size: 14px;
  line-height: 1.5;
  color: var(--pa-muted-1);
  animation: pa-in .24s ease both;
}

.ws__recap::before {
  content: "";
  flex: none;
  width: 14px;
  height: 1px;
  margin-top: 1px;
  background: var(--pa-border-3);
}

/* ── build confirm ────────────────────────────────────────────────────────
   The estimate-and-confirm panel Send opens before actually sending -- see
   sendConfirmHtml() and runConfirmChoice() in workspace.js. Restyled
   2026-08-21 from a full-screen modal into an inline panel with numbered
   choices, the way Claude Code's own permission prompt reads: a short
   question, then options you can click or answer with a digit key, most of
   the screen still visible around it rather than dimmed behind a scrim. */
.ws__confirm {
  max-width: 420px;
  margin-bottom: 4px;
  padding: 14px;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface-2);
  animation: pa-in .2s ease both;
}

/* Top-aligned like .ws__msg-ai, not centred -- the question often wraps to
   two lines, and the face glyph (.ws__msg-face, reused as-is) should sit at
   its top the way it does on a real reply, not float in the middle of it. */
.ws__confirm-head {
  display: flex;
  gap: 10px;
}

.ws__confirm-body {
  flex: 1;
  min-width: 0;
}

.ws__confirm-title {
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--pa-text-2);
}

.ws__confirm-note {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--pa-muted-2);
}

.ws__confirm-opts {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.ws__confirm-opt {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 38px;
  padding: 0 12px;
  border: 1px solid var(--pa-border);
  border-radius: 10px;
  background: var(--pa-input);
  color: var(--pa-text);
  font: inherit;
  font-size: 13px;
  text-align: left;
  cursor: pointer;
  transition: background .14s, border-color .14s;
}

.ws__confirm-opt:hover {
  background: var(--pa-hover-b);
  border-color: var(--pa-border-3);
}

.ws__confirm-num {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  background: var(--pa-hover);
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 700;
  color: var(--pa-muted-2);
}

.ws__confirm-label {
  flex: 1;
  min-width: 0;
}

/* Option 1 -- the default "go" action, the answer most people want most of
   the time, same as Claude Code's own first option always is. */
.ws__confirm-opt--go {
  border-color: var(--pa-accent);
  background: var(--pa-accent-tint);
}

.ws__confirm-opt--go .ws__confirm-num {
  background: var(--pa-accent);
  color: var(--pa-on-accent);
}

.ws__confirm-opt--go:hover {
  background: var(--pa-accent-tint-2);
}

.ws__confirm-opt--no {
  color: var(--pa-muted-2);
}

/* ── connect a game ───────────────────────────────────────────────────────
   Offered once, right after the first project. Two routes are laid out side
   by side because they are a genuine choice, not a happy path and a fallback:
   the Studio plugin needs no setup at all. */
.ws-connect {
  max-width: 460px;
  max-height: 86vh;
  overflow-y: auto;
}

.ws-connect__lead {
  margin: -4px 0 16px;
  font-size: 13px;
  color: var(--pa-muted-3c);
}

.ws-connect__opt {
  display: flex;
  flex-direction: column;
  gap: 6px;

  margin-bottom: 12px;
  padding: 13px 15px;

  font-size: 13px;
  line-height: 1.45;
  color: var(--pa-muted-3c);

  border: 1px solid var(--pa-border-2);
  border-radius: 12px;
}

.ws-connect__opt strong {
  font-size: 14px;
  color: var(--pa-text, currentColor);
}

/* The two ways to build, as real buttons rather than one plain paragraph
   next to an always-open form -- see connectGameHtml()'s 2026-08-22 note.
   Same card look as .ws-connect__opt, reset for <button> and made to
   actually respond to a pointer, since these are now the thing you click
   rather than a form doing the asking. */
.ws-connect__choice {
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: 100%;

  margin-bottom: 10px;
  padding: 13px 15px;

  font: inherit;
  font-size: 13px;
  line-height: 1.45;
  color: var(--pa-muted-3c);
  text-align: left;

  background: none;
  border: 1px solid var(--pa-border-2);
  border-radius: 12px;
  cursor: pointer;
  transition: border-color .14s, background .14s;
}

.ws-connect__choice:hover {
  border-color: var(--pa-accent);
  background: var(--pa-hover-b);
}

.ws-connect__choice strong {
  font-size: 14px;
  color: var(--pa-text, currentColor);
}

.ws-connect__opt--form .ws-label {
  margin-top: 10px;
}

.ws-connect__help {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--pa-dim-4);
}

.ws-connect__warn {
  margin: 12px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--pa-dim-4);
}

.ws-connect__err {
  margin: 10px 0 0;
  padding: 9px 11px;
  font-size: 12.5px;
  line-height: 1.45;
  border: 1px solid var(--pa-active-2);
  border-radius: 9px;
  background: var(--pa-hover-b);
}

/* Deliberately plain rather than a second bright button: it is an equal
   choice, and most people will take it. */
.ws-connect__skip {
  display: block;
  width: 100%;
  margin-top: 9px;
  padding: 9px;

  font: inherit;
  font-size: 13px;
  color: var(--pa-muted-3c);

  background: none;
  border: 0;
  border-radius: 9px;
  cursor: pointer;
}

.ws-connect__skip:hover {
  background: var(--pa-hover-b);
}

/* ── "where it builds" ────────────────────────────────────────────────────
   Shown in the create-project dialog. A build can land in Studio or straight
   in a Roblox place, and somebody who does not know which will think the
   first build failed when it went somewhere they were not looking. */
.ws-where {
  display: flex;
  flex-direction: column;
  gap: 6px;

  margin-top: 14px;
  padding: 12px 14px;

  font-size: 13px;
  line-height: 1.45;
  color: var(--pa-muted-3c);

  border: 1px solid var(--pa-border-2);
  border-radius: 11px;
}

.ws-where strong {
  font-size: 12px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--pa-dim-4);
}

.ws-where b {
  color: var(--pa-text, currentColor);
}

/* A connected game is the less common setup, so it gets a quiet accent --
   enough to confirm it is on, not enough to shout. */
.ws-where--on {
  border-color: var(--pa-active-2);
}

/* ── waiting mark ─────────────────────────────────────────────────────────
   The Payama glyph, with its four quadrants fading in turn.

   One SVG rather than a light copy and a dark copy: the two fills are
   custom properties, so the existing theme switch changes them and there is
   only one piece of markup to keep correct. Dark is the default here for the
   same reason it is in theme.css.

   The paths are one shape clipped four ways, so the wave reads as the mark
   filling in rather than four separate pieces blinking. */
.ws__mark {
  --pa-mark: #e9f4ff;
  --pa-mark-slash: #d7e2ff;

  flex: none;
  width: 20px;
  height: 20px;
}

html[data-pa-theme="light"] .ws__mark {
  --pa-mark: #79abd9;
  --pa-mark-slash: #2f6da5;
}

.ws__mark g path {
  fill: var(--pa-mark);
  animation: pa-mark-wave .8s ease-in-out infinite;
}

.ws__mark g path:nth-child(2) { animation-delay: -.6s; }
.ws__mark g path:nth-child(3) { animation-delay: -.4s; }
.ws__mark g path:nth-child(4) { animation-delay: -.2s; }

.ws__mark .ws__mark-slash {
  fill: var(--pa-mark-slash);
}

@keyframes pa-mark-wave {
  0%, 100% { opacity: .2; }
  40%      { opacity: 1; }
}

/* Someone who has asked their system to stop animations has usually asked
   for a reason. The mark stays visible and legible, it just holds still. */
@media (prefers-reduced-motion: reduce) {
  .ws__mark g path {
    animation: none;
    opacity: 1;
  }

  .ws__thinking span:last-child {
    animation: none;
  }
}

.ws__err {
  padding: 13px 15px;
  border: 1px solid var(--pa-active-2);
  border-radius: 13px;
  background: var(--pa-hover-b);
}

.ws__err-title {
  margin-bottom: 4px;
  font-size: 14px;
  font-weight: 600;
  color: var(--pa-text);
}

.ws__err-body {
  font-size: 13px;
  line-height: 1.55;
  color: var(--pa-muted);
}

.ws__err button {
  margin-top: 11px;
  height: 32px;
  padding: 0 13px;
  border: 1px solid var(--pa-active-2);
  border-radius: 9px;
  background: transparent;
  color: var(--pa-text);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background .16s;
}

.ws__err button:hover {
  background: var(--pa-hover-b);
}

/* ── new-chat nudge ───────────────────────────────────────────────────────
   Offered, not forced -- dismissible, and only asked again in a later
   conversation, never re-asked in the same one after being waved off. */
.ws__nudge {
  flex: none;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 700px;
  margin: 0 auto;
  padding: 9px 10px 9px 14px;
  border: 1px solid var(--pa-border-2);
  border-radius: 13px;
  background: var(--pa-surface-2);
  animation: pa-in .2s var(--ease-out) both;
}

.ws__nudge-text {
  flex: 1;
  min-width: 0;
  font-size: 12.5px;
  color: var(--pa-muted-2);
}

.ws__nudge-go {
  flex: none;
  height: 28px;
  padding: 0 12px;
  border: 0;
  border-radius: 999px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background .14s;
}

.ws__nudge-go:hover { background: var(--pa-accent-hover); }

.ws__nudge-x {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border: 0;
  border-radius: 999px;
  background: transparent;
  color: var(--pa-muted-3);
  cursor: pointer;
}

.ws__nudge-x:hover {
  background: var(--pa-hover-2);
  color: var(--pa-text-hi);
}

.ws__dock {
  flex: none;
  padding: 10px 24px 18px;
}

.ws__dock-inner {
  max-width: 700px;
  margin: 0 auto;
  padding: 13px;
  border: 1px solid var(--pa-accent);
  border-radius: 16px;
  background: var(--pa-input);
  transition: border-color .18s, padding .2s cubic-bezier(.4,0,.2,1);
}

.ws__dock-inner:hover {
  border-color: var(--pa-border-3);
}

.ws__dock-inner.is-typing {
  border-color: var(--pa-accent);
  box-shadow: 0 0 0 3px var(--pa-accent-tint);
}

.ws__dock textarea {
  width: 100%;
  min-height: 26px;
  max-height: 220px;
  overflow-y: auto;
  resize: none;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--pa-text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
  transition: height .16s cubic-bezier(.4,0,.2,1);
}

.ws__dock-foot {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ws__dock-send {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: 999px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws__dock-send:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

.ws__dock-stop {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: 1px solid var(--pa-active-2);
  border-radius: 999px;
  background: transparent;
  color: var(--pa-text);
  cursor: pointer;
  transition: background .16s;
}

.ws__dock-stop:hover {
  background: var(--pa-hover-b);
}

/* ── build log drawer ─────────────────────────────────────────────────────── */

/* A grid row rather than an absolutely-positioned overlay. As an overlay it
   had to be pinned to .ws__stage, which stopped working the moment the stage
   became one of two columns. Growing a row also means it pushes the layout
   instead of covering the bottom of the chat. */
.ws__log {
  grid-column: 1;
  grid-row: 2;
  height: 40px;
  overflow: hidden;
  background: var(--pa-bg);
  border-top: 1px solid var(--pa-line);
  transition: height .3s var(--ease-in-out);
}

.ws__log--open {
  height: 190px;
}

/* Project mode: a card under the side panel, matching the other two. */
.ws--open .ws__log {
  grid-column: 2;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface);
}

.ws__log-head {
  height: 40px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 16px;
}

.ws__log-toggle {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--pa-dim);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}

.ws__log-chev {
  transform: rotate(180deg);
  transition: transform .3s;
}

.ws__log--open .ws__log-chev {
  transform: rotate(0deg);
}

.ws__log-count,
.ws__log-endpoint {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--pa-muted-3);
  white-space: nowrap;
}

.ws__log-endpoint {
  margin-left: auto;
}

.ws__log-body {
  height: calc(100% - 40px);
  overflow-y: auto;
  padding: 4px 16px 16px;
  border-top: 1px solid var(--pa-hover);
}

.ws__log-empty {
  padding: 14px 0;
  font-size: 13px;
  color: var(--pa-muted-3);
}

.ws__log-line {
  display: flex;
  gap: 12px;
  padding: 4px 0;
  font-family: var(--font-mono);
  font-size: 11.5px;
  line-height: 1.55;
}

.ws__log-time {
  flex: none;
  color: var(--pa-muted-4);
}

.ws__log-kind {
  flex: none;
  width: 60px;
  color: var(--pa-muted-3b);
}

.ws__log-kind--ok { color: var(--pa-success-2); }
.ws__log-kind--error { color: var(--pa-error-text); }

.ws__log-text {
  flex: 1;
  min-width: 0;
  color: var(--pa-dim-4);
  white-space: pre-wrap;
}

/* ── modals ───────────────────────────────────────────────────────────────── */

/* Every overlay needs an explicit z-index. Without one it sits at the auto
   level, and anything inside the page that DOES have a z-index — the map's
   zoom controls, at z-index 3 — paints straight through the scrim.
   The layering matches the design: modal 60, account 80, store 82,
   confirm 90, buy 92. */
.ws-scrim {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: var(--pa-scrim-2);
  animation: pa-in .2s ease both;
}

.ws-scrim--deep {
  z-index: 80;
  background: var(--pa-scrim);
}

/* Above the store, so deleting a project from behind it still reads. */
.ws-scrim--confirm {
  z-index: 90;
}

.ws-scrim--buy {
  z-index: 92;
}

.ws-modal {
  position: relative;
  width: 100%;
  max-width: 470px;
  padding: 26px 26px 24px;
  border: 1px solid var(--pa-border);
  border-radius: 18px;
  background: var(--pa-surface);
  box-shadow: 0 40px 70px -34px var(--pa-shadow);
  animation: pa-gate .28s var(--ease-out) both;
}

/* z-index scoped to just the model preview -- see .ws-model-preview
   .ws-modal__x below for why this one needs it and the others don't. */
.ws-modal__x {
  position: absolute;
  top: 18px;
  right: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: 9px;
  background: transparent;
  color: var(--pa-muted);
  cursor: pointer;
  transition: background .16s, color .16s;
}

.ws-modal__x:hover {
  background: var(--pa-hover-b);
  color: var(--pa-text-hi);
}

.ws-modal h2 {
  margin: 0 0 20px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 30px;
  line-height: 1;
  color: var(--pa-text-1);
}

.ws-kinds {
  display: flex;
  gap: 12px;
  margin-bottom: 22px;
}

.ws-kind {
  position: relative;
  flex: 1;
  min-width: 0;
  padding: 16px 14px 15px;
  border: 1px solid var(--pa-border-2);
  border-radius: 14px;
  background: var(--pa-input);
  text-align: left;
  cursor: pointer;
  transition: border-color .18s, transform .18s;
}

.ws-kind:hover {
  border-color: var(--pa-accent);
  transform: translateY(-2px);
}

.ws-kind[aria-pressed="true"] {
  border-color: var(--pa-accent);
}

.ws-kind__tag {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .04em;
  white-space: nowrap;
}

.ws-kind__name {
  display: block;
  margin-top: 26px;
  font-size: 15px;
  font-weight: 600;
  color: var(--pa-text-2);
}

.ws-kind__desc {
  display: block;
  margin-top: 3px;
  font-size: 12.5px;
  color: var(--pa-muted-2);
}

.ws-label {
  display: block;
  margin-bottom: 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--pa-dim);
}

.ws-input {
  width: 100%;
  height: 46px;
  padding: 0 14px;
  border: 1px solid var(--pa-border-2);
  border-radius: 12px;
  background: var(--pa-bg);
  color: var(--pa-text);
  font-family: var(--font-body);
  font-size: 15px;
  caret-color: var(--pa-accent);
  transition: border-color .16s;
}

.ws-input:hover {
  border-color: var(--pa-border-3);
}

.ws-input:focus {
  outline: none;
  border-color: var(--pa-accent);
}

.ws-modal__go {
  width: 100%;
  height: 46px;
  margin-top: 18px;
  border: 0;
  border-radius: 12px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 15px;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws-modal__go:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

/* Model preview overlay -- opened by clicking a Models tab card. A large
   popup on purpose (unlike the other .ws-modal uses, all narrow forms) --
   this one holds a real 3D stage, which needs the room. */

/* "Like a full-screen popup" -- big enough that the builder behind it
   barely reads as the same page, but still a popup: the dimmed .ws-scrim
   backdrop stays visible in the margin around it (see .ws-scrim itself),
   which is what makes it a popup rather than a route change. Height capped
   and scrollable rather than forced to fit, since a tall narrow viewport
   (a laptop in a small window) would otherwise clip the wedges below the
   fold with no way to reach them. */
.ws-model-preview {
  /* Was capped at 900px -- read as small next to test/model_layout_test.png
     (Aydin's own layout reference), which runs close to the full window
     width. 1500px keeps a real margin on an ultra-wide monitor rather than
     going edge-to-edge, but is otherwise as big as the viewport allows. */
  width: min(94vw, 1500px);
  max-width: none;
  max-height: 92vh;
  overflow-y: auto;
  /* Was just .ws-modal's own plain flat --pa-surface/border/box-shadow,
     unlike every OTHER floating surface this popup opens (the Idea Forge
     window, the Color popout -- both .ws-code-chat/.ws-color-popup, same
     recipe) which all carry Payama's own blue glow. Aydin: "it looks a
     bit boring" -- this was the one big floating window in the whole app
     with no accent personality at all. Same exact recipe as those two,
     not a new one: gradient background, an accent-tinted border, and a
     blue glow layered into the shadow. */
  background: linear-gradient(165deg, var(--pa-surface-2), var(--pa-deep-2));
  border-color: color-mix(in srgb, var(--pa-accent) 22%, var(--pa-border));
  box-shadow: 0 40px 70px -34px var(--pa-shadow), 0 0 60px -18px var(--pa-accent);
}

/* Holds the RECTANGULAR baseplate viewport -- a real Roblox-Studio-style
   baseplate, not a small inset circle. The left rail, right info sidebar,
   and Customize drawer (.ws-model-preview__rail/__info/__customize, see
   their own comments below) all overlay this box; only the Physics panel
   and the dimming it triggers key off it directly. */
/* Was min(68vh, 680px) -- bumped once the old bottom pill row went away,
   then again for the 2026-10-31 redesign's own bottom bar, which now
   takes that space back below the stage instead of overlaying it. */
.ws-model-preview__stage-wrap {
  position: relative;
  width: 100%;
  height: min(74vh, 720px);
  margin: 4px auto 6px;
}

/* The baseplate itself -- fills the whole wrap now, a large rectangle
   (rounded corners only for visual polish, not to read as a circle). */
.ws-model-preview__stage {
  position: absolute;
  inset: 0;
  border-radius: 16px;
  border: 1px solid var(--pa-border-2);
  /* Shown only while there's no canvas yet (no stored bytes -- see
     hasStoredModel in lib/routes/models.js) or while one hasn't finished
     loading: a neutral stage the model sits on, not just a flat fill,
     evoking a turntable/baseplate even for the picture-only fallback. */
  background:
    radial-gradient(ellipse at 50% 70%, var(--pa-hover) 0%, transparent 60%),
    var(--pa-deep-2);
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--pa-accent);
  z-index: 1;
}

/* A soft vignette over the live 3D canvas (and the picture-only fallback)
   -- one more piece of "it looks a bit boring." Pure CSS, painted on top
   of the canvas as generated content rather than anything in the WebGL
   scene itself (this project vendors no post-processing pipeline to do a
   real render-space vignette with -- see model-viewer.js's own comment on
   why). pointer-events:none so it never intercepts hover/click/drag meant
   for the model underneath. */
.ws-model-preview__stage::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 45%, transparent 55%, rgba(0, 0, 0, .38) 100%);
}

.ws-model-preview__stage img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ws-model-preview__icon svg {
  width: 34px;
  height: 34px;
}

/* The live 3D view -- public/model-viewer.js renders into this. Its own
   background comes from the scene's gradient sky, not this rule; this is
   only what keeps it exactly filling the stage while that loads. */
.ws-model-preview__canvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: grab;
}

.ws-model-preview__canvas:active {
  cursor: grabbing;
}

/* Shown from the moment the popup opens until model-viewer.js's own
   onLoad fires (wireOverlays()'s own mount code) -- see modelPreviewHtml()'s
   own comment on why there was nothing here before. A branded pulsing
   ring + label over a soft scrim, not a native browser spinner; fades
   out rather than disappearing instantly since the sky/baseplate are
   already visible underneath by the time this hides. */
.ws-model-preview__loading {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  background: rgba(10, 11, 13, .45);
  color: rgba(255, 255, 255, .85);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  opacity: 1;
  transition: opacity .25s ease;
  pointer-events: none;
}
.ws-model-preview__loading.is-hidden {
  opacity: 0;
}

.ws-model-preview__loading-ring {
  width: 42px;
  height: 42px;
  border-radius: 999px;
  border: 3px solid color-mix(in srgb, var(--pa-accent) 30%, transparent);
  border-top-color: var(--pa-accent);
  animation: ws-loading-spin .8s linear infinite;
}

@keyframes ws-loading-spin {
  to { transform: rotate(360deg); }
}

/* ── model editor chrome ──────────────────────────────────────────────────
   Redesigned 2026-10-31 to match Attachments/example model editor 1.jpg
   (Tripo's own results page) -- left tool rail, right info sidebar, bottom
   Download/Customize bar -- replacing the right-click radial wedge menu
   this used to be. See workspace.js's own header comment above
   leftRailHtml() for the full story and what happened to every wedge's
   real functionality. */

.ws-model-preview {
  /* Still referenced by .ws-model-preview__panel and __panel-title::before
     below -- kept as an alias rather than hunting down every reference,
     same reasoning as when this was introduced for the old ring. */
  --radial-accent: var(--pa-accent);
}

/* Dims the stage behind an open panel/drawer (Physics, the primitives
   flyout, or the Customize drawer itself) so it reads as clearly in
   front, not just overlapping. */
.ws-model-preview__stage-wrap--panel-open .ws-model-preview__stage {
  filter: brightness(.55) saturate(.7);
  transition: filter .18s ease;
}

/* Left tool rail -- Select/Rotate/Scale (top group, only for a model with
   a live 3D view) then Color/Lock (always present, matching the old ring
   which always offered all six main actions regardless of
   hasStoredModel). Same translucent dark pill language the stage-tools
   toolbar used to use (blur, border, shadow), now a permanent vertical
   column down the left edge -- the reference's own left rail, and the
   same place a real 3D tool (Blender, Roblox Studio, Figma) keeps its own
   tool column. */
.ws-model-preview__rail {
  position: absolute;
  top: 16px;
  left: 16px;
  bottom: 16px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  padding: 6px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, .14);
  background: rgba(20, 21, 24, .55);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-lift);
  pointer-events: auto;
}

.ws-model-preview__rail-group {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ws-model-preview__rail-sep {
  width: 18px;
  height: 1px;
  background: rgba(255, 255, 255, .14);
}

.ws-model-preview__rail-btn {
  width: 32px;
  height: 32px;
  border-radius: 10px;
  border: none;
  background: transparent;
  color: rgba(255, 255, 255, .72);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .14s, color .14s;
}

.ws-model-preview__rail-btn:hover {
  background: rgba(255, 255, 255, .1);
  color: #fff;
}

/* Select/Rotate/Scale's own live state (model-viewer.js's gizmoMode, kept
   in sync by syncGizmoToolbar()) and Lock's own persisted state
   (migration 0016) share the same highlight -- both just mean "this is
   currently on." */
.ws-model-preview__rail-btn.is-on,
.ws-model-preview__rail-btn.is-active {
  background: var(--pa-accent);
  color: #fff;
}

/* Right info sidebar -- Created date, real vertex/face count, Rig status,
   a plain-English physics summary. Nothing invented to fill space the way
   a fake "views/likes" row would be -- every row here is real data
   already on the model (see infoSidebarHtml() in workspace.js). */
.ws-model-preview__info {
  position: absolute;
  top: 16px;
  right: 16px;
  bottom: 16px;
  width: 200px;
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, .14);
  background: rgba(20, 21, 24, .55);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-lift);
  overflow-y: auto;
  pointer-events: auto;
}

.ws-model-preview__info-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
  font-size: 12px;
}

.ws-model-preview__info-row:last-child {
  border-bottom: none;
}

.ws-model-preview__info-row span {
  color: rgba(255, 255, 255, .5);
}

.ws-model-preview__info-row b {
  color: #fff;
  font-weight: 600;
  font-size: 12.5px;
}

.ws-model-preview__info-action {
  align-self: flex-start;
  margin-top: 2px;
  padding: 6px 10px;
  border-radius: 7px;
  border: 1px solid rgba(255, 255, 255, .18);
  background: rgba(255, 255, 255, .06);
  color: #fff;
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .14s;
}

.ws-model-preview__info-action:hover {
  background: rgba(255, 255, 255, .12);
}

.ws-model-preview__info-action:disabled {
  opacity: .5;
  cursor: default;
}

/* Bottom bar -- Download (left, a real format picker) and Customize
   (right, opens the drawer below). Was the stage-tools pill row; moved
   out of the stage itself into its own full-width strip under it, since
   both of these are now real primary actions, not three small stubs. */
.ws-model-preview__bottombar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}

.ws-model-preview__dl-wrap {
  position: relative;
}

.ws-model-preview__dl-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 16px;
  border-radius: 10px;
  border: 1px solid var(--pa-border-2);
  background: var(--pa-input);
  color: var(--pa-text-hi);
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .14s, border-color .14s;
}

.ws-model-preview__dl-btn:hover {
  border-color: var(--pa-border-3);
  background: var(--pa-hover);
}

.ws-model-preview__dl-pop {
  position: absolute;
  left: 0;
  bottom: calc(100% + 8px);
  display: flex;
  flex-direction: column;
  min-width: 140px;
  padding: 6px;
  border-radius: 12px;
  border: 1px solid var(--pa-border-2);
  background: var(--pa-surface-2);
  box-shadow: var(--shadow-popover);
  z-index: 6;
}

.ws-model-preview__dl-pop button {
  height: 34px;
  padding: 0 10px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--pa-text-hi);
  text-align: left;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background .14s;
}

.ws-model-preview__dl-pop button:hover {
  background: var(--pa-hover);
}

.ws-model-preview__dl-pop button.is-disabled {
  color: var(--pa-muted-3);
  cursor: default;
}

.ws-model-preview__dl-pop button.is-disabled:hover {
  background: transparent;
}

.ws-model-preview__customize-btn {
  width: auto;
  height: 40px;
  margin-top: 0;
  padding: 0 22px;
  font-size: 13.5px;
}

/* Customize drawer -- every action that used to be a wedge or an
   accessory-row button, now a plain labelled list. Slides in from the
   right, overlaying the stage the same way the old centred panel did
   (same .ws-model-preview__stage-wrap--panel-open dimming). */
.ws-model-preview__customize {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(100%, 300px);
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 16px 14px;
  border-radius: 0 16px 16px 0;
  background: linear-gradient(165deg, var(--pa-surface-2), var(--pa-deep-2));
  border-left: 1px solid color-mix(in srgb, var(--pa-accent) 30%, var(--pa-border-2));
  box-shadow: -20px 0 50px rgba(0, 0, 0, .4);
  overflow-y: auto;
  transform: translateX(0);
  transition: transform .2s ease;
}

.ws-model-preview__customize[hidden] {
  display: flex; /* overrides the UA [hidden] -- still laid out, just pushed off-stage */
  transform: translateX(100%);
  pointer-events: none;
}

.ws-model-preview__customize-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2px 4px 10px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--pa-border-2);
  font-size: 14.5px;
  font-weight: 700;
  color: var(--pa-text-hi);
}

.ws-model-preview__customize-x {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--pa-muted);
  cursor: pointer;
  transition: background .14s, color .14s;
}

.ws-model-preview__customize-x:hover {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

.ws-model-preview__customize-row {
  display: block;
  width: 100%;
  height: 40px;
  padding: 0 10px;
  border: none;
  border-radius: 9px;
  background: transparent;
  color: var(--pa-text-hi);
  text-align: left;
  font-family: var(--font-body);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .14s, color .14s;
}

.ws-model-preview__customize-row:hover {
  background: var(--pa-hover);
}

.ws-model-preview__customize-row--disabled {
  color: var(--pa-muted-3);
  cursor: default;
}

.ws-model-preview__customize-row--disabled:hover {
  background: transparent;
}

.ws-model-preview__customize-row--danger {
  margin-top: 8px;
  color: var(--pa-danger);
}

.ws-model-preview__customize-row--danger:hover {
  background: color-mix(in srgb, var(--pa-danger) 16%, transparent);
}

/* Create primitives' own flyout -- was an arc of round buttons beyond the
   ring's outer edge; now a plain flex-wrap grid inside the Customize
   drawer, reusing the exact same open/close mechanics
   (.ws-model-preview__accessory-group/.is-open, below) every other
   no-dim accessory group already used. */
.ws-model-preview__accessory-group[hidden] {
  display: none;
}

.ws-model-preview__accessory-group {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity .16s ease, max-height .2s ease;
}

.ws-model-preview__accessory-group.is-open {
  max-height: 200px;
  opacity: 1;
}

.ws-model-preview__primitives {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 4px 10px;
}

.ws-model-preview__primitive {
  width: 34px;
  height: 34px;
  border-radius: 9px;
  border: 1px solid var(--pa-border-2);
  background: var(--pa-input);
  color: var(--pa-text-2);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .14s, border-color .14s, color .14s;
}

.ws-model-preview__primitive:hover {
  border-color: var(--pa-accent);
  color: var(--pa-text-hi);
}

/* Make Trigger/Set decomposition -- was their own hover-arc beside the
   Physics wedge; now two plain chips at the top of the Physics panel. */
.ws-model-preview__panel-quickrow {
  display: flex;
  gap: 6px;
  margin: -2px 0 2px;
}

.ws-model-preview__panel-quick {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 8px 4px;
  border-radius: 9px;
  border: 1px solid var(--pa-border-2);
  background: var(--pa-deep-2);
  color: var(--pa-muted-2);
  cursor: pointer;
  transition: background .14s, border-color .14s, color .14s;
}

.ws-model-preview__panel-quick span {
  font-size: 10.5px;
  font-weight: 600;
  text-align: center;
  line-height: 1.2;
}

.ws-model-preview__panel-quick:hover {
  border-color: var(--pa-border-3);
  color: var(--pa-text-hi);
}

.ws-model-preview__panel-quick.is-active {
  background: color-mix(in srgb, var(--pa-accent) 18%, var(--pa-deep-2));
  border-color: var(--pa-accent);
  color: var(--pa-text-hi);
}

/* ── Physics panel ────────────────────────────────────────────────────────
   What data-radial-panel="physics" shows once clicked (see workspace.js's
   physicsPanelHtml()) -- exists in the DOM from the start, [hidden] until
   wireOverlays()'s on("[data-radial-panel]", ...) handler shows it, so a
   routine open/close never remounts the live 3D viewer the way going
   through S/render() would. Overlays the stage, centred --
   .ws-model-preview__stage-wrap (this block's own parent in
   modelPreviewHtml()) is already position:relative, so this just needs to
   go absolute within it. */
.ws-model-preview__panels {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  z-index: 3;
}

/* Only clickable (as a backdrop -- see the data-close-radial-panel-backdrop
   handler in workspace.js) while a panel is actually open -- otherwise
   this transparent full-stage box would swallow clicks meant for the
   wedges/model underneath it even with nothing open. */
.ws-model-preview__stage-wrap--panel-open .ws-model-preview__panels {
  pointer-events: auto;
}

/* Fades/scales in from wherever the clicked wedge actually sits
   (--panel-origin-x/y, set inline by wireOverlays() right before opening)
   rather than a fixed corner -- see the on("[data-radial-panel]", ...)
   handler's own comment. [hidden] needs an explicit rule here since
   `display: flex` below is an author-origin rule of equal specificity to
   the UA [hidden] one and would otherwise win the cascade by source
   order. */
.ws-model-preview__panel[hidden] {
  display: none;
}

/* Was a flat --pa-surface-2 rectangle -- Aydin's own correction: "it is
   ugly - this same exact style i didnt like when you made the roblox
   plugin." A gradient background (not flat), a rounder radius closer to
   the ring's own petals, and an accent-tinted border/glow give it real
   depth instead of reading as a plain generic settings box. */
.ws-model-preview__panel {
  position: relative;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: min(90%, 420px);
  max-height: min(72%, 520px);
  overflow-y: auto;
  padding: 18px 20px;
  border-radius: 18px;
  background: linear-gradient(165deg, var(--pa-surface-2), var(--pa-deep-2));
  border: 1px solid color-mix(in srgb, var(--radial-accent) 30%, var(--pa-border-2));
  box-shadow: 0 20px 60px rgba(0, 0, 0, .5), 0 0 0 1px rgba(0, 0, 0, .2) inset;
  transform-origin: var(--panel-origin-x, 50%) var(--panel-origin-y, 50%);
  opacity: 0;
  transform: scale(.85);
  transition: opacity .18s ease, transform .18s ease;
}

.ws-model-preview__panel.is-open {
  opacity: 1;
  transform: scale(1);
}

.ws-model-preview__panel-title {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14.5px;
  font-weight: 700;
}

/* A small accent-coloured dot before the title -- cheap, but it's the
   one bit of colour breaking up an otherwise all-neutral panel, echoing
   the ring's own accent glow on its active wedge. */
.ws-model-preview__panel-title::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: var(--radial-accent);
  box-shadow: 0 0 8px var(--radial-accent);
  flex: none;
}

.ws-model-preview__panel-hint {
  margin: -4px 0 0;
  font-size: 12px;
  color: var(--pa-muted-2);
  line-height: 1.4;
}

.ws-model-preview__panel-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
}

.ws-model-preview__panel-row--number {
  justify-content: space-between;
}

.ws-model-preview__panel-row--number input {
  width: 72px;
  height: 30px;
  background: var(--pa-deep-2);
  border: 1px solid var(--pa-border-2);
  border-radius: 7px;
  color: var(--pa-text-hi);
  padding: 0 8px;
  font-size: 13px;
}

.ws-model-preview__panel-row--slider {
  gap: 12px;
}

.ws-model-preview__panel-row--slider input[type="range"] {
  flex: 1;
}

.ws-model-preview__panel-row--slider span {
  min-width: 18px;
  text-align: right;
  font-weight: 600;
  font-size: 13px;
}

.ws-model-preview__panel-textarea {
  min-height: 64px;
  resize: vertical;
  font-size: 13px;
}

.ws-model-preview__panel-result {
  margin: 0;
  padding: 10px;
  background: var(--pa-deep-2);
  border-radius: 8px;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 220px;
  overflow-y: auto;
}

.ws-model-preview__panel-action {
  width: 100%;
  height: 38px;
  border-radius: 8px;
  background: var(--pa-deep-2);
  border: 1px solid var(--pa-border-2);
  color: var(--pa-text-hi);
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .14s;
}

.ws-model-preview__panel-action:hover {
  background: var(--pa-hover);
}

.ws-model-preview__panel-action--danger {
  color: var(--pa-danger);
}

/* ── the Code wedge's own chat: a real floating, draggable window ────────
   V3 plan Phase 2. Deliberately NOT another .ws-model-preview__panel --
   Aydin's own description was "moveable like a restore downed window...
   the builder bg still shows because it's not actually full screen" --
   so this is `position: fixed`, independent of the model-preview modal's
   own scroll/overflow, draggable by its title bar (workspace.js's own
   ws-code-chat pointer-drag code, the same mousedown/move/up pattern the
   roadmap's own pan-drag already uses). Sits above every scrim layer
   (highest existing z-index is .ws-scrim--buy at 92) since it floats
   OVER the model popup, not inside it. */
.ws-code-chat {
  position: fixed;
  /* Was 760px/820px -- bumped again for the two-column "Idea Forge" body
     (Attachments/inspo4.png's own layout: history left, one big preview
     right -- see .ws-code-chat__body below) to actually have room for
     both columns side by side rather than cramming them. Still a
     floating/minimized window, not full-page -- Aydin's own words when
     this was first resized. */
  width: min(94vw, 960px);
  max-height: min(90vh, 820px);
  display: flex;
  flex-direction: column;
  /* Was a flat --pa-surface-2 rectangle -- Aydin: "the 'Code' option is
     ugly style," same root complaint as the radial panels. A gradient
     background and an accent-tinted border/glow instead of a plain flat
     box, matching that same fix. */
  background: linear-gradient(165deg, var(--pa-surface-2), var(--pa-deep-2));
  border: 1px solid color-mix(in srgb, var(--pa-accent) 28%, var(--pa-border-2));
  border-radius: 18px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .5), 0 0 40px -12px var(--pa-accent);
  z-index: 95;
  overflow: hidden;
  /* Was an instant [hidden]/not-hidden snap -- no transition at all, the
     one floating window in the popup that didn't animate open or closed
     while every radial panel around it does. Same recipe as
     .ws-model-preview__panel's own open/close (scale .85→1 + fade,
     bouncy-settle easing, 180ms) rather than a new one -- this is a
     second instance of the exact same "settings surface appearing" kind
     of motion, not a different kind that needs its own curve. See
     openCodeChat()/closeCodeChat() in workspace.js for the class timing
     this pairs with. */
  opacity: 0;
  transform: scale(.92);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s cubic-bezier(.34, 1.56, .64, 1);
}

.ws-code-chat.is-open {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

.ws-code-chat[hidden] {
  display: none;
}

/* ── the Color wedge's own popout ─────────────────────────────────────────
   Same floating-window recipe as .ws-code-chat just above (position:fixed,
   draggable by its own titlebar, appended to document.body so it survives
   the model popup's own render() cycle -- see ensureColorPopupEl() in
   workspace.js) -- much smaller, since it only ever holds a swatch grid
   and one hex field, not a whole chat. Aydin, 2026-08-24: "clicking
   colours, it should be another moveable popout and be more customisable,
   using hex codes" -- replaces the swatch arc that used to paint straight
   onto the ring. */
.ws-color-popup {
  position: fixed;
  width: 260px;
  display: flex;
  flex-direction: column;
  background: linear-gradient(165deg, var(--pa-surface-2), var(--pa-deep-2));
  border: 1px solid color-mix(in srgb, var(--pa-accent) 28%, var(--pa-border-2));
  border-radius: 16px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, .5), 0 0 40px -12px var(--pa-accent);
  z-index: 95;
  overflow: hidden;
  /* Same open/close motion as .ws-code-chat just above, same reasoning --
     a settings-surface-appearing animation, not a fresh one. */
  opacity: 0;
  transform: scale(.92);
  pointer-events: none;
  transition: opacity .18s ease, transform .18s cubic-bezier(.34, 1.56, .64, 1);
}

.ws-color-popup.is-open {
  opacity: 1;
  transform: scale(1);
  pointer-events: auto;
}

.ws-color-popup[hidden] {
  display: none;
}

.ws-color-popup__titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 8px 10px 14px;
  border-bottom: 1px solid var(--pa-border-2);
  cursor: grab;
  user-select: none;
}

.ws-color-popup__titlebar:active {
  cursor: grabbing;
}

.ws-color-popup__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 15px;
  color: var(--pa-text-hi);
}

.ws-color-popup__close {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--pa-muted-2);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background .14s, color .14s;
}

.ws-color-popup__close:hover {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

.ws-color-popup__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 14px;
}

.ws-color-popup__grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 8px;
}

.ws-color-popup__swatch {
  width: 26px;
  height: 26px;
  border-radius: 999px;
  border: 1.5px solid rgba(255, 255, 255, .18);
  box-shadow: var(--shadow-lift);
  cursor: pointer;
  padding: 0;
  transition: transform .14s, border-color .14s;
}

.ws-color-popup__swatch:hover {
  transform: scale(1.12);
}

.ws-color-popup__swatch.is-selected {
  border-color: var(--pa-accent);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--pa-accent) 40%, transparent), var(--shadow-lift);
}

.ws-color-popup__hex-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ws-color-popup__hex-preview {
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  border: 1px solid var(--pa-border-2);
}

.ws-color-popup__hex-input {
  flex: 1;
  height: 34px;
  font-size: 13px;
  font-family: var(--font-mono, monospace);
  text-transform: lowercase;
}

/* A quick red flash rather than a persistent error state -- an invalid
   hex isn't a form the user is expected to fix and resubmit, just a typo
   worth a moment's feedback before they try again. */
.ws-color-popup__hex-input.is-invalid {
  border-color: var(--pa-danger);
  animation: ws-color-popup-shake .3s;
}

@keyframes ws-color-popup-shake {
  25% { transform: translateX(-3px); }
  75% { transform: translateX(3px); }
}

.ws-color-popup__apply {
  flex: none;
  width: auto;
  height: 34px;
  margin-top: 0;
  padding: 0 14px;
  font-size: 12.5px;
}

.ws-code-chat__titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 14px;
  background: var(--pa-deep-2);
  border-bottom: 1px solid var(--pa-border-2);
  cursor: move;
  user-select: none;
  flex: none;
}

.ws-code-chat__titlebar-left {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
}

.ws-code-chat__icon-btn {
  background: none;
  border: none;
  color: var(--pa-dim);
  cursor: pointer;
  display: flex;
  padding: 6px;
  border-radius: 7px;
}

.ws-code-chat__icon-btn:hover {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

.ws-code-chat__new-idea {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 28px;
  padding: 0 10px;
  border-radius: 999px;
  background: var(--pa-surface-2);
  border: 1px solid var(--pa-border-2);
  color: var(--pa-text-hi);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
}

.ws-code-chat__new-idea:hover {
  background: var(--pa-hover);
}

.ws-code-chat__title {
  flex: 1;
  text-align: center;
  font-size: 15.5px;
  font-weight: 700;
}

.ws-code-chat__close {
  flex: 1;
  display: flex;
  justify-content: flex-end;
  background: none;
  border: none;
  color: var(--pa-dim);
  cursor: pointer;
  padding: 2px;
}

.ws-code-chat__close:hover {
  color: var(--pa-text-hi);
}

/* The two-column "Idea Forge" body -- a running history on the left,
   one big preview of the current edit on the right, matching
   Attachments/inspo4.png exactly rather than the single stacked list
   this shipped with first. */
.ws-code-chat__body {
  flex: 1;
  display: flex;
  min-height: 320px;
  overflow: hidden;
}

.ws-code-chat__history {
  flex: none;
  width: 34%;
  min-width: 200px;
  overflow-y: auto;
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  border-right: 1px solid var(--pa-border-2);
}

.ws-code-chat__preview {
  flex: 1;
  overflow-y: auto;
  padding: 22px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: 14px;
}

.ws-code-chat__hint {
  margin: 0;
  font-size: 13.5px;
  color: var(--pa-muted-2);
  line-height: 1.5;
}

.ws-code-chat__msg-user {
  align-self: flex-start;
  max-width: 100%;
  background: var(--pa-accent);
  color: #fff;
  padding: 9px 12px;
  border-radius: 12px 12px 12px 2px;
  font-size: 13px;
  line-height: 1.4;
}

/* Add Image's own attachment, shown inside the user bubble it was sent
   with -- see renderCodeChatMessages()'s own comment. */
.ws-code-chat__msg-user-image {
  display: block;
  width: 100%;
  max-width: 160px;
  border-radius: 8px;
  margin-bottom: 6px;
}

.ws-code-chat__hist-thumb {
  width: 64px;
  height: 64px;
  border-radius: 10px;
  overflow: hidden;
  border: 1px solid var(--pa-border-2);
}

.ws-code-chat__hist-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ws-code-chat__hist-error {
  color: var(--pa-danger);
  font-size: 12px;
}

.ws-code-chat__msg-error {
  color: var(--pa-danger);
  font-size: 13px;
}

/* The whole point of the bigger window (see .ws-code-chat's own comment)
   -- capped so a tall/portrait edit doesn't force the window absurdly
   tall, contained rather than cropped either way. */
.ws-code-chat__preview-img {
  width: 100%;
  max-height: 480px;
  object-fit: contain;
  background: var(--pa-deep-2);
  border-radius: 12px;
  border: 1px solid var(--pa-border-2);
  display: block;
}

/* The one action this whole window exists to lead up to -- flat
   var(--pa-accent) read as no more important than a stub tool pill right
   below it. Same gradient-CTA recipe the rest of the app already uses for
   its own "this is the one to press" buttons (creator.css's .mp-pro-pill,
   settings.css's .st-pro-cta) -- not a new accent, just finally applied
   here too. */
.ws-code-chat__make3d {
  align-self: flex-start;
  background: linear-gradient(120deg, var(--pa-accent-hover) 0%, var(--pa-accent) 46%, var(--pa-accent-active) 100%);
  color: var(--pa-on-accent);
  border: none;
  border-radius: 9px;
  padding: 10px 20px;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 10px 24px -10px var(--pa-accent-tint), inset 0 1px 0 rgba(255, 255, 255, .35);
  transition: transform .12s, box-shadow .12s;
}
.ws-code-chat__make3d:hover:not(:disabled) {
  transform: translateY(-1px);
  box-shadow: 0 14px 30px -10px var(--pa-accent-tint), inset 0 1px 0 rgba(255, 255, 255, .35);
}

.ws-code-chat__make3d:disabled {
  background: var(--pa-deep-2);
  color: var(--pa-muted-2);
  box-shadow: none;
  opacity: .6;
  cursor: default;
}

.ws-code-chat__composer {
  flex: none;
  border-top: 1px solid var(--pa-border-2);
}

.ws-code-chat__input-row {
  display: flex;
  gap: 10px;
  padding: 14px 14px 8px;
}

.ws-code-chat__input {
  flex: 1;
  resize: none;
  min-height: 44px;
  max-height: 120px;
  background: var(--pa-deep-2);
  border: 1px solid var(--pa-border-2);
  border-radius: 10px;
  color: var(--pa-text-hi);
  padding: 11px 13px;
  font-size: 14px;
}

.ws-code-chat__send {
  flex: none;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: var(--pa-accent);
  color: #fff;
  border: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ws-code-chat__send:disabled {
  opacity: .6;
  cursor: default;
}

/* Style/Add Image/Doodle/Asset Pack -- layout-only for now (see
   ensureCodeChatEl()'s own comment): matches Attachments/inspo4.png's
   composer pills, but nothing behind them yet, so they're visibly inert
   rather than pretending to work. */
.ws-code-chat__tools {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 14px 14px;
}

.ws-code-chat__tool {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 12px;
  border-radius: 999px;
  background: var(--pa-deep-2);
  border: 1px solid var(--pa-border-2);
  color: var(--pa-muted-2);
  font-size: 12px;
  font-weight: 600;
  cursor: not-allowed;
  opacity: .7;
}

/* Add Image -- the one real pill among the four (see ensureCodeChatEl()'s
   own comment). Full opacity, a real pointer, and a hover state, unlike
   the still-inert three beside it. */
.ws-code-chat__tool--active {
  cursor: pointer;
  opacity: 1;
  color: var(--pa-text-hi);
  transition: background .14s, border-color .14s;
}

.ws-code-chat__tool--active:hover {
  background: var(--pa-hover);
  border-color: var(--pa-border-3);
}

/* The "you're about to send this" chip -- see renderCodeChatPending(). */
.ws-code-chat__pending {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 14px 0;
  padding: 6px 8px;
  border-radius: 10px;
  background: var(--pa-deep-2);
  border: 1px solid var(--pa-border-2);
}

.ws-code-chat__pending img {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  object-fit: cover;
  flex: none;
}

.ws-code-chat__pending span {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  color: var(--pa-muted-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws-code-chat__pending button {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  border: none;
  background: transparent;
  color: var(--pa-muted-2);
  cursor: pointer;
}

.ws-code-chat__pending button:hover {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

/* Clickable now -- the wedge that used to open rename mode (More's own
   "Rename") doesn't exist any more in the 2026-08-23 redesign, so the
   title itself is the only way in (see modelPreviewHtml()'s own
   data-rename-model comment). */
/* Was centred below the stage, full-size Coolvetica display type; then
   briefly a pill badge floating INSIDE stage-wrap, over the sky/baseplate
   itself. Aydin's own correction the same day, after seeing that version:
   "the name button is meant to be on the grey bit, not the big bit, a lot
   smaller and no background behind it (i mean that rectangle behind it)."
   "The grey bit" is .ws-modal's own padding band (--pa-surface, 26px
   padding -- see .ws-modal's own rule) around stage-wrap, the same band
   .ws-modal__x's close button already sits in -- so this is now a sibling
   of that button in modelPreviewHtml(), not a child of stage-wrap, mirrored
   to its opposite top corner (top:18px;left:18px vs the close button's
   top:18px;right:18px). No pill, no background, no border -- plain small
   text directly on the modal's own surface colour. */
/* The close button (.ws-modal__x) has no z-index of its own -- fine on
   every OTHER .ws-modal, where it's the only positioned child. Here it's
   a sibling of .ws-model-preview__stage-wrap, which is itself a plain
   z-index:auto element containing its OWN internally-z-indexed content
   (the stage/radial/panels go up to z-index:3) -- and .ws-modal__x comes
   BEFORE stage-wrap in the DOM. Per the CSS stacking spec, z-index:auto
   siblings paint in tree order, so stage-wrap (later in the DOM) painted
   ON TOP of the close button regardless of either one's own content --
   Aydin, from a live screenshot: "the x icon is hiding behind the model
   editor." Same fix as the title got above: an explicit z-index lifts it
   out of that auto-ordered stack entirely. */
.ws-model-preview .ws-modal__x {
  /* Matches the title's own 26px on the opposite corner -- see its own
     comment just below. */
  top: 26px;
  right: 26px;
  z-index: 5;
}

/* .ws-model-preview .ws-model-preview__title, not the bare class -- this
   is an <h2>, and .ws-modal h2 (its own rule, above in this file) sets
   30px Coolvetica display type for every OTHER modal's title. That
   descendant selector's specificity (0,1,1) beats a plain class selector
   (0,1,0), so the bare class version of this rule silently lost every
   property it shares with .ws-modal h2 (font-size, font-family,
   font-weight) -- this rendered as the full 30px display title
   regardless of what this rule said. Confirmed live, 2026-08-24, not a
   caching artifact: fetching workspace.css with a cache-busted URL still
   showed the 30px computed style, which only a losing specificity fight
   explains. One extra class ancestor is enough to win. */
.ws-model-preview .ws-model-preview__title {
  position: absolute;
  /* Was 18px -- Aydin: "it needs some padding, its too close to that
     box" (the stage's own top-left corner, which starts just past
     .ws-modal's own 26px padding -- see .ws-modal's own rule). Absolute
     positioning measures from the padding EDGE, not past the padding
     itself, so 18px landed closer to the corner than .ws-modal's own
     26px padding gives everything else in this popup. Matching that
     26px is what makes the title read as sitting IN the frame, not
     crowded into its corner. */
  left: 26px;
  top: 26px;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 12.5px;
  line-height: 1.2;
  color: var(--pa-text-hi);
  cursor: pointer;
  transition: color .14s;
}

/* The pencil -- replaces the old plain accent dot as the rename
   affordance. A dot next to a name reads as a status light (online/
   unsaved/whatever), not "click to rename"; a pencil says exactly what
   clicking does. */
.ws-model-preview__title-edit {
  display: flex;
  flex: none;
  color: var(--pa-muted-2);
  transition: color .14s;
}

.ws-model-preview__title:hover {
  color: var(--radial-accent);
}

.ws-model-preview__title:hover .ws-model-preview__title-edit {
  color: var(--radial-accent);
}

/* Lives in the exact same top-left spot as .ws-model-preview__title above
   (same .ws-modal corner, same z-index) since S.modelRenaming swaps one
   for the other in modelPreviewHtml() -- absolutely positioned too, or
   the input would fall into .ws-modal's normal flow instead of staying a
   small corner control. */
.ws-model-preview__rename {
  position: absolute;
  /* Matches the title's own 26px -- see its own comment -- this swaps in
     for the title while renaming, same corner. top is 4px higher than
     left's own 26px to optically centre the taller input/button row on
     where the title's own text sat, not its box top. */
  left: 26px;
  top: 22px;
  z-index: 5;
  display: flex;
  gap: 6px;
}

.ws-model-preview__rename .ws-input {
  height: 34px;
  width: 180px;
  font-size: 13px;
}

.ws-model-preview__rename-go {
  flex: none;
  width: auto;
  height: 34px;
  margin-top: 0;
  padding: 0 14px;
  font-size: 12.5px;
}

/* .ws-model-preview__hint/__pillrow/__pill used to live here -- removed
   2026-08-24, never rebuilt; Idea Forge/Roblox Avatar/Export's old
   stage-tools pill row is gone too as of the 2026-10-31 redesign (see
   bottomBarHtml()/customizeDrawerHtml() in workspace.js for where each of
   those three actually live now). */

/* Confirm delete */

.ws-confirm {
  width: 100%;
  max-width: 400px;
  padding: 22px;
  border: 1px solid var(--pa-border);
  border-radius: 18px;
  background: var(--pa-surface);
  box-shadow: 0 40px 74px -34px var(--pa-shadow);
  animation: pa-gate .2s var(--ease-out) both;
}

.ws-confirm__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 24px;
  line-height: 1.1;
  color: var(--pa-text-2);
}

.ws-confirm p {
  margin: 9px 0 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--pa-muted-2);
}

.ws-confirm__row {
  margin-top: 20px;
  display: flex;
  gap: 9px;
}

.ws-confirm__row button {
  flex: 1;
  height: 40px;
  border-radius: 11px;
  font-family: var(--font-body);
  font-size: 13.5px;
  cursor: pointer;
  transition: background .16s, color .16s;
}

.ws-confirm__keep {
  border: 1px solid var(--pa-border);
  background: transparent;
  color: var(--pa-dim);
}

.ws-confirm__keep:hover {
  background: var(--pa-hover-2);
  color: var(--pa-text-hi);
}

.ws-confirm__go {
  border: 0;
  background: var(--pa-danger);
  color: var(--pa-on-danger);
  font-weight: 600;
}

.ws-confirm__go:hover {
  background: var(--pa-danger-hover);
}

/* ── account page ─────────────────────────────────────────────────────────── */

.ws-account {
  position: relative;
  width: 100%;
  max-width: 940px;
  height: min(560px, 100%);
  display: flex;
  border: 1px solid var(--pa-border);
  border-radius: 20px;
  overflow: hidden;
  background: var(--pa-surface);
  box-shadow: 0 46px 80px -36px var(--pa-shadow);
  animation: pa-gate .24s var(--ease-out) both;
}

.ws-account__side {
  flex: none;
  width: 268px;
  display: flex;
  flex-direction: column;
  padding: 30px 20px 20px;
  background: var(--pa-bg-2);
  border-right: 1px solid var(--pa-line);
}

.ws-account__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 30px;
  line-height: 1;
  color: var(--pa-text-2);
}

.ws-account__sub {
  margin-top: 7px;
  font-size: 13.5px;
  color: var(--pa-muted-3);
}

.ws-account__tabs {
  margin-top: 24px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ws-account__tab {
  display: flex;
  align-items: center;
  gap: 11px;
  height: 40px;
  padding: 0 12px;
  border: 0;
  border-radius: 11px;
  background: transparent;
  color: var(--pa-muted);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background .14s, color .14s;
}

.ws-account__tab:hover,
.ws-account__tab[aria-pressed="true"] {
  background: var(--pa-hover);
  color: var(--pa-text-hi);
}

.ws-account__foot {
  margin-top: auto;
  font-size: 12px;
  color: var(--pa-muted-5);
}

.ws-account__pane {
  flex: 1;
  min-width: 0;
  padding: 28px 30px;
  overflow-y: auto;
}

.ws-account__pane-head {
  display: flex;
  align-items: flex-start;
}

.ws-account__pane-title {
  flex: 1;
  min-width: 0;
  font-size: 19px;
  font-weight: 600;
  color: var(--pa-text-2);
}

.ws-account__rule {
  height: 1px;
  background: var(--pa-line);
}

.ws-account__row {
  display: grid;
  grid-template-columns: 200px 1fr auto;
  align-items: center;
  gap: 16px;
  padding: 22px 0;
}

.ws-account__row--top {
  align-items: start;
}

.ws-account__row--pair {
  grid-template-columns: 200px 1fr;
}

.ws-account__key {
  font-size: 14px;
  color: var(--pa-muted);
}

.ws-account__who {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.ws-account__face {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  border: 1px solid var(--pa-border-2);
  border-radius: 999px;
  background: var(--pa-hover-2);
  overflow: hidden;
  font-size: 13px;
  font-weight: 600;
  color: var(--pa-dim-4);
}

.ws-account__face img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.ws-link {
  padding: 0;
  border: 0;
  background: transparent;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--pa-accent);
  white-space: nowrap;
  cursor: pointer;
}

.ws-link:hover {
  color: var(--pa-accent-hover);
}

.ws-link--muted {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--pa-muted);
}

.ws-link--muted:hover {
  color: var(--pa-text-hi);
}

.ws-link--danger {
  justify-self: start;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--pa-danger-strong);
}

.ws-link--danger:hover {
  color: #ef8375;
}

.ws-account__device {
  display: flex;
  gap: 13px;
  min-width: 0;
}

.ws-account__screen {
  flex: none;
  width: 34px;
  height: 26px;
  margin-top: 2px;
  border: 1px solid var(--pa-border-2);
  border-radius: 5px;
  background: var(--pa-deep);
}

.ws-account__device-text {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
  font-size: 13.5px;
  color: var(--pa-muted-2);
}

.ws-account__device-name {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 15px;
  font-weight: 600;
  color: var(--pa-text);
}

.ws-account__badge {
  padding: 2px 8px;
  border: 1px solid var(--pa-border);
  border-radius: 999px;
  background: var(--pa-hover);
  font-size: 11px;
  font-weight: 400;
  color: var(--pa-muted);
  white-space: nowrap;
}

/* ── store ────────────────────────────────────────────────────────────────── */

.ws-store {
  position: fixed;
  inset: 0;
  z-index: 82;
  display: flex;
  flex-direction: column;
  background: var(--pa-scrim);
  backdrop-filter: blur(3px);
  animation: pa-in .2s ease both;
}

.ws-store__mid {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 14px 28px;
}

.ws-store__inner {
  width: 100%;
  /* Wide enough for three cards side by side (Free | Pro Monthly | Pro
     Annual) without any of them going narrower than they were at two. */
  max-width: 1180px;
  animation: pa-gate .22s var(--ease-out) both;
}

.ws-store__plans {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 14px;
  align-items: start;
}

.ws-plan {
  border: 1px solid var(--pa-border);
  border-radius: 16px;
  background: var(--pa-surface-2);
  overflow: hidden;
}

.ws-plan--pro {
  border-color: var(--pa-accent);
}

.ws-plan__head {
  padding: 12px 16px;
  border-bottom: 1px solid var(--pa-line);
}

.ws-plan__row {
  display: flex;
  align-items: center;
  gap: 9px;
}

.ws-plan__name {
  flex: 1;
  min-width: 0;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--pa-dim);
}

.ws-plan__tag {
  flex: none;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  white-space: nowrap;
}

.ws-plan__tag--current {
  border: 1px solid var(--pa-accent);
  color: var(--pa-accent);
}

.ws-plan__tag--popular {
  background: var(--pa-accent);
  color: var(--pa-on-accent);
}

.ws-plan__price {
  margin-top: 10px;
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pa-text-1);
}

.ws-plan__price b {
  font-weight: 400;
  font-size: 30px;
  line-height: 1;
}

.ws-plan__price i {
  font-style: normal;
  font-size: 14px;
  color: var(--pa-muted-2);
  font-family: var(--font-body);
}

/* Pro Monthly's real "was" price -- see storeHtml()'s own comment. A
   genuine strikethrough (unlike .ws-pack__price s below, which neutralizes
   <s> because it's only ever used there to style pence smaller, not to
   show an old price). Markup puts it first, ahead of the real price. */
.ws-plan__price-old {
  font-size: 16px;
  font-weight: 400;
  color: var(--pa-muted-3);
  text-decoration: line-through;
}

.ws-plan__note {
  margin-top: 9px;
  font-size: 13px;
  color: var(--pa-muted-3);
}

/* A one-line highlight under the price -- Pro's daily-credit comparison,
   Annual's per-month equivalent and saving. Its own box rather than another
   list line, so the card's one selling point doesn't get lost among the
   feature list below it. */
.ws-plan__callout {
  margin-top: 10px;
  padding: 7px 10px;
  border-radius: 9px;
  background: var(--pa-accent-tint);
  color: var(--pa-accent);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.4;
}

.ws-plan__list {
  padding: 12px 16px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.ws-plan__lead {
  font-size: 13px;
  font-weight: 600;
  color: var(--pa-dim);
}

.ws-plan__list span {
  display: flex;
  gap: 10px;
  font-size: 13.5px;
  line-height: 1.45;
  color: var(--pa-text);
}

.ws-plan__list .is-off {
  color: var(--pa-muted-2);
}

.ws-plan__divider {
  display: flex;
  align-items: center;
  gap: 11px;
  margin: 4px 0 1px;
  font-size: 11.5px;
  color: var(--pa-muted-3);
  white-space: nowrap;
}

.ws-plan__divider::before,
.ws-plan__divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--pa-line);
}

.ws-plan__buy {
  width: 100%;
  height: 42px;
  margin-top: 14px;
  border: 0;
  border-radius: 12px;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 14px;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws-plan__buy:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

.ws-packs {
  margin-top: 10px;
  padding: 11px 15px;
  border: 1px solid var(--pa-border);
  border-radius: 16px;
  background: var(--pa-bg-2);
}

.ws-packs__label {
  margin-bottom: 10px;
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
}

.ws-packs__row {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.ws-pack {
  flex: 1;
  min-width: 0;
  padding: 15px;
  border: 1px solid var(--pa-border);
  border-radius: 14px;
  background: var(--pa-surface-2);
}

.ws-pack__head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ws-pack__name {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--pa-dim);
}

.ws-pack__tag {
  flex: none;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--pa-accent);
  font-size: 10.5px;
  font-weight: 700;
  color: var(--pa-on-accent);
  white-space: nowrap;
}

.ws-pack__price {
  margin-top: 11px;
  display: flex;
  align-items: baseline;
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--pa-text-1);
}

.ws-pack__price b { font-weight: 400; font-size: 28px; line-height: 1; }
.ws-pack__price s { text-decoration: none; font-size: 19px; }

.ws-pack__note {
  margin-top: 8px;
  font-size: 12.5px;
  color: var(--pa-muted-2);
}

.ws-pack__buy {
  width: 100%;
  height: 38px;
  margin-top: 13px;
  border: 1px solid var(--pa-border-2);
  border-radius: 11px;
  background: transparent;
  color: var(--pa-text);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  cursor: pointer;
  transition: background .16s, color .16s;
}

.ws-pack__buy:hover {
  background: var(--pa-hover-2);
  color: var(--pa-text-hi);
}

/* Buy confirmation */

.ws-buy {
  width: 100%;
  max-width: 430px;
  padding: 26px 26px 22px;
  border: 1px solid var(--pa-border);
  border-radius: 18px;
  background: var(--pa-surface);
  box-shadow: 0 44px 80px -34px var(--pa-shadow);
  text-align: center;
  animation: pa-gate .2s var(--ease-out) both;
}

.ws-buy__mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  margin: 0 auto;
  border: 1px solid var(--pa-accent);
  border-radius: 999px;
  background: var(--pa-accent-tint);
}

.ws-buy__title {
  margin-top: 15px;
  font-size: 19px;
  font-weight: 600;
  color: var(--pa-text-2);
}

.ws-buy p {
  margin: 9px 0 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--pa-muted-2);
}

.ws-buy__who {
  margin-top: 17px;
  padding: 14px;
  border: 1px solid var(--pa-line);
  border-radius: 14px;
  background: var(--pa-surface-2);
}

.ws-buy__who-label {
  font-size: 12.5px;
  color: var(--pa-muted-3);
}

.ws-buy__who-name {
  margin-top: 4px;
  font-size: 16px;
  font-weight: 600;
  color: var(--pa-text);
}

.ws-buy__row {
  margin-top: 18px;
  display: flex;
  gap: 10px;
}

.ws-buy__row button {
  flex: 1;
  height: 42px;
  border-radius: 12px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13.5px;
  cursor: pointer;
  transition: background .16s;
}

.ws-buy__cancel {
  border: 1px solid var(--pa-border-2);
  background: transparent;
  color: var(--pa-text);
}

.ws-buy__cancel:hover {
  background: var(--pa-hover-2);
}

.ws-buy__go {
  border: 0;
  background: var(--pa-accent);
  color: var(--pa-on-accent);
}

.ws-buy__go:hover {
  background: var(--pa-accent-hover);
}

/* ── narrow screens ───────────────────────────────────────────────────────── */

/* The design's own breakpoints, kept as-is. */
@media (max-width: 1180px) {
  .ws__fan { display: none !important; }
}

@media (max-width: 900px) {
  .ws__rail { display: none !important; }
  .ws__panel { display: none !important; }
  .ws__side { width: 180px; }
  .ws__cards { grid-template-columns: 1fr; }
  .ws-store__plans { grid-template-columns: 1fr; }
  .ws-account { flex-direction: column; height: 100%; }
  .ws-account__side { width: 100%; padding: 20px; }
  .ws-account__tabs { flex-direction: row; margin-top: 14px; }
  .ws-account__row,
  .ws-account__row--pair { grid-template-columns: 1fr; gap: 10px; }
}

@media (max-width: 620px) {
  .ws__side { display: none; }
  .ws__hero-logo { width: 104px; height: 104px; }
  .ws__hero-name { font-size: 46px; }
  .ws__actions { gap: 6px; }
  .ws__chip { display: none; }
}

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

/* Delete control on a stored conversation row. Quiet until hovered: it sits
   next to every row in the list, and a bright × on each one turns a history
   panel into a minefield. */
.ws__chatdel {
  flex: none;
  padding: 0 6px;

  font-size: 17px;
  line-height: 1;
  color: var(--pa-muted-4);

  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  opacity: 0;
}

.ws__chatrow:hover .ws__chatdel {
  opacity: 1;
}

.ws__chatdel:hover {
  color: var(--pa-text, currentColor);
  background: var(--pa-hover-b);
}

/* Keyboard users never hover, so it must appear on focus too. */
.ws__chatdel:focus-visible {
  opacity: 1;
  outline: 2px solid var(--pa-accent);
  outline-offset: 1px;
}

/* ── game map, real ───────────────────────────────────────────────────────
   Every part Payama built, plotted from its actual coordinates. Roblox is
   y-up, so a top-down plan uses x across and z down; height does not appear.

   Replaced four hardcoded "zones" with invented part counts. A number that
   looks real and is not is worse than an empty panel. */
.ws__mapitem {
  position: absolute;
  border-radius: 2px;
  /* A faint outline on every part, not just on hover -- the map read as
     flat rectangles without one, since fill colours often sit close to
     the plot background. */
  box-shadow: 0 0 0 1px rgb(0 0 0 / 22%);
  outline: 1px solid rgb(255 255 255 / 12%);
  outline-offset: -1px;
  transition: outline-color .12s, outline-width .12s;
  cursor: default;
}

.ws__mapitem:hover {
  outline: 2px solid var(--pa-accent);
  outline-offset: 0;
  /* No z-index bump here on purpose. A huge part (like the baseplate)
     covers most of the map, so hovering it would jump it above every
     smaller part drawn after it -- the map would go solid green and
     hide everything. Smaller parts already paint on top in normal DOM
     order; the outline is enough to show what's hovered. */
}

/* The spawn is the one part worth finding at a glance. */
.ws__mapitem--spawn {
  box-shadow: 0 0 0 2px var(--pa-accent);
  z-index: 1;
}

/* Custom tooltip -- name and real dimensions -- replacing the browser's
   default title attribute, which cannot be styled and lags behind other UI
   in dark mode. */
.ws__mapitem[data-tooltip] {
  position: absolute;
}

.ws__mapitem[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  left: 50%;
  bottom: calc(100% + 7px);
  transform: translateX(-50%) translateY(4px);
  padding: 5px 9px;
  border-radius: 7px;
  background: var(--pa-surface-2);
  border: 1px solid var(--pa-border-2);
  box-shadow: 0 8px 20px -8px var(--pa-shadow);
  color: var(--pa-text);
  font-size: 11.5px;
  font-family: var(--font-mono);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity .12s, transform .12s;
  z-index: 4;
}

.ws__mapitem[data-tooltip]:hover::after {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%) translateY(0);
}

/* Scripts have no position, so they are listed rather than plotted --
   grouped by the service they run in, which is the only thing about a
   script's location that matters. */
.ws__mapscripts {
  display: flex;
  flex-direction: column;
  gap: 4px;

  padding: 10px 14px;
  border-top: 1px solid var(--pa-border-2);
}

.ws__maprow {
  display: flex;
  gap: 10px;
  font-size: 12px;
  line-height: 1.5;
}

.ws__maprow-where {
  flex: none;
  min-width: 132px;
  font-weight: 600;
  color: var(--pa-dim-4);
}

.ws__maprow-what {
  color: var(--pa-muted-3c);
}

/* ── unread ───────────────────────────────────────────────────────────────
   A reply landed in this project while you were looking at another one.
   Cleared by opening it. */
.ws__unread {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;

  width: 17px;
  height: 17px;
  margin-left: auto;

  color: #fff;
  background: var(--pa-accent);
  border-radius: 999px;
}

/* ── reply toast ──────────────────────────────────────────────────────────
   Slides in when a reply arrives for a project you have navigated away from.
   Dismissing it deliberately does NOT clear the sidebar badge -- the badge
   exists so an ignored toast is not a lost reply. */
.ws-toast {
  position: fixed;
  right: 22px;
  bottom: 22px;
  z-index: 70;

  display: flex;
  align-items: center;
  gap: 12px;

  max-width: min(360px, calc(100vw - 44px));
  padding: 12px 14px;

  background: var(--pa-surface-2);
  border: 1px solid var(--pa-border);
  border-radius: 12px;
  box-shadow: 0 16px 36px rgb(0 0 0 / 32%);

  animation: pa-toast-in .22s ease-out;
}

@keyframes pa-toast-in {
  from { transform: translateX(18px); opacity: 0; }
  to   { transform: translateX(0);    opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .ws-toast { animation: none; }
}

.ws-toast__body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 2px;

  font-size: 13px;
  color: var(--pa-muted-3c);
}

.ws-toast__open {
  flex: none;
  padding: 6px 11px;

  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--pa-on-accent);

  background: var(--pa-accent);
  border: 0;
  border-radius: 8px;
  cursor: pointer;
}

.ws-toast__x {
  flex: none;
  padding: 0 4px;

  font-size: 18px;
  line-height: 1;
  color: var(--pa-muted-4);

  background: none;
  border: 0;
  cursor: pointer;
}

/* ── credits, in the composer ─────────────────────────────────────────────
   Beside Send, so the cost of a build is visible at the moment of spending
   it rather than buried in a settings page. Clicking opens the store. */
.ws__credits {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;

  height: 32px;
  padding: 0 12px;

  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--pa-dim);

  background: var(--pa-surface-2);
  border: 1px solid var(--pa-border);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color .16s, color .16s;
}

.ws__credits:hover {
  color: var(--pa-text, currentColor);
  background: var(--pa-hover-b);
}

/* Empty is the one state worth colouring: it is the reason the composer has
   stopped accepting anything. */
.ws__credits--empty {
  color: var(--pa-error);
  border-color: var(--pa-error);
}

/* ── prompt library entry ─────────────────────────────────────────────────
   One clear way in, the same width as the composer above it and much
   shorter -- not a pill competing with Send for attention in the foot row.
   Payama blue with white text in both themes: --pa-accent does not change
   between light and dark, so this is a deliberate constant fill rather than
   the usual --pa-on-accent token (which is closer to black and reads worse
   here at this size). */
.ws__prompts-cta {
  width: 100%;
  max-width: 640px;
  margin-top: 10px;

  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;

  height: 30px;

  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: #fff;

  background: var(--pa-accent);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  transition: background .16s, transform .14s;
}

.ws__prompts-cta:hover {
  background: var(--pa-accent-hover);
  transform: translateY(-1px);
}

.ws__prompts-cta:active {
  transform: none;
  background: var(--pa-accent-active);
}

/* The dock sits in a narrower column than the hero composer. */
.ws__prompts-cta--dock {
  max-width: 700px;
  margin: 8px auto 0;
}

/* ── response mode ─────────────────────────────────────────────────────────
   Plan / Build / Chat. Replaces the isSmallTalk() guess with a choice --
   see lib/routes/message.js readMode(). Sits beside the text field, at the
   top of both composers. */
.ws__mode-wrap {
  position: relative;
  flex: none;
}

.ws__mode {
  display: inline-flex;
  align-items: center;
  gap: 6px;

  height: 28px;
  padding: 0 10px;

  font: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--pa-accent);

  background: var(--pa-accent-tint);
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  transition: background .16s;
}

.ws__mode:hover,
.ws__mode.is-on {
  background: var(--pa-hover-b);
}

.ws__mode svg { flex: none; }

/* Opens downward, right under the chip -- it now sits at the top of the
   composer, not the foot row, so there is room below it. */
.ws-pop--mode {
  top: calc(100% + 8px);
  right: auto;
  left: 0;
  bottom: auto;
  width: 260px;
  padding: 6px;
}

/* #modePop2 belongs to the docked composer, which sits near the bottom of
   the screen -- opening downward like the top composer's does pushed the
   last option past the edge of the viewport. Dropup instead: same chip,
   opens above it where the room actually is. */
#modePop2 {
  top: auto;
  bottom: calc(100% + 8px);
}

.ws__mode-opt {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: background .14s;
}

.ws__mode-opt:hover { background: var(--pa-hover); }
.ws__mode-opt.is-on { background: var(--pa-hover); }

.ws__mode-opt-icon {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-top: 1px;
  border-radius: 7px;
  background: var(--pa-accent-tint);
  color: var(--pa-accent);
}

.ws__mode-opt-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.ws__mode-opt-text b {
  font-size: 13px;
  font-weight: 600;
  color: var(--pa-text-hi);
}

.ws__mode-opt-text i {
  font-style: normal;
  font-size: 11.5px;
  color: var(--pa-muted-3);
  white-space: normal;
}

/* Separates the three mutually-exclusive mode options above from Auto's
   own row below -- a real boolean toggle, not a fourth thing to pick
   between, so it reads as its own group rather than a fourth option in
   the same list. */
.ws__mode-divider {
  height: 1px;
  margin: 4px 6px;
  background: var(--pa-border-2);
}

/* Auto's own row -- see renderModeSelector()'s own comment on why it
   moved in here. Reuses .ws__mode-opt's exact layout/hover recipe
   (icon + text), just centred (a single-line label needs no top-align
   the way a two-line mode option's icon does) and with a real on/off
   switch on the right instead of relying on the same background-tint
   .is-on the mode options above use for "which one is picked" -- a
   toggle needs to read as a toggle at a glance, not blend into that. */
.ws__mode-opt--auto {
  align-items: center;
}

.ws__mode-opt-switch {
  flex: none;
  margin-left: auto;
  width: 30px;
  height: 18px;
  border-radius: 999px;
  background: var(--pa-border-3);
  position: relative;
  transition: background .16s;
}

.ws__mode-opt-switch::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #fff;
  transition: transform .16s;
}

.ws__mode-opt--auto.is-on .ws__mode-opt-switch {
  background: var(--pa-accent);
}

.ws__mode-opt--auto.is-on .ws__mode-opt-switch::after {
  transform: translateX(12px);
}

/* ── attach / generate-3D menu ───────────────────────────────────────────
   The plain-attach and Generate-3D buttons used to be two separate icon
   buttons here -- morphed into one trigger 2026-08-24 (Aydin: "morph the
   3d generator upload icon and attachments together"), opening a small
   menu instead. Same trigger/popover shape as .ws__mode-wrap/.ws-pop--mode
   just above, own name since it's a different button/popover pair; menu
   items themselves reuse .ws__mode-opt as-is (see renderAttachMenu() in
   workspace.js), no new item styling needed. */
.ws__attach-wrap {
  position: relative;
  flex: none;
}

/* Upward and left-aligned -- upward for the same reason .ws-pop--model
   is (a foot-row element, low enough in the layout that opening downward
   risks the viewport edge); left-aligned because this trigger sits at the
   LEFT of the foot row, unlike the model chip/credits/send on the right
   that .ws-pop--model itself opens against. */
.ws-pop--attach {
  top: auto;
  left: 0;
  right: auto;
  bottom: calc(100% + 8px);
  width: 240px;
  padding: 6px;
}

/* ── model picker ──────────────────────────────────────────────────────────
   Real, not preview -- see resolveProvider() in lib/routes/message.js and
   renderModelSelector() in workspace.js. Sits in the foot row, between the
   upload button and the credits pill. */
.ws__model-wrap {
  position: relative;
  flex: none;
}

.ws__modelchip {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;

  height: 32px;
  padding: 0 12px;

  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--pa-dim);
  white-space: nowrap;

  background: transparent;
  border: 1px solid var(--pa-border);
  border-radius: 999px;
  cursor: pointer;
  transition: background .16s, color .16s, border-color .16s;
}

.ws__modelchip:hover,
.ws__modelchip.is-on {
  background: var(--pa-hover-b);
  color: var(--pa-text-hi);
  border-color: var(--pa-border-3);
}

/* One available model (true today: just Payama Cloud) -- shows what's
   active without pretending there's a choice to open. */
.ws__modelchip--static {
  cursor: default;
}

.ws__modelchip--static:hover {
  background: transparent;
  color: var(--pa-dim);
  border-color: var(--pa-border);
}

.ws__modelchip svg { flex: none; }

/* ── auto mode ────────────────────────────────────────────────────────────
   Used to be its own standalone pill button here, same size/shape as
   .ws__modelchip beside it. Moved 2026-08-24 -- Aydin: "put the auto
   toggle inside the dropdown menu for the modes as a toggle." Its own
   markup/styling now lives with the mode selector instead (see
   .ws__mode-opt--auto and .ws__mode-opt-switch, near .ws__mode-opt
   above) -- nothing left to style under a dedicated .ws__auto class. */

/* Opens upward, right-aligned -- it's a foot-row element like Send and
   Credits, which sit low enough in the layout that a downward menu risks
   the viewport edge. */
.ws-pop--model {
  top: auto;
  left: auto;
  right: 0;
  bottom: calc(100% + 8px);
  width: 260px;
  padding: 6px;
}

.ws__model-opt-label {
  padding: 7px 10px 6px;
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--pa-muted-3);
}

.ws__model-opt {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 10px;
  background: transparent;
  text-align: left;
  cursor: pointer;
  transition: background .14s;
}

.ws__model-opt:hover { background: var(--pa-hover); }
.ws__model-opt.is-on { background: var(--pa-accent-tint); }

/* Brand mark (Qwen/DeepSeek) or the generic fallback glyph -- see
   providerIcon() in workspace.js. Dims to match the name/meta text until
   the row is the active model. */
.ws__model-opt-icon {
  flex: none;
  display: flex;
  color: var(--pa-muted-3);
}

.ws__model-opt.is-on .ws__model-opt-icon { color: var(--pa-accent); }

.ws__model-opt-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.ws__model-opt-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--pa-soft);
}

.ws__model-opt.is-on .ws__model-opt-name {
  color: var(--pa-accent);
}

.ws__model-opt-meta {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--pa-muted-3);
}

/* ── settings panel sections ──────────────────────────────────────────────
   Roblox account and "build from the website" moved here from pages of my
   own making, which duplicated navigation the design already had. Settings
   is reached the way the design intends: profile icon, or the rail cog. */
.ws__set-block {
  display: flex;
  flex-direction: column;
  gap: 7px;

  padding: 13px 15px;
  margin-top: 10px;

  font-size: 12.5px;
  line-height: 1.5;
  color: var(--pa-muted-3c);

  border: 1px solid var(--pa-border-2);
  border-radius: 12px;
}

.ws__set-block strong {
  font-size: 13px;
  color: var(--pa-text, currentColor);
}

.ws__set-block .ws-btn--primary {
  align-self: flex-start;
  margin-top: 3px;
}

.ws__set-row {
  display: flex;
  align-items: center;
  gap: 9px;
}

.ws__set-avatar {
  width: 30px;
  height: 30px;
  border-radius: 999px;
  object-fit: cover;
}

.ws__set-help,
.ws__set-warn {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--pa-dim-4);
}

.ws__set-warn {
  color: var(--pa-error);
}

.ws__set-block .ws-input {
  width: 100%;
}

/* Account settings' own Appearance control -- see
   appearanceSettingsHtml()'s own comment. A row of three, not the header
   toggle's own vertical .ws-pop__item list (that's sized for a narrow
   popover; this sits inline in a wider settings panel). */
.ws-account__theme-row {
  display: flex;
  gap: 8px;
  margin-top: 2px;
}

.ws-account__theme-btn {
  flex: 1;
  height: 36px;
  border-radius: 9px;
  background: var(--pa-deep-2);
  border: 1px solid var(--pa-border-2);
  color: var(--pa-text-hi);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background .14s, border-color .14s;
}

.ws-account__theme-btn:hover {
  background: var(--pa-hover);
}

.ws-account__theme-btn--on {
  background: var(--pa-accent);
  border-color: var(--pa-accent);
  color: #fff;
}

/* ── the Build card ───────────────────────────────────────────────────────
   Shows the current PROJECT, not the last prompt.

   A forty-word request rendered as a title is unreadable and stretches the
   card until the dashboard layout breaks. The name is clamped to one line so
   a long project name cannot do the same. */
.ws__build {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-top: 7px;
  min-width: 0;
}

.ws__build-name {
  overflow: hidden;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.25;
  color: var(--pa-text);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ws__build-name--empty {
  font-weight: 500;
  color: var(--pa-muted-3);
}

.ws__build-meta {
  overflow: hidden;
  font-size: 11.5px;
  color: var(--pa-muted-3);
  text-overflow: ellipsis;
  white-space: nowrap;
}
